Author: Timm Baeder Date: 2024-11-04T15:10:10+01:00 New Revision: 2588b8be5624f721c56f44fd2a69f7327de8c0c1
URL: https://github.com/llvm/llvm-project/commit/2588b8be5624f721c56f44fd2a69f7327de8c0c1 DIFF: https://github.com/llvm/llvm-project/commit/2588b8be5624f721c56f44fd2a69f7327de8c0c1.diff LOG: [clang][bytecode] Implement bitcasts to composite types (#114776) Only fixed-size, non-bitfield integral fields for now. Added: Modified: clang/lib/AST/ByteCode/Compiler.cpp clang/lib/AST/ByteCode/Interp.h clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp clang/lib/AST/ByteCode/InterpBuiltinBitCast.h clang/lib/AST/ByteCode/Opcodes.td clang/test/AST/ByteCode/builtin-bit-cast.cpp Removed: ################################################################################ diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 75f790d17033c8..35116952901684 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -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; diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 153da14503a140..1f3134e1cd1559 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -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 //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp index 948a78f6f787e3..e458613bc1d08d 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp @@ -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) \ @@ -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"); \ } \ @@ -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; @@ -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; +} diff --git a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.h b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.h index 84ba784e95e235..494c0880a9c453 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.h +++ b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.h @@ -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 diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td index 480febd895a240..26d5f70b44396b 100644 --- a/clang/lib/AST/ByteCode/Opcodes.td +++ b/clang/lib/AST/ByteCode/Opcodes.td @@ -847,3 +847,5 @@ def BitCast : Opcode { let Args = [ArgBool, ArgUint32, ArgFltSemantics]; let HasGroup = 1; } + +def BitCastPtr : Opcode; diff --git a/clang/test/AST/ByteCode/builtin-bit-cast.cpp b/clang/test/AST/ByteCode/builtin-bit-cast.cpp index b1c218baf968ab..50382399eefc9c 100644 --- a/clang/test/AST/ByteCode/builtin-bit-cast.cpp +++ b/clang/test/AST/ByteCode/builtin-bit-cast.cpp @@ -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)); @@ -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 @@ -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 { }; @@ -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() { @@ -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 { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits