tbaeder updated this revision to Diff 514572.

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D144943/new/

https://reviews.llvm.org/D144943

Files:
  clang/lib/AST/Interp/Boolean.h
  clang/lib/AST/Interp/ByteCodeExprGen.cpp
  clang/lib/AST/Interp/ByteCodeExprGen.h
  clang/lib/AST/Interp/Descriptor.h
  clang/lib/AST/Interp/Floating.h
  clang/lib/AST/Interp/Integral.h
  clang/lib/AST/Interp/Interp.h
  clang/lib/AST/Interp/InterpBuiltin.cpp
  clang/lib/AST/Interp/Opcodes.td
  clang/lib/AST/Interp/Pointer.cpp
  clang/lib/AST/Interp/Pointer.h
  clang/lib/AST/Interp/PrimType.h
  clang/test/AST/Interp/builtin-bit-cast.cpp
  clang/test/AST/Interp/literals.cpp

Index: clang/test/AST/Interp/literals.cpp
===================================================================
--- clang/test/AST/Interp/literals.cpp
+++ clang/test/AST/Interp/literals.cpp
@@ -90,6 +90,11 @@
 static_assert(p != nullptr, "");
 static_assert(*p == 10, "");
 
+constexpr const void *cp = (void *)p;
+// FIXME: This should be an error in the new interpreter.
+constexpr const int *pm = (int*)cp; // ref-error {{ must be initialized by a constant expression}} \
+                                    // ref-note {{cast from 'const void *' is not allowed}}
+
 constexpr const int* getIntPointer() {
   return &m;
 }
