https://github.com/mmha updated https://github.com/llvm/llvm-project/pull/155663

>From ce55bc0b9ef72e399ea012eda451299fc5a2b636 Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Tue, 2 Sep 2025 19:11:42 +0200
Subject: [PATCH 1/6] [CIR] Add constant record ILE support

This patch adds basic support for constant record initializer list expressions. 
There's a couple of limitations:

* No zero initialized padding bytes in C mode
* No bitfields
* No designated initializer lists
* Record alignments are not calculated, yet
* ILEs of derived records don't work, yet
* The constant attribute is not propagated to the backend, resulting in 
non-constants being emitted in the LLVM IR
---
 .../include/clang/CIR/Dialect/IR/CIRTypes.td  |   2 +
 clang/include/clang/CIR/MissingFeatures.h     |   1 +
 clang/lib/CIR/CodeGen/CIRGenBuilder.cpp       |  40 ++
 clang/lib/CIR/CodeGen/CIRGenBuilder.h         |  20 +-
 clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h |  11 +-
 clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp  | 605 +++++++++++++++++-
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        |   3 +
 clang/lib/CIR/CodeGen/CIRGenModule.h          |  50 ++
 clang/lib/CIR/CodeGen/CIRGenTypeCache.h       |   3 +
 clang/lib/CIR/Dialect/IR/CIRTypes.cpp         |  10 +
 clang/test/CIR/CodeGen/constant-inits.cpp     |  55 ++
 11 files changed, 786 insertions(+), 14 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/constant-inits.cpp

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td 
b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
index 312d0a9422673..4eec34cb299ab 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
@@ -643,6 +643,8 @@ def CIR_RecordType : CIR_Type<"Record", "record", [
     uint64_t getElementOffset(const mlir::DataLayout &dataLayout,
               unsigned idx) const;
 
+    bool isLayoutIdentical(const RecordType &other);
+
   private:
     unsigned computeStructSize(const mlir::DataLayout &dataLayout) const;
     uint64_t computeStructAlignment(const mlir::DataLayout &dataLayout) const;
diff --git a/clang/include/clang/CIR/MissingFeatures.h 
b/clang/include/clang/CIR/MissingFeatures.h
index 70e0abe30e416..1d35509641e3b 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -134,6 +134,7 @@ struct MissingFeatures {
   static bool astRecordDeclAttr() { return false; }
   static bool cxxSupport() { return false; }
   static bool recordZeroInit() { return false; }
+  static bool recordZeroInitPadding() { return false; }
   static bool zeroSizeRecordMembers() { return false; }
   static bool recordLayoutVirtualBases() { return false; }
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
index 755c76c89a645..999c2fcbfa490 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
@@ -7,6 +7,8 @@
 
//===----------------------------------------------------------------------===//
 
 #include "CIRGenBuilder.h"
+#include "mlir/IR/BuiltinAttributes.h"
+#include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/TypeSwitch.h"
 
 using namespace clang::CIRGen;
@@ -130,6 +132,44 @@ void 
CIRGenBuilderTy::computeGlobalViewIndicesFromFlatOffset(
   computeGlobalViewIndicesFromFlatOffset(offset, subType, layout, indices);
 }
 
+static mlir::Type getAttributeType(mlir::Attribute attr) {
+  return mlir::cast<mlir::TypedAttr>(attr).getType();
+}
+
+cir::RecordType clang::CIRGen::CIRGenBuilderTy::getCompleteRecordType(
+    mlir::ArrayAttr fields, bool packed, bool padded, llvm::StringRef name,
+    const clang::RecordDecl *ast) {
+  llvm::SmallVector<mlir::Type, 8> members;
+  members.reserve(fields.size());
+  llvm::transform(fields, std::back_inserter(members), getAttributeType);
+
+  if (name.empty())
+    return getAnonRecordTy(members, packed, padded);
+
+  return getCompleteRecordTy(members, name, packed, padded);
+}
+
+mlir::Attribute clang::CIRGen::CIRGenBuilderTy::getConstRecordOrZeroAttr(
+    mlir::ArrayAttr arrayAttr, bool packed, bool padded, mlir::Type type) {
+  auto recordTy = mlir::cast_or_null<cir::RecordType>(type);
+
+  // Record type not specified: create anon record type from members.
+  if (!recordTy) {
+    llvm::SmallVector<mlir::Type, 8> members;
+    members.reserve(arrayAttr.size());
+    llvm::transform(arrayAttr, std::back_inserter(members), getAttributeType);
+    recordTy = getType<cir::RecordType>(members, packed, padded,
+                                        cir::RecordType::Struct);
+  }
+
+  // Return zero or anonymous constant record.
+  const bool isZero = llvm::all_of(
+      arrayAttr, [&](mlir::Attribute a) { return isNullValue(a); });
+  if (isZero)
+    return cir::ZeroAttr::get(recordTy);
+  return cir::ConstRecordAttr::get(recordTy, arrayAttr);
+}
+
 // This can't be defined in Address.h because that file is included by
 // CIRGenBuilder.h
 Address Address::withElementType(CIRGenBuilderTy &builder,
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h 
b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 41207afd6a43b..22c0f7d34e4a9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -12,8 +12,10 @@
 #include "Address.h"
 #include "CIRGenRecordLayout.h"
 #include "CIRGenTypeCache.h"
+#include "mlir/IR/Attributes.h"
+#include "mlir/IR/BuiltinAttributes.h"
+#include "mlir/Support/LLVM.h"
 #include "clang/CIR/Dialect/IR/CIRDataLayout.h"
-#include "clang/CIR/Interfaces/CIRTypeInterfaces.h"
 #include "clang/CIR/MissingFeatures.h"
 
 #include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"
@@ -60,6 +62,16 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
         trailingZerosNum);
   }
 
+  cir::ConstArrayAttr getConstArray(mlir::Attribute attrs,
+                                    cir::ArrayType arrayTy) const {
+    return cir::ConstArrayAttr::get(arrayTy, attrs);
+  }
+
+  mlir::Attribute getConstRecordOrZeroAttr(mlir::ArrayAttr arrayAttr,
+                                           bool packed = false,
+                                           bool padded = false,
+                                           mlir::Type type = {});
+
   cir::ConstRecordAttr getAnonConstRecord(mlir::ArrayAttr arrayAttr,
                                           bool packed = false,
                                           bool padded = false,
@@ -150,6 +162,12 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
     return type;
   }
 
+  cir::RecordType getCompleteRecordType(mlir::ArrayAttr fields,
+                                        bool packed = false,
+                                        bool padded = false,
+                                        llvm::StringRef name = "",
+                                        const clang::RecordDecl *ast = 
nullptr);
+
   /// Get an incomplete CIR struct type. If we have a complete record
   /// declaration, we may create an incomplete type and then add the
   /// members, so \p rd here may be complete.
diff --git a/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h 
b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
index d455f6e283406..7af62b9845e40 100644
--- a/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
+++ b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
@@ -81,6 +81,14 @@ class ConstantEmitter {
   //     side effects, or emitting an initialization that requires a
   //     reference to its current location.
   mlir::Attribute emitForMemory(mlir::Attribute c, QualType destType);
+  static mlir::Attribute emitForMemory(CIRGenModule &cgm, mlir::Attribute c,
+                                       clang::QualType destTy);
+
+  mlir::Attribute emitNullForMemory(mlir::Location loc, QualType t) {
+    return emitNullForMemory(loc, cgm, t);
+  }
+  static mlir::Attribute emitNullForMemory(mlir::Location loc,
+                                           CIRGenModule &cgm, QualType t);
 
   /// Try to emit the initializer of the given declaration as an abstract
   /// constant.
@@ -104,7 +112,8 @@ class ConstantEmitter {
 
   mlir::TypedAttr tryEmitPrivate(const Expr *e, QualType destType);
   mlir::Attribute tryEmitPrivate(const APValue &value, QualType destType);
-  mlir::Attribute tryEmitPrivateForMemory(const APValue &value, QualType t);
+  mlir::Attribute tryEmitPrivateForMemory(const Expr *e, QualType destTy);
+  mlir::Attribute tryEmitPrivateForMemory(const APValue &value, QualType 
destTy);
 
 private:
 #ifndef NDEBUG
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index 262d2548d5c39..81d575f9a4e28 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -12,7 +12,6 @@
 
 #include "Address.h"
 #include "CIRGenConstantEmitter.h"
-#include "CIRGenFunction.h"
 #include "CIRGenModule.h"
 #include "CIRGenRecordLayout.h"
 #include "mlir/IR/Attributes.h"
@@ -21,20 +20,571 @@
 #include "clang/AST/APValue.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Attr.h"
+#include "clang/AST/CharUnits.h"
 #include "clang/AST/OperationKinds.h"
 #include "clang/AST/RecordLayout.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/Basic/Builtins.h"
-#include "clang/Basic/Specifiers.h"
 #include "clang/CIR/Dialect/IR/CIRAttrs.h"
 #include "clang/CIR/Dialect/IR/CIRTypes.h"
+#include "clang/CIR/MissingFeatures.h"
 #include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/Sequence.h"
 #include "llvm/Support/ErrorHandling.h"
+#include <iterator>
 
 using namespace clang;
 using namespace clang::CIRGen;
 
+//===----------------------------------------------------------------------===//
+//                            ConstantAggregateBuilder
+//===----------------------------------------------------------------------===//
+
+namespace {
+class ConstExprEmitter;
+
+static mlir::TypedAttr computePadding(CIRGenModule &cgm, CharUnits size) {
+  mlir::Type eltTy = cgm.UCharTy;
+  clang::CharUnits::QuantityType arSize = size.getQuantity();
+  CIRGenBuilderTy &bld = cgm.getBuilder();
+  if (size > CharUnits::One()) {
+    SmallVector<mlir::Attribute, 4> elts(arSize, cir::ZeroAttr::get(eltTy));
+    return bld.getConstArray(mlir::ArrayAttr::get(bld.getContext(), elts),
+                             cir::ArrayType::get(eltTy, arSize));
+  }
+
+  return cir::ZeroAttr::get(eltTy);
+}
+
+static mlir::Attribute
+emitArrayConstant(CIRGenModule &cgm, mlir::Type desiredType,
+                  mlir::Type commonElementType, unsigned arrayBound,
+                  SmallVectorImpl<mlir::TypedAttr> &elements,
+                  mlir::TypedAttr filler);
+
+struct ConstantAggregateBuilderUtils {
+  CIRGenModule &cgm;
+  cir::CIRDataLayout dataLayout;
+
+  ConstantAggregateBuilderUtils(CIRGenModule &cgm)
+      : cgm(cgm), dataLayout{cgm.getModule()} {}
+
+  CharUnits getAlignment(const mlir::TypedAttr c) const {
+    return CharUnits::fromQuantity(
+        dataLayout.getAlignment(c.getType(), /*abiOrPref=*/true));
+  }
+
+  CharUnits getSize(mlir::Type ty) const {
+    return CharUnits::fromQuantity(dataLayout.getTypeAllocSize(ty));
+  }
+
+  CharUnits getSize(const mlir::TypedAttr c) const {
+    return getSize(c.getType());
+  }
+
+  mlir::TypedAttr getPadding(CharUnits size) const {
+    return computePadding(cgm, size);
+  }
+};
+
+/// Incremental builder for an mlir::TypedAttr holding a record or array
+/// constant.
+class ConstantAggregateBuilder : private ConstantAggregateBuilderUtils {
+  /// The elements of the constant. These two arrays must have the same size;
+  /// Offsets[i] describes the offset of Elems[i] within the constant. The
+  /// elements are kept in increasing offset order, and we ensure that there
+  /// is no overlap: Offsets[i+1] >= Offsets[i] + getSize(Elemes[i]).
+  ///
+  /// This may contain explicit padding elements (in order to create a
+  /// natural layout), but need not. Gaps between elements are implicitly
+  /// considered to be filled with undef.
+  llvm::SmallVector<mlir::TypedAttr, 32> elems;
+  llvm::SmallVector<CharUnits, 32> offsets;
+
+  /// The size of the constant (the maximum end offset of any added element).
+  /// May be larger than the end of Elems.back() if we split the last element
+  /// and removed some trailing undefs.
+  CharUnits size = CharUnits::Zero();
+
+  /// This is true only if laying out Elems in order as the elements of a
+  /// non-packed LLVM struct will give the correct layout.
+  bool naturalLayout = true;
+
+  std::optional<size_t> splitAt(CharUnits pos);
+
+  static mlir::Attribute
+  buildFrom(CIRGenModule &cgm, ArrayRef<mlir::TypedAttr> elems,
+            ArrayRef<CharUnits> offsets, CharUnits startOffset, CharUnits size,
+            bool naturalLayout, mlir::Type desiredTy, bool allowOversized);
+
+public:
+  ConstantAggregateBuilder(CIRGenModule &cgm)
+      : ConstantAggregateBuilderUtils(cgm) {}
+
+  /// Update or overwrite the value starting at \p offset with \c c.
+  ///
+  /// \param allowOverwrite If \c true, this constant might overwrite (part of)
+  ///        a constant that has already been added. This flag is only used to
+  ///        detect bugs.
+  bool add(mlir::TypedAttr typedAttr, CharUnits offset, bool allowOverwrite);
+
+  /// Update or overwrite the bits starting at \p offsetInBits with \p bits.
+  bool addBits(llvm::APInt bits, uint64_t offsetInBits, bool allowOverwrite);
+
+  /// Produce a constant representing the entire accumulated value, ideally of
+  /// the specified type. If \p allowOversized, the constant might be larger
+  /// than implied by \p desiredTy (eg, if there is a flexible array member).
+  /// Otherwise, the constant will be of exactly the same size as \p desiredTy
+  /// even if we can't represent it as that type.
+  mlir::Attribute build(mlir::Type desiredTy, bool allowOversized) const {
+    return buildFrom(cgm, elems, offsets, CharUnits::Zero(), size,
+                     naturalLayout, desiredTy, allowOversized);
+  }
+};
+
+template <typename Container, typename Range = std::initializer_list<
+                                  typename Container::value_type>>
+static void replace(Container &c, size_t beginOff, size_t endOff, Range vals) {
+  assert(beginOff <= endOff && "invalid replacement range");
+  llvm::replace(c, c.begin() + beginOff, c.begin() + endOff, vals);
+}
+
+bool ConstantAggregateBuilder::add(mlir::TypedAttr typedAttr, CharUnits offset,
+                                   bool allowOverwrite) {
+  // Common case: appending to a layout.
+  if (offset >= size) {
+    CharUnits align = getAlignment(typedAttr);
+    CharUnits alignedSize = size.alignTo(align);
+    if (alignedSize > offset || offset.alignTo(align) != offset)
+      naturalLayout = false;
+    else if (alignedSize < offset) {
+      elems.push_back(getPadding(offset - size));
+      offsets.push_back(size);
+    }
+    elems.push_back(typedAttr);
+    offsets.push_back(offset);
+    size = offset + getSize(typedAttr);
+    return true;
+  }
+
+  // Uncommon case: constant overlaps what we've already created.
+  std::optional<size_t> firstElemToReplace = splitAt(offset);
+  if (!firstElemToReplace)
+    return false;
+
+  CharUnits cSize = getSize(typedAttr);
+  std::optional<size_t> lastElemToReplace = splitAt(offset + cSize);
+  if (!lastElemToReplace)
+    return false;
+
+  assert((firstElemToReplace == lastElemToReplace || allowOverwrite) &&
+         "unexpectedly overwriting field");
+
+  replace(elems, *firstElemToReplace, *lastElemToReplace, {typedAttr});
+  replace(offsets, *firstElemToReplace, *lastElemToReplace, {offset});
+  size = std::max(size, offset + cSize);
+  naturalLayout = false;
+  return true;
+}
+
+/// Returns a position within Elems and Offsets such that all elements
+/// before the returned index end before Pos and all elements at or after
+/// the returned index begin at or after Pos. Splits elements as necessary
+/// to ensure this. Returns None if we find something we can't split.
+std::optional<size_t> ConstantAggregateBuilder::splitAt(CharUnits pos) {
+  if (pos >= size)
+    return offsets.size();
+
+  clang::CharUnits *firstAfterPos = llvm::upper_bound(offsets, pos);
+  if (firstAfterPos == offsets.begin())
+    return 0;
+
+  // If we already have an element starting at Pos, we're done.
+  size_t lastAtOrBeforePosIndex = firstAfterPos - offsets.begin() - 1;
+  if (offsets[lastAtOrBeforePosIndex] == pos)
+    return lastAtOrBeforePosIndex;
+
+  // We found an element starting before Pos. Check for overlap.
+  mlir::TypedAttr c =
+      mlir::cast<mlir::TypedAttr>(elems[lastAtOrBeforePosIndex]);
+  if (offsets[lastAtOrBeforePosIndex] + getSize(c) <= pos)
+    return lastAtOrBeforePosIndex + 1;
+
+  // Try to decompose it into smaller constants.
+  cgm.errorNYI("split into smaller constants");
+  return std::nullopt;
+}
+
+mlir::Attribute ConstantAggregateBuilder::buildFrom(
+    CIRGenModule &cgm, ArrayRef<mlir::TypedAttr> elems,
+    ArrayRef<CharUnits> offsets, CharUnits startOffset, CharUnits size,
+    bool naturalLayout, mlir::Type desiredTy, bool allowOversized) {
+  ConstantAggregateBuilderUtils utils(cgm);
+
+  if (elems.empty())
+    return cir::UndefAttr::get(desiredTy);
+
+  auto offset = [&](size_t i) { return offsets[i] - startOffset; };
+
+  // If we want an array type, see if all the elements are the same type and
+  // appropriately spaced.
+  if (mlir::isa<cir::ArrayType>(desiredTy)) {
+    cgm.errorNYI("array aggregate constants");
+    return {};
+  }
+
+  // The size of the constant we plan to generate. This is usually just the 
size
+  // of the initialized type, but in AllowOversized mode (i.e. flexible array
+  // init), it can be larger.
+  CharUnits desiredSize = utils.getSize(desiredTy);
+  if (size > desiredSize) {
+    assert(allowOversized && "Elems are oversized");
+    desiredSize = size;
+  }
+
+  // The natural alignment of an unpacked CIR record with the given elements.
+  CharUnits align = CharUnits::One();
+  for (mlir::TypedAttr e : elems) {
+    align = std::max(align, utils.getAlignment(e));
+  }
+
+  // The natural size of an unpacked LLVM struct with the given elements.
+  CharUnits alignedSize = size.alignTo(align);
+
+  bool packed = false;
+  bool padded = false;
+  ArrayRef<mlir::TypedAttr> unpackedElems = elems;
+
+  llvm::SmallVector<mlir::TypedAttr, 32> unpackedElemStorage;
+  if (desiredSize < alignedSize || desiredSize.alignTo(align) != desiredSize) {
+    naturalLayout = false;
+    packed = true;
+  } else if (desiredSize > alignedSize) {
+    // The natural layout would be too small. Add padding to fix it. (This
+    // is ignored if we choose a packed layout.)
+    unpackedElemStorage.assign(unpackedElems.begin(), unpackedElems.end());
+    unpackedElemStorage.push_back(utils.getPadding(desiredSize - size));
+    unpackedElems = unpackedElemStorage;
+  }
+
+  // If we don't have a natural layout, insert padding as necessary.
+  // As we go, double-check to see if we can actually just emit Elems
+  // as a non-packed record and do so opportunistically if possible.
+  llvm::SmallVector<mlir::TypedAttr, 32> packedElems;
+  if (!naturalLayout) {
+    CharUnits sizeSoFar = CharUnits::Zero();
+    for (auto [index, element] : llvm::enumerate(elems)) {
+      CharUnits align = utils.getAlignment(element);
+      CharUnits naturalOffset = sizeSoFar.alignTo(align);
+      CharUnits desiredOffset = offset(index);
+      assert(desiredOffset >= sizeSoFar && "elements out of order");
+
+      if (desiredOffset != naturalOffset)
+        packed = true;
+      if (desiredOffset != sizeSoFar)
+        packedElems.push_back(utils.getPadding(desiredOffset - sizeSoFar));
+      packedElems.push_back(element);
+      sizeSoFar = desiredOffset + utils.getSize(element);
+    }
+    // If we're using the packed layout, pad it out to the desired size if
+    // necessary.
+    if (packed) {
+      assert(sizeSoFar <= desiredSize &&
+             "requested size is too small for contents");
+
+      if (sizeSoFar < desiredSize)
+        packedElems.push_back(utils.getPadding(desiredSize - sizeSoFar));
+    }
+  }
+
+  CIRGenBuilderTy &builder = cgm.getBuilder();
+  llvm::SmallVector<mlir::Attribute, 32> arrayElements;
+  arrayElements.reserve(elems.size());
+  if (packed)
+    llvm::copy(packedElems, std::back_inserter(arrayElements));
+  else
+    llvm::copy(unpackedElems, std::back_inserter(arrayElements));
+  auto arrAttr = mlir::ArrayAttr::get(builder.getContext(), arrayElements);
+
+  cir::RecordType strType = builder.getCompleteRecordType(arrAttr, packed);
+  if (auto desired = mlir::dyn_cast<cir::RecordType>(desiredTy))
+    if (desired.isLayoutIdentical(strType))
+      strType = desired;
+
+  return builder.getConstRecordOrZeroAttr(arrAttr, packed, padded, strType);
+}
+
+//===----------------------------------------------------------------------===//
+//                            ConstRecordBuilder
+//===----------------------------------------------------------------------===//
+
+class ConstRecordBuilder {
+  CIRGenModule &cgm;
+  ConstantEmitter &emitter;
+  ConstantAggregateBuilder &builder;
+  CharUnits startOffset;
+
+public:
+  static mlir::Attribute buildRecord(ConstantEmitter &emitter,
+                                     InitListExpr *ile, QualType valTy);
+  static mlir::Attribute buildRecord(ConstantEmitter &emitter,
+                                     const APValue &value, QualType valTy);
+  static bool updateRecord(ConstantEmitter &emitter,
+                           ConstantAggregateBuilder &constant, CharUnits 
offset,
+                           InitListExpr *updater);
+
+private:
+  ConstRecordBuilder(ConstantEmitter &emitter,
+                     ConstantAggregateBuilder &builder, CharUnits startOffset)
+      : cgm(emitter.cgm), emitter(emitter), builder(builder),
+        startOffset(startOffset) {}
+
+  bool appendField(const FieldDecl *field, uint64_t fieldOffset,
+                   mlir::TypedAttr initCst, bool allowOverwrite = false);
+
+  bool appendBytes(CharUnits fieldOffsetInChars, mlir::TypedAttr initCst,
+                   bool allowOverwrite = false);
+
+  bool build(InitListExpr *ile, bool allowOverwrite);
+  bool build(const APValue &val, const RecordDecl *rd, bool isPrimaryBase,
+             const CXXRecordDecl *vTableClass, CharUnits baseOffset);
+
+  mlir::Attribute finalize(QualType ty);
+};
+
+bool ConstRecordBuilder::appendField(const FieldDecl *field,
+                                     uint64_t fieldOffset,
+                                     mlir::TypedAttr initCst,
+                                     bool allowOverwrite) {
+  const ASTContext &astContext = cgm.getASTContext();
+
+  CharUnits fieldOffsetInChars = astContext.toCharUnitsFromBits(fieldOffset);
+
+  return appendBytes(fieldOffsetInChars, initCst, allowOverwrite);
+}
+
+bool ConstRecordBuilder::appendBytes(CharUnits fieldOffsetInChars,
+                                     mlir::TypedAttr initCst,
+                                     bool allowOverwrite) {
+  return builder.add(initCst, startOffset + fieldOffsetInChars, 
allowOverwrite);
+}
+
+bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) {
+  RecordDecl *rd = ile->getType()
+                       ->castAs<clang::RecordType>()
+                       ->getOriginalDecl()
+                       ->getDefinitionOrSelf();
+  const ASTRecordLayout &layout = cgm.getASTContext().getASTRecordLayout(rd);
+
+  // Bail out if we have base classes. We could support these, but they only
+  // arise in C++1z where we will have already constant folded most interesting
+  // cases. FIXME: There are still a few more cases we can handle this way.
+  if (auto *cxxrd = dyn_cast<CXXRecordDecl>(rd))
+    if (cxxrd->getNumBases())
+      return false;
+
+  if (cgm.shouldZeroInitPadding()) {
+    assert(!cir::MissingFeatures::recordZeroInitPadding());
+    cgm.errorNYI(rd->getSourceRange(), "zero init padding");
+    return false;
+  }
+
+  unsigned elementNo = 0;
+  for (auto [index, field] : llvm::enumerate(rd->fields())) {
+
+    // If this is a union, skip all the fields that aren't being initialized.
+    if (rd->isUnion() &&
+        !declaresSameEntity(ile->getInitializedFieldInUnion(), field))
+      continue;
+
+    // Don't emit anonymous bitfields.
+    if (field->isUnnamedBitField())
+      continue;
+
+    // Get the initializer.  A record can include fields without initializers,
+    // we just use explicit null values for them.
+    Expr *init = nullptr;
+    if (elementNo < ile->getNumInits())
+      init = ile->getInit(elementNo++);
+    if (isa_and_nonnull<NoInitExpr>(init))
+      continue;
+
+    // Zero-sized fields are not emitted, but their initializers may still
+    // prevent emission of this record as a constant.
+    if (field->isZeroSize(cgm.getASTContext())) {
+      if (init->HasSideEffects(cgm.getASTContext()))
+        return false;
+      continue;
+    }
+
+    assert(!cir::MissingFeatures::recordZeroInitPadding());
+
+    // When emitting a DesignatedInitUpdateExpr, a nested InitListExpr
+    // represents additional overwriting of our current constant value, and not
+    // a new constant to emit independently.
+    if (allowOverwrite &&
+        (field->getType()->isArrayType() || field->getType()->isRecordType())) 
{
+      cgm.errorNYI(field->getSourceRange(), "designated init lists");
+      return false;
+    }
+
+    mlir::TypedAttr eltInit;
+    if (init)
+      eltInit = mlir::cast<mlir::TypedAttr>(
+          emitter.tryEmitPrivateForMemory(init, field->getType()));
+    else
+      eltInit = mlir::cast<mlir::TypedAttr>(emitter.emitNullForMemory(
+          cgm.getLoc(ile->getSourceRange()), field->getType()));
+
+    if (!eltInit)
+      return false;
+
+    if (!field->isBitField()) {
+      // Handle non-bitfield members.
+      if (!appendField(field, layout.getFieldOffset(index), eltInit,
+                       allowOverwrite))
+        return false;
+      // After emitting a non-empty field with [[no_unique_address]], we may
+      // need to overwrite its tail padding.
+      if (field->hasAttr<NoUniqueAddressAttr>())
+        allowOverwrite = true;
+    } else {
+      // Otherwise we have a bitfield.
+      if (auto constInt = dyn_cast<cir::IntAttr>(eltInit)) {
+        assert(!cir::MissingFeatures::bitfields());
+        cgm.errorNYI(field->getSourceRange(), "bitfields");
+      }
+      // We are trying to initialize a bitfield with a non-trivial constant,
+      // this must require run-time code.
+      return false;
+    }
+  }
+
+  assert(!cir::MissingFeatures::recordZeroInitPadding());
+  return true;
+}
+
+namespace {
+struct BaseInfo {
+  BaseInfo(const CXXRecordDecl *decl, CharUnits offset, unsigned index)
+      : decl(decl), offset(offset), index(index) {}
+
+  const CXXRecordDecl *decl;
+  CharUnits offset;
+  unsigned index;
+
+  bool operator<(const BaseInfo &o) const { return offset < o.offset; }
+};
+} // namespace
+
+bool ConstRecordBuilder::build(const APValue &val, const RecordDecl *rd,
+                               bool isPrimaryBase,
+                               const CXXRecordDecl *vTableClass,
+                               CharUnits offset) {
+  const ASTRecordLayout &layout = cgm.getASTContext().getASTRecordLayout(rd);
+  if (const CXXRecordDecl *cd = dyn_cast<CXXRecordDecl>(rd)) {
+    assert(!cir::MissingFeatures::vtableInitialization());
+
+    // Accumulate and sort bases, in order to visit them in address order, 
which
+    // may not be the same as declaration order.
+    SmallVector<BaseInfo, 8> bases;
+    bases.reserve(cd->getNumBases());
+    for (auto [index, base] : llvm::enumerate(cd->bases())) {
+      assert(!base.isVirtual() && "should not have virtual bases here");
+      const CXXRecordDecl *bd = base.getType()->getAsCXXRecordDecl();
+      CharUnits baseOffset = layout.getBaseClassOffset(bd);
+      bases.push_back(BaseInfo(bd, baseOffset, index));
+    }
+    llvm::stable_sort(bases);
+
+    for (BaseInfo &base : bases) {
+      bool isPrimaryBase = layout.getPrimaryBase() == base.decl;
+      build(val.getStructBase(base.index), base.decl, isPrimaryBase,
+            vTableClass, offset + base.offset);
+    }
+  }
+
+  uint64_t offsetBits = cgm.getASTContext().toBits(offset);
+
+  bool allowOverwrite = false;
+  for (auto [index, field] : llvm::enumerate(rd->fields())) {
+    // If this is a union, skip all the fields that aren't being initialized.
+    if (rd->isUnion() && !declaresSameEntity(val.getUnionField(), field))
+      continue;
+
+    // Don't emit anonymous bitfields or zero-sized fields.
+    if (field->isUnnamedBitField() || field->isZeroSize(cgm.getASTContext()))
+      continue;
+
+    // Emit the value of the initializer.
+    const APValue &fieldValue =
+        rd->isUnion() ? val.getUnionValue() : val.getStructField(index);
+    mlir::TypedAttr eltInit = mlir::cast<mlir::TypedAttr>(
+        emitter.tryEmitPrivateForMemory(fieldValue, field->getType()));
+    if (!eltInit)
+      return false;
+
+    if (!field->isBitField()) {
+      // Handle non-bitfield members.
+      if (!appendField(field, layout.getFieldOffset(index) + offsetBits,
+                       eltInit, allowOverwrite))
+        return false;
+      // After emitting a non-empty field with [[no_unique_address]], we may
+      // need to overwrite its tail padding.
+      if (field->hasAttr<NoUniqueAddressAttr>())
+        allowOverwrite = true;
+    } else {
+      assert(!cir::MissingFeatures::bitfields());
+      cgm.errorNYI(field->getSourceRange(), "bitfields");
+    }
+  }
+
+  return true;
+}
+
+mlir::Attribute ConstRecordBuilder::finalize(QualType type) {
+  type = type.getNonReferenceType();
+  RecordDecl *rd = type->castAs<clang::RecordType>()
+                       ->getOriginalDecl()
+                       ->getDefinitionOrSelf();
+  mlir::Type valTy = cgm.convertType(type);
+  return builder.build(valTy, rd->hasFlexibleArrayMember());
+}
+
+mlir::Attribute ConstRecordBuilder::buildRecord(ConstantEmitter &emitter,
+                                                InitListExpr *ile,
+                                                QualType valTy) {
+  ConstantAggregateBuilder constant(emitter.cgm);
+  ConstRecordBuilder builder(emitter, constant, CharUnits::Zero());
+
+  if (!builder.build(ile, /*allowOverwrite*/ false))
+    return nullptr;
+
+  return builder.finalize(valTy);
+}
+
+mlir::Attribute ConstRecordBuilder::buildRecord(ConstantEmitter &emitter,
+                                                const APValue &val,
+                                                QualType valTy) {
+  ConstantAggregateBuilder constant(emitter.cgm);
+  ConstRecordBuilder builder(emitter, constant, CharUnits::Zero());
+
+  const RecordDecl *rd = valTy->castAs<clang::RecordType>()
+                             ->getOriginalDecl()
+                             ->getDefinitionOrSelf();
+  const CXXRecordDecl *cd = dyn_cast<CXXRecordDecl>(rd);
+  if (!builder.build(val, rd, false, cd, CharUnits::Zero()))
+    return nullptr;
+
+  return builder.finalize(valTy);
+}
+
+bool ConstRecordBuilder::updateRecord(ConstantEmitter &emitter,
+                                      ConstantAggregateBuilder &constant,
+                                      CharUnits offset, InitListExpr *updater) 
{
+  return ConstRecordBuilder(emitter, constant, offset)
+      .build(updater, /*allowOverwrite*/ true);
+}
+
 
//===----------------------------------------------------------------------===//
 //                             ConstExprEmitter
 
//===----------------------------------------------------------------------===//
@@ -59,7 +609,7 @@ class ConstExprEmitter
   //                            Visitor Methods
   
//===--------------------------------------------------------------------===//
 
-  mlir::Attribute VisitStmt(Stmt *S, QualType T) { return {}; }
+  mlir::Attribute VisitStmt(Stmt *s, QualType t) { return {}; }
 
   mlir::Attribute VisitConstantExpr(ConstantExpr *ce, QualType t) {
     if (mlir::Attribute result = emitter.tryEmitConstantExpr(ce))
@@ -204,9 +754,9 @@ class ConstExprEmitter
     return Visit(e->getSubExpr(), t);
   }
 
-  mlir::Attribute VisitImplicitValueInitExpr(ImplicitValueInitExpr *E,
-                                             QualType T) {
-    cgm.errorNYI(E->getBeginLoc(),
+  mlir::Attribute VisitImplicitValueInitExpr(ImplicitValueInitExpr *e,
+                                             QualType t) {
+    cgm.errorNYI(e->getBeginLoc(),
                  "ConstExprEmitter::VisitImplicitValueInitExpr");
     return {};
   }
@@ -223,8 +773,7 @@ class ConstExprEmitter
     }
 
     if (ile->getType()->isRecordType()) {
-      cgm.errorNYI(ile->getBeginLoc(), "ConstExprEmitter: record ILE");
-      return {};
+      return ConstRecordBuilder::buildRecord(emitter, ile, t);
     }
 
     if (ile->getType()->isVectorType()) {
@@ -358,9 +907,11 @@ emitArrayConstant(CIRGenModule &cgm, mlir::Type 
desiredType,
     eles.push_back(element);
 
   auto arrAttr = mlir::ArrayAttr::get(builder.getContext(), eles);
-  return builder.getAnonConstRecord(arrAttr, /*isPacked=*/true);
+  return builder.getAnonConstRecord(arrAttr, /*packed=*/true);
 }
 
+} // namespace
+
 
//===----------------------------------------------------------------------===//
 //                          ConstantLValueEmitter
 
//===----------------------------------------------------------------------===//
@@ -732,6 +1283,17 @@ mlir::Attribute 
ConstantEmitter::tryEmitConstantExpr(const ConstantExpr *ce) {
   return emitAbstract(ce->getBeginLoc(), ce->getAPValueResult(), retType);
 }
 
+mlir::Attribute ConstantEmitter::tryEmitPrivateForMemory(const Expr *e,
+                                                         QualType destType) {
+  QualType nonMemoryDestType = getNonMemoryType(cgm, destType);
+  mlir::TypedAttr c = tryEmitPrivate(e, nonMemoryDestType);
+  if (c) {
+    mlir::Attribute attr = emitForMemory(c, destType);
+    return mlir::cast<mlir::TypedAttr>(attr);
+  }
+  return nullptr;
+}
+
 mlir::Attribute ConstantEmitter::tryEmitPrivateForMemory(const APValue &value,
                                                          QualType destType) {
   QualType nonMemoryDestType = getNonMemoryType(cgm, destType);
@@ -759,6 +1321,15 @@ mlir::Attribute 
ConstantEmitter::emitAbstract(SourceLocation loc,
   return c;
 }
 
+mlir::Attribute ConstantEmitter::emitNullForMemory(mlir::Location loc,
+                                                   CIRGenModule &cgm,
+                                                   QualType t) {
+  cir::ConstantOp cstOp =
+      cgm.emitNullConstant(t, loc).getDefiningOp<cir::ConstantOp>();
+  assert(cstOp && "expected cir.const op");
+  return emitForMemory(cgm, cstOp.getValue(), t);
+}
+
 mlir::Attribute ConstantEmitter::emitForMemory(mlir::Attribute c,
                                                QualType destType) {
   // For an _Atomic-qualified constant, we may need to add tail padding.
@@ -770,6 +1341,17 @@ mlir::Attribute 
ConstantEmitter::emitForMemory(mlir::Attribute c,
   return c;
 }
 
+mlir::Attribute ConstantEmitter::emitForMemory(CIRGenModule &cgm,
+                                               mlir::Attribute c,
+                                               QualType destType) {
+  // For an _Atomic-qualified constant, we may need to add tail padding.
+  if (destType->getAs<AtomicType>()) {
+    cgm.errorNYI("atomic constants");
+  }
+
+  return c;
+}
+
 mlir::TypedAttr ConstantEmitter::tryEmitPrivate(const Expr *e,
                                                 QualType destType) {
   assert(!destType->isVoidType() && "can't emit a void constant");
@@ -901,8 +1483,7 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const 
APValue &value,
     return ConstantLValueEmitter(*this, value, destType).tryEmit();
   case APValue::Struct:
   case APValue::Union:
-    cgm.errorNYI("ConstExprEmitter::tryEmitPrivate struct or union");
-    return {};
+    return ConstRecordBuilder::buildRecord(*this, value, destType);
   case APValue::ComplexInt:
   case APValue::ComplexFloat: {
     mlir::Type desiredType = cgm.convertType(destType);
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index c7f548498c5cb..8c0e49bc10a47 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -93,6 +93,9 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext,
               astContext.getTargetInfo().getPointerAlign(LangAS::Default))
           .getQuantity();
 
+  const unsigned charSize = astContext.getTargetInfo().getCharWidth();
+  UCharTy = cir::IntType::get(&getMLIRContext(), charSize, /*isSigned=*/false);
+
   // TODO(CIR): Should be updated once TypeSizeInfoAttr is upstreamed
   const unsigned sizeTypeSize =
       astContext.getTypeSize(astContext.getSignedSizeType());
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h 
b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 4f5c7f898af8c..1e72b8f7e9708 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -153,6 +153,56 @@ class CIRGenModule : public CIRGenTypeCache {
                                       bool isConstant = false,
                                       mlir::Operation *insertPoint = nullptr);
 
+  bool shouldZeroInitPadding() const {
+    // In C23 (N3096) $6.7.10:
+    // """
+    // If any object is initialized with an empty initializer, then it is
+    // subject to default initialization:
+    //  - if it is an aggregate, every member is initialized (recursively)
+    //  according to these rules, and any padding is initialized to zero bits;
+    //  - if it is a union, the first named member is initialized (recursively)
+    //  according to these rules, and any padding is initialized to zero bits.
+    //
+    // If the aggregate or union contains elements or members that are
+    // aggregates or unions, these rules apply recursively to the subaggregates
+    // or contained unions.
+    //
+    // If there are fewer initializers in a brace-enclosed list than there are
+    // elements or members of an aggregate, or fewer characters in a string
+    // literal used to initialize an array of known size than there are 
elements
+    // in the array, the remainder of the aggregate is subject to default
+    // initialization.
+    // """
+    //
+    // The standard seems ambiguous in the following two areas:
+    // 1. For a union type with empty initializer, if the first named member is
+    // not the largest member, then the bytes comes after the first named 
member
+    // but before padding are left unspecified. An example is:
+    //    union U { int a; long long b;};
+    //    union U u = {};  // The first 4 bytes are 0, but 4-8 bytes are left
+    //    unspecified.
+    //
+    // 2. It only mentions padding for empty initializer, but doesn't mention
+    // padding for a non empty initialization list. And if the aggregation or
+    // union contains elements or members that are aggregates or unions, and
+    // some are non empty initializers, while others are empty initializers,
+    // the padding initialization is unclear. An example is:
+    //    struct S1 { int a; long long b; };
+    //    struct S2 { char c; struct S1 s1; };
+    //    // The values for paddings between s2.c and s2.s1.a, between s2.s1.a
+    //    and s2.s1.b are unclear.
+    //    struct S2 s2 = { 'c' };
+    //
+    // Here we choose to zero initiailize left bytes of a union type because
+    // projects like the Linux kernel are relying on this behavior. If we don't
+    // explicitly zero initialize them, the undef values can be optimized to
+    // return garbage data. We also choose to zero initialize paddings for
+    // aggregates and unions, no matter they are initialized by empty
+    // initializers or non empty initializers. This can provide a consistent
+    // behavior. So projects like the Linux kernel can rely on it.
+    return !getLangOpts().CPlusPlus;
+  }
+
   llvm::StringMap<unsigned> cgGlobalNames;
   std::string getUniqueGlobalName(const std::string &baseName);
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypeCache.h 
b/clang/lib/CIR/CodeGen/CIRGenTypeCache.h
index eb8dcd67a4a81..cc3ce09be4f95 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypeCache.h
+++ b/clang/lib/CIR/CodeGen/CIRGenTypeCache.h
@@ -49,6 +49,9 @@ struct CIRGenTypeCache {
   cir::FP80Type FP80Ty;
   cir::FP128Type FP128Ty;
 
+  /// ClangIR char
+  mlir::Type UCharTy;
+
   /// intptr_t, size_t, and ptrdiff_t, which we assume are the same size.
   union {
     mlir::Type UIntPtrTy;
diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp 
b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index 4fecb0108e001..35b4513c5789f 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -283,6 +283,16 @@ Type RecordType::getLargestMember(const ::mlir::DataLayout 
&dataLayout) const {
       });
 }
 
+bool RecordType::isLayoutIdentical(const RecordType &other) {
+  if (getImpl() == other.getImpl())
+    return true;
+
+  if (getPacked() != other.getPacked())
+    return false;
+
+  return getMembers() == other.getMembers();
+}
+
 
//===----------------------------------------------------------------------===//
 // Data Layout information for types
 
//===----------------------------------------------------------------------===//
diff --git a/clang/test/CIR/CodeGen/constant-inits.cpp 
b/clang/test/CIR/CodeGen/constant-inits.cpp
new file mode 100644
index 0000000000000..44a7a4506bee9
--- /dev/null
+++ b/clang/test/CIR/CodeGen/constant-inits.cpp
@@ -0,0 +1,55 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu 
-Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu 
-Wno-unused-value -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+struct empty{};
+
+struct Point {
+    int x;
+    int y;
+    char c[3];
+    int z;
+    [[no_unique_address]] empty e;
+};
+
+void function() {
+    constexpr static empty e;
+
+    constexpr static Point p1{10, 20, {99, 88, 77}, 40, e};
+
+    constexpr static Point array[] {
+        {123, 456, {11, 22, 33}, 789, {}},
+        {10, 20, {0, 0 ,0}, 40}
+    };
+}
+
+// CIR: cir.global "private" internal dso_local @_ZZ8functionvE5array = 
#cir.const_array<[
+// CIR-SAME:   #cir.const_record<{#cir.int<123> : !s32i, #cir.int<456> : 
!s32i, #cir.const_array<[#cir.int<11> : !s8i, #cir.int<22> : !s8i, #cir.int<33> 
: !s8i]> : !cir.array<!s8i x 3>, #cir.int<789> : !s32i}> : !rec_Point
+// CIR-SAME:   #cir.const_record<{#cir.int<10> : !s32i, #cir.int<20> : !s32i, 
#cir.zero : !cir.array<!s8i x 3>, #cir.int<40> : !s32i}> : !rec_Point
+// CIR-SAME: ]> : !cir.array<!rec_Point x 2>
+
+// CIR: cir.global "private" internal dso_local @_ZZ8functionvE2p1 = 
#cir.const_record<{#cir.int<10> : !s32i, #cir.int<20> : !s32i, 
#cir.const_array<[#cir.int<99> : !s8i, #cir.int<88> : !s8i, #cir.int<77> : 
!s8i]> : !cir.array<!s8i x 3>, #cir.int<40> : !s32i}> : !rec_Point
+
+// CIR: cir.global "private" internal dso_local @_ZZ8functionvE1e = #cir.zero 
: !rec_empty
+
+// CIR-LABEL: cir.func dso_local @_Z8functionv()
+// CIR:   cir.return
+
+
+// LLVM-DAG: @_ZZ8functionvE5array = internal global [2 x %struct.Point] 
[%struct.Point { i32 123, i32 456, [3 x i8] c"\0B\16!", i32 789 }, 
%struct.Point { i32 10, i32 20, [3 x i8] zeroinitializer, i32 40 }]
+// LLVM-DAG: @_ZZ8functionvE2p1 = internal global %struct.Point { i32 10, i32 
20, [3 x i8] c"cXM", i32 40 }
+// LLVM-DAG: @_ZZ8functionvE1e = internal global %struct.empty zeroinitializer
+
+// LLVM-LABEL: define{{.*}} void @_Z8functionv
+// LLVM:   ret void
+
+
+// OGCG-DAG: @_ZZ8functionvE5array = internal constant [2 x %struct.Point] 
[%struct.Point { i32 123, i32 456, [3 x i8] c"\0B\16!", i32 789 }, 
%struct.Point { i32 10, i32 20, [3 x i8] zeroinitializer, i32 40 }]
+// OGCG-DAG: @_ZZ8functionvE2p1 = internal constant %struct.Point { i32 10, 
i32 20, [3 x i8] c"cXM", i32 40 }
+// OGCG-DAG: @_ZZ8functionvE1e = internal constant %struct.empty 
zeroinitializer
+
+// OGCG-LABEL: define{{.*}} void @_Z8functionv
+// OGCG:   ret void

>From 6b34cff06e532fdd4abe0e0433d8b901665eb4b9 Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Wed, 27 Aug 2025 19:48:05 +0200
Subject: [PATCH 2/6] clang-format

---
 clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h 
b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
index 7af62b9845e40..ccded3dacf031 100644
--- a/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
+++ b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
@@ -113,7 +113,8 @@ class ConstantEmitter {
   mlir::TypedAttr tryEmitPrivate(const Expr *e, QualType destType);
   mlir::Attribute tryEmitPrivate(const APValue &value, QualType destType);
   mlir::Attribute tryEmitPrivateForMemory(const Expr *e, QualType destTy);
-  mlir::Attribute tryEmitPrivateForMemory(const APValue &value, QualType 
destTy);
+  mlir::Attribute tryEmitPrivateForMemory(const APValue &value,
+                                          QualType destTy);
 
 private:
 #ifndef NDEBUG

>From 668d73f01770bd82f02d9966c5546cc8d92aebc9 Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Thu, 28 Aug 2025 16:23:57 +0200
Subject: [PATCH 3/6] Address review feedback

---
 clang/lib/CIR/CodeGen/CIRGenBuilder.cpp       | 24 ++++-----
 clang/lib/CIR/CodeGen/CIRGenBuilder.h         |  9 ++--
 clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp  | 49 +------------------
 .../CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp |  6 +--
 4 files changed, 19 insertions(+), 69 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
index 999c2fcbfa490..670a431e47f40 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
@@ -8,6 +8,7 @@
 
 #include "CIRGenBuilder.h"
 #include "mlir/IR/BuiltinAttributes.h"
+#include "clang/CIR/MissingFeatures.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/TypeSwitch.h"
 
@@ -132,21 +133,20 @@ void 
CIRGenBuilderTy::computeGlobalViewIndicesFromFlatOffset(
   computeGlobalViewIndicesFromFlatOffset(offset, subType, layout, indices);
 }
 
-static mlir::Type getAttributeType(mlir::Attribute attr) {
-  return mlir::cast<mlir::TypedAttr>(attr).getType();
-}
-
 cir::RecordType clang::CIRGen::CIRGenBuilderTy::getCompleteRecordType(
-    mlir::ArrayAttr fields, bool packed, bool padded, llvm::StringRef name,
-    const clang::RecordDecl *ast) {
-  llvm::SmallVector<mlir::Type, 8> members;
+    mlir::ArrayAttr fields, bool packed, bool padded, llvm::StringRef name) {
+  assert(!cir::MissingFeatures::astRecordDeclAttr());
+  llvm::SmallVector<mlir::Type> members;
   members.reserve(fields.size());
-  llvm::transform(fields, std::back_inserter(members), getAttributeType);
+  llvm::transform(fields, std::back_inserter(members),
+                  [](mlir::Attribute attr) {
+                    return mlir::cast<mlir::TypedAttr>(attr).getType();
+                  });
 
   if (name.empty())
     return getAnonRecordTy(members, packed, padded);
 
-  return getCompleteRecordTy(members, name, packed, padded);
+  return getCompleteNamedRecordType(members, packed, padded, name);
 }
 
 mlir::Attribute clang::CIRGen::CIRGenBuilderTy::getConstRecordOrZeroAttr(
@@ -155,11 +155,7 @@ mlir::Attribute 
clang::CIRGen::CIRGenBuilderTy::getConstRecordOrZeroAttr(
 
   // Record type not specified: create anon record type from members.
   if (!recordTy) {
-    llvm::SmallVector<mlir::Type, 8> members;
-    members.reserve(arrayAttr.size());
-    llvm::transform(arrayAttr, std::back_inserter(members), getAttributeType);
-    recordTy = getType<cir::RecordType>(members, packed, padded,
-                                        cir::RecordType::Struct);
+    recordTy = getCompleteRecordType(arrayAttr, packed, padded);
   }
 
   // Return zero or anonymous constant record.
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h 
b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 22c0f7d34e4a9..7994bcb693f1b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -138,9 +138,9 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
   ///
   /// If a record already exists and is complete, but the client tries to fetch
   /// it with a different set of attributes, this method will crash.
-  cir::RecordType getCompleteRecordTy(llvm::ArrayRef<mlir::Type> members,
-                                      llvm::StringRef name, bool packed,
-                                      bool padded) {
+  cir::RecordType getCompleteNamedRecordType(llvm::ArrayRef<mlir::Type> 
members,
+                                             bool packed, bool padded,
+                                             llvm::StringRef name) {
     const auto nameAttr = getStringAttr(name);
     auto kind = cir::RecordType::RecordKind::Struct;
     assert(!cir::MissingFeatures::astRecordDeclAttr());
@@ -165,8 +165,7 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
   cir::RecordType getCompleteRecordType(mlir::ArrayAttr fields,
                                         bool packed = false,
                                         bool padded = false,
-                                        llvm::StringRef name = "",
-                                        const clang::RecordDecl *ast = 
nullptr);
+                                        llvm::StringRef name = "");
 
   /// Get an incomplete CIR struct type. If we have a complete record
   /// declaration, we may create an incomplete type and then add the
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index 81d575f9a4e28..2fd418eda01a5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -109,8 +109,6 @@ class ConstantAggregateBuilder : private 
ConstantAggregateBuilderUtils {
   /// non-packed LLVM struct will give the correct layout.
   bool naturalLayout = true;
 
-  std::optional<size_t> splitAt(CharUnits pos);
-
   static mlir::Attribute
   buildFrom(CIRGenModule &cgm, ArrayRef<mlir::TypedAttr> elems,
             ArrayRef<CharUnits> offsets, CharUnits startOffset, CharUnits size,
@@ -167,51 +165,8 @@ bool ConstantAggregateBuilder::add(mlir::TypedAttr 
typedAttr, CharUnits offset,
   }
 
   // Uncommon case: constant overlaps what we've already created.
-  std::optional<size_t> firstElemToReplace = splitAt(offset);
-  if (!firstElemToReplace)
-    return false;
-
-  CharUnits cSize = getSize(typedAttr);
-  std::optional<size_t> lastElemToReplace = splitAt(offset + cSize);
-  if (!lastElemToReplace)
-    return false;
-
-  assert((firstElemToReplace == lastElemToReplace || allowOverwrite) &&
-         "unexpectedly overwriting field");
-
-  replace(elems, *firstElemToReplace, *lastElemToReplace, {typedAttr});
-  replace(offsets, *firstElemToReplace, *lastElemToReplace, {offset});
-  size = std::max(size, offset + cSize);
-  naturalLayout = false;
-  return true;
-}
-
-/// Returns a position within Elems and Offsets such that all elements
-/// before the returned index end before Pos and all elements at or after
-/// the returned index begin at or after Pos. Splits elements as necessary
-/// to ensure this. Returns None if we find something we can't split.
-std::optional<size_t> ConstantAggregateBuilder::splitAt(CharUnits pos) {
-  if (pos >= size)
-    return offsets.size();
-
-  clang::CharUnits *firstAfterPos = llvm::upper_bound(offsets, pos);
-  if (firstAfterPos == offsets.begin())
-    return 0;
-
-  // If we already have an element starting at Pos, we're done.
-  size_t lastAtOrBeforePosIndex = firstAfterPos - offsets.begin() - 1;
-  if (offsets[lastAtOrBeforePosIndex] == pos)
-    return lastAtOrBeforePosIndex;
-
-  // We found an element starting before Pos. Check for overlap.
-  mlir::TypedAttr c =
-      mlir::cast<mlir::TypedAttr>(elems[lastAtOrBeforePosIndex]);
-  if (offsets[lastAtOrBeforePosIndex] + getSize(c) <= pos)
-    return lastAtOrBeforePosIndex + 1;
-
-  // Try to decompose it into smaller constants.
-  cgm.errorNYI("split into smaller constants");
-  return std::nullopt;
+  cgm.errorNYI("overlapping constants");
+  return false;
 }
 
 mlir::Attribute ConstantAggregateBuilder::buildFrom(
diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp 
b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
index 6c7cf75aa2c99..bb1ac753d85af 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
@@ -627,9 +627,9 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, 
cir::RecordType *ty) {
       CIRRecordLowering baseLowering(*this, rd, /*Packed=*/lowering.packed);
       baseLowering.lower(/*NonVirtualBaseType=*/true);
       std::string baseIdentifier = getRecordTypeName(rd, ".base");
-      baseTy =
-          builder.getCompleteRecordTy(baseLowering.fieldTypes, baseIdentifier,
-                                      baseLowering.packed, 
baseLowering.padded);
+      baseTy = builder.getCompleteNamedRecordType(
+          baseLowering.fieldTypes, baseLowering.packed, baseLowering.padded,
+          baseIdentifier);
       // TODO(cir): add something like addRecordTypeName
 
       // BaseTy and Ty must agree on their packedness for getCIRFieldNo to work

>From 2d5af9e360aba121c51a8153f81ad39de441758c Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Thu, 28 Aug 2025 16:59:07 +0200
Subject: [PATCH 4/6] Add more tests

---
 clang/test/CIR/CodeGen/constant-inits.cpp | 60 +++++++++++++++++++----
 1 file changed, 50 insertions(+), 10 deletions(-)

diff --git a/clang/test/CIR/CodeGen/constant-inits.cpp 
b/clang/test/CIR/CodeGen/constant-inits.cpp
index 44a7a4506bee9..7e02b8ccc5872 100644
--- a/clang/test/CIR/CodeGen/constant-inits.cpp
+++ b/clang/test/CIR/CodeGen/constant-inits.cpp
@@ -15,6 +15,17 @@ struct Point {
     [[no_unique_address]] empty e;
 };
 
+struct [[gnu::packed]] packed {
+    char c;
+    int i;
+};
+
+struct [[gnu::packed]] alignas(8) packed_and_aligned {
+    short s;
+    char c;
+    float f;
+};
+
 void function() {
     constexpr static empty e;
 
@@ -24,32 +35,61 @@ void function() {
         {123, 456, {11, 22, 33}, 789, {}},
         {10, 20, {0, 0 ,0}, 40}
     };
+
+    constexpr static packed p2 {123, 456};
+    constexpr static packed packed_array[] {
+        p2, p2
+    };
+
+    constexpr static packed_and_aligned paa {1, 2, 3.f};
+    constexpr static packed_and_aligned paa_array[2] {
+        {1, 2, 3.f}
+    };
 }
 
-// CIR: cir.global "private" internal dso_local @_ZZ8functionvE5array = 
#cir.const_array<[
-// CIR-SAME:   #cir.const_record<{#cir.int<123> : !s32i, #cir.int<456> : 
!s32i, #cir.const_array<[#cir.int<11> : !s8i, #cir.int<22> : !s8i, #cir.int<33> 
: !s8i]> : !cir.array<!s8i x 3>, #cir.int<789> : !s32i}> : !rec_Point
-// CIR-SAME:   #cir.const_record<{#cir.int<10> : !s32i, #cir.int<20> : !s32i, 
#cir.zero : !cir.array<!s8i x 3>, #cir.int<40> : !s32i}> : !rec_Point
-// CIR-SAME: ]> : !cir.array<!rec_Point x 2>
+// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE1e = 
#cir.zero : !rec_empty
+// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE2p1 = 
#cir.const_record<{#cir.int<10> : !s32i, #cir.int<20> : !s32i, 
#cir.const_array<[#cir.int<99> : !s8i, #cir.int<88> : !s8i, #cir.int<77> : 
!s8i]> : !cir.array<!s8i x 3>, #cir.int<40> : !s32i}> : !rec_Point
+// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE2p2 = 
#cir.const_record<{#cir.int<123> : !s8i, #cir.int<456> : !s32i}> : !rec_packed
+// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE3paa = 
#cir.const_record<{#cir.int<1> : !s16i, #cir.int<2> : !s8i, 
#cir.fp<3.000000e+00> : !cir.float, #cir.zero : !u8i}> : !rec_packed_and_aligned
+
+// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE5array = 
#cir.const_array<[
+// CIR-DAG-SAME:   #cir.const_record<{#cir.int<123> : !s32i, #cir.int<456> : 
!s32i, #cir.const_array<[#cir.int<11> : !s8i, #cir.int<22> : !s8i, #cir.int<33> 
: !s8i]> : !cir.array<!s8i x 3>, #cir.int<789> : !s32i}> : !rec_Point
+// CIR-DAG-SAME:   #cir.const_record<{#cir.int<10> : !s32i, #cir.int<20> : 
!s32i, #cir.zero : !cir.array<!s8i x 3>, #cir.int<40> : !s32i}> : !rec_Point
+// CIR-DAG-SAME: ]> : !cir.array<!rec_Point x 2>
 
-// CIR: cir.global "private" internal dso_local @_ZZ8functionvE2p1 = 
#cir.const_record<{#cir.int<10> : !s32i, #cir.int<20> : !s32i, 
#cir.const_array<[#cir.int<99> : !s8i, #cir.int<88> : !s8i, #cir.int<77> : 
!s8i]> : !cir.array<!s8i x 3>, #cir.int<40> : !s32i}> : !rec_Point
+// CIR-DAG: cir.global "private" internal dso_local 
@_ZZ8functionvE12packed_array = #cir.const_array<[
+// CIR-DAG-SAME:   #cir.const_record<{#cir.int<123> : !s8i, #cir.int<456> : 
!s32i}> : !rec_packed,
+// CIR-DAG-SAME:   #cir.const_record<{#cir.int<123> : !s8i, #cir.int<456> : 
!s32i}> : !rec_packed
+// CIR-DAG-SAME: ]> : !cir.array<!rec_packed x 2>
 
-// CIR: cir.global "private" internal dso_local @_ZZ8functionvE1e = #cir.zero 
: !rec_empty
+// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE9paa_array 
= #cir.const_array<[
+// CIR-DAG-SAME:   #cir.const_record<{#cir.int<1> : !s16i, #cir.int<2> : !s8i, 
#cir.fp<3.000000e+00> : !cir.float, #cir.zero : !u8i}> : 
!rec_packed_and_aligned,
+// CIR-DAG-SAME:   #cir.zero : !rec_packed_and_aligned
+// CIR-DAG-SAME: ]> : !cir.array<!rec_packed_and_aligned x 2>
 
 // CIR-LABEL: cir.func dso_local @_Z8functionv()
 // CIR:   cir.return
 
 
-// LLVM-DAG: @_ZZ8functionvE5array = internal global [2 x %struct.Point] 
[%struct.Point { i32 123, i32 456, [3 x i8] c"\0B\16!", i32 789 }, 
%struct.Point { i32 10, i32 20, [3 x i8] zeroinitializer, i32 40 }]
-// LLVM-DAG: @_ZZ8functionvE2p1 = internal global %struct.Point { i32 10, i32 
20, [3 x i8] c"cXM", i32 40 }
+// LLVM-DAG: @_ZZ8functionvE12packed_array = internal global [2 x 
%struct.packed] [%struct.packed <{ i8 123, i32 456 }>, %struct.packed <{ i8 
123, i32 456 }>]
 // LLVM-DAG: @_ZZ8functionvE1e = internal global %struct.empty zeroinitializer
+// LLVM-DAG: @_ZZ8functionvE2p1 = internal global %struct.Point { i32 10, i32 
20, [3 x i8] c"cXM", i32 40 }
+// LLVM-DAG: @_ZZ8functionvE2p2 = internal global %struct.packed <{ i8 123, 
i32 456 }>
+// LLVM-DAG: @_ZZ8functionvE3paa = internal global %struct.packed_and_aligned 
<{ i16 1, i8 2, float 3.000000e+00, i8 0 }>
+// LLVM-DAG: @_ZZ8functionvE5array = internal global [2 x %struct.Point] 
[%struct.Point { i32 123, i32 456, [3 x i8] c"\0B\16!", i32 789 }, 
%struct.Point { i32 10, i32 20, [3 x i8] zeroinitializer, i32 40 }]
+// LLVM-DAG: @_ZZ8functionvE9paa_array = internal global [2 x 
%struct.packed_and_aligned] [%struct.packed_and_aligned <{ i16 1, i8 2, float 
3.000000e+00, i8 0 }>, %struct.packed_and_aligned zeroinitializer]
 
 // LLVM-LABEL: define{{.*}} void @_Z8functionv
 // LLVM:   ret void
 
 
-// OGCG-DAG: @_ZZ8functionvE5array = internal constant [2 x %struct.Point] 
[%struct.Point { i32 123, i32 456, [3 x i8] c"\0B\16!", i32 789 }, 
%struct.Point { i32 10, i32 20, [3 x i8] zeroinitializer, i32 40 }]
-// OGCG-DAG: @_ZZ8functionvE2p1 = internal constant %struct.Point { i32 10, 
i32 20, [3 x i8] c"cXM", i32 40 }
+// OGCG-DAG: @_ZZ8functionvE12packed_array = internal constant [2 x 
%struct.packed] [%struct.packed <{ i8 123, i32 456 }>, %struct.packed <{ i8 
123, i32 456 }>]
 // OGCG-DAG: @_ZZ8functionvE1e = internal constant %struct.empty 
zeroinitializer
+// OGCG-DAG: @_ZZ8functionvE2p1 = internal constant %struct.Point { i32 10, 
i32 20, [3 x i8] c"cXM", i32 40 }
+// OGCG-DAG: @_ZZ8functionvE2p2 = internal constant %struct.packed <{ i8 123, 
i32 456 }>
+// OGCG-DAG: @_ZZ8functionvE3paa = internal constant 
%struct.packed_and_aligned <{ i16 1, i8 2, float 3.000000e+00, i8 undef }>
+// OGCG-DAG: @_ZZ8functionvE5array = internal constant [2 x %struct.Point] 
[%struct.Point { i32 123, i32 456, [3 x i8] c"\0B\16!", i32 789 }, 
%struct.Point { i32 10, i32 20, [3 x i8] zeroinitializer, i32 40 }]
+// OGCG-DAG: @_ZZ8functionvE9paa_array = internal constant [2 x 
%struct.packed_and_aligned] [%struct.packed_and_aligned <{ i16 1, i8 2, float 
3.000000e+00, i8 undef }>, %struct.packed_and_aligned <{ i16 0, i8 0, float 
0.000000e+00, i8 undef }>]
 
 // OGCG-LABEL: define{{.*}} void @_Z8functionv
 // OGCG:   ret void

