[clang] [clang][Interp] Implement __builtin_bit_cast (PR #68288)

Timm Baeder via cfe-commits cfe-commits at lists.llvm.org
Mon Nov 27 02:11:01 PST 2023


Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/68288 at github.com>


https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/68288

>From d366ce702d40dbd9b1068d51ebb2124a90526a01 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Tue, 11 Jul 2023 13:45:04 +0200
Subject: [PATCH 01/12] Bitcasts

---
 clang/lib/AST/CMakeLists.txt               |   1 +
 clang/lib/AST/Interp/Boolean.h             |  15 +-
 clang/lib/AST/Interp/ByteCodeExprGen.cpp   |  71 +++
 clang/lib/AST/Interp/ByteCodeExprGen.h     |   1 +
 clang/lib/AST/Interp/Floating.h            |  11 +
 clang/lib/AST/Interp/Integral.h            |  19 +-
 clang/lib/AST/Interp/Interp.cpp            |  17 +
 clang/lib/AST/Interp/Interp.h              |  66 ++
 clang/lib/AST/Interp/InterpBitcast.cpp     | 482 +++++++++++++++
 clang/lib/AST/Interp/Opcodes.td            |  17 +
 clang/lib/AST/Interp/PrimType.h            |   4 +
 clang/test/AST/Interp/builtin-bit-cast.cpp | 683 +++++++++++++++++++++
 clang/test/AST/Interp/literals.cpp         |   5 +
 13 files changed, 1389 insertions(+), 3 deletions(-)
 create mode 100644 clang/lib/AST/Interp/InterpBitcast.cpp
 create mode 100644 clang/test/AST/Interp/builtin-bit-cast.cpp

diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt
index fe3f8c485ec1c56e..5a188b583022bda8 100644
--- a/clang/lib/AST/CMakeLists.txt
+++ b/clang/lib/AST/CMakeLists.txt
@@ -74,6 +74,7 @@ add_clang_library(clangAST
   Interp/Frame.cpp
   Interp/Function.cpp
   Interp/InterpBuiltin.cpp
+  Interp/InterpBitcast.cpp
   Interp/Floating.cpp
   Interp/Interp.cpp
   Interp/InterpBlock.cpp
diff --git a/clang/lib/AST/Interp/Boolean.h b/clang/lib/AST/Interp/Boolean.h
index 336f7941dfc479df..6531c5ac6945b37b 100644
--- a/clang/lib/AST/Interp/Boolean.h
+++ b/clang/lib/AST/Interp/Boolean.h
@@ -9,14 +9,15 @@
 #ifndef LLVM_CLANG_AST_INTERP_BOOLEAN_H
 #define LLVM_CLANG_AST_INTERP_BOOLEAN_H
 
-#include <cstddef>
-#include <cstdint>
 #include "Integral.h"
 #include "clang/AST/APValue.h"
+#include "clang/AST/ASTContext.h"
 #include "clang/AST/ComparisonCategories.h"
 #include "llvm/ADT/APSInt.h"
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/raw_ostream.h"
+#include <cstddef>
+#include <cstdint>
 
 namespace clang {
 namespace interp {
@@ -66,6 +67,9 @@ class Boolean final {
   Boolean toUnsigned() const { return *this; }
 
   constexpr static unsigned bitWidth() { return 1; }
+  constexpr static unsigned objectReprBits() { return 8; }
+  constexpr static unsigned valueReprBytes(const ASTContext &Ctx) { return 1; }
+
   bool isZero() const { return !V; }
   bool isMin() const { return isZero(); }
 
@@ -107,6 +111,13 @@ class Boolean final {
     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>
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 5dc1f9dfb10ff321..9956a01a5ed208ce 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -73,6 +73,74 @@ template <class Emitter> class OptionScope final {
 } // namespace interp
 } // namespace clang
 
+//  This function is constexpr if and only if To, From, and the types of
+//  all subobjects of To and From are types T such that...
+//  (3.1) - is_union_v<T> is false;
+//  (3.2) - is_pointer_v<T> is false;
+//  (3.3) - is_member_pointer_v<T> is false;
+//  (3.4) - is_volatile_v<T> is false; and
+//  (3.5) - T has no non-static data members of reference type
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::emitBuiltinBitCast(const CastExpr *E) {
+  const Expr *SubExpr = E->getSubExpr();
+  QualType FromType = SubExpr->getType();
+  QualType ToType = E->getType();
+  std::optional<PrimType> ToT = classify(ToType);
+
+  // FIXME: This is wrong. We need to do the bitcast and then
+  //   throw away the result, so we still get the diagnostics.
+  if (DiscardResult)
+    return this->discard(SubExpr);
+
+  if (ToType->isNullPtrType()) {
+    if (!this->discard(SubExpr))
+      return false;
+
+    return this->emitNullPtr(E);
+  }
+
+  if (FromType->isNullPtrType() && ToT) {
+    if (!this->discard(SubExpr))
+      return false;
+
+    return visitZeroInitializer(*ToT, ToType, E);
+  }
+  assert(!ToType->isReferenceType());
+
+  // Get a pointer to the value-to-cast on the stack.
+  if (!this->visit(SubExpr))
+    return false;
+
+  if (!ToT || ToT == PT_Ptr) {
+    // Conversion to an array or record type.
+    return this->emitBitCastPtr(E);
+  }
+
+  assert(ToT);
+
+  // 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);
+    CharUnits FloatSize = Ctx.getASTContext().getTypeSizeInChars(ToType);
+    return this->emitBitCastFP(TargetSemantics, FloatSize.getQuantity(), E);
+  }
+
+  bool ToTypeIsUChar = (ToType->isSpecificBuiltinType(BuiltinType::UChar) ||
+                        ToType->isSpecificBuiltinType(BuiltinType::Char_U));
+
+  if (!this->emitBitCast(*ToT, ToTypeIsUChar || ToType->isStdByteType(), E))
+    return false;
+
+  if (DiscardResult)
+    return this->emitPop(*ToT, E);
+
+  return true;
+}
+
 template <class Emitter>
 bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
   auto *SubExpr = CE->getSubExpr();
@@ -93,6 +161,9 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
         });
   }
 
+  case CK_LValueToRValueBitCast:
+    return this->emitBuiltinBitCast(CE);
+
   case CK_UncheckedDerivedToBase:
   case CK_DerivedToBase: {
     if (!this->visit(SubExpr))
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h
index bc1d5d11a1151356..cbb1b5d1aa04c4e2 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.h
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -288,6 +288,7 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
   bool emitRecordDestruction(const Descriptor *Desc);
   unsigned collectBaseOffset(const RecordType *BaseType,
                              const RecordType *DerivedType);
+  bool emitBuiltinBitCast(const CastExpr *E);
 
 protected:
   /// Variable to storage mapping.
diff --git a/clang/lib/AST/Interp/Floating.h b/clang/lib/AST/Interp/Floating.h
index e4ac76d8509fb838..130c4ae402a341b9 100644
--- a/clang/lib/AST/Interp/Floating.h
+++ b/clang/lib/AST/Interp/Floating.h
@@ -15,6 +15,7 @@
 
 #include "Primitives.h"
 #include "clang/AST/APValue.h"
+#include "clang/AST/ASTContext.h"
 #include "llvm/ADT/APFloat.h"
 
 namespace clang {
@@ -84,6 +85,12 @@ class Floating final {
   }
 
   unsigned bitWidth() const { return F.semanticsSizeInBits(F.getSemantics()); }
+  unsigned objectReprBits() { return F.semanticsSizeInBits(F.getSemantics()); }
+
+  unsigned valueReprBytes(const ASTContext &Ctx) {
+    return Ctx.toCharUnitsFromBits(F.semanticsSizeInBits(F.getSemantics()))
+        .getQuantity();
+  }
 
   bool isSigned() const { return true; }
   bool isNegative() const { return F.isNegative(); }
@@ -134,6 +141,10 @@ class Floating final {
 
     return Floating(APFloat(Sem, API));
   }
+  void bitcastToMemory(std::byte *Buff) {
+    llvm::APInt API = F.bitcastToAPInt();
+    llvm::StoreIntToMemory(API, (uint8_t *)Buff, bitWidth() / 8);
+  }
 
   // === Serialization support ===
   size_t bytesToSerialize() const {
diff --git a/clang/lib/AST/Interp/Integral.h b/clang/lib/AST/Interp/Integral.h
index cc1cab8f39fb1e5f..2e47c2eaeeaa78cf 100644
--- a/clang/lib/AST/Interp/Integral.h
+++ b/clang/lib/AST/Interp/Integral.h
@@ -13,8 +13,9 @@
 #ifndef LLVM_CLANG_AST_INTERP_INTEGRAL_H
 #define LLVM_CLANG_AST_INTERP_INTEGRAL_H
 
-#include "clang/AST/ComparisonCategories.h"
 #include "clang/AST/APValue.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ComparisonCategories.h"
 #include "llvm/ADT/APSInt.h"
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/raw_ostream.h"
@@ -119,6 +120,10 @@ template <unsigned Bits, bool Signed> class Integral final {
   }
 
   constexpr static unsigned bitWidth() { return Bits; }
+  constexpr static unsigned objectReprBits() { return Bits; }
+  constexpr static unsigned valueReprBytes(const ASTContext &Ctx) {
+    return Ctx.toCharUnitsFromBits(Bits).getQuantity();
+  }
 
   bool isZero() const { return !V; }
 
@@ -185,6 +190,18 @@ template <unsigned Bits, bool Signed> class Integral final {
     return Integral(Value);
   }
 
+  static Integral bitcastFromMemory(const std::byte *Buff) {
+    ReprT V;
+
+    std::memcpy(&V, Buff, sizeof(ReprT));
+    return Integral(V);
+  }
+
+  void bitcastToMemory(std::byte *Buff) const {
+    assert(Buff);
+    std::memcpy(Buff, &V, sizeof(ReprT));
+  }
+
   static bool inRange(int64_t Value, unsigned NumBits) {
     return CheckRange<ReprT, Min, Max>(Value);
   }
diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index 13b77e9a87725c73..7fa121fc823da198 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -607,7 +607,24 @@ bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR) {
       return false;
     }
   }
+  return false;
+}
+
+bool CheckBitcast(InterpState &S, CodePtr OpPC, unsigned IndeterminateBits,
+                  bool TargetIsUCharOrByte) {
+
+  // This is always fine.
+  if (IndeterminateBits == 0)
+    return true;
+
+  // Indeterminate bits can only be bitcast to unsigned char or std::byte.
+  if (TargetIsUCharOrByte)
+    return true;
 
+  const Expr *E = S.Current->getExpr(OpPC);
+  QualType ExprType = E->getType();
+  S.FFDiag(E, diag::note_constexpr_bit_cast_indet_dest)
+      << ExprType << S.getLangOpts().CharIsSigned << E->getSourceRange();
   return false;
 }
 
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 4f7778bdd2ff3337..4bf2c83baa2cc712 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -187,6 +187,9 @@ bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result,
 /// Checks why the given DeclRefExpr is invalid.
 bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR);
 
+bool CheckBitcast(InterpState &S, CodePtr OpPC, unsigned IndeterminateBits,
+                  bool TargetIsUCharOrByte);
+
 /// Interpreter entry point.
 bool Interpret(InterpState &S, APValue &Result);
 
@@ -198,6 +201,18 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
 bool InterpretOffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E,
                        llvm::ArrayRef<int64_t> ArrayIndices, int64_t &Result);
 
+/// 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, CodePtr OpPC, const Pointer &P, std::byte *Buff,
+               size_t BuffSize, unsigned &IndeterminateBits);
+
+/// 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,
+                    CodePtr PC);
+
 enum class ArithOp { Add, Sub };
 
 //===----------------------------------------------------------------------===//
@@ -1561,6 +1576,57 @@ template <PrimType TIn, PrimType TOut> bool Cast(InterpState &S, CodePtr OpPC) {
   return true;
 }
 