Index: clang/test/AST/Interp/builtin-bit-cast.cpp
===================================================================
--- /dev/null
+++ clang/test/AST/Interp/builtin-bit-cast.cpp
@@ -0,0 +1,228 @@
+// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify -std=c++2a -fsyntax-only %s
+// RUN: %clang_cc1 -verify=ref -std=c++2a -fsyntax-only %s
+// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -triple aarch64_be-linux-gnu -fexperimental-new-constant-interpreter %s
+// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -triple aarch64_be-linux-gnu -fexperimental-new-constant-interpreter %s
+
+
+/// ref-no-diagnostics
+
+
+/// FIXME: This is a version of clang/test/SemaCXX/builtin-bit-cast.cpp with
+///   the currently supported subset of operations. They should *all* be
+///   supported though.
+
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#  define LITTLE_END 1
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#  define LITTLE_END 0
+#else
+#  error "huh?"
+#endif
+
+template <class T, class V> struct is_same {
+  static constexpr bool value = false;
+};
+template <class T> struct is_same<T, T> {
+  static constexpr bool value = true;
+};
+
+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 round_trip(const Init &init) {
+  return bit_cast<Init>(bit_cast<Intermediate>(init)) == init;
+}
+
+static_assert(round_trip<unsigned>((int)-1));
+static_assert(round_trip<unsigned>((int)0x12345678));
+static_assert(round_trip<unsigned>((int)0x87654321));
+static_assert(round_trip<unsigned>((int)0x0C05FEFE));
+static_assert(round_trip<float>((int)0x0C05FEFE));
+
+constexpr unsigned char input[] = {0xCA, 0xFE, 0xBA, 0xBE};
+constexpr unsigned expected = LITTLE_END ? 0xBEBAFECA : 0xCAFEBABE;
+static_assert(bit_cast<unsigned>(input) == expected);
+
+constexpr short S[] = {10, 20};
+constexpr int I = __builtin_bit_cast(int, S);
+static_assert(I == (LITTLE_END ? 1310730 : 655380));
+
+
+struct int_splicer {
+  unsigned x;
+  unsigned y;
+
+  constexpr int_splicer() : x(1), y(2) {}
+  constexpr int_splicer(unsigned x, unsigned y) : x(x), y(y) {}
+
+  constexpr bool operator==(const int_splicer &other) const {
+    return other.x == x && other.y == y;
+  }
+};
+
+constexpr int_splicer splice(0x0C05FEFE, 0xCAFEBABE);
+
+static_assert(bit_cast<unsigned long long>(splice) == (LITTLE_END
+                                                           ? 0xCAFEBABE0C05FEFE
+                                                           : 0x0C05FEFECAFEBABE));
+
+constexpr int_splicer IS = bit_cast<int_splicer>(0xCAFEBABE0C05FEFE);
+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));
+
+struct base2 {
+};
+
+struct base3 {
+  unsigned z;
+  constexpr base3() : z(3) {}
+};
+
+struct bases : int_splicer, base2, base3 {
+  unsigned doublez;
+  constexpr bases() : doublez(4) {}
+};
+
+struct tuple4 {
+  unsigned x, y, z, doublez;
+
+  constexpr bool operator==(tuple4 const &other) const {
+    return x == other.x && y == other.y &&
+           z == other.z && doublez == other.doublez;
+  }
+};
+constexpr bases b;// = {{1, 2}, {}, {3}, 4};
+constexpr tuple4 t4 = bit_cast<tuple4>(b);
+
+// Regardless of endianness, this should hold:
+static_assert(t4.x == 1);
+static_assert(t4.y == 2);
+static_assert(t4.z == 3);
+static_assert(t4.doublez == 4);
+//static_assert(t4 == tuple4{1, 2, 3, 4});
+static_assert(round_trip<tuple4>(b));
+
+
+namespace WithBases {
+  struct Base {
+    char A[3] = {1,2,3};
+  };
+
+  struct A : Base {
+    char B = 12;
+  };
+
+  constexpr A a;
+  constexpr unsigned I = __builtin_bit_cast(unsigned, a);
+  static_assert(I == (LITTLE_END ? 201523713 : 16909068));
+};
+
+
+namespace test_array_fill {
+  constexpr unsigned char a[4] = {1, 2};
+  constexpr unsigned int i = bit_cast<unsigned int>(a);
+  static_assert(i == (LITTLE_END ? 0x00000201 : 0x01020000));
+}
+
+namespace Another {
+  constexpr char C[] = {1,2,3,4};
+  struct F{ short a; short b; };
+  constexpr F f = __builtin_bit_cast(F, C);
+
+#if LITTLE_END
+  static_assert(f.a == 513);
+  static_assert(f.b == 1027);
+#else
+  static_assert(f.a == 258);
+  static_assert(f.b == 772);
+#endif
+}
+
+
+namespace FloatToDouble {
+  constexpr float F1[] = {1.0f, 2.0f};
+  constexpr double D1 = __builtin_bit_cast(double, F1);
+  static_assert(D1 > 0);
+};
+
+
+namespace array_members {
+  struct S {
+    int ar[3];
+
+    constexpr bool operator==(const S &rhs) {
+      return ar[0] == rhs.ar[0] && ar[1] == rhs.ar[1] && ar[2] == rhs.ar[2];
+    }
+  };
+
+  struct G {
+    int a, b, c;
+
+    constexpr bool operator==(const G &rhs) {
+      return a == rhs.a && b == rhs.b && c == rhs.c;
+    }
+  };
+
+  constexpr S s{{1, 2, 3}};
+  constexpr G g = bit_cast<G>(s);
+  static_assert(g.a == 1 && g.b == 2 && g.c == 3);
+
+  static_assert(round_trip<G>(s));
+  static_assert(round_trip<S>(g));
+}
+
+namespace CompositeArrays {
+  struct F {
+    int a;
+    int b;
+  };
+
+  static_assert(sizeof(long) == 2 * sizeof(int));
+
+  constexpr F ff[] = {{1,2}};
+  constexpr long L = __builtin_bit_cast(long, ff);
+
+#if LITTLE_END
+  static_assert(L == 8589934593);
+#else
+  static_assert(L == 4294967298);
+#endif
+}
+
+typedef decltype(nullptr) nullptr_t;
+
+/// FIXME: This works in the current interpreter, but it's weird and GCC
+///   rejects it as well.
+constexpr int test_from_nullptr_pass = (__builtin_bit_cast(unsigned char[8], nullptr), 0); // expected-error {{must be initialized by a constant expression}}
+
+/// FIXME: The current interpreter allows bitcasting to nullptr_t, but
+///   GCC does not.
+constexpr int test_to_nullptr() {
+  nullptr_t npt = __builtin_bit_cast(nullptr_t, 0ul);
+
+  struct indet_mem {
+    unsigned char data[sizeof(void *)];
+  };
+  indet_mem im = __builtin_bit_cast(indet_mem, nullptr);
+  nullptr_t npt2 = __builtin_bit_cast(nullptr_t, im);
+
+  return 0;
+}
+
+constexpr int ttn = test_to_nullptr(); // expected-error {{must be initialized by a constant expression}}
+
+
+
+
Index: clang/lib/AST/Interp/PrimType.h
===================================================================
--- clang/lib/AST/Interp/PrimType.h
+++ clang/lib/AST/Interp/PrimType.h
@@ -62,6 +62,10 @@
 /// Returns the size of a primitive type in bytes.
 size_t primSize(PrimType Type);
 