>From 52e2f6e619c23f5226d498441d2a9243b9b22ffc Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Thu, 28 Aug 2025 17:30:16 +0200
Subject: [PATCH 5/6] Add simple test case

---
 clang/test/CIR/CodeGen/constant-inits.cpp | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/clang/test/CIR/CodeGen/constant-inits.cpp 
b/clang/test/CIR/CodeGen/constant-inits.cpp
index 7e02b8ccc5872..c9153c91ebc22 100644
--- a/clang/test/CIR/CodeGen/constant-inits.cpp
+++ b/clang/test/CIR/CodeGen/constant-inits.cpp
@@ -26,6 +26,10 @@ struct [[gnu::packed]] alignas(8) packed_and_aligned {
     float f;
 };
 
+struct simple {
+    int a, b;
+};
+
 void function() {
     constexpr static empty e;
 
@@ -45,9 +49,15 @@ void function() {
     constexpr static packed_and_aligned paa_array[2] {
         {1, 2, 3.f}
     };
+
+    constexpr static simple s{0, -1};
+    constexpr static simple simple_array[] {
+        s, {1111, 2222}, s
+    };
 }
 
 // CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE1e = 
#cir.zero : !rec_empty
+// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE1s = 
#cir.const_record<{#cir.int<0> : !s32i, #cir.int<-1> : !s32i}> : !rec_simple
 // CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE2p1 = 
#cir.const_record<{#cir.int<10> : !s32i, #cir.int<20> : !s32i, 
#cir.const_array<[#cir.int<99> : !s8i, #cir.int<88> : !s8i, #cir.int<77> : 
!s8i]> : !cir.array<!s8i x 3>, #cir.int<40> : !s32i}> : !rec_Point
 // CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE2p2 = 
