Skip to content

[clang][bytecode] Implement bitcasts to composite types #114776

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions clang/lib/AST/ByteCode/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6467,10 +6467,8 @@ bool Compiler<Emitter>::emitBuiltinBitCast(const CastExpr *E) {
if (!this->visit(SubExpr))
return false;

if (!ToT || ToT == PT_Ptr) {
// Conversion to an array or record type.
assert(false && "Implement bitcast to pointers.");
}
if (!ToT || ToT == PT_Ptr)
return this->emitBitCastPtr(E);
assert(ToT);

const llvm::fltSemantics *TargetSemantics = nullptr;
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/AST/ByteCode/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -3080,6 +3080,16 @@ inline bool BitCast(InterpState &S, CodePtr OpPC, bool TargetIsUCharOrByte,
return true;
}

inline bool BitCastPtr(InterpState &S, CodePtr OpPC) {
const Pointer &FromPtr = S.Stk.pop<Pointer>();
Pointer &ToPtr = S.Stk.peek<Pointer>();

if (!DoBitCastPtr(S, OpPC, FromPtr, ToPtr))
return false;

return true;
}

//===----------------------------------------------------------------------===//
// Read opcode arguments
//===----------------------------------------------------------------------===//
Expand Down
55 changes: 49 additions & 6 deletions clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,7 @@ using DataFunc =
} \
} while (0)

/// Float is a special case that sometimes needs the floating point semantics
/// to be available.
#define BITCAST_TYPE_SWITCH_WITH_FLOAT(Expr, B) \
#define BITCAST_TYPE_SWITCH_FIXED_SIZE(Expr, B) \
do { \
switch (Expr) { \
TYPE_SWITCH_CASE(PT_Sint8, B) \
Expand All @@ -61,10 +59,7 @@ using DataFunc =
TYPE_SWITCH_CASE(PT_Uint32, B) \
TYPE_SWITCH_CASE(PT_Sint64, B) \
TYPE_SWITCH_CASE(PT_Uint64, B) \
TYPE_SWITCH_CASE(PT_IntAP, B) \
TYPE_SWITCH_CASE(PT_IntAPS, B) \
TYPE_SWITCH_CASE(PT_Bool, B) \
TYPE_SWITCH_CASE(PT_Float, B) \
default: \
llvm_unreachable("Unhandled bitcast type"); \
} \
Expand Down Expand Up @@ -92,6 +87,12 @@ struct BitcastBuffer {

const std::byte *data() const { return Data.data(); }

std::byte *getBytes(unsigned BitOffset) const {
if (BitOffset % 8 == 0)
return const_cast<std::byte *>(data() + (BitOffset / 8));
assert(false && "hmm, how to best handle this?");
}

bool allInitialized() const {
// FIXME: Implement.
return true;
Expand Down Expand Up @@ -377,3 +378,45 @@ bool clang::interp::DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,

return Success;
}

bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,
const Pointer &FromPtr, Pointer &ToPtr) {
assert(FromPtr.isLive());
assert(FromPtr.isBlockPointer());
assert(ToPtr.isBlockPointer());

QualType FromType = FromPtr.getType();
QualType ToType = ToPtr.getType();

if (!CheckBitcastType(S, OpPC, FromType, /*IsToType=*/false))
return false;

if (!CheckBitcastType(S, OpPC, ToType, /*IsToType=*/true))
return false;

BitcastBuffer Buffer;
readPointerToBuffer(S.getContext(), FromPtr, Buffer,
/*ReturnOnUninit=*/false);

// Now read the values out of the buffer again and into ToPtr.
size_t BitOffset = 0;
bool Success = enumeratePointerFields(
ToPtr, S.getContext(),
[&](const Pointer &P, PrimType T, size_t _) -> bool {
BITCAST_TYPE_SWITCH_FIXED_SIZE(T, {
T &Val = P.deref<T>();

std::byte *M = Buffer.getBytes(BitOffset);

if (llvm::sys::IsBigEndianHost)
swapBytes(M, T::bitWidth() / 8);

Val = T::bitcastFromMemory(M, T::bitWidth());
P.initialize();
BitOffset += T::bitWidth();
});
return true;
});

return Success;
}
2 changes: 2 additions & 0 deletions clang/lib/AST/ByteCode/InterpBuiltinBitCast.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ class CodePtr;

bool DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
std::byte *Buff, size_t BuffSize, bool &HasIndeterminateBits);
bool DoBitCastPtr(InterpState &S, CodePtr OpPC, const Pointer &FromPtr,
Pointer &ToPtr);

} // namespace interp
} // namespace clang
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/ByteCode/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -847,3 +847,5 @@ def BitCast : Opcode {
let Args = [ArgBool, ArgUint32, ArgFltSemantics];
let HasGroup = 1;
}