+template <PrimType Name, class ToT = typename PrimConv<Name>::T>
+bool BitCast(InterpState &S, CodePtr OpPC, bool TargetIsUCharOrByte) {
+  const Pointer &FromPtr = S.Stk.pop<Pointer>();
+
+  size_t BuffSize = ToT::valueReprBytes(S.getCtx());
+  std::vector<std::byte> Buff(BuffSize);
+  unsigned IndeterminateBits = 0;
+
+  if (!DoBitCast(S, OpPC, FromPtr, Buff.data(), BuffSize, IndeterminateBits))
+    return false;
+
+  if (!CheckBitcast(S, OpPC, IndeterminateBits, TargetIsUCharOrByte))
+    return false;
+
+  S.Stk.push<ToT>(ToT::bitcastFromMemory(Buff.data()));
+  return true;
+}
+
+/// Bitcast TO a float.
+inline bool BitCastFP(InterpState &S, CodePtr OpPC,
+                      const llvm::fltSemantics *Sem, uint32_t TargetSize) {
+  const Pointer &FromPtr = S.Stk.pop<Pointer>();
+
+  std::vector<std::byte> Buff(TargetSize);
+  unsigned IndeterminateBits = 0;
+
+  if (!DoBitCast(S, OpPC, FromPtr, Buff.data(), TargetSize, IndeterminateBits))
+    return false;
+
+  if (!CheckBitcast(S, OpPC, IndeterminateBits, /*TargetIsUCharOrByte=*/false))
+    return false;
+
+  S.Stk.push<Floating>(Floating::bitcastFromMemory(Buff.data(), *Sem));
+  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>();
+
+  // FIXME: We should CheckLoad() for FromPtr and ToPtr here, I think.
+  if (!DoBitCastToPtr(S, FromPtr, ToPtr, OpPC))
+    return false;
+
+  return true;
+}
+
 /// 1) Pops a Floating from the stack.
 /// 2) Pushes a new floating on the stack that uses the given semantics.
 inline bool CastFP(InterpState &S, CodePtr OpPC, const llvm::fltSemantics *Sem,
diff --git a/clang/lib/AST/Interp/InterpBitcast.cpp b/clang/lib/AST/Interp/InterpBitcast.cpp
new file mode 100644
index 0000000000000000..91326fc8e7880808
--- /dev/null
+++ b/clang/lib/AST/Interp/InterpBitcast.cpp
@@ -0,0 +1,482 @@
+//===--- InterpBitcast.cpp - Interpreter for the constexpr VM ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "Boolean.h"
+#include "Interp.h"
+#include "PrimType.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecordLayout.h"
+#include "clang/Basic/Builtins.h"
+#include "clang/Basic/TargetInfo.h"
+
+namespace clang {
+namespace interp {
+
+// TODO: Try to e-duplicate the primitive and composite versions.
+
+/// Used to iterate over pointer fields.
+using DataFunc =
+    llvm::function_ref<bool(const Pointer &P, PrimType Ty, size_t Offset)>;
+
+#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)
+
+/// Float is a special case that sometimes needs the floating point semantics
+/// to be available.
+#define BITCAST_TYPE_SWITCH_WITH_FLOAT(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)                                             \
+      TYPE_SWITCH_CASE(PT_Float, B)                                            \
+    default:                                                                   \
+      llvm_unreachable("Unhandled bitcast type");                              \
+    }                                                                          \
+  } while (0)
+
+/// 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]);
+}
+
+/// Track what bytes have been initialized to known values and which ones
+/// have indeterminate value.
+/// All offsets are in bytes.
+struct ByteTracker {
+  std::vector<bool> Initialized;
+  std::vector<std::byte> Data;
+
+  ByteTracker() = default;
+
+  size_t size() const {
+    assert(Initialized.size() == Data.size());
+    return Initialized.size();
+  }
+
+  std::byte *getBytes(size_t Offset) { return Data.data() + Offset; }
+  bool allInitialized(size_t Offset, size_t Size) const {
+    for (size_t I = Offset; I != (Size + Offset); ++I) {
+      if (!Initialized[I])
+        return false;
+    }
+    return true;
+  }
+
+  std::byte *getWritableBytes(size_t Offset, size_t Size, bool InitValue) {
+    assert(Offset >= Data.size());
+    assert(Size > 0);
+
+    size_t OldSize = Data.size();
+    Data.resize(Offset + Size);
+
+    // Everything from the old size to the new offset is indeterminate.
+    for (size_t I = OldSize; I != Offset; ++I)
+      Initialized.push_back(false);
+    for (size_t I = Offset; I != Offset + Size; ++I)
+      Initialized.push_back(InitValue);
+
+    return Data.data() + Offset;
+  }
+
+  void markUninitializedUntil(size_t Offset) {
+    assert(Offset >= Data.size());
+
+    size_t NBytes = Offset - Data.size();
+    for (size_t I = 0; I != NBytes; ++I)
+      Initialized.push_back(false);
+    Data.resize(Offset);
+  }
+
+  void zeroUntil(size_t Offset) {
+    assert(Offset >= Data.size());
+
+    assert(Data.size() == Initialized.size());
+    size_t NBytes = Offset - Data.size();
+    for (size_t I = 0; I != NBytes; ++I) {
+      Initialized.push_back(true);
+      Data.push_back(std::byte{0});
+    }
+  }
+};
+
+struct BitcastBuffer {
+  std::byte *Buff;
+  size_t ByteOffset = 0;
+  size_t Offset = 0;
+  size_t BuffSize;
+  unsigned IndeterminateBits = 0;
+  bool BigEndian;
+
+  constexpr BitcastBuffer(std::byte *Buff, size_t BuffSize, bool BigEndian)
+      : Buff(Buff), BuffSize(BuffSize), BigEndian(BigEndian) {}
+
+  std::byte *getBytes(size_t ByteOffset, size_t N) {
+    assert(ByteOffset >= this->ByteOffset && "we don't support stepping back");
+
+    // All untouched bits before the requested bit offset
+    // are indeterminate values. This will be important later,
+    // because they can't be read into non-uchar/non-std::byte
+    // values.
+    IndeterminateBits += (ByteOffset - this->ByteOffset);
+
+    size_t OldOffset = this->Offset;
+
+    this->Offset += N;
+    this->ByteOffset = ByteOffset + N;
+
+    if (BigEndian)
+      return Buff + BuffSize - OldOffset - N;
+
+    // Little Endian target.
+    return Buff + OldOffset;
+  }
+};
+
+/// We use this to recursively iterate over all fields and elemends of a pointer
+/// and extract relevant data for a bitcast.
+static bool enumerateData(const Pointer &P, const Context &Ctx, size_t Offset,
+                          DataFunc F) {
+  const Descriptor *FieldDesc = P.getFieldDesc();
+  assert(FieldDesc);
+
+  // Primitives.
+  if (FieldDesc->isPrimitive())
+    return F(P, *Ctx.classify(FieldDesc->getType()), Offset);
+
+  // Primitive arrays.
+  if (FieldDesc->isPrimitiveArray()) {
+    QualType ElemType =
+        FieldDesc->getType()->getAsArrayTypeUnsafe()->getElementType();
+    size_t ElemSize =
+        Ctx.getASTContext().getTypeSizeInChars(ElemType).getQuantity();
+    PrimType ElemT = *Ctx.classify(ElemType);
+    for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) {
+      if (!F(P.atIndex(I), ElemT, Offset))
+        return false;
+      Offset += ElemSize;
+    }
+    return true;
+  }
+
+  // Composite arrays.
+  if (FieldDesc->isCompositeArray()) {
+    QualType ElemType =
+        FieldDesc->getType()->getAsArrayTypeUnsafe()->getElementType();
+    size_t ElemSize =
+        Ctx.getASTContext().getTypeSizeInChars(ElemType).getQuantity();
+    for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) {
+      enumerateData(P.atIndex(I).narrow(), Ctx, Offset, F);
+      Offset += ElemSize;
+    }
+    return true;
+  }
+
+  // Records.
+  if (FieldDesc->isRecord()) {
+    const Record *R = FieldDesc->ElemRecord;
+    const ASTRecordLayout &Layout =
+        Ctx.getASTContext().getASTRecordLayout(R->getDecl());
+    for (const auto &B : R->bases()) {
+      Pointer Elem = P.atField(B.Offset);
+      size_t ByteOffset =
+          Offset +
+          Layout.getBaseClassOffset(cast<CXXRecordDecl>(B.Decl)).getQuantity();
+      if (!enumerateData(Elem, Ctx, ByteOffset, F))
+        return false;
+    }
+    // TODO: Virtual bases?
+
+    for (unsigned I = 0; I != R->getNumFields(); ++I) {
+      Pointer Elem = P.atField(R->getField(I)->Offset);
+      CharUnits FieldOffset =
+          Ctx.getASTContext().toCharUnitsFromBits(Layout.getFieldOffset(I));
+      size_t ByteOffset = Offset + FieldOffset.getQuantity();
+      if (!enumerateData(Elem, Ctx, ByteOffset, F))
+        return false;
+    }
+    return true;
+  }
+
+  llvm_unreachable("Unhandled data type");
+}
+
+static bool enumeratePointerFields(const Pointer &P, const Context &Ctx,
+                                   DataFunc F) {
+  return enumerateData(P, Ctx, 0, F);
+}
+
+//  This function is constexpr if and only if To, From, and the types of
+//  all subobjects of To and From are types T such that...
+//  (3.1) - is_union_v<T> is false;
+//  (3.2) - is_pointer_v<T> is false;
+//  (3.3) - is_member_pointer_v<T> is false;
+//  (3.4) - is_volatile_v<T> is false; and
+//  (3.5) - T has no non-static data members of reference type
+//
+// NOTE: This is a version of checkBitCastConstexprEligibilityType() in
+// ExprConstant.cpp.
+static bool CheckBitcastType(InterpState &S, CodePtr OpPC, QualType T,
+                             bool IsToType) {
+  enum {
+    E_Union = 0,
+    E_Pointer,
+    E_MemberPointer,
+    E_Volatile,
+    E_Reference,
+  };
+  enum { C_Member, C_Base };
+
+  auto diag = [&](int Reason) -> bool {
+    const Expr *E = S.Current->getExpr(OpPC);
+    S.FFDiag(E, diag::note_constexpr_bit_cast_invalid_type)
+        << static_cast<int>(IsToType) << (Reason == E_Reference) << Reason
+        << E->getSourceRange();
+    return false;
+  };
+  auto note = [&](int Construct, QualType NoteType, SourceRange NoteRange) {
+    S.Note(NoteRange.getBegin(), diag::note_constexpr_bit_cast_invalid_subtype)
+        << NoteType << Construct << T << NoteRange;
+    return false;
+  };
+
+  T = T.getCanonicalType();
+
+  if (T->isUnionType())
+    return diag(E_Union);
+  if (T->isPointerType())
+    return diag(E_Pointer);
+  if (T->isMemberPointerType())
+    return diag(E_MemberPointer);
+  if (T.isVolatileQualified())
+    return diag(E_Volatile);
+
+  if (const RecordDecl *RD = T->getAsRecordDecl()) {
+    if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
+      for (const CXXBaseSpecifier &BS : CXXRD->bases()) {
+        if (!CheckBitcastType(S, OpPC, BS.getType(), IsToType))
+          return note(C_Base, BS.getType(), BS.getBeginLoc());
+      }
+    }
+    for (const FieldDecl *FD : RD->fields()) {
+      if (FD->getType()->isReferenceType())
+        return diag(E_Reference);
+      if (!CheckBitcastType(S, OpPC, FD->getType(), IsToType))
+        return note(C_Member, FD->getType(), FD->getSourceRange());
+    }
+  }
+
+  if (T->isArrayType() &&
+      !CheckBitcastType(S, OpPC, S.getCtx().getBaseElementType(T), IsToType))
+    return false;
+
+  return true;
+}
+
+/// Bitcast all fields from \p P into \p Buff.
+/// This is used for bitcasting TO a single primitive value.
+bool DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &P, std::byte *Buff,
+               size_t BuffSize, unsigned &IndeterminateBits) {
+  assert(P.isLive());
+  assert(Buff);
+  assert(BuffSize > 0);
+
+  BitcastBuffer F(Buff, BuffSize, S.getCtx().getTargetInfo().isBigEndian());
+
+  if (!CheckBitcastType(S, OpPC, P.getType(), /*IsToType=*/false))
+    return false;
+
+  const Context &Ctx = S.getContext();
+  const ASTContext &ASTCtx = Ctx.getASTContext();
+  uint64_t PointerSize =
+      ASTCtx
+          .toCharUnitsFromBits(
+              ASTCtx.getTargetInfo().getPointerWidth(LangAS::Default))
+          .getQuantity();
+
+  bool Success = enumeratePointerFields(
+      P, S.getContext(),
+      [&](const Pointer &Ptr, PrimType T, size_t ByteOffset) -> bool {
+        if (!Ptr.isInitialized())
+          return false;
+        if (T == PT_Ptr) {
+          assert(Ptr.getType()->isNullPtrType());
+          std::byte *M = F.getBytes(ByteOffset, PointerSize);
+          std::memset(M, 0, PointerSize);
+          return true;
+        }
+
+        BITCAST_TYPE_SWITCH_WITH_FLOAT(T, {
+          T Val = Ptr.deref<T>();
+          unsigned ObjectReprBytes =
+              ASTCtx.getTypeSizeInChars(Ptr.getType()).getQuantity();
+          std::byte *M = F.getBytes(ByteOffset, ObjectReprBytes);
+          Val.bitcastToMemory(M);
+        });
+        return true;
+      });
+
+  IndeterminateBits = F.IndeterminateBits;
+  return Success;
+}
+
+//  This function is constexpr if and only if To, From, and the types of
+//  all subobjects of To and From are types T such that...
+//  (3.1) - is_union_v<T> is false;
+//  (3.2) - is_pointer_v<T> is false;
+//  (3.3) - is_member_pointer_v<T> is false;
+//  (3.4) - is_volatile_v<T> is false; and
+//  (3.5) - T has no non-static data members of reference type
+bool DoBitCastToPtr(InterpState &S, const Pointer &P, Pointer &DestPtr,
+                    CodePtr OpPC) {
+  assert(P.isLive());
+  assert(DestPtr.isLive());
+
+  QualType FromType = P.getType();
+  QualType ToType = DestPtr.getType();
+
+  if (!CheckBitcastType(S, OpPC, FromType, /*IsToType=*/false))
+    return false;
+
+  if (!CheckBitcastType(S, OpPC, ToType, /*IsToType=*/true))
+    return false;
+
+  const Context &Ctx = S.getContext();
+  const ASTContext &ASTCtx = Ctx.getASTContext();
+  uint64_t PointerSize =
+      ASTCtx
+          .toCharUnitsFromBits(
+              ASTCtx.getTargetInfo().getPointerWidth(LangAS::Default))
+          .getQuantity();
+  bool BigEndian = ASTCtx.getTargetInfo().isBigEndian();
+
+  ByteTracker Bytes;
+  enumeratePointerFields(
+      P, S.getContext(),
+      [&](const Pointer &P, PrimType T, size_t ByteOffset) -> bool {
+        bool PtrInitialized = P.isInitialized();
+        if (!PtrInitialized) {
+          Bytes.markUninitializedUntil(ByteOffset + primSize(T));
+          return true;
+        }
+
+        assert(P.isInitialized());
+        // nullptr_t is a PT_Ptr for us, but it's still not std::is_pointer_v.
+        if (T == PT_Ptr) {
+          assert(P.getType()->isNullPtrType());
+          std::byte *M = Bytes.getWritableBytes(ByteOffset, PointerSize,
+                                                /*InitValue=*/true);
+          std::memset(M, 0, PointerSize);
+          return true;
+        }
+        BITCAST_TYPE_SWITCH_WITH_FLOAT(T, {
+          T Val = P.deref<T>();
+          unsigned ObjectReprBytes =
+              ASTCtx.getTypeSizeInChars(P.getType()).getQuantity();
+          unsigned ValueReprBytes = Val.valueReprBytes(ASTCtx);
+          assert(ObjectReprBytes >= ValueReprBytes);
+
+          std::byte *Dest = Bytes.getWritableBytes(ByteOffset, ValueReprBytes,
+                                                   PtrInitialized);
+          Val.bitcastToMemory(Dest);
+          Bytes.zeroUntil(ByteOffset + ObjectReprBytes);
+
+          if (BigEndian)
+            swapBytes(Dest, ValueReprBytes);
+        });
+        return true;
+      });
+
+  bool Success = enumeratePointerFields(
+      DestPtr, S.getContext(),
+      [&](const Pointer &P, PrimType T, size_t ByteOffset) -> bool {
+        if (T == PT_Float) {
+          const QualType FloatType = P.getFieldDesc()->getType();
+          const auto &Sem = ASTCtx.getFloatTypeSemantics(FloatType);
+          size_t ValueReprBytes =
+              ASTCtx.toCharUnitsFromBits(APFloat::semanticsSizeInBits(Sem))
+                  .getQuantity();
+
+          std::byte *M = Bytes.getBytes(ByteOffset);
+
+          if (BigEndian)
+            swapBytes(M, ValueReprBytes);
+          P.deref<Floating>() = Floating::bitcastFromMemory(M, Sem);
+          P.initialize();
+          return true;
+        }
+        if (T == PT_Ptr) {
+          assert(P.getType()->isNullPtrType());
+          // Just need to write out a nullptr.
+          P.deref<Pointer>() = Pointer();
+          P.initialize();
+          return true;
+        }
+
+        BITCAST_TYPE_SWITCH(T, {
+          T &Val = P.deref<T>();
+
+          size_t ValueReprBytes = T::valueReprBytes(ASTCtx);
+          // Check if any of the bits we're about to read are uninitialized.
+          bool HasIndeterminateBytes =
+              !Bytes.allInitialized(ByteOffset, ValueReprBytes);
+
+          if (HasIndeterminateBytes) {
+            // Always an error, unless the type of the field we're reading is
+            // either unsigned char or std::byte.
+            bool TargetIsUCharOrBytes =
+                (ValueReprBytes == 1 &&
+                 (P.getType()->isSpecificBuiltinType(BuiltinType::UChar) ||
+                  P.getType()->isSpecificBuiltinType(BuiltinType::Char_U) ||
+                  P.getType()->isStdByteType()));
+
+            if (!TargetIsUCharOrBytes) {
+              const Expr *E = S.Current->getExpr(OpPC);
+              QualType ExprType = P.getType();
+              S.FFDiag(E, diag::note_constexpr_bit_cast_indet_dest)
+                  << ExprType << S.getLangOpts().CharIsSigned
+                  << E->getSourceRange();
+              return false;
+            }
+          }
+
+          std::byte *M = Bytes.getBytes(ByteOffset);
+          if (BigEndian)
+            swapBytes(M, ValueReprBytes);
+          Val = T::bitcastFromMemory(M);
+
+          if (!HasIndeterminateBytes)
+            P.initialize();
+        });
+        return true;
+      });
+
+  return Success;
+}
+} // namespace interp
+} // namespace clang
diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td
index 69068e87d5720ab9..2c1e39621d79fe1e 100644
--- a/clang/lib/AST/Interp/Opcodes.td
+++ b/clang/lib/AST/Interp/Opcodes.td
@@ -585,6 +585,23 @@ def Cast: Opcode {
   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;
+  let Args = [ArgBool];
+}
+
+def BitCastPtr : Opcode;
+
+def BitCastFP : Opcode {
+  let Types = [];
+  let Args = [ArgFltSemantics, ArgUint32];
+}
+
 def CastFP : Opcode {
   let Types = [];
   let Args = [ArgFltSemantics, ArgRoundingMode];
diff --git a/clang/lib/AST/Interp/PrimType.h b/clang/lib/AST/Interp/PrimType.h
index 8c5e87f37be18673..66f4b747a13db4ce 100644
--- a/clang/lib/AST/Interp/PrimType.h
+++ b/clang/lib/AST/Interp/PrimType.h
@@ -87,6 +87,10 @@ template <> struct PrimConv<PT_FnPtr> {
 /// 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 *);
diff --git a/clang/test/AST/Interp/builtin-bit-cast.cpp b/clang/test/AST/Interp/builtin-bit-cast.cpp
new file mode 100644
index 0000000000000000..c5b8ca12570a138a
--- /dev/null
+++ b/clang/test/AST/Interp/builtin-bit-cast.cpp
@@ -0,0 +1,683 @@
+// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -fexperimental-new-constant-interpreter %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=ref -std=c++2a -fsyntax-only -triple aarch64_be-linux-gnu %s
+// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -fexperimental-new-constant-interpreter -triple powerpc64le-unknown-unknown -mabi=ieeelongdouble %s
+// RUN: %clang_cc1 -verify=ref -std=c++2a -fsyntax-only -triple powerpc64le-unknown-unknown -mabi=ieeelongdouble %s
+// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -fexperimental-new-constant-interpreter -triple powerpc64-unknown-unknown -mabi=ieeelongdouble %s
+// RUN: %clang_cc1 -verify=ref -std=c++2a -fsyntax-only -triple powerpc64-unknown-unknown -mabi=ieeelongdouble %s
+
+/// FIXME: This is a version of
+///   clang/test/SemaCXX/constexpr-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
+
+typedef decltype(nullptr) nullptr_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); // ref-note 2{{indeterminate value can only initialize}} \
+                                       // expected-note 2{{indeterminate value can only initialize}}
+}
+
+template <class Intermediate, class Init>
+constexpr bool round_trip(const Init &init) {
+  return bit_cast<Init>(bit_cast<Intermediate>(init)) == init;
+}
+
+/// We can ignore it.
+constexpr int foo() {
+  (void)__builtin_bit_cast(int, 3);
+  return 1;
+}
+static_assert(foo() == 1, "");
+
+namespace Ints {
+  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));
+}
+
+namespace FloatToDouble {
+  constexpr float F1[] = {1.0f, 2.0f};
+  constexpr double D1 = __builtin_bit_cast(double, F1);
+  static_assert(D1 > 0);
+}
+
+namespace Arrays {
+  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));
+};
+
+
+
+void test_array() {
+  constexpr unsigned char input[] = {0xCA, 0xFE, 0xBA, 0xBE};
+  constexpr unsigned expected = LITTLE_END ? 0xBEBAFECA : 0xCAFEBABE;
+  static_assert(bit_cast<unsigned>(input) == expected);
+}
+
+
+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 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
+}
+
+
+
+#ifdef __CHAR_UNSIGNED__
+// ref-note at +5 {{indeterminate value can only initialize an object of type 'unsigned char', 'char', or 'std::byte'; 'unsigned long' is invalid}}
+#else
+// ref-note at +3 {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'unsigned long' is invalid}}
+#endif
+// ref-error at +1 {{constexpr variable 'test_from_nullptr' must be initialized by a constant expression}}
+constexpr unsigned long test_from_nullptr = __builtin_bit_cast(unsigned long, nullptr);
+
+constexpr int test_from_nullptr_pass = (__builtin_bit_cast(unsigned char[8], nullptr), 0);
+
+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();
+
+namespace IndeterminateToPrimitive {
+  struct S {
+    bool a;
+    // One byte of padding.
+    short b;
+  };
+  constexpr S s{true, 12};
+
+  static_assert(sizeof(S) == sizeof(int), "");
+  constexpr int A = __builtin_bit_cast(int, s); // ref-error {{must be initialized by a constant expression}} \
+                                                // ref-note {{indeterminate value}} \
+                                                // expected-error {{must be initialized by a constant expression}} \
+                                                // expected-note {{indeterminate value}}
+}
+
+namespace test_partially_initialized {
+  struct pad {
+    signed char x;
+    int y;
+  };
+
+  struct no_pad {
+    signed char x;
+    signed char p1, p2, p3;
+    int y;
+  };
+
+  static_assert(sizeof(pad) == sizeof(no_pad));
+
+  constexpr pad pir{4, 4};
+  // expected-error at +2 {{constexpr variable 'piw' must be initialized by a constant expression}}
+  // expected-note at +1 {{in call to 'bit_cast(pir)'}}
+  constexpr int piw = bit_cast<no_pad>(pir).x;
+  // ref-error at -1 {{constexpr variable 'piw' must be initialized by a constant expression}}
+  // ref-note at -2 {{in call to}}
+
+  // expected-error at +2 {{constexpr variable 'bad' must be initialized by a constant expression}}
+  // expected-note at +1 {{in call to 'bit_cast(pir)'}}
+  constexpr no_pad bad = bit_cast<no_pad>(pir);
+  // ref-error at -1 {{constexpr variable 'bad' must be initialized by a constant expression}}
+  // ref-note at -2 {{in call to}}
+
+  constexpr pad fine = bit_cast<pad>(no_pad{1, 2, 3, 4, 5});
+  static_assert(fine.x == 1 && fine.y == 5);
+}
+
+namespace bad_types {
+  union X {
+    int x;
+  };
+
+  struct G {
+    int g;
+  };
+  // ref-error at +2 {{constexpr variable 'g' must be initialized by a constant expression}}
+  // ref-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 -1 {{constexpr variable 'g' must be initialized by a constant expression}}
+  // expected-note at -2 {{bit_cast from a union type is not allowed in a constant expression}}
+
+  // ref-error at +2 {{constexpr variable 'x' must be initialized by a constant expression}}
+  // ref-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});
+  // expected-error at -1 {{constexpr variable 'x' must be initialized by a constant expression}}
+  // expected-note at -2 {{bit_cast to a union type is not allowed in a constant expression}}
+
+  struct has_pointer {
+    int *ptr; // ref-note 2{{invalid type 'int *' is a member of}} \
+              // expected-note 2{{invalid type 'int *' is a member of}}
+  };
+
+  // ref-error at +2 {{constexpr variable 'ptr' must be initialized by a constant expression}}
+  // ref-note at +1 {{bit_cast from a pointer type is not allowed in a constant expression}}
+  constexpr unsigned long ptr = __builtin_bit_cast(unsigned long, has_pointer{0});
+  // expected-error at -1 {{constexpr variable 'ptr' must be initialized by a constant expression}}
+  // expected-note at -2 {{bit_cast from a pointer type is not allowed in a constant expression}}
+
+
+  // ref-error at +2 {{constexpr variable 'hptr' must be initialized by a constant expression}}
+  // ref-note at +1 {{bit_cast to a pointer type is not allowed in a constant expression}}
+  constexpr has_pointer hptr =  __builtin_bit_cast(has_pointer, 0ul);
+  // expected-error at -1 {{constexpr variable 'hptr' must be initialized by a constant expression}}
+  // expected-note at -2 {{bit_cast to a pointer type is not allowed in a constant expression}}
+}
+
+namespace backtrace {
+  struct A {
+    int *ptr; // expected-note {{invalid type 'int *' is a member of 'backtrace::A'}} \
+              // ref-note {{invalid type 'int *' is a member of 'backtrace::A'}}
+  };
+
+  struct B {
+    A as[10]; // expected-note {{invalid type 'A[10]' is a member of 'backtrace::B'}} \
+              // ref-note {{invalid type 'A[10]' is a member of 'backtrace::B'}}
+  };
+
+    struct C : B { // expected-note {{invalid type 'B' is a base of 'backtrace::C'}} \
+                   // ref-note {{invalid type 'B' is a base of 'backtrace::C'}}
+  };
+
+  struct E {
+    unsigned long ar[10];
+  };
+
+  constexpr E e = __builtin_bit_cast(E, C{}); // expected-error {{must be initialized by a constant expression}} \
+                                              // expected-note {{bit_cast from a pointer type is not allowed}} \
+                                              // ref-error {{must be initialized by a constant expression}} \
+                                              // ref-note {{bit_cast from a pointer type is not allowed}}
+}
+
+namespace ReferenceMember {
+  struct ref_mem {
+    const int &rm;
+  };
+
+  typedef unsigned long ulong;
+  constexpr int global_int = 0;
+
+   constexpr ulong run_ref_mem = __builtin_bit_cast(ulong, ref_mem{global_int}); // ref-error {{must be initialized by a constant expression}} \
+                                                                                 // ref-note {{bit_cast from a type with a reference member}} \
+                                                                                 // expected-error {{must be initialized by a constant expression}} \
+                                                                                 // expected-note {{bit_cast from a type with a reference member}}
+}
+
+namespace FromUnion {
+  union u {
+    int im;
+  };
+
+  constexpr int run_u = __builtin_bit_cast(int, u{32}); // ref-error {{must be initialized by a constant expression}} \
+                                                        // ref-note {{bit_cast from a union type}} \
+                                                        // expected-error {{must be initialized by a constant expression}} \
+                                                        // expected-note {{bit_cast from a union type}}
+}
+
+
+struct vol_mem {
+   volatile int x; // expected-note {{invalid type 'volatile int' is a member of 'vol_mem'}}
+};
+
+namespace VolatileMember {
+  constexpr int run_vol_mem = __builtin_bit_cast(int, vol_mem{43}); // ref-error {{must be initialized by a constant expression}} \
+                                                                    // ref-note {{non-literal type 'vol_mem'}} \
+                                                                    // expected-error {{must be initialized by a constant expression}} \
+                                                                    // expected-note {{bit_cast from a volatile type}}
+}
+
+namespace MemberPointer {
+  /// FIXME: The diagnostic for bitcasts is properly implemented, but we lack support for member pointers.
+#if 0
+  struct mem_ptr {
+    int vol_mem::*x; // expected-note{{invalid type 'int vol_mem::*' is a member of 'mem_ptr'}}
+  };
+  constexpr int run_mem_ptr = __builtin_bit_cast(unsigned long, mem_ptr{nullptr}); // ref-error {{must be initialized by a constant expression}} \
+                                                                                   // ref-note {{bit_cast from a member pointer type}} \
+                                                                                   // expected-error {{must be initialized by a constant expression}} \
+                                                                                   // expected-note {{bit_cast from a member pointer type}}
+#endif
+}
+
+  struct A { char c; /* char padding : 8; */ short s; };
+  struct B { unsigned char x[4]; };
+
+  constexpr B one() {
+    A a = {1, 2};
+    return bit_cast<B>(a);
+  }
+  constexpr char good_one = one().x[0] + one().x[2] + one().x[3];
+  // ref-error at +2 {{constexpr variable 'bad_one' must be initialized by a constant expression}}
+  // ref-note at +1 {{read of uninitialized object is not allowed in a constant expression}}
+  constexpr char bad_one = one().x[1];
+  // expected-error at -1 {{constexpr variable 'bad_one' must be initialized by a constant expression}}
+  // expected-note at -2 {{read of uninitialized object is not allowed in a constant expression}}
+
+
+  constexpr A two() {
+    B b = one(); // b.x[1] is indeterminate.
+    b.x[0] = 'a';
+    b.x[2] = 1;
+    b.x[3] = 2;
+    return bit_cast<A>(b);
+  }
+  constexpr short good_two = two().c + two().s;
+
+
+  namespace std {
+  enum byte : unsigned char {};
+  }
+
+  enum my_byte : unsigned char {};
+
+  struct pad {
+    char a;
+    int b;
+  };
+
+  constexpr int ok_byte = (__builtin_bit_cast(std::byte[8], pad{1, 2}), 0);
+  constexpr int ok_uchar = (__builtin_bit_cast(unsigned char[8], pad{1, 2}), 0);
+
+#ifdef __CHAR_UNSIGNED__
+  // ref-note at +5 {{indeterminate value can only initialize an object of type 'unsigned char', 'char', or 'std::byte'; 'my_byte' is invalid}}}}
+#else
+  // ref-note at +3 {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'my_byte' is invalid}}
+#endif
+  // ref-error at +1 {{must be initialized by a constant expression}}
+  constexpr int bad_my_byte = (__builtin_bit_cast(my_byte[8], pad{1, 2}), 0); // {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'my_byte' is invalid}}
+
+#ifndef __CHAR_UNSIGNED__
+  // ref-error at +3 {{must be initialized by a constant expression}}
+  // ref-note at +2 {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'char' is invalid}}
+#endif
+  constexpr int bad_char =  (__builtin_bit_cast(char[8], pad{1, 2}), 0);
+
+  struct pad_buffer { unsigned char data[sizeof(pad)]; };
+  constexpr bool test_pad_buffer() {
+    pad x = {1, 2};
+    pad_buffer y = __builtin_bit_cast(pad_buffer, x);
+    pad z = __builtin_bit_cast(pad, y);
+    return x.a == z.a && x.b == z.b;
+  }
+  static_assert(test_pad_buffer());
+
+namespace ValueRepr {
+  /// FIXME: This is broken.
+  constexpr bool b = __builtin_bit_cast(bool, (char)123);  // ref-error {{must be initialized by a constant expression}} \
+                                                           // ref-note {{value 123 cannot be represented in type 'bool'}}
+}
+
+namespace FloatMember {
+  struct A {
+    float a;
+  };
+  struct B {
+    unsigned char a[4];
+  };
+
+  constexpr B b = __builtin_bit_cast(B, A{1.0});
+
+#if LITTLE_END
+  static_assert(b.a[0] == 0, "");
+  static_assert(b.a[1] == 0, "");
+  static_assert(b.a[2] == 128, "");
+  static_assert(b.a[3] == 63, "");
+#else
+  static_assert(b.a[0] == 63, "");
+  static_assert(b.a[1] == 128, "");
+  static_assert(b.a[2] == 0, "");
+  static_assert(b.a[3] == 0, "");
+#endif
+
+  constexpr A a = __builtin_bit_cast(A, B{{1 << 6, 0, 0, 1 << 6}});
+  static_assert(static_cast<int>(a.a) == 2, "");
+}
+
+namespace Misc {
+  struct A {
+    decltype(nullptr) a;
+  };
+  /// Not sure why this doesn't work in the current interpreter; GCC accepts it.
+  constexpr unsigned long long L = __builtin_bit_cast(unsigned long long, A{nullptr}); // ref-error {{must be initialized by a constant expression}} \
+                                                                                       // ref-note {{indeterminate value}}
+
+  /// Bitcast into a nullptr_t field.
+  struct B {
+    unsigned char a[8];
+  };
+
+  constexpr A a = __builtin_bit_cast(A, B{{0, 0, 0, 0, 0, 0, 0, 0}});
+  static_assert(a.a == nullptr, "");
+
+  /// Uninitialized local variable bitcast'ed to a uchar.
+  constexpr int primUChar() {
+    signed char A;
+    unsigned char B = __builtin_bit_cast(unsigned char, A);
+    /// FIXME: The new interpreter doesn't print the proper diagnostic here; The read from B
+    ///   should be uninitialized, since the bitcast returns indeterminate bits. However,
+    ///   the code uses primitive values and those are always initialized.
+    return B; // ref-note {{read of uninitialized object}}
+  }
+  static_assert(primUChar() == 0, ""); // ref-error {{not an integral constant expression}} \
+                                       // ref-note {{in call to}} \
+                                       // expected-error {{not an integral constant expression}}
+
+  constexpr int primUChar2() {
+    signed char A;
+    unsigned char B = __builtin_bit_cast(unsigned char, A) + 1;
+    /// Same problem as above, but the diagnostic of the current interpreter is just as bad as ours.
+    return B;
+  }
+  static_assert(primUChar2() == 0, ""); // ref-error {{not an integral constant expression}} \
+                                        // expected-error {{not an integral constant expression}}
+
+  /// This time, the uchar is in a struct.
+  constexpr int primUChar3() {
+    struct B {
+      unsigned char b;
+    };
+    signed char A;
+    B b = __builtin_bit_cast(B, A);
+    return b.b; // ref-note {{read of uninitialized object}} \
+                // expected-note {{read of uninitialized object}}
+  }
+  static_assert(primUChar3() == 0, ""); // ref-error {{not an integral constant expression}} \
+                                        // ref-note {{in call to}} \
+                                        // expected-error {{not an integral constant expression}} \
+                                        // expected-note {{in call to}}
+
+  /// Fully initialized local variable that should end up being un-initalized again because the
+  /// bit cast returns bits of indeterminate value.
+  constexpr int primUChar4() {
+    unsigned char c = 'a';
+    signed char cu;
+
+    c = __builtin_bit_cast(unsigned char, cu);
+
+    return c; // ref-note {{read of uninitialized object}}
+  }
+  static_assert(primUChar4() == 0, ""); // ref-error {{not an integral constant expression}} \
+                                        // ref-note {{in call to}} \
+                                        // expected-error {{not an integral constant expression}}
+
+
+  /// Casting an uninitialized struct.
+  constexpr int primUChar5(bool DoBC) {
+    struct A {
+      unsigned char a; // ref-note {{subobject declared here}}
+    };
+    struct B {
+      signed char b;
+    };
+
+    A a = {12};
+
+    if (DoBC) {
+      B b;
+      a = __builtin_bit_cast(A, b); // expected-note {{in call to}} \
+                                    // expected-note {{read of uninitialized object}} \
+                                    // ref-note {{subobject 'a' is not initialized}} \
+                                    // ref-note {{in call to}}
+    }
+
+    return a.a;
+  }
+  static_assert(primUChar5(false) == 12, "");
+  static_assert(primUChar5(true) == 12, ""); // expected-error {{not an integral constant expression}} \
+                                             // expected-note {{in call to 'primUChar5(true)'}} \
+                                             // ref-error {{not an integral constant expression}} \
+                                             // ref-note {{in call to 'primUChar5(true)'}}
+}
+
+
+constexpr unsigned char identity1a = 42;
+constexpr unsigned char identity1b = __builtin_bit_cast(unsigned char, identity1a);
+static_assert(identity1b == 42);
+
+#ifdef __PPC64__
+namespace LongDouble {
+  struct bytes {
+    unsigned char d[sizeof(long double)];
+  };
+
+  constexpr long double ld = 3.1425926539;
+  constexpr long double ldmax = __LDBL_MAX__;
+  static_assert(round_trip<bytes>(ld), "");
+  static_assert(round_trip<bytes>(ldmax), "");
+
+  constexpr bytes b = __builtin_bit_cast(bytes, ld);
+
+#if LITTLE_END
+static_assert(b.d[0] == 0);
+static_assert(b.d[1] == 0);
+static_assert(b.d[2] == 0);
+static_assert(b.d[3] == 0);
+static_assert(b.d[4] == 0);
+static_assert(b.d[5] == 0);
+
+static_assert(b.d[6] == 0);
+static_assert(b.d[7] == 144);
+static_assert(b.d[8] == 62);
+static_assert(b.d[9] == 147);
+static_assert(b.d[10] == 224);
+static_assert(b.d[11] == 121);
+static_assert(b.d[12] == 64);
+static_assert(b.d[13] == 146);
+static_assert(b.d[14] == 0);
+static_assert(b.d[15] == 64);
+#else
+static_assert(b.d[0] == 64);
+static_assert(b.d[1] == 0);
+static_assert(b.d[2] == 146);
+static_assert(b.d[3] == 64);
+static_assert(b.d[4] == 121);
+static_assert(b.d[5] == 224);
+static_assert(b.d[6] == 147);
+static_assert(b.d[7] == 62);
+static_assert(b.d[8] == 144);
+static_assert(b.d[9] == 0);
+
+static_assert(b.d[10] == 0);
+static_assert(b.d[11] == 0);
+static_assert(b.d[12] == 0);
+static_assert(b.d[13] == 0);
+static_assert(b.d[14] == 0);
+static_assert(b.d[15] == 0);
+#endif
+}
+#endif // __PPC64__
+
+#ifdef __x86_64
+namespace LongDoubleX86 {
+  struct bytes {
+    unsigned char d[16]; // ref-note {{declared here}}
+  };
+
+  constexpr long double ld = 3.1425926539;
+  static_assert(round_trip<bytes>(ld), "");
+
+  /// The current interpreter rejects this (probably because the APFloat only
+  /// uses 10 bytes to represent the value instead of the full 16 that the long double
+  /// takes up). MSVC and GCC accept it though.
+  constexpr bytes b = __builtin_bit_cast(bytes, ld); // ref-error {{must be initialized by a constant expression}} \
+                                                     // ref-note {{subobject 'd' is not initialized}}
+}
+#endif
+
+namespace StringLiterals {
+  template<int n>
+  struct StrBuff {
+    char data[n];
+  };
+
+  constexpr StrBuff<4> Foo = __builtin_bit_cast(StrBuff<4>, "foo"); // ref-error {{must be initialized by a constant expression}} \
+                                                                    // ref-note 4{{declared here}}
+  static_assert(Foo.data[0] == 'f', "");  // ref-error {{not an integral constant expression}} \
+                                          // ref-note {{initializer of 'Foo' is not a constant expression}}
+  static_assert(Foo.data[1] == 'o', "");  // ref-error {{not an integral constant expression}} \
+                                          // ref-note {{initializer of 'Foo' is not a constant expression}}
+  static_assert(Foo.data[2] == 'o', "");  // ref-error {{not an integral constant expression}} \
+                                          // ref-note {{initializer of 'Foo' is not a constant expression}}
+  static_assert(Foo.data[3] == '\0', ""); // ref-error {{not an integral constant expression}} \
+                                          // ref-note {{initializer of 'Foo' is not a constant expression}}
+};
diff --git a/clang/test/AST/Interp/literals.cpp b/clang/test/AST/Interp/literals.cpp
index 85adfe551384d274..6a881b0c1563adc0 100644
--- a/clang/test/AST/Interp/literals.cpp
+++ b/clang/test/AST/Interp/literals.cpp
@@ -148,6 +148,11 @@ constexpr const int *p = &m;
 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;
 }