#cir.const_record<{#cir.int<123> : !s8i, #cir.int<456> : !s32i}> : !rec_packed
 // CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE3paa = 
#cir.const_record<{#cir.int<1> : !s16i, #cir.int<2> : !s8i, 
#cir.fp<3.000000e+00> : !cir.float, #cir.zero : !u8i}> : !rec_packed_and_aligned
@@ -57,6 +67,12 @@ void function() {
 // CIR-DAG-SAME:   #cir.const_record<{#cir.int<10> : !s32i, #cir.int<20> : 
!s32i, #cir.zero : !cir.array<!s8i x 3>, #cir.int<40> : !s32i}> : !rec_Point
 // CIR-DAG-SAME: ]> : !cir.array<!rec_Point x 2>
 
+// CIR-DAG: cir.global "private" internal dso_local 
@_ZZ8functionvE12simple_array = #cir.const_array<[
+// CIR-DAG-SAME:   #cir.const_record<{#cir.int<0> : !s32i, #cir.int<-1> : 
!s32i}> : !rec_simple,
+// CIR-DAG-SAME:   #cir.const_record<{#cir.int<1111> : !s32i, #cir.int<2222> : 
!s32i}> : !rec_simple,
+// CIR-DAG-SAME:   #cir.const_record<{#cir.int<0> : !s32i, #cir.int<-1> : 
!s32i}> : !rec_simple
+// CIR-DAG-SAME: ]> : !cir.array<!rec_simple x 3>
+
 // CIR-DAG: cir.global "private" internal dso_local 
