Skip to content

Commit 2588b8b

Browse files
authored
[clang][bytecode] Implement bitcasts to composite types (llvm#114776)
Only fixed-size, non-bitfield integral fields for now.
1 parent c0ce44e commit 2588b8b

File tree

6 files changed

+86
-31
lines changed

6 files changed

+86
-31
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6467,10 +6467,8 @@ bool Compiler<Emitter>::emitBuiltinBitCast(const CastExpr *E) {
64676467
if (!this->visit(SubExpr))
64686468
return false;
64696469

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

64766474
const llvm::fltSemantics *TargetSemantics = nullptr;

clang/lib/AST/ByteCode/Interp.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3080,6 +3080,16 @@ inline bool BitCast(InterpState &S, CodePtr OpPC, bool TargetIsUCharOrByte,
30803080
return true;
30813081
}
30823082

3083+
inline bool BitCastPtr(InterpState &S, CodePtr OpPC) {
3084+
const Pointer &FromPtr = S.Stk.pop<Pointer>();
3085+
Pointer &ToPtr = S.Stk.peek<Pointer>();
3086+
3087+
if (!DoBitCastPtr(S, OpPC, FromPtr, ToPtr))
3088+
return false;
3089+
3090+
return true;
3091+
}
3092+
30833093
//===----------------------------------------------------------------------===//
30843094
// Read opcode arguments
30853095
//===----------------------------------------------------------------------===//

clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,7 @@ using DataFunc =
4848
} \
4949
} while (0)
5050

51-
/// Float is a special case that sometimes needs the floating point semantics
52-
/// to be available.
53-
#define BITCAST_TYPE_SWITCH_WITH_FLOAT(Expr, B) \
51+
#define BITCAST_TYPE_SWITCH_FIXED_SIZE(Expr, B) \
5452
do { \
5553
switch (Expr) { \
5654
TYPE_SWITCH_CASE(PT_Sint8, B) \
@@ -61,10 +59,7 @@ using DataFunc =
6159
TYPE_SWITCH_CASE(PT_Uint32, B) \
6260
TYPE_SWITCH_CASE(PT_Sint64, B) \
6361
TYPE_SWITCH_CASE(PT_Uint64, B) \
64-
TYPE_SWITCH_CASE(PT_IntAP, B) \
65-
TYPE_SWITCH_CASE(PT_IntAPS, B) \
6662
TYPE_SWITCH_CASE(PT_Bool, B) \
67-
TYPE_SWITCH_CASE(PT_Float, B) \
6863
default: \
6964
llvm_unreachable("Unhandled bitcast type"); \
7065
} \
@@ -92,6 +87,12 @@ struct BitcastBuffer {
9287

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

90+
std::byte *getBytes(unsigned BitOffset) const {
91+
if (BitOffset % 8 == 0)
92+
return const_cast<std::byte *>(data() + (BitOffset / 8));
93+
assert(false && "hmm, how to best handle this?");
94+
}
95+
9596
bool allInitialized() const {
9697
// FIXME: Implement.
9798
return true;
@@ -377,3 +378,45 @@ bool clang::interp::DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
377378

378379
return Success;
379380
}
381+
382+
bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,
383+
const Pointer &FromPtr, Pointer &ToPtr) {
384+
assert(FromPtr.isLive());
385+
assert(FromPtr.isBlockPointer());
386+
assert(ToPtr.isBlockPointer());
387+
388+
QualType FromType = FromPtr.getType();
389+
QualType ToType = ToPtr.getType();
390+
391+
if (!CheckBitcastType(S, OpPC, FromType, /*IsToType=*/false))
392+
return false;
393+
394+
if (!CheckBitcastType(S, OpPC, ToType, /*IsToType=*/true))
395+
return false;
396+
397+
BitcastBuffer Buffer;
398+
readPointerToBuffer(S.getContext(), FromPtr, Buffer,
399+
/*ReturnOnUninit=*/false);
400+
401+
// Now read the values out of the buffer again and into ToPtr.
402+
size_t BitOffset = 0;
403+
bool Success = enumeratePointerFields(
404+
ToPtr, S.getContext(),
405+
[&](const Pointer &P, PrimType T, size_t _) -> bool {
406+
BITCAST_TYPE_SWITCH_FIXED_SIZE(T, {
407+
T &Val = P.deref<T>();
408+
409+
std::byte *M = Buffer.getBytes(BitOffset);
410+
411+
if (llvm::sys::IsBigEndianHost)
412+
swapBytes(M, T::bitWidth() / 8);
413+
414+
Val = T::bitcastFromMemory(M, T::bitWidth());
415+
P.initialize();
416+
BitOffset += T::bitWidth();
417+
});
418+
return true;
419+
});
420+
421+
return Success;
422+
}

clang/lib/AST/ByteCode/InterpBuiltinBitCast.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ class CodePtr;
1919

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

2325
} // namespace interp
2426
} // namespace clang

