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

via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 5 01:13:31 PDT 2023


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

<details>
<summary>Changes</summary>

>From https://reviews.llvm.org/D154951 - which is never gonna make it out of Phabricator anyway.


The tests are failing because https://reviews.llvm.org/D154581 is not pushed.

Aaron's review mentioned that we should support bitfields before pushing this.

---

Patch is 56.88 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/68288.diff


13 Files Affected:

- (modified) clang/lib/AST/CMakeLists.txt (+1) 
- (modified) clang/lib/AST/Interp/Boolean.h (+13-2) 
- (modified) clang/lib/AST/Interp/ByteCodeExprGen.cpp (+71) 
- (modified) clang/lib/AST/Interp/ByteCodeExprGen.h (+1) 
- (modified) clang/lib/AST/Interp/Floating.h (+11) 
- (modified) clang/lib/AST/Interp/Integral.h (+18-1) 
- (modified) clang/lib/AST/Interp/Interp.cpp (+17) 
- (modified) clang/lib/AST/Interp/Interp.h (+66) 
- (added) clang/lib/AST/Interp/InterpBitcast.cpp (+482) 
- (modified) clang/lib/AST/Interp/Opcodes.td (+17) 
- (modified) clang/lib/AST/Interp/PrimType.h (+4) 
- (added) clang/test/AST/Interp/builtin-bit-cast.cpp (+683) 
- (modified) clang/test/AST/Interp/literals.cpp (+5) 


``````````diff
diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt
index fe3f8c485ec1c56..5a188b583022bda 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 c3ed3d61f76ca1c..d395264ab2de448 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 {
@@ -65,6 +66,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(); }
 
@@ -106,6 +110,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 3cf8bf874b1d210..8cc010d6f100ccb 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -67,6 +67,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(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();
@@ -87,6 +155,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 47a3f75f13459d0..8f04c4809ccf41d 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.h
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -280,6 +280,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 a22b3fa79f3992f..727d37520047b77 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(); }
@@ -133,6 +140,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 4dbe9c9bcb14b43..9c78bb04876ea02 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"
@@ -116,6 +117,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; }
 
@@ -182,6 +187,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 8e851595963184c..d2c65daafc7d135 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -576,7 +576,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 d62e64bedb213ac..c14f890e188ecdb 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -183,6 +183,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);
 
@@ -194,6 +197,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 };
 
 //===----------------------------------------------------------------------===//
@@ -1557,6 +1572,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 000000000000000..91326fc8e788080
--- /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 ...
[truncated]

``````````

</details>


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


More information about the cfe-commits mailing list