@_ZZ8functionvE12packed_array = #cir.const_array<[
 // CIR-DAG-SAME:   #cir.const_record<{#cir.int<123> : !s8i, #cir.int<456> : 
!s32i}> : !rec_packed,
 // CIR-DAG-SAME:   #cir.const_record<{#cir.int<123> : !s8i, #cir.int<456> : 
!s32i}> : !rec_packed
@@ -72,7 +88,9 @@ void function() {
 
 
 // LLVM-DAG: @_ZZ8functionvE12packed_array = internal global [2 x 
%struct.packed] [%struct.packed <{ i8 123, i32 456 }>, %struct.packed <{ i8 
123, i32 456 }>]
+// LLVM-DAG: @_ZZ8functionvE12simple_array = internal global [3 x 
%struct.simple] [%struct.simple { i32 0, i32 -1 }, %struct.simple { i32 1111, 
i32 2222 }, %struct.simple { i32 0, i32 -1 }]
 // LLVM-DAG: @_ZZ8functionvE1e = internal global %struct.empty zeroinitializer
+// LLVM-DAG: @_ZZ8functionvE1s = internal global %struct.simple { i32 0, i32 
-1 }
 // LLVM-DAG: @_ZZ8functionvE2p1 = internal global %struct.Point { i32 10, i32 