+template <PrimType PrimT> constexpr size_t primSize() {
+  return sizeof(typename PrimConv<PrimT>::T);
+}
+
 /// Aligns a size to the pointer alignment.
 constexpr size_t align(size_t Size) {
   return ((Size + alignof(void *) - 1) / alignof(void *)) * alignof(void *);
@@ -98,6 +102,24 @@
       TYPE_SWITCH_CASE(PT_FnPtr, B)                                            \
     }                                                                          \
   } while (0)
+
+#define BITCAST_TYPE_SWITCH(Expr, B)                                           \
+  do {                                                                         \
+    switch (Expr) {                                                            \
+      TYPE_SWITCH_CASE(PT_Sint8, B)                                            \
+      TYPE_SWITCH_CASE(PT_Uint8, B)                                            \
+      TYPE_SWITCH_CASE(PT_Sint16, B)                                           \
+      TYPE_SWITCH_CASE(PT_Uint16, B)                                           \
+      TYPE_SWITCH_CASE(PT_Sint32, B)                                           \
+      TYPE_SWITCH_CASE(PT_Uint32, B)                                           \
+      TYPE_SWITCH_CASE(PT_Sint64, B)                                           \
+      TYPE_SWITCH_CASE(PT_Uint64, B)                                           \
+      TYPE_SWITCH_CASE(PT_Bool, B)                                             \
+    default:                                                                   \
+      llvm_unreachable("Unhandled bitcast type");                              \
+    }                                                                          \
+  } while (0)
+
 #define COMPOSITE_TYPE_SWITCH(Expr, B, D)                                      \
   do {                                                                         \
     switch (Expr) {                                                            \
Index: clang/lib/AST/Interp/Pointer.h
===================================================================
--- clang/lib/AST/Interp/Pointer.h
+++ clang/lib/AST/Interp/Pointer.h
@@ -59,6 +59,7 @@
 ///                      │
 ///                      │
 ///                     Base
+using DataFunc = llvm::function_ref<void(const Pointer &P, PrimType Ty)>;
 class Pointer {
 private:
   static constexpr unsigned PastEndMark = ~0u;
@@ -337,6 +338,9 @@
   /// Deactivates an entire strurcutre.
   void deactivate() const;
 
+  /// Calls F for all fields in this Pointer.
+  void enumerateFields(const Context &Ctx, DataFunc F) const;
+
   /// Checks if two pointers are comparable.
   static bool hasSameBase(const Pointer &A, const Pointer &B);
   /// Checks if two pointers can be subtracted.
Index: clang/lib/AST/Interp/Pointer.cpp
===================================================================
--- clang/lib/AST/Interp/Pointer.cpp
+++ clang/lib/AST/Interp/Pointer.cpp
@@ -245,3 +245,54 @@
 
   return Result;
 }
+
+static void enumerateData(const Pointer &P, const Context &Ctx, DataFunc F) {
+  const Descriptor *FieldDesc = P.getFieldDesc();
+  assert(FieldDesc);
+
+  // Primitives.
+  if (FieldDesc->isPrimitive()) {
+    F(P, *Ctx.classify(FieldDesc->getType()));
+    return;
+  }
+
+  // Primitive arrays.
+  if (FieldDesc->isPrimitiveArray()) {
+    QualType ElemType =
+        FieldDesc->getType()->getAsArrayTypeUnsafe()->getElementType();
+    PrimType ElemT = *Ctx.classify(ElemType);
+    for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) {
+      F(P.atIndex(I), ElemT);
+    }
+    return;
+  }
+
+  // Composite arrays.
+  if (FieldDesc->isCompositeArray()) {
+    for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I)
+      enumerateData(P.atIndex(I).narrow(), Ctx, F);
+    return;
+  }
+
+  // Records.
+  if (FieldDesc->isRecord()) {
+    const Record *R = FieldDesc->ElemRecord;
+    for (unsigned I = 0; I != R->getNumBases(); ++I) {
+      Pointer Elem = P.atField(R->getBase(I)->Offset);
+      enumerateData(Elem, Ctx, F);
+    }
+    // TODO: Virtual bases?
+
+    for (unsigned I = 0; I != R->getNumFields(); ++I) {
+      Pointer Elem = P.atField(R->getField(I)->Offset);
+      enumerateData(Elem, Ctx, F);
+    }
+    return;
+  }
+
+  llvm_unreachable("Unhandled data type");
+}
+
+void Pointer::enumerateFields(const Context &Ctx, DataFunc F) const {
+  enumerateData(*this, Ctx, F);
+}
Index: clang/lib/AST/Interp/Opcodes.td
===================================================================
--- clang/lib/AST/Interp/Opcodes.td
+++ clang/lib/AST/Interp/Opcodes.td
@@ -544,6 +544,22 @@
   let HasGroup = 1;
 }
 
