[clang] [clang][bytecode] Implement bitcasts to composite types (PR #114776)

Timm Baeder via cfe-commits cfe-commits at lists.llvm.org
Mon Nov 4 03:47:44 PST 2024


https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/114776

Only fixed-size, non-bitfield integral fields for now.

>From d0d198a55f7925143c34719c1393d004cd95b967 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Sat, 2 Nov 2024 01:53:28 +0100
Subject: [PATCH] [clang][bytecode] Implement bitcasts to composite types

Only fixed-size, non-bitfield integral fields for now.
---
 clang/lib/AST/ByteCode/Compiler.cpp           |  6 +-
 clang/lib/AST/ByteCode/Interp.h               | 10 ++++
 .../lib/AST/ByteCode/InterpBuiltinBitCast.cpp | 55 +++++++++++++++++--
 clang/lib/AST/ByteCode/InterpBuiltinBitCast.h |  2 +
 clang/lib/AST/ByteCode/Opcodes.td             |  2 +
 clang/test/AST/ByteCode/builtin-bit-cast.cpp  | 42 +++++++-------
 6 files changed, 86 insertions(+), 31 deletions(-)

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 {



More information about the cfe-commits mailing list