20, [3 x i8] c"cXM", i32 40 }
 // LLVM-DAG: @_ZZ8functionvE2p2 = internal global %struct.packed <{ i8 123, 
i32 456 }>
 // LLVM-DAG: @_ZZ8functionvE3paa = internal global %struct.packed_and_aligned 
<{ i16 1, i8 2, float 3.000000e+00, i8 0 }>
@@ -84,7 +102,9 @@ void function() {
 
 
 // OGCG-DAG: @_ZZ8functionvE12packed_array = internal constant [2 x 
%struct.packed] [%struct.packed <{ i8 123, i32 456 }>, %struct.packed <{ i8 
123, i32 456 }>]
+// OGCG-DAG: @_ZZ8functionvE12simple_array = internal constant [3 x 
%struct.simple] [%struct.simple { i32 0, i32 -1 }, %struct.simple { i32 1111, 
i32 2222 }, %struct.simple { i32 0, i32 -1 }]
 // OGCG-DAG: @_ZZ8functionvE1e = internal constant %struct.empty 
zeroinitializer
+// OGCG-DAG: @_ZZ8functionvE1s = internal constant %struct.simple { i32 0, i32 
-1 }
 // OGCG-DAG: @_ZZ8functionvE2p1 = internal constant %struct.Point { i32 10, 