+def BitCastTypeClass : TypeClass {
+  let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool];
+}
+
+def BitCast : Opcode {
+  let Types = [BitCastTypeClass];
+  let HasGroup = 1;
+}
+
+def BitCastPtr : Opcode;
+
+def BitCastFP : Opcode {
+  let Types = [];
+  let Args = [ArgFltSemantics];
+}
+
 def CastFP : Opcode {
   let Types = [];
   let Args = [ArgFltSemantics, ArgRoundingMode];
Index: clang/lib/AST/Interp/InterpBuiltin.cpp
===================================================================
--- clang/lib/AST/Interp/InterpBuiltin.cpp
+++ clang/lib/AST/Interp/InterpBuiltin.cpp
@@ -9,6 +9,7 @@
 #include "Interp.h"
 #include "PrimType.h"
 #include "clang/Basic/Builtins.h"
+#include "clang/Basic/TargetInfo.h"
 
 namespace clang {
 namespace interp {
@@ -29,5 +30,108 @@
   return false;
 }
 
+struct Foo {
+  std::byte *Buff;
+  unsigned Offset;
+  size_t BuffSize;
+  bool BigEndian;
+
+  constexpr Foo(std::byte *Buff, size_t BuffSize, bool BigEndian)
+      : Buff(Buff), Offset(0), BuffSize(BuffSize), BigEndian(BigEndian) {}
+
+  std::byte *getBytesAndAdvance(size_t N) {
+    unsigned OldOffset = Offset;
+    Offset += N;
+
+    assert(OldOffset < BuffSize);
+
+    if (BigEndian) {
+      return Buff + BuffSize - OldOffset - N;
+    }
+
+    // Little Endian target.
+    return Buff + OldOffset;
+  }
+};
+
+bool DoBitCast(InterpState &S, const Pointer &P, std::byte *Buff,
+               size_t BuffSize) {
+  Foo F(Buff, BuffSize, S.getCtx().getTargetInfo().isBigEndian());
+
+  P.enumerateFields(S.getContext(), [&](const Pointer &Ptr, PrimType Ty) {
+    if (Ty == PT_Float) {
+      Floating &Val = Ptr.deref<Floating>();
+      std::byte *M = F.getBytesAndAdvance(Val.bitWidth() / 8);
+      Val.bitcastToMemory(M);
+      return;
+    }
+
+    BITCAST_TYPE_SWITCH(Ty, {
+      T Val = Ptr.deref<T>();
+      unsigned SizeInBytes = Val.bitWidth() / 8;
+      std::byte *M = F.getBytesAndAdvance(SizeInBytes);
+      Val.bitcastToMemory(M);
+    });
+  });
+
+  return true;
+}
+
+/// Rotate things around for big endian targets.
+static void swapBytes(std::byte *M, size_t N) {
+  for (size_t I = 0; I != (N / 2); ++I)
+    std::swap(M[I], M[N - 1 - I]);
+}
+
+bool DoBitCastToPtr(InterpState &S, const Pointer &P, Pointer &DestPtr) {
+  const Context &Ctx = S.getContext();
+  bool BigEndian = S.getCtx().getTargetInfo().isBigEndian();
+
+  std::vector<std::byte> Data;
+  P.enumerateFields(Ctx, [&](const Pointer &P, PrimType Ty) {
+    BITCAST_TYPE_SWITCH(Ty, {
+      T Val = P.deref<T>();
+      unsigned SizeInBytes = Val.bitWidth() / 8;
+      unsigned Offset = Data.size();
+      Data.resize(Data.size() + SizeInBytes);
+      Val.bitcastToMemory(Data.data() + Offset);
+
+      if (BigEndian)
+        swapBytes(Data.data() + Offset, SizeInBytes);
+    });
+  });
+
+  unsigned Offset = 0;
+  DestPtr.enumerateFields(Ctx, [&](const Pointer &P, PrimType Ty) {
+    if (Ty == PT_Float) {
+      const auto &Sem = Ctx.getASTContext().getFloatTypeSemantics(
+          P.getFieldDesc()->getType());
+      size_t Size = APFloat::semanticsSizeInBits(Sem) / 8;
+
+      std::byte *M = Data.data() + Offset;
+
+      if (BigEndian)
+        swapBytes(M, Size);
+      P.deref<Floating>() = Floating::bitcastFromMemory(M, Sem);
+      Offset += Size;
+      return;
+    }
+
+    BITCAST_TYPE_SWITCH(Ty, {
+      T &Val = P.deref<T>();
+
+      size_t Size = Val.bitWidth() / 8;
+      std::byte *M = Data.data() + Offset;
+      if (BigEndian)
+        swapBytes(M, Size);
+
+      Val = T::bitcastFromMemory(M);
+      P.initialize();
+      Offset += Size;
+    });
+  });
+  return true;
+}
+
 } // namespace interp
 } // namespace clang