def BitCastPtr : Opcode;
42 changes: 21 additions & 21 deletions clang/test/AST/ByteCode/builtin-bit-cast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ struct int_splicer {

constexpr int_splicer splice(0x0C05FEFE, 0xCAFEBABE);

#if 0
#if 1
static_assert(bit_cast<unsigned long long>(splice) == (LITTLE_END
? 0xCAFEBABE0C05FEFE
: 0x0C05FEFECAFEBABE));
Expand All @@ -297,8 +297,8 @@ static_assert(bit_cast<int_splicer>(0xCAFEBABE0C05FEFE).x == (LITTLE_END
? 0x0C05FEFE
: 0xCAFEBABE));

static_assert(round_trip<unsigned long long>(splice));
static_assert(round_trip<long long>(splice));
static_assert(check_round_trip<unsigned long long>(splice));
static_assert(check_round_trip<long long>(splice));
#endif


Expand Down Expand Up @@ -340,13 +340,12 @@ void test_record() {
? 0xCAFEBABE0C05FEFE
: 0x0C05FEFECAFEBABE));

/// FIXME: Bit casts to composite types.
// static_assert(bit_cast<int_splicer>(0xCAFEBABE0C05FEFE).x == (LITTLE_END
// ? 0x0C05FEFE
// : 0xCAFEBABE));
static_assert(bit_cast<int_splicer>(0xCAFEBABE0C05FEFE).x == (LITTLE_END
? 0x0C05FEFE
: 0xCAFEBABE));

// static_assert(check_round_trip<unsigned long long>(splice));
// static_assert(check_round_trip<long long>(splice));
static_assert(check_round_trip<unsigned long long>(splice));
static_assert(check_round_trip<long long>(splice));

struct base2 {
};
Expand All @@ -368,13 +367,14 @@ void test_record() {
z == other.z && doublez == other.doublez;
}
};
// constexpr bases b = {{1, 2}, {}, {3}, 4};
// constexpr tuple4 t4 = bit_cast<tuple4>(b);
// static_assert(t4 == tuple4{1, 2, 3, 4});
// static_assert(round_trip<tuple4>(b));

// constexpr auto b2 = bit_cast<bases>(t4);
// static_assert(t4 == b2);
constexpr bases b = {{1, 2}, {}, {3}, 4};
constexpr tuple4 t4 = bit_cast<tuple4>(b);
static_assert(t4 == tuple4{1, 2, 3, 4});
static_assert(check_round_trip<tuple4>(b));

/// FIXME: We need to initialize the base pointers in the pointer we're bitcasting to.
// constexpr auto b2 = bit_cast<bases>(t4);
// static_assert(t4 == b2);
}

void test_partially_initialized() {
Expand Down Expand Up @@ -411,16 +411,16 @@ void bad_types() {
};
static_assert(__builtin_bit_cast(int, X{0}) == 0); // both-error {{not an integral constant expression}} \
// both-note {{bit_cast from a union type is not allowed in a constant expression}}
#if 0
#if 1

struct G {
int g;
};
// expected-error@+2 {{constexpr variable 'g' must be initialized by a constant expression}}
// expected-note@+1 {{bit_cast from a union type is not allowed in a constant expression}}
// both-error@+2 {{constexpr variable 'g' must be initialized by a constant expression}}
// both-note@+1 {{bit_cast from a union type is not allowed in a constant expression}}
constexpr G g = __builtin_bit_cast(G, X{0});
// expected-error@+2 {{constexpr variable 'x' must be initialized by a constant expression}}
// expected-note@+1 {{bit_cast to a union type is not allowed in a constant expression}}
// both-error@+2 {{constexpr variable 'x' must be initialized by a constant expression}}
// both-note@+1 {{bit_cast to a union type is not allowed in a constant expression}}
constexpr X x = __builtin_bit_cast(X, G{0});
#endif
struct has_pointer {
Expand Down
Loading