i32 20, [3 x i8] c"cXM", i32 40 }
 // OGCG-DAG: @_ZZ8functionvE2p2 = internal constant %struct.packed <{ i8 123, 
i32 456 }>
 // OGCG-DAG: @_ZZ8functionvE3paa = internal constant 
%struct.packed_and_aligned <{ i16 1, i8 2, float 3.000000e+00, i8 undef }>

>From d637f0673af855d9010f423b4b5b424e1ad31b9b Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Thu, 4 Sep 2025 02:56:53 +0200
Subject: [PATCH 6/6] Address review feedback

---
 .../CIR/Dialect/Builder/CIRBaseBuilder.h      |   8 ++
 .../clang/CIR/Dialect/IR/CIRDataLayout.h      |   2 +-
 clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp  | 136 ++++++++++--------
 3 files changed, 88 insertions(+), 58 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h 
b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index b2e3841c8d7df..74bee3220ee03 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -227,6 +227,14 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
     return cir::AllocaOp::create(*this, loc, addrType, type, name, alignment);
   }
 
+  /// Get constant address of a global variable as an MLIR attribute.
+  /// This wrapper infers the attribute type through the global op.
+  cir::GlobalViewAttr getGlobalViewAttr(cir::GlobalOp globalOp,
+                                        mlir::ArrayAttr indices = {}) {
+    cir::PointerType type = getPointerTo(globalOp.getSymType());
+    return getGlobalViewAttr(type, globalOp, indices);
+  }
+
   /// Get constant address of a global variable as an MLIR attribute.
   cir::GlobalViewAttr getGlobalViewAttr(cir::PointerType type,
                                         cir::GlobalOp globalOp,
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h 
b/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h
index ecc681ee310e3..c0bb666522c37 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h
@@ -36,7 +36,7 @@ class CIRDataLayout {
   bool isBigEndian() const { return bigEndian; }
 
   /// Internal helper method that returns requested alignment for type.
-  llvm::Align getAlignment(mlir::Type ty, bool abiOrPref) const;
+  llvm::Align getAlignment(mlir::Type ty, bool useABIAlign) const;
 
   llvm::Align getABITypeAlign(mlir::Type ty) const {
     return getAlignment(ty, true);
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index 2fd418eda01a5..f660544d13cfa 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -11,6 +11,7 @@
 
//===----------------------------------------------------------------------===//
 
 #include "Address.h"
+#include "CIRGenCXXABI.h"
 #include "CIRGenConstantEmitter.h"
 #include "CIRGenModule.h"
 #include "CIRGenRecordLayout.h"
@@ -28,8 +29,10 @@
 #include "clang/CIR/Dialect/IR/CIRAttrs.h"
 #include "clang/CIR/Dialect/IR/CIRTypes.h"
 #include "clang/CIR/MissingFeatures.h"
+#include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Support/ErrorHandling.h"
+#include <functional>
 #include <iterator>
 
 using namespace clang;
@@ -47,7 +50,7 @@ static mlir::TypedAttr computePadding(CIRGenModule &cgm, 
CharUnits size) {
   clang::CharUnits::QuantityType arSize = size.getQuantity();
   CIRGenBuilderTy &bld = cgm.getBuilder();
   if (size > CharUnits::One()) {
-    SmallVector<mlir::Attribute, 4> elts(arSize, cir::ZeroAttr::get(eltTy));
+    SmallVector<mlir::Attribute> elts(arSize, cir::ZeroAttr::get(eltTy));
     return bld.getConstArray(mlir::ArrayAttr::get(bld.getContext(), elts),
                              cir::ArrayType::get(eltTy, arSize));
   }
@@ -70,7 +73,7 @@ struct ConstantAggregateBuilderUtils {
 
   CharUnits getAlignment(const mlir::TypedAttr c) const {
     return CharUnits::fromQuantity(
-        dataLayout.getAlignment(c.getType(), /*abiOrPref=*/true));
+        dataLayout.getAlignment(c.getType(), /*useABIAlign=*/true));
   }
 
   CharUnits getSize(mlir::Type ty) const {
@@ -89,30 +92,36 @@ struct ConstantAggregateBuilderUtils {
 /// Incremental builder for an mlir::TypedAttr holding a record or array
 /// constant.
 class ConstantAggregateBuilder : private ConstantAggregateBuilderUtils {
-  /// The elements of the constant. These two arrays must have the same size;
-  /// Offsets[i] describes the offset of Elems[i] within the constant. The
-  /// elements are kept in increasing offset order, and we ensure that there
-  /// is no overlap: Offsets[i+1] >= Offsets[i] + getSize(Elemes[i]).
+  struct Element {
+    Element(mlir::TypedAttr element, CharUnits offset)
+        : element(element), offset(offset) {}
+
+    mlir::TypedAttr element;
+    /// Describes the offset of `element` within the constant.
+    CharUnits offset;
+  };
+  /// The elements of the constant. The elements are kept in increasing offset
+  /// order, and we ensure that there is no overlap:
+  /// elements.offset[i+1] >= elements.offset[i] + getSize(elements.element[i])
   ///
   /// This may contain explicit padding elements (in order to create a
   /// natural layout), but need not. Gaps between elements are implicitly
   /// considered to be filled with undef.
-  llvm::SmallVector<mlir::TypedAttr, 32> elems;
-  llvm::SmallVector<CharUnits, 32> offsets;
+  llvm::SmallVector<Element, 32> elements;
 
   /// The size of the constant (the maximum end offset of any added element).
-  /// May be larger than the end of Elems.back() if we split the last element
+  /// May be larger than the end of elems.back() if we split the last element
   /// and removed some trailing undefs.
   CharUnits size = CharUnits::Zero();
 
-  /// This is true only if laying out Elems in order as the elements of a
+  /// This is true only if laying out elems in order as the elements of a
   /// non-packed LLVM struct will give the correct layout.
   bool naturalLayout = true;
 
-  static mlir::Attribute
-  buildFrom(CIRGenModule &cgm, ArrayRef<mlir::TypedAttr> elems,
-            ArrayRef<CharUnits> offsets, CharUnits startOffset, CharUnits size,
-            bool naturalLayout, mlir::Type desiredTy, bool allowOversized);
+  static mlir::Attribute buildFrom(CIRGenModule &cgm, ArrayRef<Element> elems,
+                                   CharUnits startOffset, CharUnits size,
+                                   bool naturalLayout, mlir::Type desiredTy,
+                                   bool allowOversized);
 
 public:
   ConstantAggregateBuilder(CIRGenModule &cgm)
@@ -134,8 +143,8 @@ class ConstantAggregateBuilder : private 
ConstantAggregateBuilderUtils {
   /// Otherwise, the constant will be of exactly the same size as \p desiredTy
   /// even if we can't represent it as that type.
   mlir::Attribute build(mlir::Type desiredTy, bool allowOversized) const {
-    return buildFrom(cgm, elems, offsets, CharUnits::Zero(), size,
-                     naturalLayout, desiredTy, allowOversized);
+    return buildFrom(cgm, elements, CharUnits::Zero(), size, naturalLayout,
+                     desiredTy, allowOversized);
   }
 };
 
@@ -152,14 +161,12 @@ bool ConstantAggregateBuilder::add(mlir::TypedAttr 
typedAttr, CharUnits offset,
   if (offset >= size) {
     CharUnits align = getAlignment(typedAttr);
     CharUnits alignedSize = size.alignTo(align);
-    if (alignedSize > offset || offset.alignTo(align) != offset)
+    if (alignedSize > offset || offset.alignTo(align) != offset) {
       naturalLayout = false;
-    else if (alignedSize < offset) {
-      elems.push_back(getPadding(offset - size));
-      offsets.push_back(size);
+    } else if (alignedSize < offset) {
+      elements.emplace_back(getPadding(offset - size), size);
     }
-    elems.push_back(typedAttr);
-    offsets.push_back(offset);
+    elements.emplace_back(typedAttr, offset);
     size = offset + getSize(typedAttr);
     return true;
   }
@@ -169,17 +176,16 @@ bool ConstantAggregateBuilder::add(mlir::TypedAttr 
typedAttr, CharUnits offset,
   return false;
 }
 
-mlir::Attribute ConstantAggregateBuilder::buildFrom(
-    CIRGenModule &cgm, ArrayRef<mlir::TypedAttr> elems,
-    ArrayRef<CharUnits> offsets, CharUnits startOffset, CharUnits size,
-    bool naturalLayout, mlir::Type desiredTy, bool allowOversized) {
+mlir::Attribute
+ConstantAggregateBuilder::buildFrom(CIRGenModule &cgm, ArrayRef<Element> elems,
+                                    CharUnits startOffset, CharUnits size,
+                                    bool naturalLayout, mlir::Type desiredTy,
+                                    bool allowOversized) {
   ConstantAggregateBuilderUtils utils(cgm);
 
   if (elems.empty())
     return cir::UndefAttr::get(desiredTy);
 
-  auto offset = [&](size_t i) { return offsets[i] - startOffset; };
-
   // If we want an array type, see if all the elements are the same type and
   // appropriately spaced.
   if (mlir::isa<cir::ArrayType>(desiredTy)) {
@@ -192,45 +198,46 @@ mlir::Attribute ConstantAggregateBuilder::buildFrom(
   // init), it can be larger.
   CharUnits desiredSize = utils.getSize(desiredTy);
   if (size > desiredSize) {
-    assert(allowOversized && "Elems are oversized");
+    assert(allowOversized && "elems are oversized");
     desiredSize = size;
   }
 
   // The natural alignment of an unpacked CIR record with the given elements.
   CharUnits align = CharUnits::One();
-  for (mlir::TypedAttr e : elems) {
+  for (auto [e, offset] : elems)
     align = std::max(align, utils.getAlignment(e));
-  }
 
   // The natural size of an unpacked LLVM struct with the given elements.
   CharUnits alignedSize = size.alignTo(align);
 
   bool packed = false;
   bool padded = false;
-  ArrayRef<mlir::TypedAttr> unpackedElems = elems;
 
-  llvm::SmallVector<mlir::TypedAttr, 32> unpackedElemStorage;
+  llvm::SmallVector<mlir::Attribute, 32> unpackedElems;
   if (desiredSize < alignedSize || desiredSize.alignTo(align) != desiredSize) {
     naturalLayout = false;
     packed = true;
-  } else if (desiredSize > alignedSize) {
+  } else {
     // The natural layout would be too small. Add padding to fix it. (This
     // is ignored if we choose a packed layout.)
-    unpackedElemStorage.assign(unpackedElems.begin(), unpackedElems.end());
-    unpackedElemStorage.push_back(utils.getPadding(desiredSize - size));
-    unpackedElems = unpackedElemStorage;
+    unpackedElems.reserve(elems.size() + 1);
+    llvm::transform(elems, std::back_inserter(unpackedElems),
+                    std::mem_fn(&Element::element));
+    if (desiredSize > alignedSize)
+      unpackedElems.push_back(utils.getPadding(desiredSize - size));
   }
 
   // If we don't have a natural layout, insert padding as necessary.
   // As we go, double-check to see if we can actually just emit Elems
   // as a non-packed record and do so opportunistically if possible.
-  llvm::SmallVector<mlir::TypedAttr, 32> packedElems;
+  llvm::SmallVector<mlir::Attribute, 32> packedElems;
+  packedElems.reserve(elems.size());
   if (!naturalLayout) {
     CharUnits sizeSoFar = CharUnits::Zero();
-    for (auto [index, element] : llvm::enumerate(elems)) {
+    for (auto [element, offset] : elems) {
       CharUnits align = utils.getAlignment(element);
       CharUnits naturalOffset = sizeSoFar.alignTo(align);
-      CharUnits desiredOffset = offset(index);
+      CharUnits desiredOffset = offset - startOffset;
       assert(desiredOffset >= sizeSoFar && "elements out of order");
 
       if (desiredOffset != naturalOffset)
@@ -252,20 +259,15 @@ mlir::Attribute ConstantAggregateBuilder::buildFrom(
   }
 
   CIRGenBuilderTy &builder = cgm.getBuilder();
-  llvm::SmallVector<mlir::Attribute, 32> arrayElements;
-  arrayElements.reserve(elems.size());
-  if (packed)
-    llvm::copy(packedElems, std::back_inserter(arrayElements));
-  else
-    llvm::copy(unpackedElems, std::back_inserter(arrayElements));
-  auto arrAttr = mlir::ArrayAttr::get(builder.getContext(), arrayElements);
+  auto arrAttr = mlir::ArrayAttr::get(builder.getContext(),
+                                      packed ? packedElems : unpackedElems);
 
-  cir::RecordType strType = builder.getCompleteRecordType(arrAttr, packed);
+  cir::RecordType recordType = builder.getCompleteRecordType(arrAttr, packed);
   if (auto desired = mlir::dyn_cast<cir::RecordType>(desiredTy))
-    if (desired.isLayoutIdentical(strType))
-      strType = desired;
+    if (desired.isLayoutIdentical(recordType))
+      recordType = desired;
 
-  return builder.getConstRecordOrZeroAttr(arrAttr, packed, padded, strType);
+  return builder.getConstRecordOrZeroAttr(arrAttr, packed, padded, recordType);
 }
 
 
//===----------------------------------------------------------------------===//
@@ -437,11 +439,29 @@ bool ConstRecordBuilder::build(const APValue &val, const 
RecordDecl *rd,
                                CharUnits offset) {
   const ASTRecordLayout &layout = cgm.getASTContext().getASTRecordLayout(rd);
   if (const CXXRecordDecl *cd = dyn_cast<CXXRecordDecl>(rd)) {
-    assert(!cir::MissingFeatures::vtableInitialization());
+    // Add a vtable pointer, if we need one and it hasn't already been added.
+    if (layout.hasOwnVFPtr()) {
+      CIRGenBuilderTy &builder = cgm.getBuilder();
+      cir::GlobalOp vtable =
+          cgm.getCXXABI().getAddrOfVTable(vTableClass, CharUnits());
+      clang::VTableLayout::AddressPointLocation addressPoint =
+          cgm.getItaniumVTableContext()
+              .getVTableLayout(vTableClass)
+              .getAddressPoint(BaseSubobject(cd, offset));
+      assert(!cir::MissingFeatures::addressPointerAuthInfo());
+      mlir::ArrayAttr indices = builder.getArrayAttr({
+          builder.getI32IntegerAttr(addressPoint.VTableIndex),
+          builder.getI32IntegerAttr(addressPoint.AddressPointIndex),
+      });
+      cir::GlobalViewAttr vtableInit =
+          cgm.getBuilder().getGlobalViewAttr(vtable, indices);
+      if (!appendBytes(offset, vtableInit))
+        return false;
+    }
 
     // Accumulate and sort bases, in order to visit them in address order, 
which
     // may not be the same as declaration order.
-    SmallVector<BaseInfo, 8> bases;
+    SmallVector<BaseInfo> bases;
     bases.reserve(cd->getNumBases());
     for (auto [index, base] : llvm::enumerate(cd->bases())) {
       assert(!base.isVirtual() && "should not have virtual bases here");
@@ -449,7 +469,9 @@ bool ConstRecordBuilder::build(const APValue &val, const 
RecordDecl *rd,
       CharUnits baseOffset = layout.getBaseClassOffset(bd);
       bases.push_back(BaseInfo(bd, baseOffset, index));
     }
-    llvm::stable_sort(bases);
+#ifdef EXPENSIVE_CHECKS
+    assert(llvm::is_sorted(bases) && "bases not sorted by offset");
+#endif
 
     for (BaseInfo &base : bases) {
       bool isPrimaryBase = layout.getPrimaryBase() == base.decl;
@@ -815,7 +837,7 @@ emitArrayConstant(CIRGenModule &cgm, mlir::Type desiredType,
       // If all the elements had the same type up to the trailing zeroes and
       // there are eight or more nonzero elements, emit a struct of two arrays
       // (the nonzero data and the zeroinitializer).
-      SmallVector<mlir::Attribute, 4> eles;
+      SmallVector<mlir::Attribute> eles;
       eles.reserve(nonzeroLength);
       for (const auto &element : elements)
         eles.push_back(element);
@@ -845,7 +867,7 @@ emitArrayConstant(CIRGenModule &cgm, mlir::Type desiredType,
   }
 
   if (commonElementType) {
-    SmallVector<mlir::Attribute, 4> eles;
+    SmallVector<mlir::Attribute> eles;
     eles.reserve(elements.size());
 
     for (const auto &element : elements)
@@ -856,7 +878,7 @@ emitArrayConstant(CIRGenModule &cgm, mlir::Type desiredType,
         mlir::ArrayAttr::get(builder.getContext(), eles));
   }
 
-  SmallVector<mlir::Attribute, 4> eles;
+  SmallVector<mlir::Attribute> eles;
   eles.reserve(elements.size());
   for (auto const &element : elements)
     eles.push_back(element);

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to