Index: clang/lib/AST/Interp/Interp.h
===================================================================
--- clang/lib/AST/Interp/Interp.h
+++ clang/lib/AST/Interp/Interp.h
@@ -149,6 +149,17 @@
 /// Interpret a builtin function.
 bool InterpretBuiltin(InterpState &S, CodePtr &PC, unsigned BuiltinID);
 
+/// Perform a bitcast of all fields of P into Buff. This performs the
+/// actions of a __builtin_bit_cast expression when the target type
+/// is primitive.
+bool DoBitCast(InterpState &S, const Pointer &P, std::byte *Buff,
+               size_t BuffSize);
+
+/// Perform a bitcast of all fields of P into the fields of DestPtr.
+/// This performs the actions of a __builtin_bit_cast expression when
+/// the target type is a composite type.
+bool DoBitCastToPtr(InterpState &S, const Pointer &P, Pointer &DestPtr);
+
 enum class ArithOp { Add, Sub };
 
 //===----------------------------------------------------------------------===//
@@ -1317,6 +1328,50 @@
   return true;
 }
 
+template <PrimType Name, class ToT = typename PrimConv<Name>::T>
+bool BitCast(InterpState &S, CodePtr OpPC) {
+  const Pointer &FromPtr = S.Stk.pop<Pointer>();
+
+  constexpr size_t BuffSize = primSize<Name>();
+  std::byte Buff[BuffSize];
+
+  if (!DoBitCast(S, FromPtr, Buff, BuffSize))
+    return false;
+
+  S.Stk.push<ToT>(ToT::bitcastFromMemory(Buff));
+  return true;
+}
+
+/// 1) Pops a pointer from the stack
+/// 2) Peeks a pointer
+/// 3) Bitcasts the contents of the first pointer to the
+///   fields of the second pointer.
+inline bool BitCastPtr(InterpState &S, CodePtr OpPC) {
+  const Pointer &FromPtr = S.Stk.pop<Pointer>();
+  Pointer &ToPtr = S.Stk.peek<Pointer>();
+
+  if (!DoBitCastToPtr(S, FromPtr, ToPtr))
+    return false;
+
+  return true;
+}
+
+/// Bitcast TO a float.
+inline bool BitCastFP(InterpState &S, CodePtr OpPC,
+                      const llvm::fltSemantics *Sem) {
+  const Pointer &FromPtr = S.Stk.pop<Pointer>();
+
+  // FIXME: Graaah.
+  size_t BuffSize = APFloat::semanticsSizeInBits(*Sem) / 8;
+  std::byte Buff[BuffSize];
+
+  if (!DoBitCast(S, FromPtr, Buff, BuffSize))
+    return false;
+
+  S.Stk.push<Floating>(Floating::bitcastFromMemory(Buff, *Sem));
+  return true;
+}
+
 /// 1) Pops a Floating from the stack.
 /// 2) Pushes a new floating on the stack that uses the given semantics.
 /// Not templated, so implemented in Interp.cpp.