clang/lib/AST/ByteCode/Opcodes.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -847,3 +847,5 @@ def BitCast : Opcode {
847847
let Args = [ArgBool, ArgUint32, ArgFltSemantics];
848848
let HasGroup = 1;
849849
}
850+
851+
def BitCastPtr : Opcode;

clang/test/AST/ByteCode/builtin-bit-cast.cpp

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ struct int_splicer {
287287

288288
constexpr int_splicer splice(0x0C05FEFE, 0xCAFEBABE);
289289

290-
#if 0
290+
#if 1
291291
static_assert(bit_cast<unsigned long long>(splice) == (LITTLE_END
292292
? 0xCAFEBABE0C05FEFE
293293
: 0x0C05FEFECAFEBABE));
@@ -297,8 +297,8 @@ static_assert(bit_cast<int_splicer>(0xCAFEBABE0C05FEFE).x == (LITTLE_END
297297
? 0x0C05FEFE
298298
: 0xCAFEBABE));
299299

300-
static_assert(round_trip<unsigned long long>(splice));
301-
static_assert(round_trip<long long>(splice));
300+
static_assert(check_round_trip<unsigned long long>(splice));
301+
static_assert(check_round_trip<long long>(splice));
302302
#endif
303303

304304

@@ -340,13 +340,12 @@ void test_record() {
340340
? 0xCAFEBABE0C05FEFE
341341
: 0x0C05FEFECAFEBABE));
342342

343-
/// FIXME: Bit casts to composite types.
344-
// static_assert(bit_cast<int_splicer>(0xCAFEBABE0C05FEFE).x == (LITTLE_END
345-
// ? 0x0C05FEFE
346-
// : 0xCAFEBABE));
343+
static_assert(bit_cast<int_splicer>(0xCAFEBABE0C05FEFE).x == (LITTLE_END
344+
? 0x0C05FEFE
345+
: 0xCAFEBABE));
347346

348-
// static_assert(check_round_trip<unsigned long long>(splice));
349-
// static_assert(check_round_trip<long long>(splice));
347+
static_assert(check_round_trip<unsigned long long>(splice));
348+
static_assert(check_round_trip<long long>(splice));
350349

351350
struct base2 {
352351
};
@@ -368,13 +367,14 @@ void test_record() {
368367
z == other.z && doublez == other.doublez;
369368
}
370369
};
371-
// constexpr bases b = {{1, 2}, {}, {3}, 4};
372-
// constexpr tuple4 t4 = bit_cast<tuple4>(b);
373-
// static_assert(t4 == tuple4{1, 2, 3, 4});
374-
// static_assert(round_trip<tuple4>(b));
375-
376-
// constexpr auto b2 = bit_cast<bases>(t4);
377-
// static_assert(t4 == b2);
370+
constexpr bases b = {{1, 2}, {}, {3}, 4};
371+
constexpr tuple4 t4 = bit_cast<tuple4>(b);
372+
static_assert(t4 == tuple4{1, 2, 3, 4});
373+
static_assert(check_round_trip<tuple4>(b));
374+
375+
/// FIXME: We need to initialize the base pointers in the pointer we're bitcasting to.
376+
// constexpr auto b2 = bit_cast<bases>(t4);
377+
// static_assert(t4 == b2);
378378
}
379379

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

416416
struct G {
417417
int g;
418418
};
419-
// expected-error@+2 {{constexpr variable 'g' must be initialized by a constant expression}}
420-
// expected-note@+1 {{bit_cast from a union type is not allowed in a constant expression}}
419+
// both-error@+2 {{constexpr variable 'g' must be initialized by a constant expression}}
420+
// both-note@+1 {{bit_cast from a union type is not allowed in a constant expression}}
421421
constexpr G g = __builtin_bit_cast(G, X{0});
422-
// expected-error@+2 {{constexpr variable 'x' must be initialized by a constant expression}}
423-
// expected-note@+1 {{bit_cast to a union type is not allowed in a constant expression}}
422+
// both-error@+2 {{constexpr variable 'x' must be initialized by a constant expression}}
423+
// both-note@+1 {{bit_cast to a union type is not allowed in a constant expression}}
424424
constexpr X x = __builtin_bit_cast(X, G{0});
425425
#endif
426426
struct has_pointer {

0 commit comments

Comments
 (0)