[clang] [clang][bytecode] Implement bitcasts to composite types (PR #114776)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Nov 4 03:48:21 PST 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Timm Baeder (tbaederr)
<details>
<summary>Changes</summary>
Only fixed-size, non-bitfield integral fields for now.
---
Full diff: https://github.com/llvm/llvm-project/pull/114776.diff
6 Files Affected:
- (modified) clang/lib/AST/ByteCode/Compiler.cpp (+2-4)
- (modified) clang/lib/AST/ByteCode/Interp.h (+10)
- (modified) clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp (+49-6)
- (modified) clang/lib/AST/ByteCode/InterpBuiltinBitCast.h (+2)
- (modified) clang/lib/AST/ByteCode/Opcodes.td (+2)
- (modified) clang/test/AST/ByteCode/builtin-bit-cast.cpp (+21-21)
``````````diff
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 8160707e8654d6..dbc9486a911260 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 at +2 {{constexpr variable 'g' must be initialized by a constant expression}}
- // expected-note at +1 {{bit_cast from a union type is not allowed in a constant expression}}
+ // both-error at +2 {{constexpr variable 'g' must be initialized by a constant expression}}
+ // both-note at +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 at +2 {{constexpr variable 'x' must be initialized by a constant expression}}
- // expected-note at +1 {{bit_cast to a union type is not allowed in a constant expression}}
+ // both-error at +2 {{constexpr variable 'x' must be initialized by a constant expression}}
+ // both-note at +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 {
``````````
</details>
https://github.com/llvm/llvm-project/pull/114776
More information about the cfe-commits
mailing list