Index: clang/lib/AST/Interp/Integral.h
===================================================================
--- clang/lib/AST/Interp/Integral.h
+++ clang/lib/AST/Interp/Integral.h
@@ -173,6 +173,17 @@
     return Integral(Value);
   }
 
+  static Integral bitcastFromMemory(const std::byte *Buff) {
+    ReprT V;
+
+    memcpy(&V, Buff, sizeof(ReprT));
+    return Integral(V);
+  }
+
+  void bitcastToMemory(std::byte *Buff) const {
+    std::memcpy(Buff, &V, sizeof(ReprT));
+  }
+
   static bool inRange(int64_t Value, unsigned NumBits) {
     return CheckRange<ReprT, Min, Max>(Value);
   }
Index: clang/lib/AST/Interp/Floating.h
===================================================================
--- clang/lib/AST/Interp/Floating.h
+++ clang/lib/AST/Interp/Floating.h
@@ -103,6 +103,20 @@
     return Status;
   }
 
+  static Floating bitcastFromMemory(const std::byte *Buff,
+                                    const llvm::fltSemantics &Sem) {
+    size_t Size = APFloat::semanticsSizeInBits(Sem);
+    llvm::APInt API(Size, true);
+    llvm::LoadIntFromMemory(API, (const uint8_t *)Buff, Size / 8);
+
+    return Floating(APFloat(Sem, API));
+  }
+
+  void bitcastToMemory(std::byte *Buff) {
+    llvm::APInt API = F.bitcastToAPInt();
+    llvm::StoreIntToMemory(API, (uint8_t *)Buff, bitWidth() / 8);
+  }
+
   // -------
 
   static APFloat::opStatus add(Floating A, Floating B, llvm::RoundingMode RM,
Index: clang/lib/AST/Interp/Descriptor.h
===================================================================
--- clang/lib/AST/Interp/Descriptor.h
+++ clang/lib/AST/Interp/Descriptor.h
@@ -174,6 +174,8 @@
 
   /// Checks if the descriptor is of an array of primitives.
   bool isPrimitiveArray() const { return IsArray && !ElemDesc; }
+  /// Checks if the descriptor is of an array of composites.
+  bool isCompositeArray() const { return IsArray && ElemDesc; }
   /// Checks if the descriptor is of an array of zero size.
   bool isZeroSizeArray() const { return Size == 0; }
   /// Checks if the descriptor is of an array of unknown size.
Index: clang/lib/AST/Interp/ByteCodeExprGen.h
===================================================================
--- clang/lib/AST/Interp/ByteCodeExprGen.h
+++ clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -267,6 +267,7 @@
   bool emitRecordDestruction(const Descriptor *Desc);
   bool emitDerivedToBaseCasts(const RecordType *DerivedType,
                               const RecordType *BaseType, const Expr *E);
+  bool emitBuiltinBitCast(const CastExpr *E);
 
 protected:
   /// Variable to storage mapping.
Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp
===================================================================
--- clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -63,6 +63,53 @@
 } // namespace interp
 } // namespace clang
 
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::emitBuiltinBitCast(const CastExpr *E) {
+  const Expr *SubExpr = E->getSubExpr();
+  QualType FromType = SubExpr->getType();
+  QualType ToType = E->getType();
+
+  // A few invariants. These are invalid for bitcasts.
+  // FIXME: Diagnostics.
+  if (ToType->isArrayType() || ToType->isPointerType())
+    return false;
+
+  assert(!ToType->isReferenceType());
+
+  // Get a pointer to the value-to-cast on the stack.
+  if (!this->visit(SubExpr))
+    return false;
+
+  std::optional<PrimType> FromT = classify(FromType);
+  std::optional<PrimType> ToT = classify(ToType);
+
+  if (!ToT) {
+    // Conversion to an array or record type.
+    return this->emitBitCastPtr(E);
+  }
+
+  // FIXME: Diagnostics.
+  if (*ToT == PT_Ptr)
+    return false;
+
+  assert(ToT);
+  // Source and dest type are the same, just emit a load.
+  if (FromT && *FromT == ToT)
+    return this->emitLoad(*ToT, E);
+
+  // Conversion to a primitive type. FromType can be another
+  // primitive type, or a record/array.
+  //
+  // Same thing for floats, but we need the target
+  // semantics here.
+  if (*ToT == PT_Float) {
+    const auto *TargetSemantics = &Ctx.getFloatSemantics(ToType);
+    return this->emitBitCastFP(TargetSemantics, E);
+  }
+
+  return this->emitBitCast(*ToT, E);
+}
+
 template <class Emitter>
 bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
   auto *SubExpr = CE->getSubExpr();