>From 378efdba41268f05978c94fb58fcccf265c9675d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Thu, 5 Oct 2023 11:45:09 +0200
Subject: [PATCH 02/12] Implement bitfield bit casts to prim types

---
 clang/lib/AST/Interp/InterpBitcast.cpp     | 40 ++++++++++++++--------
 clang/lib/AST/Interp/Record.h              |  3 ++
 clang/test/AST/Interp/builtin-bit-cast.cpp | 25 ++++++++++++++
 3 files changed, 53 insertions(+), 15 deletions(-)

diff --git a/clang/lib/AST/Interp/InterpBitcast.cpp b/clang/lib/AST/Interp/InterpBitcast.cpp
index 91326fc8e7880808..5d5b5d549d566f35 100644
--- a/clang/lib/AST/Interp/InterpBitcast.cpp
+++ b/clang/lib/AST/Interp/InterpBitcast.cpp
@@ -136,7 +136,7 @@ struct BitcastBuffer {
   constexpr BitcastBuffer(std::byte *Buff, size_t BuffSize, bool BigEndian)
       : Buff(Buff), BuffSize(BuffSize), BigEndian(BigEndian) {}
 
-  std::byte *getBytes(size_t ByteOffset, size_t N) {
+  std::byte *getBytes(size_t ByteOffset, size_t NBytes, size_t NBits) {
     assert(ByteOffset >= this->ByteOffset && "we don't support stepping back");
 
     // All untouched bits before the requested bit offset
@@ -144,14 +144,15 @@ struct BitcastBuffer {
     // because they can't be read into non-uchar/non-std::byte
     // values.
     IndeterminateBits += (ByteOffset - this->ByteOffset);
+    IndeterminateBits += ((NBytes * 8) - NBits);
 
     size_t OldOffset = this->Offset;
 
-    this->Offset += N;
-    this->ByteOffset = ByteOffset + N;
+    this->Offset += NBytes;
+    this->ByteOffset = ByteOffset + NBytes;
 
     if (BigEndian)
-      return Buff + BuffSize - OldOffset - N;
+      return Buff + BuffSize - OldOffset - NBytes;
 
     // Little Endian target.
     return Buff + OldOffset;
@@ -213,7 +214,8 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, size_t Offset,
     // TODO: Virtual bases?
 
     for (unsigned I = 0; I != R->getNumFields(); ++I) {
-      Pointer Elem = P.atField(R->getField(I)->Offset);
+      const Record::Field *Fi = R->getField(I);
+      Pointer Elem = P.atField(Fi->Offset);
       CharUnits FieldOffset =
           Ctx.getASTContext().toCharUnitsFromBits(Layout.getFieldOffset(I));
       size_t ByteOffset = Offset + FieldOffset.getQuantity();
@@ -302,6 +304,7 @@ static bool CheckBitcastType(InterpState &S, CodePtr OpPC, QualType T,
 /// This is used for bitcasting TO a single primitive value.
 bool DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &P, std::byte *Buff,
                size_t BuffSize, unsigned &IndeterminateBits) {
+  llvm::errs() << __PRETTY_FUNCTION__ << "\n";
   assert(P.isLive());
   assert(Buff);
   assert(BuffSize > 0);
@@ -313,11 +316,10 @@ bool DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &P, std::byte *Buff,
 
   const Context &Ctx = S.getContext();
   const ASTContext &ASTCtx = Ctx.getASTContext();
-  uint64_t PointerSize =
-      ASTCtx
-          .toCharUnitsFromBits(
-              ASTCtx.getTargetInfo().getPointerWidth(LangAS::Default))
-          .getQuantity();
+  uint64_t PointerSizeBits =
+      ASTCtx.getTargetInfo().getPointerWidth(LangAS::Default);
+  uint64_t PointerSizeBytes =
+      ASTCtx.toCharUnitsFromBits(PointerSizeBits).getQuantity();
 
   bool Success = enumeratePointerFields(
       P, S.getContext(),
@@ -326,16 +328,24 @@ bool DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &P, std::byte *Buff,
           return false;
         if (T == PT_Ptr) {
           assert(Ptr.getType()->isNullPtrType());
-          std::byte *M = F.getBytes(ByteOffset, PointerSize);
-          std::memset(M, 0, PointerSize);
+          std::byte *M =
+              F.getBytes(ByteOffset, PointerSizeBytes, PointerSizeBits);
+          std::memset(M, 0, PointerSizeBytes);
           return true;
         }
 
+        CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(Ptr.getType());
+        unsigned ObjectReprBytes =
+            ASTCtx.getTypeSizeInChars(Ptr.getType()).getQuantity();
+        unsigned BitWidth;
+        if (const FieldDecl *FD = Ptr.getField(); FD && FD->isBitField())
+          BitWidth = FD->getBitWidthValue(ASTCtx);
+        else
+          BitWidth = ASTCtx.toBits(ObjectReprChars);
+
         BITCAST_TYPE_SWITCH_WITH_FLOAT(T, {
           T Val = Ptr.deref<T>();
-          unsigned ObjectReprBytes =
-              ASTCtx.getTypeSizeInChars(Ptr.getType()).getQuantity();
-          std::byte *M = F.getBytes(ByteOffset, ObjectReprBytes);
+          std::byte *M = F.getBytes(ByteOffset, ObjectReprBytes, BitWidth);
           Val.bitcastToMemory(M);
         });
         return true;
diff --git a/clang/lib/AST/Interp/Record.h b/clang/lib/AST/Interp/Record.h
index b0952af2d1ac6ccf..dbaa0eb3fed70a29 100644
--- a/clang/lib/AST/Interp/Record.h
+++ b/clang/lib/AST/Interp/Record.h
@@ -30,6 +30,9 @@ class Record final {
     unsigned Offset;
     Descriptor *Desc;
     bool isBitField() const { return Decl->isBitField(); }
+    unsigned getBitWidth(const ASTContext &C) const {
+      return Decl->getBitWidthValue(C);
+    }
   };
 
   /// Describes a base class.
diff --git a/clang/test/AST/Interp/builtin-bit-cast.cpp b/clang/test/AST/Interp/builtin-bit-cast.cpp
index c5b8ca12570a138a..f47591427c90015d 100644
--- a/clang/test/AST/Interp/builtin-bit-cast.cpp
+++ b/clang/test/AST/Interp/builtin-bit-cast.cpp
@@ -681,3 +681,28 @@ namespace StringLiterals {
   static_assert(Foo.data[3] == '\0', ""); // ref-error {{not an integral constant expression}} \
                                           // ref-note {{initializer of 'Foo' is not a constant expression}}
 };
+
+/// The current interpreter does not support bitcasts involving bitfields at all,
+/// so the following is mainly from comparing diagnostic output with GCC.
+namespace Bitfields {
+  struct S {
+    char a : 8;
+  };
+
+  constexpr S s{4};
+  constexpr char c = __builtin_bit_cast(char, s); // ref-error {{must be initialized by a constant expression}} \
+                                                  // ref-note {{bit_cast involving bit-field is not yet supported}} \
+                                                  // ref-note{{declared here}}
+  static_assert(c == 4, ""); // ref-error {{not an integral constant expression}} \
+                             // ref-note {{initializer of 'c' is not a constant expression}}
+
+
+  struct S2 {
+    char a : 4;
+  };
+  constexpr S2 s2{4};
+  constexpr char c2 = __builtin_bit_cast(char, s2); // expected-error {{must be initialized by a constant expression}} \
+                                                    // expected-note {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'char' is invalid}} \
+                                                    // ref-error {{must be initialized by a constant expression}} \
+                                                    // ref-note {{bit_cast involving bit-field is not yet supported}}
+}

>From 117029cf68887134e1ae98db890078e3d65740f1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Tue, 17 Oct 2023 16:19:04 +0200
Subject: [PATCH 03/12] More bitfield work

---
 clang/lib/AST/Interp/InterpBitcast.cpp     | 124 ++++++++++++---------
 clang/test/AST/Interp/builtin-bit-cast.cpp |  48 ++++++++
 2 files changed, 118 insertions(+), 54 deletions(-)

diff --git a/clang/lib/AST/Interp/InterpBitcast.cpp b/clang/lib/AST/Interp/InterpBitcast.cpp
index 5d5b5d549d566f35..60d9eb544a84602e 100644
--- a/clang/lib/AST/Interp/InterpBitcast.cpp
+++ b/clang/lib/AST/Interp/InterpBitcast.cpp
@@ -12,6 +12,7 @@
 #include "clang/AST/RecordLayout.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/TargetInfo.h"
+#include "llvm/ADT/BitVector.h"
 
 namespace clang {
 namespace interp {
@@ -20,7 +21,7 @@ namespace interp {
 
 /// Used to iterate over pointer fields.
 using DataFunc =
-    llvm::function_ref<bool(const Pointer &P, PrimType Ty, size_t Offset)>;
+    llvm::function_ref<bool(const Pointer &P, PrimType Ty, size_t BitOffset)>;
 
 #define BITCAST_TYPE_SWITCH(Expr, B)                                           \
   do {                                                                         \
@@ -65,14 +66,14 @@ static void swapBytes(std::byte *M, size_t N) {
     std::swap(M[I], M[N - 1 - I]);
 }
 
-/// Track what bytes have been initialized to known values and which ones
+/// Track what bits have been initialized to known values and which ones
 /// have indeterminate value.
-/// All offsets are in bytes.
-struct ByteTracker {
-  std::vector<bool> Initialized;
+/// All offsets are in bits.
+struct BitTracker {
+  llvm::BitVector Initialized;
   std::vector<std::byte> Data;
 
-  ByteTracker() = default;
+  BitTracker() = default;
 
   size_t size() const {
     assert(Initialized.size() == Data.size());
@@ -126,36 +127,53 @@ struct ByteTracker {
 };
 
 struct BitcastBuffer {
+  llvm::BitVector Data;
   std::byte *Buff;
-  size_t ByteOffset = 0;
-  size_t Offset = 0;
+  size_t BitOffset = 0;
   size_t BuffSize;
   unsigned IndeterminateBits = 0;
-  bool BigEndian;
 
-  constexpr BitcastBuffer(std::byte *Buff, size_t BuffSize, bool BigEndian)
-      : Buff(Buff), BuffSize(BuffSize), BigEndian(BigEndian) {}
-
-  std::byte *getBytes(size_t ByteOffset, size_t NBytes, size_t NBits) {
-    assert(ByteOffset >= this->ByteOffset && "we don't support stepping back");
+  BitcastBuffer(std::byte *Buff, size_t BuffSize)
+      : Buff(Buff), BuffSize(BuffSize) {}
+
+  void pushData(const std::byte *data, size_t BitOffset, size_t BitWidth) {
+    assert(BitOffset >= Data.size());
+    // First, fill up the bit vector until BitOffset. The bits are all 0
+    // but we record them as indeterminate.
+    {
+      size_t FillBits = BitOffset - Data.size();
+      IndeterminateBits += FillBits;
+      Data.resize(BitOffset, false);
+    }
 
-    // All untouched bits before the requested bit offset
-    // are indeterminate values. This will be important later,
-    // because they can't be read into non-uchar/non-std::byte
-    // values.
-    IndeterminateBits += (ByteOffset - this->ByteOffset);
-    IndeterminateBits += ((NBytes * 8) - NBits);
+    size_t BitsHandled = 0;
+    // Read all full bytes first
+    for (size_t I = 0; I != BitWidth / 8; ++I) {
+      for (unsigned X = 0; X != 8; ++X) {
+        Data.push_back((data[I] & std::byte(1 << X)) != std::byte{0});
+        ++BitsHandled;
+      }
+    }
 
-    size_t OldOffset = this->Offset;
+    // Rest of the bits.
+    assert((BitWidth - BitsHandled) < 8);
+    for (size_t I = 0, E = (BitWidth - BitsHandled); I != E; ++I) {
+      Data.push_back((data[BitWidth / 8] & std::byte(1 << I)) != std::byte{0});
+      ++BitsHandled;
+    }
+  }
 
-    this->Offset += NBytes;
-    this->ByteOffset = ByteOffset + NBytes;
+  void pushZeroes(size_t Amount) { Data.resize(Data.size() + Amount, false); }
 
-    if (BigEndian)
-      return Buff + BuffSize - OldOffset - NBytes;
+  void finish() {
+    // Fill up with zeroes until the buffer is BuffSize in size.
+    // The added bits are of indeterminate value.
+    assert(Data.size() <= (BuffSize * 8));
+    size_t Remainder = (BuffSize * 8) - Data.size();
+    for (size_t I = 0; I != Remainder; ++I)
+      Data.push_back(false);
 
-    // Little Endian target.
-    return Buff + OldOffset;
+    IndeterminateBits += Remainder;
   }
 };
 
@@ -174,13 +192,12 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, size_t Offset,
   if (FieldDesc->isPrimitiveArray()) {
     QualType ElemType =
         FieldDesc->getType()->getAsArrayTypeUnsafe()->getElementType();
-    size_t ElemSize =
-        Ctx.getASTContext().getTypeSizeInChars(ElemType).getQuantity();
+    size_t ElemSizeInBits = Ctx.getASTContext().getTypeSize(ElemType);
     PrimType ElemT = *Ctx.classify(ElemType);
     for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) {
       if (!F(P.atIndex(I), ElemT, Offset))
         return false;
-      Offset += ElemSize;
+      Offset += ElemSizeInBits;
     }
     return true;
   }
@@ -189,11 +206,10 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, size_t Offset,
   if (FieldDesc->isCompositeArray()) {
     QualType ElemType =
         FieldDesc->getType()->getAsArrayTypeUnsafe()->getElementType();
-    size_t ElemSize =
-        Ctx.getASTContext().getTypeSizeInChars(ElemType).getQuantity();
+    size_t ElemSizeInBits = Ctx.getASTContext().getTypeSize(ElemType);
     for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) {
       enumerateData(P.atIndex(I).narrow(), Ctx, Offset, F);
-      Offset += ElemSize;
+      Offset += ElemSizeInBits;
     }
     return true;
   }
@@ -205,10 +221,10 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, size_t Offset,
         Ctx.getASTContext().getASTRecordLayout(R->getDecl());
     for (const auto &B : R->bases()) {
       Pointer Elem = P.atField(B.Offset);
-      size_t ByteOffset =
-          Offset +
-          Layout.getBaseClassOffset(cast<CXXRecordDecl>(B.Decl)).getQuantity();
-      if (!enumerateData(Elem, Ctx, ByteOffset, F))
+      CharUnits ByteOffset =
+          Layout.getBaseClassOffset(cast<CXXRecordDecl>(B.Decl));
+      size_t BitOffset = Offset + Ctx.getASTContext().toBits(ByteOffset);
+      if (!enumerateData(Elem, Ctx, BitOffset, F))
         return false;
     }
     // TODO: Virtual bases?
@@ -216,10 +232,8 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, size_t Offset,
     for (unsigned I = 0; I != R->getNumFields(); ++I) {
       const Record::Field *Fi = R->getField(I);
       Pointer Elem = P.atField(Fi->Offset);
-      CharUnits FieldOffset =
-          Ctx.getASTContext().toCharUnitsFromBits(Layout.getFieldOffset(I));
-      size_t ByteOffset = Offset + FieldOffset.getQuantity();
-      if (!enumerateData(Elem, Ctx, ByteOffset, F))
+      size_t BitOffset = Offset + Layout.getFieldOffset(I);
+      if (!enumerateData(Elem, Ctx, BitOffset, F))
         return false;
     }
     return true;
@@ -309,34 +323,28 @@ bool DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &P, std::byte *Buff,
   assert(Buff);
   assert(BuffSize > 0);
 
-  BitcastBuffer F(Buff, BuffSize, S.getCtx().getTargetInfo().isBigEndian());
+  BitcastBuffer F(Buff, BuffSize);
 
   if (!CheckBitcastType(S, OpPC, P.getType(), /*IsToType=*/false))
     return false;
 
   const Context &Ctx = S.getContext();
   const ASTContext &ASTCtx = Ctx.getASTContext();
-  uint64_t PointerSizeBits =
+  uint64_t PointerSizeInBits =
       ASTCtx.getTargetInfo().getPointerWidth(LangAS::Default);
-  uint64_t PointerSizeBytes =
-      ASTCtx.toCharUnitsFromBits(PointerSizeBits).getQuantity();
 
   bool Success = enumeratePointerFields(
       P, S.getContext(),
-      [&](const Pointer &Ptr, PrimType T, size_t ByteOffset) -> bool {
+      [&](const Pointer &Ptr, PrimType T, size_t BitOffset) -> bool {
         if (!Ptr.isInitialized())
           return false;
         if (T == PT_Ptr) {
           assert(Ptr.getType()->isNullPtrType());
-          std::byte *M =
-              F.getBytes(ByteOffset, PointerSizeBytes, PointerSizeBits);
-          std::memset(M, 0, PointerSizeBytes);
+          F.pushZeroes(PointerSizeInBits);
           return true;
         }
 
         CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(Ptr.getType());
-        unsigned ObjectReprBytes =
-            ASTCtx.getTypeSizeInChars(Ptr.getType()).getQuantity();
         unsigned BitWidth;
         if (const FieldDecl *FD = Ptr.getField(); FD && FD->isBitField())
           BitWidth = FD->getBitWidthValue(ASTCtx);
@@ -345,13 +353,19 @@ bool DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &P, std::byte *Buff,
 
         BITCAST_TYPE_SWITCH_WITH_FLOAT(T, {
           T Val = Ptr.deref<T>();
-          std::byte *M = F.getBytes(ByteOffset, ObjectReprBytes, BitWidth);
-          Val.bitcastToMemory(M);
+          std::byte Buff[sizeof(T)];
+          Val.bitcastToMemory(Buff);
+          F.pushData(Buff, BitOffset, BitWidth);
         });
         return true;
       });
 
+  F.finish();
+
   IndeterminateBits = F.IndeterminateBits;
+  assert(F.Data.size() == BuffSize * 8);
+  std::memcpy(Buff, F.Data.getData().data(), BuffSize);
+
   return Success;
 }
 
@@ -385,10 +399,11 @@ bool DoBitCastToPtr(InterpState &S, const Pointer &P, Pointer &DestPtr,
           .getQuantity();
   bool BigEndian = ASTCtx.getTargetInfo().isBigEndian();
 
-  ByteTracker Bytes;
+  BitTracker Bytes;
   enumeratePointerFields(
       P, S.getContext(),
       [&](const Pointer &P, PrimType T, size_t ByteOffset) -> bool {
+        ByteOffset /= 8;
         bool PtrInitialized = P.isInitialized();
         if (!PtrInitialized) {
           Bytes.markUninitializedUntil(ByteOffset + primSize(T));
@@ -425,6 +440,7 @@ bool DoBitCastToPtr(InterpState &S, const Pointer &P, Pointer &DestPtr,
   bool Success = enumeratePointerFields(
       DestPtr, S.getContext(),
       [&](const Pointer &P, PrimType T, size_t ByteOffset) -> bool {
+        ByteOffset /= 8;
         if (T == PT_Float) {
           const QualType FloatType = P.getFieldDesc()->getType();
           const auto &Sem = ASTCtx.getFloatTypeSemantics(FloatType);
diff --git a/clang/test/AST/Interp/builtin-bit-cast.cpp b/clang/test/AST/Interp/builtin-bit-cast.cpp
index f47591427c90015d..4aa17501a30e3fb5 100644
--- a/clang/test/AST/Interp/builtin-bit-cast.cpp
+++ b/clang/test/AST/Interp/builtin-bit-cast.cpp
@@ -22,6 +22,9 @@
 
 typedef decltype(nullptr) nullptr_t;
 
+
+#if 0
+
 static_assert(sizeof(int) == 4);
 static_assert(sizeof(long long) == 8);
 
@@ -682,6 +685,7 @@ namespace StringLiterals {
                                           // ref-note {{initializer of 'Foo' is not a constant expression}}
 };
 
+#endif
 /// The current interpreter does not support bitcasts involving bitfields at all,
 /// so the following is mainly from comparing diagnostic output with GCC.
 namespace Bitfields {
@@ -705,4 +709,48 @@ namespace Bitfields {
                                                     // expected-note {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'char' is invalid}} \
                                                     // ref-error {{must be initialized by a constant expression}} \
                                                     // ref-note {{bit_cast involving bit-field is not yet supported}}
+
+  struct A {
+    unsigned char a : 4;
+    unsigned char b : 4;
+  };
+
+  constexpr A b{12, 3};
+  static_assert(b.a == 12, "");
+  static_assert(b.b == 3, "");
+  constexpr unsigned char a = __builtin_bit_cast(unsigned char, b); // ref-error {{must be initialized by a constant expression}} \
+                                                                    // ref-note {{bit_cast involving bit-field is not yet supported}} \
+                                                                    // ref-note {{declared here}}
+  static_assert(a == 60, ""); // ref-error {{not an integral constant expression}} \
+                              // ref-note {{initializer of 'a' is not a constant expression}}
+
+  struct Byte {
+    unsigned char a : 1;
+    unsigned char b : 1;
+    unsigned char c : 1;
+    unsigned char d : 1;
+    unsigned char e : 1;
+    unsigned char f : 1;
+    unsigned char g : 1;
+    unsigned char h : 1;
+  };
+
+  constexpr Byte B = {1, 1, 0, 1, 1, 0, 0, 1};
+  constexpr char C = __builtin_bit_cast(char, B); // ref-error {{must be initialized by a constant expression}} \
+                                                  // ref-note {{bit_cast involving bit-field is not yet supported}} \
+                                                  // ref-note {{declared here}}
+
+  static_assert(C == -101); // ref-error {{not an integral constant expression}} \
+                            // ref-note {{initializer of 'C' is not a constant expression}}
+
+  struct P {
+    unsigned short s1 : 5;
+    short s2;
+  };
+
+  constexpr P p = {24, -10};
+  constexpr int I = __builtin_bit_cast(int, p); // ref-error {{must be initialized by a constant expression}} \
+                                                // ref-note {{bit_cast involving bit-field is not yet supported}} \
+                                                // expected-error {{must be initialized by a constant expression}} \
+                                                // expected-note {{indeterminate value}}
 }

>From ded806338a9e5c9a9cfca86aaf2e1f8f1436bbd4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Tue, 17 Oct 2023 18:57:58 +0200
Subject: [PATCH 04/12] All existing tests work.

---
 clang/lib/AST/Interp/Boolean.h             |   3 +
 clang/lib/AST/Interp/Integral.h            |   3 +
 clang/lib/AST/Interp/InterpBitcast.cpp     | 179 +++++++++++++++------
 clang/test/AST/Interp/builtin-bit-cast.cpp |  17 +-
 4 files changed, 147 insertions(+), 55 deletions(-)

diff --git a/clang/lib/AST/Interp/Boolean.h b/clang/lib/AST/Interp/Boolean.h
index 6531c5ac6945b37b..0708294dab95a105 100644
--- a/clang/lib/AST/Interp/Boolean.h
+++ b/clang/lib/AST/Interp/Boolean.h
@@ -69,6 +69,9 @@ class Boolean final {
   constexpr static unsigned bitWidth() { return 1; }
   constexpr static unsigned objectReprBits() { return 8; }
   constexpr static unsigned valueReprBytes(const ASTContext &Ctx) { return 1; }
+  constexpr static unsigned valueReprBits(const ASTContext &Ctx) {
+    return 8;
+  } // FIXME: Is this correct?
 
   bool isZero() const { return !V; }
   bool isMin() const { return isZero(); }
diff --git a/clang/lib/AST/Interp/Integral.h b/clang/lib/AST/Interp/Integral.h
index 2e47c2eaeeaa78cf..bcef75bbbaeb8db5 100644
--- a/clang/lib/AST/Interp/Integral.h
+++ b/clang/lib/AST/Interp/Integral.h
@@ -121,6 +121,9 @@ template <unsigned Bits, bool Signed> class Integral final {
 
   constexpr static unsigned bitWidth() { return Bits; }
   constexpr static unsigned objectReprBits() { return Bits; }
+  constexpr static unsigned valueReprBits(const ASTContext &Ctx) {
+    return Bits;
+  }
   constexpr static unsigned valueReprBytes(const ASTContext &Ctx) {
     return Ctx.toCharUnitsFromBits(Bits).getQuantity();
   }
diff --git a/clang/lib/AST/Interp/InterpBitcast.cpp b/clang/lib/AST/Interp/InterpBitcast.cpp
index 60d9eb544a84602e..b9922f855d4601e8 100644
--- a/clang/lib/AST/Interp/InterpBitcast.cpp
+++ b/clang/lib/AST/Interp/InterpBitcast.cpp
@@ -71,25 +71,68 @@ static void swapBytes(std::byte *M, size_t N) {
 /// All offsets are in bits.
 struct BitTracker {
   llvm::BitVector Initialized;
+  llvm::BitVector Data_;
   std::vector<std::byte> Data;
 
   BitTracker() = default;
 
   size_t size() const {
     assert(Initialized.size() == Data.size());
+    assert(Initialized.size() == Data_.size());
     return Initialized.size();
   }
 
-  std::byte *getBytes(size_t Offset) { return Data.data() + Offset; }
+  std::byte *getBytes(size_t Offset) {
+    assert(false);
+    return Data.data() + Offset;
+  }
+
+  const std::byte *getBytes(size_t BitOffset, int a) {
+    assert(BitOffset % 8 == 0);
+    return reinterpret_cast<const std::byte *>(Data_.getData().data()) +
+           (BitOffset / 8);
+  }
+
   bool allInitialized(size_t Offset, size_t Size) const {
-    for (size_t I = Offset; I != (Size + Offset); ++I) {
-      if (!Initialized[I])
-        return false;
+    return Initialized.find_first_unset_in(Offset, Offset + Size) == -1;
+  }
+
+  void pushData(const std::byte *data, size_t BitOffset, size_t BitWidth) {
+    assert(BitOffset >= Data_.size());
+    // First, fill up the bit vector until BitOffset. The bits are all 0
+    // but we record them as indeterminate.
+    {
+      Data_.resize(BitOffset, false);
+      Initialized.resize(BitOffset, false);
     }
-    return true;
+
+    size_t BitsHandled = 0;
+    // Read all full bytes first
+    for (size_t I = 0; I != BitWidth / 8; ++I) {
+      for (unsigned X = 0; X != 8; ++X) {
+        Data_.push_back((data[I] & std::byte(1 << X)) != std::byte{0});
+        Initialized.push_back(true);
+        ++BitsHandled;
+      }
+    }
+
+    // Rest of the bits.
+    assert((BitWidth - BitsHandled) < 8);
+    for (size_t I = 0, E = (BitWidth - BitsHandled); I != E; ++I) {
+      Data_.push_back((data[BitWidth / 8] & std::byte(1 << I)) != std::byte{0});
+      Initialized.push_back(true);
+      ++BitsHandled;
+    }
+  }
+
+  void pushZeroes(size_t Amount) {
+    Data_.resize(Data_.size() + Amount, false);
+    Initialized.resize(Data_.size() + Amount, true);
   }
 
+#if 0
   std::byte *getWritableBytes(size_t Offset, size_t Size, bool InitValue) {
+    assert(false);
     assert(Offset >= Data.size());
     assert(Size > 0);
 
@@ -104,17 +147,17 @@ struct BitTracker {
 
     return Data.data() + Offset;
   }
+#endif
 
   void markUninitializedUntil(size_t Offset) {
-    assert(Offset >= Data.size());
-
-    size_t NBytes = Offset - Data.size();
-    for (size_t I = 0; I != NBytes; ++I)
-      Initialized.push_back(false);
-    Data.resize(Offset);
+    assert(Offset >= Data_.size());
+    Data_.resize(Offset, false);
+    Initialized.resize(Offset, false);
+    return;
   }
 
   void zeroUntil(size_t Offset) {
+    assert(false);
     assert(Offset >= Data.size());
 
     assert(Data.size() == Initialized.size());
@@ -330,6 +373,7 @@ bool DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &P, std::byte *Buff,
 
   const Context &Ctx = S.getContext();
   const ASTContext &ASTCtx = Ctx.getASTContext();
+  bool BigEndian = ASTCtx.getTargetInfo().isBigEndian();
   uint64_t PointerSizeInBits =
       ASTCtx.getTargetInfo().getPointerWidth(LangAS::Default);
 
@@ -353,9 +397,13 @@ bool DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &P, std::byte *Buff,
 
         BITCAST_TYPE_SWITCH_WITH_FLOAT(T, {
           T Val = Ptr.deref<T>();
-          std::byte Buff[sizeof(T)];
+          std::byte *Buff = (std::byte *)std::malloc(
+              ObjectReprChars.getQuantity()); //[sizeof(T)];
           Val.bitcastToMemory(Buff);
+          if (BigEndian)
+            swapBytes(Buff, ObjectReprChars.getQuantity());
           F.pushData(Buff, BitOffset, BitWidth);
+          std::free(Buff);
         });
         return true;
       });
@@ -366,6 +414,8 @@ bool DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &P, std::byte *Buff,
   assert(F.Data.size() == BuffSize * 8);
   std::memcpy(Buff, F.Data.getData().data(), BuffSize);
 
+  if (BigEndian)
+    swapBytes(Buff, BuffSize);
   return Success;
 }
 
@@ -392,68 +442,87 @@ bool DoBitCastToPtr(InterpState &S, const Pointer &P, Pointer &DestPtr,
 
   const Context &Ctx = S.getContext();
   const ASTContext &ASTCtx = Ctx.getASTContext();
-  uint64_t PointerSize =
-      ASTCtx
-          .toCharUnitsFromBits(
-              ASTCtx.getTargetInfo().getPointerWidth(LangAS::Default))
-          .getQuantity();
+  uint64_t PointerSizeInBits =
+      ASTCtx.getTargetInfo().getPointerWidth(LangAS::Default);
   bool BigEndian = ASTCtx.getTargetInfo().isBigEndian();
 
   BitTracker Bytes;
   enumeratePointerFields(
       P, S.getContext(),
-      [&](const Pointer &P, PrimType T, size_t ByteOffset) -> bool {
-        ByteOffset /= 8;
+      [&](const Pointer &P, PrimType T, size_t BitOffset) -> bool {
         bool PtrInitialized = P.isInitialized();
         if (!PtrInitialized) {
-          Bytes.markUninitializedUntil(ByteOffset + primSize(T));
+          Bytes.markUninitializedUntil(BitOffset + (primSize(T) * 8));
           return true;
         }
 
         assert(P.isInitialized());
         // nullptr_t is a PT_Ptr for us, but it's still not std::is_pointer_v.
         if (T == PT_Ptr) {
-          assert(P.getType()->isNullPtrType());
-          std::byte *M = Bytes.getWritableBytes(ByteOffset, PointerSize,
-                                                /*InitValue=*/true);
-          std::memset(M, 0, PointerSize);
+          Bytes.pushZeroes(PointerSizeInBits);
           return true;
         }
         BITCAST_TYPE_SWITCH_WITH_FLOAT(T, {
           T Val = P.deref<T>();
           unsigned ObjectReprBytes =
               ASTCtx.getTypeSizeInChars(P.getType()).getQuantity();
+
+          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 ValueReprBytes = Val.valueReprBytes(ASTCtx);
           assert(ObjectReprBytes >= ValueReprBytes);
 
-          std::byte *Dest = Bytes.getWritableBytes(ByteOffset, ValueReprBytes,
-                                                   PtrInitialized);
-          Val.bitcastToMemory(Dest);
-          Bytes.zeroUntil(ByteOffset + ObjectReprBytes);
+          std::byte *Buff = (std::byte *)std::malloc(
+              ObjectReprChars.getQuantity()); //[sizeof(T)];
+          // std::byte Buff[sizeof(T)];
+          Val.bitcastToMemory(Buff);
 
           if (BigEndian)
-            swapBytes(Dest, ValueReprBytes);
+            swapBytes(Buff, BitWidth / 8);
+          // if (BigEndian) XXX
+          // swapBytes(Dest, ValueReprBytes);
+          Bytes.pushData(Buff, BitOffset, BitWidth);
+          std::free(Buff);
         });
         return true;
       });
 
   bool Success = enumeratePointerFields(
       DestPtr, S.getContext(),
-      [&](const Pointer &P, PrimType T, size_t ByteOffset) -> bool {
-        ByteOffset /= 8;
+      [&](const Pointer &P, PrimType T, size_t BitOffset) -> bool {
         if (T == PT_Float) {
           const QualType FloatType = P.getFieldDesc()->getType();
           const auto &Sem = ASTCtx.getFloatTypeSemantics(FloatType);
-          size_t ValueReprBytes =
-              ASTCtx.toCharUnitsFromBits(APFloat::semanticsSizeInBits(Sem))
-                  .getQuantity();
+          size_t ValueReprBits = ASTCtx.getTypeSize(FloatType);
+
+          CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(P.getType());
+          const std::byte *M = Bytes.getBytes(BitOffset, 1234);
+          std::byte *Buff = (std::byte *)std::malloc(
+              ObjectReprChars.getQuantity()); //[sizeof(T)];
+          std::memcpy(Buff, M, ObjectReprChars.getQuantity());
+          // Val.bitcastToMemory(Buff);
+          if (BigEndian)
+            swapBytes(Buff, ObjectReprChars.getQuantity());
 
-          std::byte *M = Bytes.getBytes(ByteOffset);
+          // F.pushData(Buff, BitOffset, BitWidth);
 
-          if (BigEndian)
-            swapBytes(M, ValueReprBytes);
-          P.deref<Floating>() = Floating::bitcastFromMemory(M, Sem);
+          // const std::byte *M = Bytes.getBytes(BitOffset, 1234);
+          // size_t ValueReprBytes =
+          // ASTCtx.toCharUnitsFromBits(APFloat::semanticsSizeInBits(Sem))
+          //.getQuantity();
+
+          // std::byte *M = Bytes.getBytes(ByteOffset);
+
+          // if (BigEndian)
+          // swapBytes(M, ValueReprBytes);
+          P.deref<Floating>() = Floating::bitcastFromMemory(Buff, Sem);
           P.initialize();
+          std::free(Buff);
           return true;
         }
         if (T == PT_Ptr) {
@@ -467,19 +536,19 @@ bool DoBitCastToPtr(InterpState &S, const Pointer &P, Pointer &DestPtr,
         BITCAST_TYPE_SWITCH(T, {
           T &Val = P.deref<T>();
 
-          size_t ValueReprBytes = T::valueReprBytes(ASTCtx);
+          // size_t ValueReprBytes = T::valueReprBytes(ASTCtx);
+          size_t ValueReprBits = T::valueReprBits(ASTCtx);
           // Check if any of the bits we're about to read are uninitialized.
-          bool HasIndeterminateBytes =
-              !Bytes.allInitialized(ByteOffset, ValueReprBytes);
+          bool HasIndeterminateBits =
+              !Bytes.allInitialized(BitOffset, ValueReprBits);
 
-          if (HasIndeterminateBytes) {
+          if (HasIndeterminateBits) {
             // Always an error, unless the type of the field we're reading is
             // either unsigned char or std::byte.
             bool TargetIsUCharOrBytes =
-                (ValueReprBytes == 1 &&
-                 (P.getType()->isSpecificBuiltinType(BuiltinType::UChar) ||
-                  P.getType()->isSpecificBuiltinType(BuiltinType::Char_U) ||
-                  P.getType()->isStdByteType()));
+                (P.getType()->isSpecificBuiltinType(BuiltinType::UChar) ||
+                 P.getType()->isSpecificBuiltinType(BuiltinType::Char_U) ||
+                 P.getType()->isStdByteType());
 
             if (!TargetIsUCharOrBytes) {
               const Expr *E = S.Current->getExpr(OpPC);
@@ -491,12 +560,20 @@ bool DoBitCastToPtr(InterpState &S, const Pointer &P, Pointer &DestPtr,
             }
           }
 
-          std::byte *M = Bytes.getBytes(ByteOffset);
-          if (BigEndian)
-            swapBytes(M, ValueReprBytes);
-          Val = T::bitcastFromMemory(M);
+          assert(!P.getField()->isBitField());
+
+          std::byte *Copy = (std::byte *)std::malloc(ValueReprBits / 8);
+          const std::byte *M = Bytes.getBytes(BitOffset, 1234);
+
+          std::memcpy(Copy, M, ValueReprBits / 8);
+
+          if (BigEndian) {
+            swapBytes(Copy, ValueReprBits / 8);
+          }
+          Val = T::bitcastFromMemory(Copy);
+          std::free(Copy);
 
-          if (!HasIndeterminateBytes)
+          if (!HasIndeterminateBits)
             P.initialize();
         });
         return true;
diff --git a/clang/test/AST/Interp/builtin-bit-cast.cpp b/clang/test/AST/Interp/builtin-bit-cast.cpp
index 4aa17501a30e3fb5..4f695710877a81d7 100644
--- a/clang/test/AST/Interp/builtin-bit-cast.cpp
+++ b/clang/test/AST/Interp/builtin-bit-cast.cpp
@@ -23,7 +23,6 @@
 typedef decltype(nullptr) nullptr_t;
 
 
-#if 0
 
 static_assert(sizeof(int) == 4);
 static_assert(sizeof(long long) == 8);
@@ -409,13 +408,15 @@ namespace MemberPointer {
     A a = {1, 2};
     return bit_cast<B>(a);
   }
+  /// FIXME: The following tests need the InitMap changes.
+#if 0
   constexpr char good_one = one().x[0] + one().x[2] + one().x[3];
   // ref-error at +2 {{constexpr variable 'bad_one' must be initialized by a constant expression}}
   // ref-note at +1 {{read of uninitialized object is not allowed in a constant expression}}
   constexpr char bad_one = one().x[1];
   // expected-error at -1 {{constexpr variable 'bad_one' must be initialized by a constant expression}}
   // expected-note at -2 {{read of uninitialized object is not allowed in a constant expression}}
-
+#endif
 
   constexpr A two() {
     B b = one(); // b.x[1] is indeterminate.
@@ -685,7 +686,6 @@ namespace StringLiterals {
                                           // ref-note {{initializer of 'Foo' is not a constant expression}}
 };
 
-#endif
 /// The current interpreter does not support bitcasts involving bitfields at all,
 /// so the following is mainly from comparing diagnostic output with GCC.
 namespace Bitfields {
@@ -753,4 +753,13 @@ namespace Bitfields {
                                                 // ref-note {{bit_cast involving bit-field is not yet supported}} \
                                                 // expected-error {{must be initialized by a constant expression}} \
                                                 // expected-note {{indeterminate value}}
-}
+
+
+  struct CharStruct {
+    unsigned char v;
+  };
+  constexpr CharStruct CS = __builtin_bit_cast(CharStruct, B);  // ref-error {{must be initialized by a constant expression}} \
+                                                                // ref-note {{bit_cast involving bit-field is not yet supported}} \
+                                                                // ref-note {{declared here}}
+  static_assert(CS.v == 155);} // ref-error {{not an integral constant expression}} \
+                               // ref-note {{initializer of 'CS' is not a constant expression}}

>From 8b024210b7b93e47f5d1a4c88c6e40f391f4088a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Wed, 18 Oct 2023 10:53:15 +0200
Subject: [PATCH 05/12] Work on unifying the two implementations

---
 clang/lib/AST/Interp/Interp.cpp        |   4 +-
 clang/lib/AST/Interp/Interp.h          |  18 +-
 clang/lib/AST/Interp/InterpBitcast.cpp | 261 +++++++------------------
 3 files changed, 79 insertions(+), 204 deletions(-)

diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index 7fa121fc823da198..d98d69936fcb6c70 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -610,11 +610,11 @@ bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR) {
   return false;
 }
 
-bool CheckBitcast(InterpState &S, CodePtr OpPC, unsigned IndeterminateBits,
+bool CheckBitcast(InterpState &S, CodePtr OpPC, bool HasIndeterminateBits,
                   bool TargetIsUCharOrByte) {
 
   // This is always fine.
-  if (IndeterminateBits == 0)
+  if (!HasIndeterminateBits)
     return true;
 
   // Indeterminate bits can only be bitcast to unsigned char or std::byte.
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 4bf2c83baa2cc712..5d69d59053dc5b98 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -187,7 +187,7 @@ bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result,
 /// Checks why the given DeclRefExpr is invalid.
 bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR);
 
-bool CheckBitcast(InterpState &S, CodePtr OpPC, unsigned IndeterminateBits,
+bool CheckBitcast(InterpState &S, CodePtr OpPC, bool HasIndeterminateBits,
                   bool TargetIsUCharOrByte);
 
 /// Interpreter entry point.
@@ -205,7 +205,7 @@ bool InterpretOffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E,
 /// actions of a __builtin_bit_cast expression when the target type
 /// is primitive.
 bool DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &P, std::byte *Buff,
-               size_t BuffSize, unsigned &IndeterminateBits);
+               size_t BuffSize, bool &HasIndeterminateBits);
 
 /// Perform a bitcast of all fields of P into the fields of DestPtr.
 /// This performs the actions of a __builtin_bit_cast expression when
@@ -1582,12 +1582,12 @@ bool BitCast(InterpState &S, CodePtr OpPC, bool TargetIsUCharOrByte) {
 
   size_t BuffSize = ToT::valueReprBytes(S.getCtx());
   std::vector<std::byte> Buff(BuffSize);
-  unsigned IndeterminateBits = 0;
+  bool HasIndeterminateBits = false;
 
-  if (!DoBitCast(S, OpPC, FromPtr, Buff.data(), BuffSize, IndeterminateBits))
+  if (!DoBitCast(S, OpPC, FromPtr, Buff.data(), BuffSize, HasIndeterminateBits))
     return false;
 
-  if (!CheckBitcast(S, OpPC, IndeterminateBits, TargetIsUCharOrByte))
+  if (!CheckBitcast(S, OpPC, HasIndeterminateBits, TargetIsUCharOrByte))
     return false;
 
   S.Stk.push<ToT>(ToT::bitcastFromMemory(Buff.data()));
@@ -1600,12 +1600,14 @@ inline bool BitCastFP(InterpState &S, CodePtr OpPC,
   const Pointer &FromPtr = S.Stk.pop<Pointer>();
 
   std::vector<std::byte> Buff(TargetSize);
-  unsigned IndeterminateBits = 0;
+  bool HasIndeterminateBits = false;
 
-  if (!DoBitCast(S, OpPC, FromPtr, Buff.data(), TargetSize, IndeterminateBits))
+  if (!DoBitCast(S, OpPC, FromPtr, Buff.data(), TargetSize,
+                 HasIndeterminateBits))
     return false;
 
-  if (!CheckBitcast(S, OpPC, IndeterminateBits, /*TargetIsUCharOrByte=*/false))
+  if (!CheckBitcast(S, OpPC, HasIndeterminateBits,
+                    /*TargetIsUCharOrByte=*/false))
     return false;
 
   S.Stk.push<Floating>(Floating::bitcastFromMemory(Buff.data(), *Sem));
diff --git a/clang/lib/AST/Interp/InterpBitcast.cpp b/clang/lib/AST/Interp/InterpBitcast.cpp
index b9922f855d4601e8..b62535c387315f31 100644
--- a/clang/lib/AST/Interp/InterpBitcast.cpp
+++ b/clang/lib/AST/Interp/InterpBitcast.cpp
@@ -72,21 +72,14 @@ static void swapBytes(std::byte *M, size_t N) {
 struct BitTracker {
   llvm::BitVector Initialized;
   llvm::BitVector Data_;
-  std::vector<std::byte> Data;
 
   BitTracker() = default;
 
   size_t size() const {
-    assert(Initialized.size() == Data.size());
     assert(Initialized.size() == Data_.size());
     return Initialized.size();
   }
 
-  std::byte *getBytes(size_t Offset) {
-    assert(false);
-    return Data.data() + Offset;
-  }
-
   const std::byte *getBytes(size_t BitOffset, int a) {
     assert(BitOffset % 8 == 0);
     return reinterpret_cast<const std::byte *>(Data_.getData().data()) +
@@ -97,6 +90,8 @@ struct BitTracker {
     return Initialized.find_first_unset_in(Offset, Offset + Size) == -1;
   }
 
+  bool allInitialized() const { return Initialized.all(); }
+
   void pushData(const std::byte *data, size_t BitOffset, size_t BitWidth) {
     assert(BitOffset >= Data_.size());
     // First, fill up the bit vector until BitOffset. The bits are all 0
@@ -126,97 +121,15 @@ struct BitTracker {
   }
 
   void pushZeroes(size_t Amount) {
-    Data_.resize(Data_.size() + Amount, false);
-    Initialized.resize(Data_.size() + Amount, true);
-  }
-
-#if 0
-  std::byte *getWritableBytes(size_t Offset, size_t Size, bool InitValue) {
-    assert(false);
-    assert(Offset >= Data.size());
-    assert(Size > 0);
-
-    size_t OldSize = Data.size();
-    Data.resize(Offset + Size);
-
-    // Everything from the old size to the new offset is indeterminate.
-    for (size_t I = OldSize; I != Offset; ++I)
-      Initialized.push_back(false);
-    for (size_t I = Offset; I != Offset + Size; ++I)
-      Initialized.push_back(InitValue);
-
-    return Data.data() + Offset;
+    size_t N = Data_.size();
+    Data_.resize(N + Amount, false);
+    Initialized.resize(N + Amount, true);
   }
-#endif
 
   void markUninitializedUntil(size_t Offset) {
     assert(Offset >= Data_.size());
     Data_.resize(Offset, false);
     Initialized.resize(Offset, false);
-    return;
-  }
-
-  void zeroUntil(size_t Offset) {
-    assert(false);
-    assert(Offset >= Data.size());
-
-    assert(Data.size() == Initialized.size());
-    size_t NBytes = Offset - Data.size();
-    for (size_t I = 0; I != NBytes; ++I) {
-      Initialized.push_back(true);
-      Data.push_back(std::byte{0});
-    }
-  }
-};
-
-struct BitcastBuffer {
-  llvm::BitVector Data;
-  std::byte *Buff;
-  size_t BitOffset = 0;
-  size_t BuffSize;
-  unsigned IndeterminateBits = 0;
-
-  BitcastBuffer(std::byte *Buff, size_t BuffSize)
-      : Buff(Buff), BuffSize(BuffSize) {}
-
-  void pushData(const std::byte *data, size_t BitOffset, size_t BitWidth) {
-    assert(BitOffset >= Data.size());
-    // First, fill up the bit vector until BitOffset. The bits are all 0
-    // but we record them as indeterminate.
-    {
-      size_t FillBits = BitOffset - Data.size();
-      IndeterminateBits += FillBits;
-      Data.resize(BitOffset, false);
-    }
-
-    size_t BitsHandled = 0;
-    // Read all full bytes first
-    for (size_t I = 0; I != BitWidth / 8; ++I) {
-      for (unsigned X = 0; X != 8; ++X) {
-        Data.push_back((data[I] & std::byte(1 << X)) != std::byte{0});
-        ++BitsHandled;
-      }
-    }
-
-    // Rest of the bits.
-    assert((BitWidth - BitsHandled) < 8);
-    for (size_t I = 0, E = (BitWidth - BitsHandled); I != E; ++I) {
-      Data.push_back((data[BitWidth / 8] & std::byte(1 << I)) != std::byte{0});
-      ++BitsHandled;
-    }
-  }
-
-  void pushZeroes(size_t Amount) { Data.resize(Data.size() + Amount, false); }
-
-  void finish() {
-    // Fill up with zeroes until the buffer is BuffSize in size.
-    // The added bits are of indeterminate value.
-    assert(Data.size() <= (BuffSize * 8));
-    size_t Remainder = (BuffSize * 8) - Data.size();
-    for (size_t I = 0; I != Remainder; ++I)
-      Data.push_back(false);
-
-    IndeterminateBits += Remainder;
   }
 };
 
@@ -237,12 +150,12 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, size_t Offset,
         FieldDesc->getType()->getAsArrayTypeUnsafe()->getElementType();
     size_t ElemSizeInBits = Ctx.getASTContext().getTypeSize(ElemType);
     PrimType ElemT = *Ctx.classify(ElemType);
+    bool Ok = true;
     for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) {
-      if (!F(P.atIndex(I), ElemT, Offset))
-        return false;
+      Ok = Ok && F(P.atIndex(I), ElemT, Offset);
       Offset += ElemSizeInBits;
     }
-    return true;
+    return Ok;
   }
 
   // Composite arrays.
@@ -262,13 +175,13 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, size_t Offset,
     const Record *R = FieldDesc->ElemRecord;
     const ASTRecordLayout &Layout =
         Ctx.getASTContext().getASTRecordLayout(R->getDecl());
+    bool Ok = true;
     for (const auto &B : R->bases()) {
       Pointer Elem = P.atField(B.Offset);
       CharUnits ByteOffset =
           Layout.getBaseClassOffset(cast<CXXRecordDecl>(B.Decl));
       size_t BitOffset = Offset + Ctx.getASTContext().toBits(ByteOffset);
-      if (!enumerateData(Elem, Ctx, BitOffset, F))
-        return false;
+      Ok = Ok && enumerateData(Elem, Ctx, BitOffset, F);
     }
     // TODO: Virtual bases?
 
@@ -276,10 +189,9 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, size_t Offset,
       const Record::Field *Fi = R->getField(I);
       Pointer Elem = P.atField(Fi->Offset);
       size_t BitOffset = Offset + Layout.getFieldOffset(I);
-      if (!enumerateData(Elem, Ctx, BitOffset, F))
-        return false;
+      Ok = Ok && enumerateData(Elem, Ctx, BitOffset, F);
     }
-    return true;
+    return Ok;
   }
 
   llvm_unreachable("Unhandled data type");
@@ -357,66 +269,83 @@ static bool CheckBitcastType(InterpState &S, CodePtr OpPC, QualType T,
   return true;
 }
 
-/// Bitcast all fields from \p P into \p Buff.
-/// This is used for bitcasting TO a single primitive value.
-bool DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &P, std::byte *Buff,
-               size_t BuffSize, unsigned &IndeterminateBits) {
-  llvm::errs() << __PRETTY_FUNCTION__ << "\n";
-  assert(P.isLive());
-  assert(Buff);
-  assert(BuffSize > 0);
-
-  BitcastBuffer F(Buff, BuffSize);
-
-  if (!CheckBitcastType(S, OpPC, P.getType(), /*IsToType=*/false))
-    return false;
-
-  const Context &Ctx = S.getContext();
+static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr,
+                                BitTracker &Bits, bool RetrnOnUninit) {
   const ASTContext &ASTCtx = Ctx.getASTContext();
-  bool BigEndian = ASTCtx.getTargetInfo().isBigEndian();
   uint64_t PointerSizeInBits =
       ASTCtx.getTargetInfo().getPointerWidth(LangAS::Default);
+  bool BigEndian = ASTCtx.getTargetInfo().isBigEndian();
 
-  bool Success = enumeratePointerFields(
-      P, S.getContext(),
-      [&](const Pointer &Ptr, PrimType T, size_t BitOffset) -> bool {
-        if (!Ptr.isInitialized())
-          return false;
+  return enumeratePointerFields(
+      FromPtr, Ctx,
+      [&](const Pointer &P, PrimType T, size_t BitOffset) -> bool {
+        if (!P.isInitialized()) {
+          Bits.markUninitializedUntil(BitOffset +
+                                      (primSize(T) * 8)); /// primSize() usage.
+          return RetrnOnUninit;
+        }
+
+        assert(P.isInitialized());
+        // nullptr_t is a PT_Ptr for us, but it's still not std::is_pointer_v.
         if (T == PT_Ptr) {
-          assert(Ptr.getType()->isNullPtrType());
-          F.pushZeroes(PointerSizeInBits);
+          assert(P.getType()->isNullPtrType());
+          Bits.pushZeroes(PointerSizeInBits);
           return true;
         }
 
-        CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(Ptr.getType());
+        unsigned ObjectReprBytes =
+            ASTCtx.getTypeSizeInChars(P.getType()).getQuantity();
+
+        CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(P.getType());
         unsigned BitWidth;
-        if (const FieldDecl *FD = Ptr.getField(); FD && FD->isBitField())
+        if (const FieldDecl *FD = P.getField(); FD && FD->isBitField())
           BitWidth = FD->getBitWidthValue(ASTCtx);
         else
           BitWidth = ASTCtx.toBits(ObjectReprChars);
-
         BITCAST_TYPE_SWITCH_WITH_FLOAT(T, {
-          T Val = Ptr.deref<T>();
-          std::byte *Buff = (std::byte *)std::malloc(
-              ObjectReprChars.getQuantity()); //[sizeof(T)];
+          T Val = P.deref<T>();
+          unsigned ValueReprBytes = Val.valueReprBytes(ASTCtx);
+          assert(ObjectReprBytes >= ValueReprBytes);
+
+          std::byte *Buff =
+              (std::byte *)std::malloc(ObjectReprChars.getQuantity());
           Val.bitcastToMemory(Buff);
+
           if (BigEndian)
-            swapBytes(Buff, ObjectReprChars.getQuantity());
-          F.pushData(Buff, BitOffset, BitWidth);
+            swapBytes(Buff, BitWidth / 8);
+          Bits.pushData(Buff, BitOffset, BitWidth);
           std::free(Buff);
         });
         return true;
       });
+}
 
-  F.finish();
+/// Bitcast all fields from \p P into \p Buff.
+/// This is used for bitcasting TO a single primitive value.
+bool DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &P, std::byte *Buff,
+               size_t BuffSize, bool &HasIndeterminateBits) {
+  assert(P.isLive());
+  assert(Buff);
+  assert(BuffSize > 0);
 
-  IndeterminateBits = F.IndeterminateBits;
-  assert(F.Data.size() == BuffSize * 8);
-  std::memcpy(Buff, F.Data.getData().data(), BuffSize);
+  if (!CheckBitcastType(S, OpPC, P.getType(), /*IsToType=*/false))
+    return false;
+
+  const ASTContext &ASTCtx = S.getContext().getASTContext();
+  bool BigEndian = ASTCtx.getTargetInfo().isBigEndian();
+
+  BitTracker Bits;
+  bool Success = readPointerToBuffer(S.getContext(), P, Bits, false);
+
+  Bits.markUninitializedUntil(BuffSize * 8);
+  assert(Bits.size() == BuffSize * 8);
+
+  HasIndeterminateBits = !Bits.allInitialized();
+  std::memcpy(Buff, Bits.Data_.getData().data(), BuffSize);
 
   if (BigEndian)
     swapBytes(Buff, BuffSize);
-  return Success;
+  return Success; // && !HasIndeterminateBits;
 }
 
 //  This function is constexpr if and only if To, From, and the types of
@@ -442,55 +371,11 @@ bool DoBitCastToPtr(InterpState &S, const Pointer &P, Pointer &DestPtr,
 
   const Context &Ctx = S.getContext();
   const ASTContext &ASTCtx = Ctx.getASTContext();
-  uint64_t PointerSizeInBits =
-      ASTCtx.getTargetInfo().getPointerWidth(LangAS::Default);
   bool BigEndian = ASTCtx.getTargetInfo().isBigEndian();
 
   BitTracker Bytes;
-  enumeratePointerFields(
-      P, S.getContext(),
-      [&](const Pointer &P, PrimType T, size_t BitOffset) -> bool {
-        bool PtrInitialized = P.isInitialized();
-        if (!PtrInitialized) {
-          Bytes.markUninitializedUntil(BitOffset + (primSize(T) * 8));
-          return true;
-        }
-
-        assert(P.isInitialized());
-        // nullptr_t is a PT_Ptr for us, but it's still not std::is_pointer_v.
-        if (T == PT_Ptr) {
-          Bytes.pushZeroes(PointerSizeInBits);
-          return true;
-        }
-        BITCAST_TYPE_SWITCH_WITH_FLOAT(T, {
-          T Val = P.deref<T>();
-          unsigned ObjectReprBytes =
-              ASTCtx.getTypeSizeInChars(P.getType()).getQuantity();
-
-          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 ValueReprBytes = Val.valueReprBytes(ASTCtx);
-          assert(ObjectReprBytes >= ValueReprBytes);
-
-          std::byte *Buff = (std::byte *)std::malloc(
-              ObjectReprChars.getQuantity()); //[sizeof(T)];
-          // std::byte Buff[sizeof(T)];
-          Val.bitcastToMemory(Buff);
-
-          if (BigEndian)
-            swapBytes(Buff, BitWidth / 8);
-          // if (BigEndian) XXX
-          // swapBytes(Dest, ValueReprBytes);
-          Bytes.pushData(Buff, BitOffset, BitWidth);
-          std::free(Buff);
-        });
-        return true;
-      });
+  if (!readPointerToBuffer(Ctx, P, Bytes, true))
+    return false;
 
   bool Success = enumeratePointerFields(
       DestPtr, S.getContext(),
@@ -509,17 +394,6 @@ bool DoBitCastToPtr(InterpState &S, const Pointer &P, Pointer &DestPtr,
           if (BigEndian)
             swapBytes(Buff, ObjectReprChars.getQuantity());
 
-          // F.pushData(Buff, BitOffset, BitWidth);
-
-          // const std::byte *M = Bytes.getBytes(BitOffset, 1234);
-          // size_t ValueReprBytes =
-          // ASTCtx.toCharUnitsFromBits(APFloat::semanticsSizeInBits(Sem))
-          //.getQuantity();
-
-          // std::byte *M = Bytes.getBytes(ByteOffset);
-
-          // if (BigEndian)
-          // swapBytes(M, ValueReprBytes);
           P.deref<Floating>() = Floating::bitcastFromMemory(Buff, Sem);
           P.initialize();
           std::free(Buff);
@@ -536,7 +410,6 @@ bool DoBitCastToPtr(InterpState &S, const Pointer &P, Pointer &DestPtr,
         BITCAST_TYPE_SWITCH(T, {
           T &Val = P.deref<T>();
 
-          // size_t ValueReprBytes = T::valueReprBytes(ASTCtx);
           size_t ValueReprBits = T::valueReprBits(ASTCtx);
           // Check if any of the bits we're about to read are uninitialized.
           bool HasIndeterminateBits =

>From 144886a3e4a27a6d3fd39fb799a02344c6a63a07 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Wed, 18 Oct 2023 12:17:53 +0200
Subject: [PATCH 06/12] More bitfield tests

---
 clang/test/AST/Interp/builtin-bit-cast.cpp | 40 ++++++++++++++++++++--
 1 file changed, 38 insertions(+), 2 deletions(-)

diff --git a/clang/test/AST/Interp/builtin-bit-cast.cpp b/clang/test/AST/Interp/builtin-bit-cast.cpp
index 4f695710877a81d7..ebc7138e6971f977 100644
--- a/clang/test/AST/Interp/builtin-bit-cast.cpp
+++ b/clang/test/AST/Interp/builtin-bit-cast.cpp
@@ -761,5 +761,41 @@ namespace Bitfields {
   constexpr CharStruct CS = __builtin_bit_cast(CharStruct, B);  // ref-error {{must be initialized by a constant expression}} \
                                                                 // ref-note {{bit_cast involving bit-field is not yet supported}} \
                                                                 // ref-note {{declared here}}
-  static_assert(CS.v == 155);} // ref-error {{not an integral constant expression}} \
-                               // ref-note {{initializer of 'CS' is not a constant expression}}
+  static_assert(CS.v == 155); // ref-error {{not an integral constant expression}} \
+                              // ref-note {{initializer of 'CS' is not a constant expression}}
+
+
+  struct I3 {
+    int a;
+    int b : 10;
+    int c;
+  };
+
+  struct I32 {
+    int a;
+    int b;
+    int c;
+  };
+
+  constexpr I3 i3 {5, 10, 15};
+  constexpr I32 i32 = __builtin_bit_cast(I32, i3); // ref-error {{must be initialized by a constant expression}} \
+                                                   // ref-note {{bit_cast involving bit-field is not yet supported}} \
+                                                   // expected-error {{must be initialized by a constant expression}} \
+                                                   // expected-note {{indeterminate value can only initialize an object of type 'unsigned char'}}
+
+  struct I33 {
+    int a;
+    unsigned char b;
+    int c;
+  };
+
+  constexpr I33 i33 = __builtin_bit_cast(I33, i3); // ref-error {{must be initialized by a constant expression}} \
+                                                   // ref-note {{bit_cast involving bit-field is not yet supported}} \
+                                                   // ref-note 3{{declared here}}
+  static_assert(i33.a == 5, ""); // ref-error {{not an integral constant expression}} \
+                                 // ref-note {{initializer of 'i33' is not a constant expression}}
+  static_assert(i33.b == 10, ""); // ref-error {{not an integral constant expression}} \
+                                  // ref-note {{initializer of 'i33' is not a constant expression}}
+  static_assert(i33.c == 15, ""); // ref-error {{not an integral constant expression}} \
+                                  // ref-note {{initializer of 'i33' is not a constant expression}}
+}

>From 916a79899882fb5386bd52ad3d1c9952f01dd3c1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Wed, 18 Oct 2023 15:46:31 +0200
Subject: [PATCH 07/12] Add string-to-int bitcast test

---
 clang/test/AST/Interp/builtin-bit-cast.cpp | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/clang/test/AST/Interp/builtin-bit-cast.cpp b/clang/test/AST/Interp/builtin-bit-cast.cpp
index ebc7138e6971f977..554f8e280d1ecedd 100644
--- a/clang/test/AST/Interp/builtin-bit-cast.cpp
+++ b/clang/test/AST/Interp/builtin-bit-cast.cpp
@@ -31,9 +31,24 @@ template <class To, class From>
 constexpr To bit_cast(const From &from) {
   static_assert(sizeof(To) == sizeof(From));
   return __builtin_bit_cast(To, from); // ref-note 2{{indeterminate value can only initialize}} \
-                                       // expected-note 2{{indeterminate value can only initialize}}
+                                       // expected-note 2{{indeterminate value can only initialize}} \
+                                       // ref-note {{subexpression not valid}}
 }
 
+
+/// Current interpreter does not support this.
+/// https://github.com/llvm/llvm-project/issues/63686
+constexpr int FromString = bit_cast<int>("abc"); // ref-error {{must be initialized by a constant expression}} \
+                                                 // ref-note {{in call to}} \
+                                                 // ref-note {{declared here}}
+#if LITTLE_END
+static_assert(FromString == 6513249); // ref-error {{is not an integral constant expression}} \
+                                      // ref-note {{initializer of 'FromString' is not a constant expression}}
+#else
+static_assert(FromString == 1633837824); // ref-error {{is not an integral constant expression}} \
+                                         // ref-note {{initializer of 'FromString' is not a constant expression}}
+#endif
+
 template <class Intermediate, class Init>
 constexpr bool round_trip(const Init &init) {
   return bit_cast<Init>(bit_cast<Intermediate>(init)) == init;

>From fcfdeace9bd05bac855ab9deb1e2674f42e24ecf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Thu, 9 Nov 2023 12:46:21 +0100
Subject: [PATCH 08/12] Address review comments

---
 clang/lib/AST/Interp/InterpBitcast.cpp     |  3 ---
 clang/test/AST/Interp/builtin-bit-cast.cpp | 13 +++++++++++++
 2 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/clang/lib/AST/Interp/InterpBitcast.cpp b/clang/lib/AST/Interp/InterpBitcast.cpp
index b62535c387315f31..ea60aaa87b54da79 100644
--- a/clang/lib/AST/Interp/InterpBitcast.cpp
+++ b/clang/lib/AST/Interp/InterpBitcast.cpp
@@ -17,8 +17,6 @@
 namespace clang {
 namespace interp {
 
-// TODO: Try to e-duplicate the primitive and composite versions.
-
 /// Used to iterate over pointer fields.
 using DataFunc =
     llvm::function_ref<bool(const Pointer &P, PrimType Ty, size_t BitOffset)>;
@@ -383,7 +381,6 @@ bool DoBitCastToPtr(InterpState &S, const Pointer &P, Pointer &DestPtr,
         if (T == PT_Float) {
           const QualType FloatType = P.getFieldDesc()->getType();
           const auto &Sem = ASTCtx.getFloatTypeSemantics(FloatType);
-          size_t ValueReprBits = ASTCtx.getTypeSize(FloatType);
 
           CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(P.getType());
           const std::byte *M = Bytes.getBytes(BitOffset, 1234);
diff --git a/clang/test/AST/Interp/builtin-bit-cast.cpp b/clang/test/AST/Interp/builtin-bit-cast.cpp
index 554f8e280d1ecedd..da9115787d75bb5b 100644
--- a/clang/test/AST/Interp/builtin-bit-cast.cpp
+++ b/clang/test/AST/Interp/builtin-bit-cast.cpp
@@ -49,6 +49,19 @@ static_assert(FromString == 1633837824); // ref-error {{is not an integral const
                                          // ref-note {{initializer of 'FromString' is not a constant expression}}
 #endif
 
+
+struct S {
+  int i, j, k;
+};
+constexpr S func() {
+  constexpr int array[] = { 12, 42, 128 };
+  return __builtin_bit_cast(S, array);
+}
+constexpr S s = func();
+static_assert(s.i == 12, "");
+static_assert(s.j == 42, "");
+static_assert(s.k == 128, "");
+
 template <class Intermediate, class Init>
 constexpr bool round_trip(const Init &init) {
   return bit_cast<Init>(bit_cast<Intermediate>(init)) == init;

>From ca031a4d7f1bd258d559f6a1a0505ce900182b83 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Mon, 27 Nov 2023 09:47:34 +0100
Subject: [PATCH 09/12] Support IntegralAP bit casts

---
 clang/lib/AST/Interp/Boolean.h             |  5 ++---
 clang/lib/AST/Interp/ByteCodeExprGen.cpp   |  4 +++-
 clang/lib/AST/Interp/Integral.h            |  3 ++-
 clang/lib/AST/Interp/IntegralAP.h          | 19 ++++++++++++++++++-
 clang/lib/AST/Interp/Interp.h              |  9 +++++----
 clang/lib/AST/Interp/InterpBitcast.cpp     | 14 +++++++++-----
 clang/lib/AST/Interp/Opcodes.td            |  4 ++--
 clang/test/AST/Interp/builtin-bit-cast.cpp | 13 +++++++++++++
 8 files changed, 54 insertions(+), 17 deletions(-)

diff --git a/clang/lib/AST/Interp/Boolean.h b/clang/lib/AST/Interp/Boolean.h
index 0708294dab95a105..fca5e56fda18d54f 100644
--- a/clang/lib/AST/Interp/Boolean.h
+++ b/clang/lib/AST/Interp/Boolean.h
@@ -16,8 +16,6 @@
 #include "llvm/ADT/APSInt.h"
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/raw_ostream.h"
-#include <cstddef>
-#include <cstdint>
 
 namespace clang {
 namespace interp {
@@ -114,7 +112,8 @@ class Boolean final {
     return Boolean(!Value.isZero());
   }
 
-  static Boolean bitcastFromMemory(const std::byte *Buff) {
+  static Boolean bitcastFromMemory(const std::byte *Buff, unsigned BitWidth) {
+    assert(BitWidth == 8);
     bool Val = static_cast<bool>(*Buff);
     return Boolean(Val);
   }
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 9956a01a5ed208ce..526d13195212a49f 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -131,8 +131,10 @@ bool ByteCodeExprGen<Emitter>::emitBuiltinBitCast(const CastExpr *E) {
 
   bool ToTypeIsUChar = (ToType->isSpecificBuiltinType(BuiltinType::UChar) ||
                         ToType->isSpecificBuiltinType(BuiltinType::Char_U));
+  uint32_t ResultBitWidth = std::max(Ctx.getBitWidth(ToType), 8u);
 
-  if (!this->emitBitCast(*ToT, ToTypeIsUChar || ToType->isStdByteType(), E))
+  if (!this->emitBitCast(*ToT, ToTypeIsUChar || ToType->isStdByteType(),
+                         ResultBitWidth, E))
     return false;
 
   if (DiscardResult)
diff --git a/clang/lib/AST/Interp/Integral.h b/clang/lib/AST/Interp/Integral.h
index bcef75bbbaeb8db5..45cd37a5867a6265 100644
--- a/clang/lib/AST/Interp/Integral.h
+++ b/clang/lib/AST/Interp/Integral.h
@@ -193,7 +193,8 @@ template <unsigned Bits, bool Signed> class Integral final {
     return Integral(Value);
   }
 
-  static Integral bitcastFromMemory(const std::byte *Buff) {
+  static Integral bitcastFromMemory(const std::byte *Buff, unsigned BitWidth) {
+    assert(BitWidth == sizeof(ReprT) * 8);
     ReprT V;
 
     std::memcpy(&V, Buff, sizeof(ReprT));
diff --git a/clang/lib/AST/Interp/IntegralAP.h b/clang/lib/AST/Interp/IntegralAP.h
index 9019f32e6cef2a34..bfa29682f977b3c9 100644
--- a/clang/lib/AST/Interp/IntegralAP.h
+++ b/clang/lib/AST/Interp/IntegralAP.h
@@ -15,6 +15,7 @@
 
 #include "clang/AST/APValue.h"
 #include "clang/AST/ComparisonCategories.h"
+#include "llvm/ADT/APInt.h"
 #include "llvm/ADT/APSInt.h"
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/raw_ostream.h"
@@ -61,7 +62,7 @@ template <bool Signed> class IntegralAP final {
 
   IntegralAP(APInt V) : V(V) {}
   /// Arbitrary value for uninitialized variables.
-  IntegralAP() : IntegralAP(-1, 1024) {}
+  IntegralAP() : IntegralAP(-1, 17) {}
 
   IntegralAP operator-() const { return IntegralAP(-V); }
   IntegralAP operator-(const IntegralAP &Other) const {
@@ -123,6 +124,11 @@ template <bool Signed> class IntegralAP final {
   }
 
   constexpr unsigned bitWidth() const { return V.getBitWidth(); }
+  constexpr unsigned objectReprBits() { return bitWidth(); }
+  constexpr unsigned valueReprBits(const ASTContext &Ctx) { return bitWidth(); }
+  constexpr unsigned valueReprBytes(const ASTContext &Ctx) {
+    return Ctx.toCharUnitsFromBits(bitWidth()).getQuantity();
+  }
 
   APSInt toAPSInt(unsigned Bits = 0) const {
     if (Bits == 0)
@@ -145,6 +151,17 @@ template <bool Signed> class IntegralAP final {
 
   unsigned countLeadingZeros() const { return V.countl_zero(); }
 
+  static IntegralAP bitcastFromMemory(const std::byte *Buff,
+                                      unsigned BitWidth) {
+    APInt V(BitWidth, static_cast<uint64_t>(0), Signed);
+    llvm::LoadIntFromMemory(V, (const uint8_t *)Buff, BitWidth / 8);
+    return IntegralAP(V);
+  }
+
+  void bitcastToMemory(std::byte *Buff) const {
+    llvm::StoreIntToMemory(V, (uint8_t *)Buff, bitWidth() / 8);
+  }
+
   void print(llvm::raw_ostream &OS) const { OS << V; }
   std::string toDiagnosticString(const ASTContext &Ctx) const {
     std::string NameStr;
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 5d69d59053dc5b98..79c35d90954b94e5 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -1577,11 +1577,12 @@ template <PrimType TIn, PrimType TOut> bool Cast(InterpState &S, CodePtr OpPC) {
 }
 
 template <PrimType Name, class ToT = typename PrimConv<Name>::T>
-bool BitCast(InterpState &S, CodePtr OpPC, bool TargetIsUCharOrByte) {
+bool BitCast(InterpState &S, CodePtr OpPC, bool TargetIsUCharOrByte,
+             uint32_t ResultBitWidth) {
   const Pointer &FromPtr = S.Stk.pop<Pointer>();
 
-  size_t BuffSize = ToT::valueReprBytes(S.getCtx());
-  std::vector<std::byte> Buff(BuffSize);
+  size_t BuffSize = ResultBitWidth / 8;
+  llvm::SmallVector<std::byte> Buff(BuffSize);
   bool HasIndeterminateBits = false;
 
   if (!DoBitCast(S, OpPC, FromPtr, Buff.data(), BuffSize, HasIndeterminateBits))
@@ -1590,7 +1591,7 @@ bool BitCast(InterpState &S, CodePtr OpPC, bool TargetIsUCharOrByte) {
   if (!CheckBitcast(S, OpPC, HasIndeterminateBits, TargetIsUCharOrByte))
     return false;
 
-  S.Stk.push<ToT>(ToT::bitcastFromMemory(Buff.data()));
+  S.Stk.push<ToT>(ToT::bitcastFromMemory(Buff.data(), ResultBitWidth));
   return true;
 }
 
diff --git a/clang/lib/AST/Interp/InterpBitcast.cpp b/clang/lib/AST/Interp/InterpBitcast.cpp
index ea60aaa87b54da79..baa2e5680ee18a2c 100644
--- a/clang/lib/AST/Interp/InterpBitcast.cpp
+++ b/clang/lib/AST/Interp/InterpBitcast.cpp
@@ -32,6 +32,8 @@ 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)                                             \
     default:                                                                   \
       llvm_unreachable("Unhandled bitcast type");                              \
@@ -51,6 +53,8 @@ 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:                                                                   \
@@ -384,10 +388,10 @@ bool DoBitCastToPtr(InterpState &S, const Pointer &P, Pointer &DestPtr,
 
           CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(P.getType());
           const std::byte *M = Bytes.getBytes(BitOffset, 1234);
-          std::byte *Buff = (std::byte *)std::malloc(
-              ObjectReprChars.getQuantity()); //[sizeof(T)];
+          std::byte *Buff =
+              (std::byte *)std::malloc(ObjectReprChars.getQuantity());
           std::memcpy(Buff, M, ObjectReprChars.getQuantity());
-          // Val.bitcastToMemory(Buff);
+
           if (BigEndian)
             swapBytes(Buff, ObjectReprChars.getQuantity());
 
@@ -407,7 +411,7 @@ bool DoBitCastToPtr(InterpState &S, const Pointer &P, Pointer &DestPtr,
         BITCAST_TYPE_SWITCH(T, {
           T &Val = P.deref<T>();
 
-          size_t ValueReprBits = T::valueReprBits(ASTCtx);
+          size_t ValueReprBits = Val.valueReprBits(ASTCtx);
           // Check if any of the bits we're about to read are uninitialized.
           bool HasIndeterminateBits =
               !Bytes.allInitialized(BitOffset, ValueReprBits);
@@ -440,7 +444,7 @@ bool DoBitCastToPtr(InterpState &S, const Pointer &P, Pointer &DestPtr,
           if (BigEndian) {
             swapBytes(Copy, ValueReprBits / 8);
           }
-          Val = T::bitcastFromMemory(Copy);
+          Val = T::bitcastFromMemory(Copy, Val.bitWidth());
           std::free(Copy);
 
           if (!HasIndeterminateBits)
diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td
index 2c1e39621d79fe1e..ff7ecbb96e1a0b9d 100644
--- a/clang/lib/AST/Interp/Opcodes.td
+++ b/clang/lib/AST/Interp/Opcodes.td
@@ -586,13 +586,13 @@ def Cast: Opcode {
 }
 
 def BitCastTypeClass : TypeClass {
-  let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool];
+  let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, IntAP, IntAPS, Bool];
 }
 
 def BitCast : Opcode {
   let Types = [BitCastTypeClass];
   let HasGroup = 1;
-  let Args = [ArgBool];
+  let Args = [ArgBool, ArgUint32];
 }
 
 def BitCastPtr : Opcode;
diff --git a/clang/test/AST/Interp/builtin-bit-cast.cpp b/clang/test/AST/Interp/builtin-bit-cast.cpp
index da9115787d75bb5b..729633fc14298ef9 100644
--- a/clang/test/AST/Interp/builtin-bit-cast.cpp
+++ b/clang/test/AST/Interp/builtin-bit-cast.cpp
@@ -74,6 +74,19 @@ constexpr int foo() {
 }
 static_assert(foo() == 1, "");
 
+
+namespace bitint {
+  constexpr _BitInt(sizeof(int) * 8) BI = ~0;
+  constexpr unsigned int I = __builtin_bit_cast(unsigned int, BI);
+  static_assert(I == ~0u, "");
+
+  constexpr _BitInt(sizeof(int) * 8) IB = __builtin_bit_cast(_BitInt(sizeof(int) * 8), I); // ref-error {{must be initialized by a constant expression}} \
+                                                                                           // ref-note {{constexpr bit cast involving type '_BitInt(32)' is not yet supported}} \
+                                                                                           // ref-note {{declared here}}
+  static_assert(IB == ~0u, ""); // ref-error {{not an integral constant expression}} \
+                                // ref-note {{initializer of 'IB' is not a constant expression}}
+}
+
 namespace Ints {
   static_assert(round_trip<unsigned>((int)-1));
   static_assert(round_trip<unsigned>((int)0x12345678));

>From 5e803f7f5a94b1713f157d79960a59720ff74c06 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Mon, 27 Nov 2023 09:53:20 +0100
Subject: [PATCH 10/12] Address other review comments

---
 clang/lib/AST/Interp/InterpBitcast.cpp | 19 ++++++++-----------
 1 file changed, 8 insertions(+), 11 deletions(-)

diff --git a/clang/lib/AST/Interp/InterpBitcast.cpp b/clang/lib/AST/Interp/InterpBitcast.cpp
index baa2e5680ee18a2c..3b5833604a469b10 100644
--- a/clang/lib/AST/Interp/InterpBitcast.cpp
+++ b/clang/lib/AST/Interp/InterpBitcast.cpp
@@ -272,7 +272,7 @@ static bool CheckBitcastType(InterpState &S, CodePtr OpPC, QualType T,
 }
 
 static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr,
-                                BitTracker &Bits, bool RetrnOnUninit) {
+                                BitTracker &Bits, bool ReturnOnUninit) {
   const ASTContext &ASTCtx = Ctx.getASTContext();
   uint64_t PointerSizeInBits =
       ASTCtx.getTargetInfo().getPointerWidth(LangAS::Default);
@@ -284,7 +284,7 @@ static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr,
         if (!P.isInitialized()) {
           Bits.markUninitializedUntil(BitOffset +
                                       (primSize(T) * 8)); /// primSize() usage.
-          return RetrnOnUninit;
+          return ReturnOnUninit;
         }
 
         assert(P.isInitialized());
@@ -337,7 +337,8 @@ bool DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &P, std::byte *Buff,
   bool BigEndian = ASTCtx.getTargetInfo().isBigEndian();
 
   BitTracker Bits;
-  bool Success = readPointerToBuffer(S.getContext(), P, Bits, false);
+  bool Success =
+      readPointerToBuffer(S.getContext(), P, Bits, /*ReturnOnUninit=*/false);
 
   Bits.markUninitializedUntil(BuffSize * 8);
   assert(Bits.size() == BuffSize * 8);
@@ -350,13 +351,9 @@ bool DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &P, std::byte *Buff,
   return Success; // && !HasIndeterminateBits;
 }
 
-//  This function is constexpr if and only if To, From, and the types of
-//  all subobjects of To and From are types T such that...
-//  (3.1) - is_union_v<T> is false;
-//  (3.2) - is_pointer_v<T> is false;
-//  (3.3) - is_member_pointer_v<T> is false;
-//  (3.4) - is_volatile_v<T> is false; and
-//  (3.5) - T has no non-static data members of reference type
+/// Bitcast from a Pointer to a Pointer.
+/// We read all fields from \p P into a buffer, then iterate
+/// over the fields of \p DestPtr and read from the buffer.
 bool DoBitCastToPtr(InterpState &S, const Pointer &P, Pointer &DestPtr,
                     CodePtr OpPC) {
   assert(P.isLive());
@@ -376,7 +373,7 @@ bool DoBitCastToPtr(InterpState &S, const Pointer &P, Pointer &DestPtr,
   bool BigEndian = ASTCtx.getTargetInfo().isBigEndian();
 
   BitTracker Bytes;
-  if (!readPointerToBuffer(Ctx, P, Bytes, true))
+  if (!readPointerToBuffer(Ctx, P, Bytes, /*ReturnOnUninit=*/true))
     return false;
 
   bool Success = enumeratePointerFields(

>From 63157ba19e9bcede87a50c35cfe2c8260f7a117c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Mon, 27 Nov 2023 10:52:36 +0100
Subject: [PATCH 11/12] Rename a member and address more review comments.

---
 clang/lib/AST/Interp/InterpBitcast.cpp | 30 ++++++++++++++------------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/clang/lib/AST/Interp/InterpBitcast.cpp b/clang/lib/AST/Interp/InterpBitcast.cpp
index 3b5833604a469b10..084c4a9e46da4c3e 100644
--- a/clang/lib/AST/Interp/InterpBitcast.cpp
+++ b/clang/lib/AST/Interp/InterpBitcast.cpp
@@ -73,18 +73,18 @@ static void swapBytes(std::byte *M, size_t N) {
 /// All offsets are in bits.
 struct BitTracker {
   llvm::BitVector Initialized;
-  llvm::BitVector Data_;
+  llvm::BitVector Data;
 
   BitTracker() = default;
 
   size_t size() const {
-    assert(Initialized.size() == Data_.size());
+    assert(Initialized.size() == Data.size());
     return Initialized.size();
   }
 
   const std::byte *getBytes(size_t BitOffset, int a) {
     assert(BitOffset % 8 == 0);
-    return reinterpret_cast<const std::byte *>(Data_.getData().data()) +
+    return reinterpret_cast<const std::byte *>(Data.getData().data()) +
            (BitOffset / 8);
   }
 
@@ -95,11 +95,14 @@ struct BitTracker {
   bool allInitialized() const { return Initialized.all(); }
 
   void pushData(const std::byte *data, size_t BitOffset, size_t BitWidth) {
-    assert(BitOffset >= Data_.size());
+    assert(BitOffset >= Data.size());
+    Data.reserve(BitOffset + BitWidth);
+    Initialized.reserve(BitOffset + BitWidth);
+
     // First, fill up the bit vector until BitOffset. The bits are all 0
     // but we record them as indeterminate.
     {
-      Data_.resize(BitOffset, false);
+      Data.resize(BitOffset, false);
       Initialized.resize(BitOffset, false);
     }
 
@@ -107,7 +110,7 @@ struct BitTracker {
     // Read all full bytes first
     for (size_t I = 0; I != BitWidth / 8; ++I) {
       for (unsigned X = 0; X != 8; ++X) {
-        Data_.push_back((data[I] & std::byte(1 << X)) != std::byte{0});
+        Data.push_back((data[I] & std::byte(1 << X)) != std::byte{0});
         Initialized.push_back(true);
         ++BitsHandled;
       }
@@ -116,21 +119,21 @@ struct BitTracker {
     // Rest of the bits.
     assert((BitWidth - BitsHandled) < 8);
     for (size_t I = 0, E = (BitWidth - BitsHandled); I != E; ++I) {
-      Data_.push_back((data[BitWidth / 8] & std::byte(1 << I)) != std::byte{0});
+      Data.push_back((data[BitWidth / 8] & std::byte(1 << I)) != std::byte{0});
       Initialized.push_back(true);
       ++BitsHandled;
     }
   }
 
   void pushZeroes(size_t Amount) {
-    size_t N = Data_.size();
-    Data_.resize(N + Amount, false);
+    size_t N = Data.size();
+    Data.resize(N + Amount, false);
     Initialized.resize(N + Amount, true);
   }
 
   void markUninitializedUntil(size_t Offset) {
-    assert(Offset >= Data_.size());
-    Data_.resize(Offset, false);
+    assert(Offset >= Data.size());
+    Data.resize(Offset, false);
     Initialized.resize(Offset, false);
   }
 };
@@ -185,7 +188,6 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, size_t Offset,
       size_t BitOffset = Offset + Ctx.getASTContext().toBits(ByteOffset);
       Ok = Ok && enumerateData(Elem, Ctx, BitOffset, F);
     }
-    // TODO: Virtual bases?
 
     for (unsigned I = 0; I != R->getNumFields(); ++I) {
       const Record::Field *Fi = R->getField(I);
@@ -344,11 +346,11 @@ bool DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &P, std::byte *Buff,
   assert(Bits.size() == BuffSize * 8);
 
   HasIndeterminateBits = !Bits.allInitialized();
-  std::memcpy(Buff, Bits.Data_.getData().data(), BuffSize);
+  std::memcpy(Buff, Bits.Data.getData().data(), BuffSize);
 
   if (BigEndian)
     swapBytes(Buff, BuffSize);
-  return Success; // && !HasIndeterminateBits;
+  return Success;
 }
 
 /// Bitcast from a Pointer to a Pointer.

>From 921cd37664d8503c93fefba6246fc34d34a7df0c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Mon, 27 Nov 2023 11:07:55 +0100
Subject: [PATCH 12/12] Handle BitCast and BitCastFP opcodes the same

---
 clang/lib/AST/Interp/ByteCodeExprGen.cpp | 15 ++++--------
 clang/lib/AST/Interp/Interp.h            | 31 +++++++-----------------
 clang/lib/AST/Interp/Opcodes.td          |  9 ++-----
 3 files changed, 16 insertions(+), 39 deletions(-)

diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 526d13195212a49f..340f25f79e3c5f53 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -118,23 +118,18 @@ bool ByteCodeExprGen<Emitter>::emitBuiltinBitCast(const CastExpr *E) {
 
   assert(ToT);
 
+  const llvm::fltSemantics *TargetSemantics = nullptr;
+  if (ToT == PT_Float)
+    TargetSemantics = &Ctx.getFloatSemantics(ToType);
+
   // 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);
-    CharUnits FloatSize = Ctx.getASTContext().getTypeSizeInChars(ToType);
-    return this->emitBitCastFP(TargetSemantics, FloatSize.getQuantity(), E);
-  }
-
   bool ToTypeIsUChar = (ToType->isSpecificBuiltinType(BuiltinType::UChar) ||
                         ToType->isSpecificBuiltinType(BuiltinType::Char_U));
   uint32_t ResultBitWidth = std::max(Ctx.getBitWidth(ToType), 8u);
 
   if (!this->emitBitCast(*ToT, ToTypeIsUChar || ToType->isStdByteType(),
-                         ResultBitWidth, E))
+                         ResultBitWidth, TargetSemantics, E))
     return false;
 
   if (DiscardResult)
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 79c35d90954b94e5..faf496204432823a 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -1578,7 +1578,8 @@ template <PrimType TIn, PrimType TOut> bool Cast(InterpState &S, CodePtr OpPC) {
 
 template <PrimType Name, class ToT = typename PrimConv<Name>::T>
 bool BitCast(InterpState &S, CodePtr OpPC, bool TargetIsUCharOrByte,
-             uint32_t ResultBitWidth) {
+             uint32_t ResultBitWidth, const llvm::fltSemantics *Sem) {
+  assert(ResultBitWidth > 0);
   const Pointer &FromPtr = S.Stk.pop<Pointer>();
 
   size_t BuffSize = ResultBitWidth / 8;
@@ -1591,27 +1592,13 @@ bool BitCast(InterpState &S, CodePtr OpPC, bool TargetIsUCharOrByte,
   if (!CheckBitcast(S, OpPC, HasIndeterminateBits, TargetIsUCharOrByte))
     return false;
 
-  S.Stk.push<ToT>(ToT::bitcastFromMemory(Buff.data(), ResultBitWidth));
-  return true;
-}
-
-/// Bitcast TO a float.
-inline bool BitCastFP(InterpState &S, CodePtr OpPC,
-                      const llvm::fltSemantics *Sem, uint32_t TargetSize) {
-  const Pointer &FromPtr = S.Stk.pop<Pointer>();
-
-  std::vector<std::byte> Buff(TargetSize);
-  bool HasIndeterminateBits = false;
-
-  if (!DoBitCast(S, OpPC, FromPtr, Buff.data(), TargetSize,
-                 HasIndeterminateBits))
-    return false;
-
-  if (!CheckBitcast(S, OpPC, HasIndeterminateBits,
-                    /*TargetIsUCharOrByte=*/false))
-    return false;
-
-  S.Stk.push<Floating>(Floating::bitcastFromMemory(Buff.data(), *Sem));
+  if constexpr (std::is_same_v<ToT, Floating>) {
+    assert(Sem);
+    S.Stk.push<Floating>(Floating::bitcastFromMemory(Buff.data(), *Sem));
+  } else {
+    assert(!Sem);
+    S.Stk.push<ToT>(ToT::bitcastFromMemory(Buff.data(), ResultBitWidth));
+  }
   return true;
 }
 
diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td
index ff7ecbb96e1a0b9d..76f10318f6699dc2 100644
--- a/clang/lib/AST/Interp/Opcodes.td
+++ b/clang/lib/AST/Interp/Opcodes.td
@@ -586,22 +586,17 @@ def Cast: Opcode {
 }
 
 def BitCastTypeClass : TypeClass {
-  let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, IntAP, IntAPS, Bool];
+  let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, IntAP, IntAPS, Bool, Float];
 }
 
 def BitCast : Opcode {
   let Types = [BitCastTypeClass];
   let HasGroup = 1;
-  let Args = [ArgBool, ArgUint32];
+  let Args = [ArgBool, ArgUint32, ArgFltSemantics];
 }
 
 def BitCastPtr : Opcode;
 
-def BitCastFP : Opcode {
-  let Types = [];
-  let Args = [ArgFltSemantics, ArgUint32];
-}
-
 def CastFP : Opcode {
   let Types = [];
   let Args = [ArgFltSemantics, ArgRoundingMode];



More information about the cfe-commits mailing list