[clang] 5b32c59 - [clang][bytecode] Implement bitcasts to floating-point values (#114485) (#114712)

via cfe-commits cfe-commits at lists.llvm.org
Sun Nov 3 10:42:29 PST 2024


Author: Timm Baeder
Date: 2024-11-03T19:42:26+01:00
New Revision: 5b32c5954b1d00435a2264f8d1bd1fd9db9cb022

URL: https://github.com/llvm/llvm-project/commit/5b32c5954b1d00435a2264f8d1bd1fd9db9cb022
DIFF: https://github.com/llvm/llvm-project/commit/5b32c5954b1d00435a2264f8d1bd1fd9db9cb022.diff

LOG: [clang][bytecode] Implement bitcasts to floating-point values (#114485) (#114712)

This time I tested on big-endian hosts.

Added: 
    clang/test/AST/ByteCode/builtin-bit-cast-long-double.cpp

Modified: 
    clang/lib/AST/ByteCode/Interp.h
    clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
    clang/test/AST/ByteCode/builtin-bit-cast.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index ade6f7424b1fd6..153da14503a140 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -3062,7 +3062,17 @@ inline bool BitCast(InterpState &S, CodePtr OpPC, bool TargetIsUCharOrByte,
     return false;
 
   if constexpr (std::is_same_v<T, Floating>) {
-    assert(false && "Implement bitcasting to a floating type");
+    assert(Sem);
+    ptr
diff _t Offset = 0;
+
+    if (llvm::sys::IsBigEndianHost) {
+      unsigned NumBits = llvm::APFloatBase::getSizeInBits(*Sem);
+      assert(NumBits % 8 == 0);
+      assert(NumBits <= ResultBitWidth);
+      Offset = (ResultBitWidth - NumBits) / 8;
+    }
+
+    S.Stk.push<Floating>(T::bitcastFromMemory(Buff.data() + Offset, *Sem));
   } else {
     assert(!Sem);
     S.Stk.push<T>(T::bitcastFromMemory(Buff.data(), ResultBitWidth));

diff  --git a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
index 1acd49de307b96..8160707e8654d6 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
@@ -315,19 +315,34 @@ static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr,
           assert(false && "Implement casting to pointer types");
 
         CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(P.getType());
-        unsigned BitWidth;
-        if (const FieldDecl *FD = P.getField(); FD && FD->isBitField())
-          BitWidth = FD->getBitWidthValue(ASTCtx);
-        else
-          BitWidth = ASTCtx.toBits(ObjectReprChars);
-
+        unsigned BitWidth = ASTCtx.toBits(ObjectReprChars);
         llvm::SmallVector<std::byte> Buff(ObjectReprChars.getQuantity());
-        BITCAST_TYPE_SWITCH_WITH_FLOAT(T, {
-          T Val = P.deref<T>();
-          Val.bitcastToMemory(Buff.data());
-        });
-        if (SwapData)
-          swapBytes(Buff.data(), ObjectReprChars.getQuantity());
+        // Work around floating point types that contain unused padding bytes.
+        // This is really just `long double` on x86, which is the only
+        // fundamental type with padding bytes.
+        if (T == PT_Float) {
+          Floating &F = P.deref<Floating>();
+          unsigned NumBits =
+              llvm::APFloatBase::getSizeInBits(F.getAPFloat().getSemantics());
+          assert(NumBits % 8 == 0);
+          assert(NumBits <= (ObjectReprChars.getQuantity() * 8));
+          F.bitcastToMemory(Buff.data());
+          // Now, only (maybe) swap the actual size of the float, excluding the
+          // padding bits.
+          if (SwapData)
+            swapBytes(Buff.data(), NumBits / 8);
+
+        } else {
+          if (const FieldDecl *FD = P.getField(); FD && FD->isBitField())
+            BitWidth = FD->getBitWidthValue(ASTCtx);
+
+          BITCAST_TYPE_SWITCH(T, {
+            T Val = P.deref<T>();
+            Val.bitcastToMemory(Buff.data());
+          });
+          if (SwapData)
+            swapBytes(Buff.data(), ObjectReprChars.getQuantity());
+        }
 
         if (BitWidth != (Buff.size() * 8) && BigEndianTarget) {
           Buffer.pushData(Buff.data() + (Buff.size() - 1 - (BitWidth / 8)),

diff  --git a/clang/test/AST/ByteCode/builtin-bit-cast-long-double.cpp b/clang/test/AST/ByteCode/builtin-bit-cast-long-double.cpp
new file mode 100644
index 00000000000000..230680ff3ced7d
--- /dev/null
+++ b/clang/test/AST/ByteCode/builtin-bit-cast-long-double.cpp
@@ -0,0 +1,87 @@
+// RUN: %clang_cc1 -verify=ref,both -std=c++2a -fsyntax-only -triple x86_64-apple-macosx10.14.0 %s
+// RUN: %clang_cc1 -verify=ref,both -std=c++2a -fsyntax-only -triple x86_64-apple-macosx10.14.0 %s -fno-signed-char
+// RUN: %clang_cc1 -verify=ref,both -std=c++2a -fsyntax-only -triple aarch64_be-linux-gnu %s
+
+// RUN: %clang_cc1 -verify=expected,both -std=c++2a -fsyntax-only -triple x86_64-apple-macosx10.14.0 %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -verify=expected,both -std=c++2a -fsyntax-only -triple x86_64-apple-macosx10.14.0 %s -fno-signed-char -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -verify=expected,both -std=c++2a -fsyntax-only -triple aarch64_be-linux-gnu %s -fexperimental-new-constant-interpreter
+
+// both-no-diagnostics
+
+typedef decltype(nullptr) nullptr_t;
+typedef __INTPTR_TYPE__ intptr_t;
+
+static_assert(sizeof(int) == 4);
+static_assert(sizeof(long long) == 8);
+
+template <class To, class From>
+constexpr To bit_cast(const From &from) {
+  static_assert(sizeof(To) == sizeof(From));
+  return __builtin_bit_cast(To, from);
+}
+
+template <class Intermediate, class Init>
+constexpr bool check_round_trip(const Init &init) {
+  return bit_cast<Init>(bit_cast<Intermediate>(init)) == init;
+}
+
+template <class Intermediate, class Init>
+constexpr Init round_trip(const Init &init) {
+  return bit_cast<Init>(bit_cast<Intermediate>(init));
+}
+
+
+
+
+namespace test_long_double {
+#if __x86_64
+#if 0
+constexpr __int128_t test_cast_to_int128 = bit_cast<__int128_t>((long double)0); // expected-error{{must be initialized by a constant expression}}\
+                                                                                 // expected-note{{in call}}
+#endif
+constexpr long double ld = 3.1425926539;
+
+struct bytes {
+  unsigned char d[16];
+};
+
+// static_assert(round_trip<bytes>(ld), "");
+
+static_assert(round_trip<long double>(10.0L));
+
+#if 0
+constexpr bool f(bool read_uninit) {
+  bytes b = bit_cast<bytes>(ld);
+  unsigned char ld_bytes[10] = {
+    0x0,  0x48, 0x9f, 0x49, 0xf0,
+    0x3c, 0x20, 0xc9, 0x0,  0x40,
+  };
+
+  for (int i = 0; i != 10; ++i)
+    if (ld_bytes[i] != b.d[i])
+      return false;
+
+  if (read_uninit && b.d[10]) // expected-note{{read of uninitialized object is not allowed in a constant expression}}
+    return false;
+
+  return true;
+}
+
+static_assert(f(/*read_uninit=*/false), "");
+static_assert(f(/*read_uninit=*/true), ""); // expected-error{{static assertion expression is not an integral constant expression}} \
+                                            // expected-note{{in call to 'f(true)'}}
+#endif
+constexpr bytes ld539 = {
+  0x0, 0x0,  0x0,  0x0,
+  0x0, 0x0,  0xc0, 0x86,
+  0x8, 0x40, 0x0,  0x0,
+  0x0, 0x0,  0x0,  0x0,
+};
+
+constexpr long double fivehundredandthirtynine = 539.0;
+
+static_assert(bit_cast<long double>(ld539) == fivehundredandthirtynine, "");
+#else
+static_assert(round_trip<__int128_t>(34.0L));
+#endif
+}

diff  --git a/clang/test/AST/ByteCode/builtin-bit-cast.cpp b/clang/test/AST/ByteCode/builtin-bit-cast.cpp
index c5380647c94aee..b1c218baf968ab 100644
--- a/clang/test/AST/ByteCode/builtin-bit-cast.cpp
+++ b/clang/test/AST/ByteCode/builtin-bit-cast.cpp
@@ -87,6 +87,8 @@ static_assert(__builtin_bit_cast(__int128_t, OneBit) == (LITTLE_END ? 1 : Expect
 
 #endif
 
+static_assert(check_round_trip<double>(17.0));
+
 
 namespace simple {
   constexpr int A = __builtin_bit_cast(int, 10);
@@ -119,7 +121,7 @@ namespace simple {
   static_assert(check_round_trip<unsigned>((int)0x12345678));
   static_assert(check_round_trip<unsigned>((int)0x87654321));
   static_assert(check_round_trip<unsigned>((int)0x0C05FEFE));
-  // static_assert(round_trip<float>((int)0x0C05FEFE));
+  static_assert(round_trip<float>((int)0x0C05FEFE));
 
 
   /// This works in GCC and in the bytecode interpreter, but the current interpreter


        


More information about the cfe-commits mailing list