@@ -83,6 +130,9 @@
         });
   }
 
+  case CK_LValueToRValueBitCast:
+    return this->emitBuiltinBitCast(CE);
+
   case CK_UncheckedDerivedToBase:
   case CK_DerivedToBase: {
     if (!this->visit(SubExpr))
@@ -137,6 +187,7 @@
   case CK_NonAtomicToAtomic:
   case CK_NoOp:
   case CK_UserDefinedConversion:
+  case CK_BitCast:
     return this->visit(SubExpr);
 
   case CK_IntegralToBoolean:
@@ -1451,6 +1502,8 @@
     return this->visit(CE);
   } else if (const auto *DIE = dyn_cast<CXXDefaultInitExpr>(Initializer)) {
     return this->visitInitializer(DIE->getExpr());
+  } else if (const auto *BBCE = dyn_cast<BuiltinBitCastExpr>(Initializer)) {
+    return this->visit(BBCE);
   } else if (const auto *CE = dyn_cast<CastExpr>(Initializer)) {
     return this->visitInitializer(CE->getSubExpr());
   } else if (const auto *CE = dyn_cast<CXXBindTemporaryExpr>(Initializer)) {
Index: clang/lib/AST/Interp/Boolean.h
===================================================================
--- clang/lib/AST/Interp/Boolean.h
+++ clang/lib/AST/Interp/Boolean.h
@@ -105,6 +105,13 @@
     return Boolean(!Value.isZero());
   }
 
+  static Boolean bitcastFromMemory(const std::byte *Buff) {
+    bool Val = static_cast<bool>(*Buff);
+    return Boolean(Val);
+  }
+
+  void bitcastToMemory(std::byte *Buff) { std::memcpy(Buff, &V, sizeof(V)); }
+
   static Boolean zero() { return from(false); }
 
   template <typename T>
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to