https://github.com/koparasy updated 
https://github.com/llvm/llvm-project/pull/232835

>From 2f0eae448c418338d997bf55027f3ff5c560ced4 Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <[email protected]>
Date: Fri, 16 Oct 2026 09:09:31 -0700
Subject: [PATCH 1/6] [CIR] Lower array globals with many trailing zeros as
 packed structs

A global such as `T x[NUM_ELEMENTS] = {1}` reaches LLVM lowering as a
`#cir.const_array` with one explicit element and NUM_ELEMENTS trailing zeros.
When NUM_ELEMENTS is large that lowering becomes impractical and slow.
This PR fixes such cases by following OG:
1. when there are 8 or more explicit elements of the same type,
the array is lowered as a packed struct with  a trailing zero-initialized array.
2. When an array initializer ends in 8 or more zeros,
the array is lowered as a packed struct with the explicit elements
followed by one zero-initialized array.

`adjustGlobalTypeForInit` decides this shape, recursing through nested
arrays and records. Both value builders follow it: the
single-attribute path (lowerConstArrayAttr, now element-by-element when
a split is involved) and the insertvalue fallback (CIRAttrToValue).
Neither visits the trailing zeros.

`cxx11-initializer-aggregate.cpp` earlier the compilation was
not completing on my system.

 now compiles with -fclangir in about
0.1s, and its huge-array globals match classic codegen. Several
existing tests now produce the same output as classic codegen for
these globals. With the changes in this PR, the compilation of that test looks 
like:

Version | avg wall time (5 runs) |peak memory
OG      |       0.062s               |   111 MB
CIR     |   0.070s               |   129 MB

With these changes in place we now differ to OG in the following
cases (which I think are harmless):
- Classic codegen never splits string-literal initializers. CIR emits
  wide strings, and C string literals inside records, as integer lists,
  so these now split.
- A union initializer inherits the union's packed flag, so it can be
  `<{ }>` where classic codegen emits `{ }`
  (bitint-record-layout.c). AFAIU this is existing behavior.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
---
 clang/include/clang/CIR/LoweringHelpers.h     |   6 +
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp |  41 ++--
 clang/lib/CIR/Lowering/LoweringHelpers.cpp    | 139 +++++++++++-
 .../test/CIR/CodeGen/array-trailing-zeros.cpp |  39 ++++
 clang/test/CIR/CodeGen/array.cpp              |  19 +-
 clang/test/CIR/CodeGen/bitint-record-layout.c |   2 +-
 .../CodeGen/cxx11-initializer-aggregate.cpp   | 198 ++++++++++++++++++
 .../CIR/CodeGen/record-with-padded-union.cpp  |   7 +-
 clang/test/CIR/CodeGen/replace-global.cpp     |   2 +-
 clang/test/CIR/CodeGen/string-literals.cpp    |   2 +-
 .../CIR/CodeGen/union-init-padding-array.c    |  19 +-
 clang/test/CIR/CodeGen/wide-string.cpp        |   2 +-
 clang/test/CIR/Lowering/array.cpp             |   2 +-
 .../Lowering/const-array-trailing-zeros.cir   |  28 +++
 14 files changed, 459 insertions(+), 47 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/array-trailing-zeros.cpp
 create mode 100644 clang/test/CIR/CodeGen/cxx11-initializer-aggregate.cpp
 create mode 100644 clang/test/CIR/Lowering/const-array-trailing-zeros.cir

diff --git a/clang/include/clang/CIR/LoweringHelpers.h 
b/clang/include/clang/CIR/LoweringHelpers.h
index bfd1182aa64891..c2ee7789085652 100644
--- a/clang/include/clang/CIR/LoweringHelpers.h
+++ b/clang/include/clang/CIR/LoweringHelpers.h
@@ -42,6 +42,12 @@ std::optional<mlir::Attribute> lowerConstRecordAttr(
     cir::ConstRecordAttr constRecord, mlir::SymbolTableCollection 
&symbolTables,
     const mlir::TypeConverter *converter, mlir::ModuleOp moduleOp = {});
 
+/// Return \p attr with the trailing zeros of any array that
+/// adjustGlobalTypeForInit would split off written out as explicit zero
+/// elements, so that it lowers to its own type. For constants that, unlike
+/// global initializers, cannot change type.
+mlir::Attribute spellOutSplitTrailingZeros(mlir::Attribute attr);
+
 /// Adjust \p llvmType (the converted type of \p init) to the concrete LLVM 
type
 /// a global constant initialized with \p init actually lowers to. This differs
 /// from a plain type conversion for flexible-array-member and union
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index f0f037291debfc..3964881cf23c46 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -910,17 +910,20 @@ mlir::Value 
CIRAttrToValue::visitCirAttr(cir::BlockAddrDiffAttr blockAddrDiff) {
 
 // ConstArrayAttr visitor
 mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstArrayAttr attr) {
-  mlir::Type llvmTy = converter->convertType(attr.getType());
+  mlir::Type convertedTy = converter->convertType(attr.getType());
   mlir::DataLayout dataLayout(parentOp->getParentOfType<mlir::ModuleOp>());
-  llvmTy = adjustGlobalTypeForInit(llvmTy, attr, *converter, dataLayout);
+  mlir::Type llvmTy =
+      adjustGlobalTypeForInit(convertedTy, attr, *converter, dataLayout);
   mlir::Location loc = parentOp->getLoc();
   mlir::Value result;
 
   // When the array can be represented as a single dense constant, emit one
-  // llvm.mlir.constant instead of a chain of llvm.insertvalue ops.
-  if (std::optional<mlir::Attribute> denseAttr =
-          lowerConstArrayAttr(attr, symbolTables, converter))
-    return mlir::LLVM::ConstantOp::create(rewriter, loc, llvmTy, *denseAttr);
+  // llvm.mlir.constant instead of a chain of llvm.insertvalue ops. A dense
+  // constant has the converted type, so it cannot take an adjusted shape.
+  if (llvmTy == convertedTy)
+    if (std::optional<mlir::Attribute> denseAttr =
+            lowerConstArrayAttr(attr, symbolTables, converter))
+      return mlir::LLVM::ConstantOp::create(rewriter, loc, llvmTy, *denseAttr);
 
   if (attr.hasTrailingZeros())
     result = mlir::LLVM::ZeroOp::create(rewriter, loc, llvmTy);
@@ -929,6 +932,17 @@ mlir::Value 
CIRAttrToValue::visitCirAttr(cir::ConstArrayAttr attr) {
 
   // Iteratively lower each constant element of the array.
   if (auto arrayAttr = mlir::dyn_cast<mlir::ArrayAttr>(attr.getElts())) {
+    // Fewer struct members than elements: adjustGlobalTypeForInit grouped the
+    // elements of an array ending in many zeros into one leading array.
+    auto structTy = mlir::dyn_cast<mlir::LLVM::LLVMStructType>(llvmTy);
+    if (structTy && structTy.getBody().size() < arrayAttr.size()) {
+      auto arrayTy = mlir::cast<cir::ArrayType>(attr.getType());
+      auto prefix = cir::ConstArrayAttr::get(
+          cir::ArrayType::get(arrayTy.getElementType(), arrayAttr.size()),
+          arrayAttr);
+      return mlir::LLVM::InsertValueOp::create(rewriter, loc, result,
+                                               visit(prefix), int64_t{0});
+    }
     for (auto [idx, elt] : llvm::enumerate(arrayAttr)) {
       mlir::Value init = visit(elt);
       result =
@@ -2785,8 +2799,10 @@ mlir::LogicalResult 
CIRToLLVMConstantOpLowering::matchAndRewrite(
     }
     attr = op.getValue();
   } else if (const auto arrTy = mlir::dyn_cast<cir::ArrayType>(op.getType())) {
-    const auto constArr = mlir::dyn_cast<cir::ConstArrayAttr>(op.getValue());
-    if (!constArr && !isa<cir::ZeroAttr, cir::UndefAttr>(op.getValue()))
+    // Unlike a global initializer, the constant must keep its type.
+    mlir::Attribute value = spellOutSplitTrailingZeros(op.getValue());
+    const auto constArr = mlir::dyn_cast<cir::ConstArrayAttr>(value);
+    if (!constArr && !isa<cir::ZeroAttr, cir::UndefAttr>(value))
       return op.emitError() << "array does not have a constant initializer";
 
     std::optional<mlir::Attribute> denseAttr;
@@ -2794,15 +2810,16 @@ mlir::LogicalResult 
CIRToLLVMConstantOpLowering::matchAndRewrite(
                                                      typeConverter))) {
       attr = denseAttr.value();
     } else {
-      const mlir::Value initVal = lowerCirAttrAsValue(
-          op, op.getValue(), rewriter, symbolTables, typeConverter);
+      const mlir::Value initVal =
+          lowerCirAttrAsValue(op, value, rewriter, symbolTables, 
typeConverter);
       rewriter.replaceOp(op, initVal);
       return mlir::success();
     }
   } else if (const auto recordAttr =
                  mlir::dyn_cast<cir::ConstRecordAttr>(op.getValue())) {
-    auto initVal = lowerCirAttrAsValue(op, recordAttr, rewriter, symbolTables,
-                                       typeConverter);
+    auto initVal =
+        lowerCirAttrAsValue(op, spellOutSplitTrailingZeros(recordAttr),
+                            rewriter, symbolTables, typeConverter);
     rewriter.replaceOp(op, initVal);
     return mlir::success();
   } else if (const auto vecTy = mlir::dyn_cast<cir::VectorType>(op.getType())) 
{
diff --git a/clang/lib/CIR/Lowering/LoweringHelpers.cpp 
b/clang/lib/CIR/Lowering/LoweringHelpers.cpp
index 4191c3c9c8a4d7..f25c66af058869 100644
--- a/clang/lib/CIR/Lowering/LoweringHelpers.cpp
+++ b/clang/lib/CIR/Lowering/LoweringHelpers.cpp
@@ -369,10 +369,101 @@ static bool containsBlockAddress(mlir::Attribute attr) {
   return false;
 }
 
+/// The fewest trailing zeros that adjustGlobalArrayTypeForInit splits off an
+/// array constant into a zero-initialized tail, as in classic codegen.
+static constexpr int minSplitTrailingZeros = 8;
+
+static bool shouldSplitTrailingZeros(cir::ConstArrayAttr attr) {
+  auto elts = mlir::dyn_cast<mlir::ArrayAttr>(attr.getElts());
+  return elts && !elts.empty() &&
+         attr.getTrailingZerosNum() >= minSplitTrailingZeros;
+}
+
+static bool containsSplitTrailingZeros(mlir::Attribute attr) {
+  if (auto record = mlir::dyn_cast<cir::ConstRecordAttr>(attr))
+    return llvm::any_of(record.getMembers(), containsSplitTrailingZeros);
+  auto array = mlir::dyn_cast<cir::ConstArrayAttr>(attr);
+  if (!array)
+    return false;
+  if (shouldSplitTrailingZeros(array))
+    return true;
+  auto elts = mlir::dyn_cast<mlir::ArrayAttr>(array.getElts());
+  return elts && llvm::any_of(elts, containsSplitTrailingZeros);
+}
+
+mlir::Attribute spellOutSplitTrailingZeros(mlir::Attribute attr) {
+  if (!containsSplitTrailingZeros(attr))
+    return attr;
+
+  if (auto record = mlir::dyn_cast<cir::ConstRecordAttr>(attr)) {
+    auto members =
+        llvm::map_to_vector(record.getMembers(), spellOutSplitTrailingZeros);
+    return cir::ConstRecordAttr::get(
+        mlir::cast<cir::RecordType>(record.getType()),
+        mlir::ArrayAttr::get(attr.getContext(), members));
+  }
+
+  auto array = mlir::cast<cir::ConstArrayAttr>(attr);
+  auto arrayTy = mlir::cast<cir::ArrayType>(array.getType());
+  auto elts = llvm::map_to_vector(mlir::cast<mlir::ArrayAttr>(array.getElts()),
+                                  spellOutSplitTrailingZeros);
+  if (!shouldSplitTrailingZeros(array))
+    return cir::ConstArrayAttr::get(
+        arrayTy, mlir::ArrayAttr::get(attr.getContext(), elts),
+        array.getTrailingZerosNum());
+  elts.append(array.getTrailingZerosNum(),
+              cir::ZeroAttr::get(arrayTy.getElementType()));
+  return cir::ConstArrayAttr::get(arrayTy,
+                                  mlir::ArrayAttr::get(attr.getContext(), 
elts),
+                                  /*trailingZerosNum=*/0);
+}
+
 static std::optional<mlir::Attribute> lowerConstRecordMemberAttr(
     mlir::Attribute attr, mlir::SymbolTableCollection &symbolTables,
     const mlir::TypeConverter *converter, mlir::ModuleOp moduleOp);
 
+/// Lower \p constArr, which is or contains an array split by
+/// adjustGlobalArrayTypeForInit, element by element into the shape of its
+/// adjusted type. A dense attribute would span the split-off zeros.
+static std::optional<mlir::Attribute> lowerConstArrayAttrByElement(
+    cir::ConstArrayAttr constArr, mlir::SymbolTableCollection &symbolTables,
+    const mlir::TypeConverter *converter, mlir::ModuleOp moduleOp) {
+  mlir::MLIRContext *ctx = constArr.getContext();
+  auto arrayTy = mlir::cast<cir::ArrayType>(constArr.getType());
+  auto elts = mlir::cast<mlir::ArrayAttr>(constArr.getElts());
+  mlir::Type adjustedTy =
+      adjustGlobalTypeForInit(converter->convertType(arrayTy), constArr,
+                              *converter, mlir::DataLayout(moduleOp));
+
+  llvm::SmallVector<mlir::Attribute> lowered;
+  // Fewer members than elements: the elements were grouped into one array.
+  auto structTy = mlir::dyn_cast<mlir::LLVM::LLVMStructType>(adjustedTy);
+  if (structTy && structTy.getBody().size() < elts.size()) {
+    auto prefix = cir::ConstArrayAttr::get(
+        cir::ArrayType::get(arrayTy.getElementType(), elts.size()), elts);
+    std::optional<mlir::Attribute> loweredPrefix =
+        lowerConstArrayAttr(prefix, symbolTables, converter, moduleOp);
+    if (!loweredPrefix)
+      return std::nullopt;
+    lowered.push_back(*loweredPrefix);
+  } else {
+    for (mlir::Attribute elt : elts) {
+      std::optional<mlir::Attribute> loweredElt =
+          lowerConstRecordMemberAttr(elt, symbolTables, converter, moduleOp);
+      if (!loweredElt)
+        return std::nullopt;
+      lowered.push_back(*loweredElt);
+    }
+  }
+
+  // A split array ends in one zero-initialized member; otherwise its few
+  // trailing zeros are spelled out.
+  lowered.append(
+      shouldSplitTrailingZeros(constArr) ? 1 : constArr.getTrailingZerosNum(),
+      mlir::LLVM::ZeroAttr::get(ctx));
+  return mlir::ArrayAttr::get(ctx, lowered);
+}
+
 std::optional<mlir::Attribute> lowerConstArrayAttr(
     cir::ConstArrayAttr constArr, mlir::SymbolTableCollection &symbolTables,
     const mlir::TypeConverter *converter, mlir::ModuleOp moduleOp) {
@@ -398,6 +489,10 @@ std::optional<mlir::Attribute> lowerConstArrayAttr(
   if (containsBlockAddress(constArr))
     return std::nullopt;
 
+  if (containsSplitTrailingZeros(constArr))
+    return lowerConstArrayAttrByElement(constArr, symbolTables, converter,
+                                        moduleOp);
+
   if (mlir::isa<mlir::StringAttr>(constArr.getElts()))
     return convertStringAttrToDenseElementsAttr(constArr,
                                                 converter->convertType(type));
@@ -707,6 +802,13 @@ static mlir::Type adjustGlobalUnionTypeForInit(
 // Unions in an array can cause individual elements to be of different types.
 // This function adjusts the array type and replaces it with a struct type if
 // necessary, OR leaves it as a 'new' array type if necessary.
+//
+// Like classic codegen, an array ending in many zeros becomes a packed struct
+// of its explicit elements and one zero-initialized array, so the zeros are
+// never spelled out:
+//   <{ T0, T1, [N x T] }>
+// or, when 8 or more elements share a type,
+//   <{ [M x T], [N x T] }>
 static mlir::Type adjustGlobalArrayTypeForInit(
     mlir::LLVM::LLVMArrayType arrayTy, cir::ConstArrayAttr arrayInit,
     const mlir::TypeConverter &converter, const mlir::DataLayout &dataLayout) {
@@ -716,26 +818,41 @@ static mlir::Type adjustGlobalArrayTypeForInit(
     return arrayTy;
 
   mlir::Type origEltTy = arrayTy.getElementType();
-  llvm::SmallVector<mlir::Type> adjustedElts(arrayTy.getNumElements(),
-                                             origEltTy);
-  bool changed = false;
+  llvm::SmallVector<mlir::Type> adjustedElts;
+  adjustedElts.reserve(elts.size());
+  bool changed = elts.size() > arrayTy.getNumElements();
 
-  for (auto [idx, elt] : llvm::enumerate(elts)) {
+  for (mlir::Attribute elt : elts) {
     mlir::Type adjusted =
         adjustGlobalTypeForInit(origEltTy, elt, converter, dataLayout);
+    changed |= adjusted != origEltTy;
+    adjustedElts.push_back(adjusted);
+  }
 
-    if (idx >= arrayTy.getNumElements()) {
-      adjustedElts.push_back(adjusted);
-      changed = true;
-    } else if (adjusted != origEltTy) {
-      adjustedElts[idx] = adjusted;
-      changed = true;
-    }
+
+  if (shouldSplitTrailingZeros(arrayInit)) {
+    mlir::Type commonEltTy =
+        llvm::all_equal(adjustedElts) ? adjustedElts.front() : mlir::Type{};
+    llvm::SmallVector<mlir::Type> body;
+    if (commonEltTy && elts.size() >= minSplitTrailingZeros)
+      body.push_back(
+          mlir::LLVM::LLVMArrayType::get(commonEltTy, adjustedElts.size()));
+    else
+      body.append(adjustedElts);
+    body.push_back(
+        mlir::LLVM::LLVMArrayType::get(commonEltTy ? commonEltTy : origEltTy,
+                                       arrayInit.getTrailingZerosNum()));
+    return mlir::LLVM::LLVMStructType::getLiteral(arrayTy.getContext(), body,
+                                                  /*isPacked=*/true);
   }
 
   if (!changed)
     return arrayTy;
 
+  adjustedElts.resize(
+      std::max<size_t>(adjustedElts.size(), arrayTy.getNumElements()),
+      origEltTy);
+
   if (llvm::all_equal(adjustedElts))
     return mlir::LLVM::LLVMArrayType::get(adjustedElts.front(),
                                           adjustedElts.size());
diff --git a/clang/test/CIR/CodeGen/array-trailing-zeros.cpp 
b/clang/test/CIR/CodeGen/array-trailing-zeros.cpp
new file mode 100644
index 00000000000000..a6d831e1e4f1e0
--- /dev/null
+++ b/clang/test/CIR/CodeGen/array-trailing-zeros.cpp
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -emit-llvm %s 
-o %t.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s
+
+// An array global ending in 8 or more zeros lowers, as in classic codegen, to
+// a packed struct of its explicit elements and one zero-initialized array, so
+// the zeros are never spelled out. Spelling out the 2 GiB arrays here would
+// not finish.
+
+unsigned char huge[1024 * 1024 * 1024 * 2u] = {1};
+// CIR: cir.global external @huge = #cir.const_array<[#cir.int<1> : !u8i], 
trailing_zeros> : !cir.array<!u8i x 2147483648>
+// LLVM: @huge = global <{ i8, [2147483647 x i8] }> <{ i8 1, [2147483647 x i8] 
zeroinitializer }>
+
+unsigned char cube[1024][1024][1024] = {{{1}}};
+// LLVM: @cube = global <{ <{ <{ i8, [1023 x i8] }>, [1023 x <{ i8, [1023 x 
i8] }>] }>, [1023 x <{ <{ i8, [1023 x i8] }>, [1023 x <{ i8, [1023 x i8] }>] 
}>] }> <{ <{ <{ i8, [1023 x i8] }>, [1023 x <{ i8, [1023 x i8] }>] }> <{ <{ i8, 
[1023 x i8] }> <{ i8 1, [1023 x i8] zeroinitializer }>, [1023 x <{ i8, [1023 x 
i8] }>] zeroinitializer }>, [1023 x <{ <{ i8, [1023 x i8] }>, [1023 x <{ i8, 
[1023 x i8] }>] }>] zeroinitializer }>
+
+// Address target for the pointer arrays below.
+int few[4] = {1};
+
+int inner[2][10] = {{1}, {2, 3}};
+// LLVM: @inner = global <{ <{ i32, [9 x i32] }>, <{ i32, i32, [8 x i32] }> }> 
<{ <{ i32, [9 x i32] }> <{ i32 1, [9 x i32] zeroinitializer }>, <{ i32, i32, [8 
x i32] }> <{ i32 2, i32 3, [8 x i32] zeroinitializer }> }>
+
+int *ptrs[10] = {&few[0], &few[1]};
+// LLVM: @ptrs = global <{ ptr, ptr, [8 x ptr] }> <{ ptr @few, ptr 
getelementptr {{.*}}(i8, ptr @few, i64 4), [8 x ptr] zeroinitializer }>
+
+struct Rec { int a; char c[12]; } recs[2] = {{1, {'x'}}, {2}};
+// LLVM: @recs = global <{ { i32, <{ i8, [11 x i8] }> }, %struct.Rec }> <{ { 
i32, <{ i8, [11 x i8] }> } { i32 1, <{ i8, [11 x i8] }> <{ i8 120, [11 x i8] 
zeroinitializer }> }, %struct.Rec { i32 2, [12 x i8] zeroinitializer } }>
+
+// An address with an offset needs the insertvalue initializer region, which
+// builds the same packed struct.
+int *many_ptrs[16] = {&few[1], &few[1], &few[1], &few[1],
+                      &few[1], &few[1], &few[1], &few[1]};
+// LLVM: @many_ptrs = global <{ [8 x ptr], [8 x ptr] }> <{ [8 x ptr] [ptr 
getelementptr {{.*}}(i8, ptr @few, i64 4), ptr getelementptr {{.*}}(i8, ptr 
@few, i64 4), ptr getelementptr {{.*}}(i8, ptr @few, i64 4), ptr getelementptr 
{{.*}}(i8, ptr @few, i64 4), ptr getelementptr {{.*}}(i8, ptr @few, i64 4), ptr 
getelementptr {{.*}}(i8, ptr @few, i64 4), ptr getelementptr {{.*}}(i8, ptr 
@few, i64 4), ptr getelementptr {{.*}}(i8, ptr @few, i64 4)], [8 x ptr] 
zeroinitializer }>
+
+int *ptr_grid[2][10] = {{&few[1]}, {&few[2]}};
+// LLVM: @ptr_grid = global [2 x <{ ptr, [9 x ptr] }>] [<{ ptr, [9 x ptr] }> 
<{ ptr getelementptr {{.*}}(i8, ptr @few, i64 4), [9 x ptr] zeroinitializer }>, 
<{ ptr, [9 x ptr] }> <{ ptr getelementptr {{.*}}(i8, ptr @few, i64 8), [9 x 
ptr] zeroinitializer }>]
diff --git a/clang/test/CIR/CodeGen/array.cpp b/clang/test/CIR/CodeGen/array.cpp
index 9b60dd96789b9a..61017e019c207b 100644
--- a/clang/test/CIR/CodeGen/array.cpp
+++ b/clang/test/CIR/CodeGen/array.cpp
@@ -54,8 +54,7 @@ int dd[3][2] = {{1, 2}, {3, 4}, {5, 6}};
 int e[10] = {1, 2};
 // CIR: cir.global external @e = #cir.const_array<[#cir.int<1> : !s32i, 
#cir.int<2> : !s32i], trailing_zeros> : !cir.array<!s32i x 10>
 
-// FIXME: we should figure out how to lower this with a 'trailing-zeros' type 
thing, like classic codegen.
-// LLVM: @e = global [10 x i32] [i32 1, i32 2, i32 0, i32 0, i32 0, i32 0, i32 
0, i32 0, i32 0, i32 0]
+// LLVM: @e = global <{ i32, i32, [8 x i32] }> <{ i32 1, i32 2, [8 x i32] 
zeroinitializer }>
 
 // OGCG: @e = global <{ i32, i32, [8 x i32] }> <{ i32 1, i32 2, [8 x i32] 
zeroinitializer }>
 
@@ -74,9 +73,11 @@ int g[16] = {1, 2, 3, 4, 5, 6, 7, 8};
 // CIR-SAME:                     #cir.int<7> : !s32i, #cir.int<8> : !s32i], 
trailing_zeros>
 // CIR-SAME:                     : !cir.array<!s32i x 16>
 
-// LLVM:       @g = global [16 x i32] [i32 1, i32 2, i32 3, i32 4, i32 5, 
-// LLVM-SAME:                          i32 6, i32 7, i32 8, i32 0, i32 0,
-// LLVM-SAME:                          i32 0, i32 0, i32 0, i32 0, i32 0, i32 
0]
+// LLVM:       @g = global <{ [8 x i32], [8 x i32] }>
+// LLVM-SAME:          <{ [8 x i32]
+// LLVM-SAME:              [i32 1, i32 2, i32 3, i32 4,
+// LLVM-SAME:               i32 5, i32 6, i32 7, i32 8],
+// LLVM-SAME:             [8 x i32] zeroinitializer }>
 
 // OGCG:       @g = global <{ [8 x i32], [8 x i32] }> 
 // OGCG-SAME:          <{ [8 x i32]
@@ -95,9 +96,11 @@ int h[16] = {1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0};
 // CIR-SAME:                     #cir.int<7> : !s32i, #cir.int<8> : !s32i], 
trailing_zeros>
 // CIR-SAME:                     : !cir.array<!s32i x 16>
 
-// LLVM:       @h = global [16 x i32] [i32 1, i32 2, i32 3, i32 4, i32 5, i32 
6,
-// LLVM-SAME:                          i32 7, i32 8, i32 0, i32 0, i32 0, i32 
0,
-// LLVM-SAME:                          i32 0, i32 0, i32 0, i32 0]
+// LLVM:       @h = global <{ [8 x i32], [8 x i32] }>
+// LLVM-SAME:          <{ [8 x i32]
+// LLVM-SAME:              [i32 1, i32 2, i32 3, i32 4,
+// LLVM-SAME:               i32 5, i32 6, i32 7, i32 8],
+// LLVM-SAME:             [8 x i32] zeroinitializer }>
 
 // OGCG:       @h = global <{ [8 x i32], [8 x i32] }>
 // OGCG-SAME:          <{ [8 x i32]
diff --git a/clang/test/CIR/CodeGen/bitint-record-layout.c 
b/clang/test/CIR/CodeGen/bitint-record-layout.c
index e0e0ff959aefb9..a16a79a4090107 100644
--- a/clang/test/CIR/CodeGen/bitint-record-layout.c
+++ b/clang/test/CIR/CodeGen/bitint-record-layout.c
@@ -149,7 +149,7 @@ union BIUnionArr biuarr = { .arr = { 'a', 'b', 'c' } };
 // CIR-DAG: cir.global external @biuarr = 
#cir.const_record<{#cir.const_array<[#cir.int<97> : !s8i, #cir.int<98> : !s8i, 
#cir.int<99> : !s8i], trailing_zeros> : !cir.array<!s8i x 20>}> : 
!rec_BIUnionArr align(8)
 // Classic-codegen represents the constant by splitting the init part of
 // the string and the zeros separately, plus not as a string.  Else this is 
effectively the same.
-// LLVMCIR-DAG: @biuarr = global <{ [20 x i8], [4 x i8] }> <{ [20 x i8] 
c"abc\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", [4 x i8] 
zeroinitializer }>, align 8
+// LLVMCIR-DAG: @biuarr = global <{ <{ i8, i8, i8, [17 x i8] }>, [4 x i8] }> 
<{ <{ i8, i8, i8, [17 x i8] }> <{ i8 97, i8 98, i8 99, [17 x i8] 
zeroinitializer }>, [4 x i8] zeroinitializer }>, align 8
 // OGCG-DAG:    @biuarr = global { <{ i8, i8, i8, [17 x i8] }>, [4 x i8] } { 
<{ i8, i8, i8, [17 x i8] }> <{ i8 97, i8 98, i8 99, [17 x i8] zeroinitializer 
}>, [4 x i8] zeroinitializer }, align 8
 
 struct First65 f65[2] = {{1, 222}, {3, 444}};
diff --git a/clang/test/CIR/CodeGen/cxx11-initializer-aggregate.cpp 
b/clang/test/CIR/CodeGen/cxx11-initializer-aggregate.cpp
new file mode 100644
index 00000000000000..7c36d3370cf12d
--- /dev/null
+++ b/clang/test/CIR/CodeGen/cxx11-initializer-aggregate.cpp
@@ -0,0 +1,198 @@
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-linux-gnu -fclangir -emit-llvm %s 
-o %t-cir11.ll
+// RUN: FileCheck --check-prefixes=CHECK,LLVM --input-file=%t-cir11.ll %s
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-linux-gnu -fclangir -emit-llvm %s 
-o %t-cir17.ll
+// RUN: FileCheck --check-prefixes=CHECK,LLVM --input-file=%t-cir17.ll %s
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-linux-gnu -emit-llvm %s -o %t11.ll
+// RUN: FileCheck --check-prefixes=CHECK,OGCG --input-file=%t11.ll %s
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-linux-gnu -emit-llvm %s -o %t17.ll
+// RUN: FileCheck --check-prefixes=CHECK,OGCG --input-file=%t17.ll %s
+
+struct A { int a, b; int f(); };
+
+namespace NonAggregateCopyInAggregateInit { // PR32044
+  struct A { constexpr A(int n) : x(n), y() {} int x, y; } extern a;
+  // CHECK-DAG: @_ZN31NonAggregateCopyInAggregateInit1bE ={{.*}} global 
%{{.*}} { ptr @_ZN31NonAggregateCopyInAggregateInit1aE }
+  struct B { A &p; } b{{a}};
+  // CHECK-DAG: @_ZGRN31NonAggregateCopyInAggregateInit1cE_ = internal global 
%[[A:.*]] { i32 1, i32 0 }
+  // CHECK-DAG: @_ZN31NonAggregateCopyInAggregateInit1cE ={{.*}} global 
%{{.*}} { ptr @_ZGRN31NonAggregateCopyInAggregateInit1cE_ }
+  struct C { A &&p; } c{{1}};
+}
+
+namespace NearlyZeroInit {
+  // CHECK-DAG: @_ZN14NearlyZeroInit1aE ={{.*}} global {{.*}} <{ i32 1, i32 2, 
i32 3, [120 x i32] zeroinitializer }>
+  int a[123] = {1, 2, 3};
+  // CHECK-DAG: @_ZN14NearlyZeroInit1bE ={{.*}} global {{.*}} { i32 1, <{ i32, 
[2147483647 x i32] }> <{ i32 2, [2147483647 x i32] zeroinitializer }> }
+  struct B { int n; int arr[1024 * 1024 * 1024 * 2u]; } b = {1, {2}};
+}
+
+namespace PR37560 {
+  union U {
+    char x;
+    int a;
+  };
+  // FIXME: [dcl.init]p2, the padding bits of the union object should be
+  // initialized to 0, not undef, which would allow us to collapse the tail
+  // of these arrays to zeroinitializer.
+  // LLVM-DAG: @_ZN7PR375601cE ={{.*}} global [1 x %"{{[^"]*}}"] 
zeroinitializer
+  // OGCG-DAG: @_ZN7PR375601cE ={{.*}} global <{ { i8, [3 x i8] } }> <{ { i8, 
[3 x i8] } { i8 0, [3 x i8] undef } }>
+  U c[1] = {};
+  // LLVM-DAG: @_ZN7PR375601dE ={{.*}} global {{.*}} <{ { i8, [3 x i8] } { i8 
97, [3 x i8] zeroinitializer }, %"{{[^"]*}}" { i32 123 }, { i8, [3 x i8] } { i8 
98, [3 x i8] zeroinitializer }, [13 x %"{{[^"]*}}"] zeroinitializer }>
+  // OGCG-DAG: @_ZN7PR375601dE ={{.*}} global {{.*}} <{ { i8, [3 x i8] } { i8 
97, [3 x i8] undef }, %"{{[^"]*}}" { i32 123 }, { i8, [3 x i8] } { i8 98, [3 x 
i8] undef }, { i8, [3 x i8] } { i8 0, [3 x i8] undef },
+  U d[16] = {'a', {.a = 123}, 'b'};
+  // LLVM-DAG: @_ZN7PR375601eE ={{.*}} global {{.*}} <{ %"{{[^"]*}}" { i32 123 
}, %"{{[^"]*}}" { i32 456 }, [14 x %"{{[^"]*}}"] zeroinitializer }>
+  // OGCG-DAG: @_ZN7PR375601eE ={{.*}} global {{.*}} <{ %"{{[^"]*}}" { i32 123 
}, %"{{[^"]*}}" { i32 456 }, { i8, [3 x i8] } { i8 0, [3 x i8] undef },
+  U e[16] = {{.a = 123}, {.a = 456}};
+
+  union V {
+    int a;
+    char x;
+  };
+  // CHECK-DAG: @_ZN7PR375601fE ={{.*}} global [1 x %"{{[^"]*}}"] 
zeroinitializer
+  V f[1] = {};
+  // LLVM-DAG: @_ZN7PR375601gE ={{.*}} global {{.*}} <{ { i8, [3 x i8] } { i8 
97, [3 x i8] zeroinitializer }, %"{{[^"]*}}" { i32 123 }, { i8, [3 x i8] } { i8 
98, [3 x i8] zeroinitializer }, [13 x %"{{[^"]*}}"] zeroinitializer }>
+  // OGCG-DAG: @_ZN7PR375601gE ={{.*}} global {{.*}} <{ { i8, [3 x i8] } { i8 
97, [3 x i8] undef }, %"{{[^"]*}}" { i32 123 }, { i8, [3 x i8] } { i8 98, [3 x 
i8] undef }, [13 x %"{{[^"]*}}"] zeroinitializer }>
+  V g[16] = {{.x = 'a'}, {.a = 123}, {.x = 'b'}};
+  // CHECK-DAG: @_ZN7PR375601hE ={{.*}} global {{.*}} <{ %"{{[^"]*}}" { i32 
123 }, %"{{[^"]*}}" { i32 456 }, [14 x %"{{[^"]*}}"] zeroinitializer }>
+  V h[16] = {{.a = 123}, {.a = 456}};
+  // CHECK-DAG: @_ZN7PR375601iE ={{.*}} global [4 x %"{{[^"]*}}"] 
[%"{{[^"]*}}" { i32 123 }, %"{{[^"]*}}" { i32 456 }, %"{{[^"]*}}" 
zeroinitializer, %"{{[^"]*}}" zeroinitializer]
+  V i[4] = {{.a = 123}, {.a = 456}};
+}
+
+// CIR emits the global initializers before the other functions.
+// LLVM-LABEL: define {{.*}}@__cxx_global_var_init(
+// LLVM: icmp ne {{.*}}, getelementptr inbounds nuw (i8, ptr @{{.*}}, i64 600)
+// LLVM: call {{.*}}@_ZN14NonTrivialInit1AC1Ev(
+// LLVM: getelementptr {{.*}}, i64 1
+// LLVM: call i32 @__cxa_atexit(
+// LLVM-LABEL: define {{.*}}@__cxx_global_var_init.1(
+// LLVM: getelementptr inbounds {{.*}} @_ZN8ZeroInit7data_e1E
+
+// CHECK-LABEL: define {{.*}}@_Z3fn1i(
+int fn1(int x) {
+  // CHECK: %[[INITLIST:.*]] = alloca %struct.A
+  // CHECK: %[[A:.*]] = getelementptr inbounds nuw %struct.A, ptr 
%[[INITLIST]], i32 0, i32 0
+  // CHECK: store i32 %{{.*}}, ptr %[[A]], align 4
+  // CHECK: %[[B:.*]] = getelementptr inbounds nuw %struct.A, ptr 
%[[INITLIST]], i32 0, i32 1
+  // CHECK: store i32 5, ptr %[[B]], align 4
+  // CHECK: call noundef i32 @_ZN1A1fEv(ptr {{[^,]*}} %[[INITLIST]])
+  return A{x, 5}.f();
+}
+
+struct B { int &r; int &f() { return r; } };
+
+// CHECK-LABEL: define {{.*}}@_Z3fn2Ri(
+int &fn2(int &v) {
+  // CHECK: %[[INITLIST2:.*]] = alloca %struct.B, align 8
+  // CHECK: %[[R:.*]] = getelementptr inbounds nuw %struct.B, ptr 
%[[INITLIST2:.*]], i32 0, i32 0
+  // CHECK: store ptr %{{.*}}, ptr %[[R]], align 8
+  // CHECK: call noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) 
ptr @_ZN1B1fEv(ptr {{[^,]*}} %[[INITLIST2:.*]])
+  return B{v}.f();
+}
+
+// OGCG-LABEL: define {{.*}}@__cxx_global_var_init(
+//
+// OGCG: call {{.*}}@_ZN14NonTrivialInit1AC1Ev(
+// OGCG: getelementptr inbounds {{.*}}, i64 1
+// OGCG: br i1
+//
+// OGCG: getelementptr inbounds {{.*}}, i64 1
+// OGCG: icmp eq {{.*}}, getelementptr inbounds nuw (i8, ptr @{{.*}}, i64 600)
+// OGCG: br i1
+//
+// OGCG: call i32 @__cxa_atexit(
+namespace NonTrivialInit {
+  struct A { A(); A(const A&) = delete; ~A(); };
+  struct B { A a[20]; };
+  // NB, this must be large enough to be worth memsetting for this test to be
+  // meaningful.
+  B b[30] = {};
+}
+
+namespace ZeroInit {
+  enum { Zero, One };
+  constexpr int zero() { return 0; }
+  constexpr int *null() { return nullptr; }
+  struct Filler {
+    int x;
+    Filler();
+  };
+  struct S1 {
+    int x;
+  };
+
+  // These declarations, if implemented elementwise, require huge
+  // amout of memory and compiler time.
+  unsigned char data_1[1024 * 1024 * 1024 * 2u] = { 0 };
+  unsigned char data_2[1024 * 1024 * 1024 * 2u] = { Zero };
+  unsigned char data_3[1024][1024][1024] = {{{0}}};
+  unsigned char data_4[1024 * 1024 * 1024 * 2u] = { zero() };
+  int *data_5[1024 * 1024 * 512] = { nullptr };
+  int *data_6[1024 * 1024 * 512] = { null() };
+  struct S1 data_7[1024 * 1024 * 512] = {{0}};
+  char data_8[1000 * 1000 * 1000] = {};
+  int (&&data_9)[1000 * 1000 * 1000] = {0};
+  unsigned char data_10[1024 * 1024 * 1024 * 2u] = { 1 };
+  unsigned char data_11[1024 * 1024 * 1024 * 2u] = { One };
+  unsigned char data_12[1024][1024][1024] = {{{1}}};
+
+  // This variable must be initialized elementwise.
+  Filler data_e1[1024] = {};
+  // OGCG: getelementptr inbounds {{.*}} @_ZN8ZeroInit7data_e1E
+
+  // CIR does not yet use memset to zero these; it stores each field.
+  struct Largeish {
+    long a, b, c;
+  };
+  // CHECK: define {{.*}}@_ZN8ZeroInit9largeish1Ev(
+  // CHECK-NOT }
+  // OGCG: call {{.*}}memset
+  // LLVM: store i64 0
+  Largeish largeish1() { return {}; }
+  // CHECK: define {{.*}}@_ZN8ZeroInit9largeish2Ev(
+  // CHECK-NOT }
+  // CHECK: call {{.*}}memset
+  Largeish largeish2() { return Largeish(); }
+  // CHECK: define {{.*}}@_ZN8ZeroInit9largeish3Ev(
+  // CHECK-NOT }
+  // OGCG: call {{.*}}memset
+  // LLVM: store i64 0
+  Largeish largeish3() { return Largeish{}; }
+  // CHECK: define {{.*}}@_ZN8ZeroInit9largeish4Ev(
+  // CHECK-NOT }
+  // OGCG: call {{.*}}memset
+  // LLVM: store i64 0
+  Largeish largeish4() { return (Largeish){}; }
+  // CHECK: define {{.*}}@_ZN8ZeroInit9largeish5Ev(
+  // CHECK-NOT }
+  // OGCG: call {{.*}}memset
+  // LLVM: store i64 0
+  Largeish largeish5() { return {0, 0, 0}; }
+
+  typedef __attribute__((ext_vector_type(4))) char CI4;
+  struct Conversions {
+    _Complex int a;
+    _Complex float b;
+    short c;
+    long double d;
+    CI4 e;
+    char f;
+    char g;
+    int *h;
+    long i;
+  };
+  // CHECK: define {{.*}}@_ZN8ZeroInit11conversionsEv(
+  // CHECK-NOT }
+  // OGCG: call {{.*}}memset
+  // LLVM: store { i32, i32 } zeroinitializer
+  Conversions conversions() {
+    return {0,
+            0,
+            0,
+            0,
+            CI4(0),
+            static_cast<char>(0.0),
+            char(0 + 0i),
+            reinterpret_cast<int *>(0),
+            reinterpret_cast<long>((int *)nullptr)};
+  }
+}
diff --git a/clang/test/CIR/CodeGen/record-with-padded-union.cpp 
b/clang/test/CIR/CodeGen/record-with-padded-union.cpp
index 6c0eba263fe539..d3c7727c07b616 100644
--- a/clang/test/CIR/CodeGen/record-with-padded-union.cpp
+++ b/clang/test/CIR/CodeGen/record-with-padded-union.cpp
@@ -1,9 +1,9 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o 
%t.cir
 // RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o 
%t-cir.ll
-// RUN: FileCheck --check-prefix=LLVM,LLVMCIR --input-file=%t-cir.ll %s
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
-// RUN: FileCheck --check-prefix=LLVM,OGCG --input-file=%t.ll %s
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s
 
 struct SSO {
   char *p;
@@ -26,8 +26,7 @@ SSO s1 {nullptr, 1, {.local{}}};
 // LLVM: @s1 = global { ptr, i64, { [16 x i8] } } { ptr null, i64 1, { [16 x 
i8] } zeroinitializer }
 SSO s2 {nullptr, 1, {'a', 'b', 'c', 'd'}};
 // CIR: cir.global external @s2 = #cir.const_record<{#cir.ptr<null> : 
!cir.ptr<!s8i>, #cir.int<1> : !u64i, 
#cir.const_record<{#cir.const_array<[#cir.int<97> : !s8i, #cir.int<98> : !s8i, 
#cir.int<99> : !s8i, #cir.int<100> : !s8i], trailing_zeros> : !cir.array<!s8i x 
16>}> : !rec_anon2E0}> : !rec_SSO
-// LLVMCIR: @s2 = global { ptr, i64, { [16 x i8] } } { ptr null, i64 1, { [16 
x i8] } { [16 x i8] c"abcd\00\00\00\00\00\00\00\00\00\00\00\00" } }
-// OGCG:    @s2 = global { ptr, i64, { <{ i8, i8, i8, i8, [12 x i8] }> } } { 
ptr null, i64 1, { <{ i8, i8, i8, i8, [12 x i8] }> } { <{ i8, i8, i8, i8, [12 x 
i8] }> <{ i8 97, i8 98, i8 99, i8 100, [12 x i8] zeroinitializer }> } }
+// LLVM: @s2 = global { ptr, i64, { <{ i8, i8, i8, i8, [12 x i8] }> } } { ptr 
null, i64 1, { <{ i8, i8, i8, i8, [12 x i8] }> } { <{ i8, i8, i8, i8, [12 x i8] 
}> <{ i8 97, i8 98, i8 99, i8 100, [12 x i8] zeroinitializer }> } }
 
 struct SSO2 {
   char *p;
diff --git a/clang/test/CIR/CodeGen/replace-global.cpp 
b/clang/test/CIR/CodeGen/replace-global.cpp
index 54e7d9cbd0f07d..4d5f40faac115c 100644
--- a/clang/test/CIR/CodeGen/replace-global.cpp
+++ b/clang/test/CIR/CodeGen/replace-global.cpp
@@ -58,7 +58,7 @@ char *get_ptr_to_element() { return ptrToElement; }
 // CIR:   %[[GS_AS_VOID:.*]] = cir.cast bitcast %[[GS_PTR]] : !cir.ptr<!rec_S> 
-> !cir.ptr<!void>
 // CIR:   cir.call @_Z3usePv(%[[GS_AS_VOID]]) : (!cir.ptr<!void> {{.*}}) -> ()
 
-// LLVM: @_ZL2gS = internal global %struct.S { [28 x i8] 
c"PK\03\04\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00"
 }, align 1
+// LLVM: @_ZL2gS = internal global { <{ i8, i8, i8, i8, [24 x i8] }> } { <{ 
i8, i8, i8, i8, [24 x i8] }> <{ i8 80, i8 75, i8 3, i8 4, [24 x i8] 
zeroinitializer }> }, align 1
 // LLVM: @ptrToS = global ptr @_ZL2gS, align 8
 // LLVM: @gSMulti = global {{.*}} align 1
 // LLVM: @ptrToElement = global ptr getelementptr
diff --git a/clang/test/CIR/CodeGen/string-literals.cpp 
b/clang/test/CIR/CodeGen/string-literals.cpp
index 896016b0f39efc..9bb14827f0565b 100644
--- a/clang/test/CIR/CodeGen/string-literals.cpp
+++ b/clang/test/CIR/CodeGen/string-literals.cpp
@@ -44,7 +44,7 @@ char const *array[] {
 
 wchar_t zeroPadding[20] = L"hi";
 // CIR: cir.global external @zeroPadding = #cir.const_array<[#cir.int<104> : 
!u32i, #cir.int<105> : !u32i], trailing_zeros> : !cir.array<!u32i x 20> 
-// LLVM: @zeroPadding = global [20 x i32] [i32 104, i32 105, i32 0, i32 0, i32 
0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, 
i32 0, i32 0, i32 0, i32 0]
+// LLVM: @zeroPadding = global <{ i32, i32, [18 x i32] }> <{ i32 104, i32 105, 
[18 x i32] zeroinitializer }>
 // OGCG: @zeroPadding = global [20 x i32] [i32 104, i32 105, i32 0, i32 0, i32 
0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, 
i32 0, i32 0, i32 0, i32 0]
 
 // CIR: cir.global "private" constant cir_private dso_local 
@[[STR5_GLOBAL:.*]] = #cir.const_array<"abcd" : !cir.array<!s8i x 4>, 
trailing_zeros> : !cir.array<!s8i x 5>
diff --git a/clang/test/CIR/CodeGen/union-init-padding-array.c 
b/clang/test/CIR/CodeGen/union-init-padding-array.c
index 94abba3d858de0..f824326569dd1d 100644
--- a/clang/test/CIR/CodeGen/union-init-padding-array.c
+++ b/clang/test/CIR/CodeGen/union-init-padding-array.c
@@ -1,9 +1,9 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o 
%t.cir
 // RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o 
%t-cir.ll
-// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: FileCheck --check-prefixes=LLVM,LLVMCIR --input-file=%t-cir.ll %s
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
-// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s
+// RUN: FileCheck --check-prefixes=LLVM,OGCG --input-file=%t.ll %s
 
 union Wide { char text[12]; unsigned d[3]; };
 union Word { char text[4]; unsigned d; };
@@ -30,20 +30,24 @@ union Odd odd[2] = { { .c = "ab" }, { .c = "cd" } };
 
 union Wide same[2] = { { .text = "abc" }, { .text = "xyz" } };
 // CIR: cir.global external @same = 
#cir.const_array<[#cir.const_record<{#cir.const_array<[#cir.int<97> : !s8i, 
#cir.int<98> : !s8i, #cir.int<99> : !s8i], trailing_zeros> : !cir.array<!s8i x 
12>}> : !rec_Wide, #cir.const_record<{#cir.const_array<[#cir.int<120> : !s8i, 
#cir.int<121> : !s8i, #cir.int<122> : !s8i], trailing_zeros> : !cir.array<!s8i 
x 12>}> : !rec_Wide]> : !cir.array<!rec_Wide x 2> 
-// LLVM: @same = global [2 x { [12 x i8] }] [{ [12 x i8] } { [12 x i8] 
c"abc\00\00\00\00\00\00\00\00\00" }, { [12 x i8] } { [12 x i8] 
c"xyz\00\00\00\00\00\00\00\00\00" }]
+// LLVMCIR: @same = global [2 x { <{ i8, i8, i8, [9 x i8] }> }] [{ <{ i8, i8, 
i8, [9 x i8] }> } { <{ i8, i8, i8, [9 x i8] }> <{ i8 97, i8 98, i8 99, [9 x i8] 
zeroinitializer }> }, { <{ i8, i8, i8, [9 x i8] }> } { <{ i8, i8, i8, [9 x i8] 
}> <{ i8 120, i8 121, i8 122, [9 x i8] zeroinitializer }> }]
+// OGCG:    @same = global [2 x { [12 x i8] }] [{ [12 x i8] } { [12 x i8] 
c"abc\00\00\00\00\00\00\00\00\00" }, { [12 x i8] } { [12 x i8] 
c"xyz\00\00\00\00\00\00\00\00\00" }]
 
 union Wide mixed[2] = { { .text = "abc" }, { .d = { 1, 2, 3 } } };
 // CIR: cir.global external @mixed = 
#cir.const_array<[#cir.const_record<{#cir.const_array<[#cir.int<97> : !s8i, 
#cir.int<98> : !s8i, #cir.int<99> : !s8i], trailing_zeros> : !cir.array<!s8i x 
12>}> : !rec_Wide, #cir.const_record<{#cir.const_array<[#cir.int<1> : !u32i, 
#cir.int<2> : !u32i, #cir.int<3> : !u32i]> : !cir.array<!u32i x 3>}> : 
!rec_Wide]> : !cir.array<!rec_Wide x 2> 
-// LLVM: @mixed = global <{ { [12 x i8] }, %union.Wide }> <{ { [12 x i8] } { 
[12 x i8] c"abc\00\00\00\00\00\00\00\00\00" }, %union.Wide { [3 x i32] [i32 1, 
i32 2, i32 3] } }>
+// LLVMCIR: @mixed = global <{ { <{ i8, i8, i8, [9 x i8] }> }, %union.Wide }> 
<{ { <{ i8, i8, i8, [9 x i8] }> } { <{ i8, i8, i8, [9 x i8] }> <{ i8 97, i8 98, 
i8 99, [9 x i8] zeroinitializer }> }, %union.Wide { [3 x i32] [i32 1, i32 2, 
i32 3] } }>
+// OGCG:    @mixed = global <{ { [12 x i8] }, %union.Wide }> <{ { [12 x i8] } 
{ [12 x i8] c"abc\00\00\00\00\00\00\00\00\00" }, %union.Wide { [3 x i32] [i32 
1, i32 2, i32 3] } }>
 
 union Wide tail[2] = { { .text = "abc" } };
 // CIR: cir.global external @tail = 
#cir.const_array<[#cir.const_record<{#cir.const_array<[#cir.int<97> : !s8i, 
#cir.int<98> : !s8i, #cir.int<99> : !s8i], trailing_zeros> : !cir.array<!s8i x 
12>}> : !rec_Wide], trailing_zeros> : !cir.array<!rec_Wide x 2> 
-// LLVM: @tail = global <{ { [12 x i8] }, %union.Wide }> <{ { [12 x i8] } { 
[12 x i8] c"abc\00\00\00\00\00\00\00\00\00" }, %union.Wide zeroinitializer }>
+// LLVMCIR: @tail = global <{ { <{ i8, i8, i8, [9 x i8] }> }, %union.Wide }> 
<{ { <{ i8, i8, i8, [9 x i8] }> } { <{ i8, i8, i8, [9 x i8] }> <{ i8 97, i8 98, 
i8 99, [9 x i8] zeroinitializer }> }, %union.Wide zeroinitializer }>
+// OGCG:    @tail = global <{ { [12 x i8] }, %union.Wide }> <{ { [12 x i8] } { 
[12 x i8] c"abc\00\00\00\00\00\00\00\00\00" }, %union.Wide zeroinitializer }>
 
 union Wide grid[2][2] = { { { .text = "a" }, { .text = "b" } },
                           { { .text = "c" }, { .text = "d" } } };
 // CIR: cir.global external @grid = 
#cir.const_array<[#cir.const_array<[#cir.const_record<{#cir.const_array<[#cir.int<97>
 : !s8i], trailing_zeros> : !cir.array<!s8i x 12>}> : !rec_Wide, 
#cir.const_record<{#cir.const_array<[#cir.int<98> : !s8i], trailing_zeros> : 
!cir.array<!s8i x 12>}> : !rec_Wide]> : !cir.array<!rec_Wide x 2>, 
#cir.const_array<[#cir.const_record<{#cir.const_array<[#cir.int<99> : !s8i], 
trailing_zeros> : !cir.array<!s8i x 12>}> : !rec_Wide, 
#cir.const_record<{#cir.const_array<[#cir.int<100> : !s8i], trailing_zeros> : 
!cir.array<!s8i x 12>}> : !rec_Wide]> : !cir.array<!rec_Wide x 2>]> : 
!cir.array<!cir.array<!rec_Wide x 2> x 2> 
-// LLVM: @grid = global [2 x [2 x { [12 x i8] }]] {{\[\[}}2 x { [12 x i8] }] 
[{ [12 x i8] } { [12 x i8] c"a\00\00\00\00\00\00\00\00\00\00\00" }, { [12 x i8] 
} { [12 x i8] c"b\00\00\00\00\00\00\00\00\00\00\00" }], [2 x { [12 x i8] }] [{ 
[12 x i8] } { [12 x i8] c"c\00\00\00\00\00\00\00\00\00\00\00" }, { [12 x i8] } 
{ [12 x i8] c"d\00\00\00\00\00\00\00\00\00\00\00" }]]
+// LLVMCIR: @grid = global [2 x [2 x { <{ i8, [11 x i8] }> }]] {{\[\[}}2 x { 
<{ i8, [11 x i8] }> }] [{ <{ i8, [11 x i8] }> } { <{ i8, [11 x i8] }> <{ i8 97, 
[11 x i8] zeroinitializer }> }, { <{ i8, [11 x i8] }> } { <{ i8, [11 x i8] }> 
<{ i8 98, [11 x i8] zeroinitializer }> }], [2 x { <{ i8, [11 x i8] }> }] [{ <{ 
i8, [11 x i8] }> } { <{ i8, [11 x i8] }> <{ i8 99, [11 x i8] zeroinitializer }> 
}, { <{ i8, [11 x i8] }> } { <{ i8, [11 x i8] }> <{ i8 100, [11 x i8] 
zeroinitializer }> }]]
+// OGCG:    @grid = global [2 x [2 x { [12 x i8] }]] {{\[\[}}2 x { [12 x i8] 
}] [{ [12 x i8] } { [12 x i8] c"a\00\00\00\00\00\00\00\00\00\00\00" }, { [12 x 
i8] } { [12 x i8] c"b\00\00\00\00\00\00\00\00\00\00\00" }], [2 x { [12 x i8] }] 
[{ [12 x i8] } { [12 x i8] c"c\00\00\00\00\00\00\00\00\00\00\00" }, { [12 x i8] 
} { [12 x i8] c"d\00\00\00\00\00\00\00\00\00\00\00" }]]
 
 struct PadElem { int info; union Str u; };
 struct PadElem pad_elems[2] = { { 1, { 2 } }, { 3, { 4 } } };
@@ -58,7 +62,8 @@ struct PadHolder pad_holder = { 1, { { 2 }, { 3 } } };
 struct Holder { int a; union Wide arr[2]; int b; };
 struct Holder holder = { 1, { { .text = "xy" }, { .text = "zw" } }, 2 };
 // CIR: cir.global external @holder = #cir.const_record<{#cir.int<1> : !s32i, 
#cir.const_array<[#cir.const_record<{#cir.const_array<[#cir.int<120> : !s8i, 
#cir.int<121> : !s8i], trailing_zeros> : !cir.array<!s8i x 12>}> : !rec_Wide, 
#cir.const_record<{#cir.const_array<[#cir.int<122> : !s8i, #cir.int<119> : 
!s8i], trailing_zeros> : !cir.array<!s8i x 12>}> : !rec_Wide]> : 
!cir.array<!rec_Wide x 2>, #cir.int<2> : !s32i}> : !rec_Holder 
-// LLVM: @holder = global { i32, [2 x { [12 x i8] }], i32 } { i32 1, [2 x { 
[12 x i8] }] [{ [12 x i8] } { [12 x i8] c"xy\00\00\00\00\00\00\00\00\00\00" }, 
{ [12 x i8] } { [12 x i8] c"zw\00\00\00\00\00\00\00\00\00\00" }], i32 2 }
+// LLVMCIR: @holder = global { i32, [2 x { <{ i8, i8, [10 x i8] }> }], i32 } { 
i32 1, [2 x { <{ i8, i8, [10 x i8] }> }] [{ <{ i8, i8, [10 x i8] }> } { <{ i8, 
i8, [10 x i8] }> <{ i8 120, i8 121, [10 x i8] zeroinitializer }> }, { <{ i8, 
i8, [10 x i8] }> } { <{ i8, i8, [10 x i8] }> <{ i8 122, i8 119, [10 x i8] 
zeroinitializer }> }], i32 2 }
+// OGCG:    @holder = global { i32, [2 x { [12 x i8] }], i32 } { i32 1, [2 x { 
[12 x i8] }] [{ [12 x i8] } { [12 x i8] c"xy\00\00\00\00\00\00\00\00\00\00" }, 
{ [12 x i8] } { [12 x i8] c"zw\00\00\00\00\00\00\00\00\00\00" }], i32 2 }
 
 extern int ext[4];
 union Ptr relocs[2] = { { .p = &ext[1] }, { .p = &ext[2] } };
diff --git a/clang/test/CIR/CodeGen/wide-string.cpp 
b/clang/test/CIR/CodeGen/wide-string.cpp
index a7c920409fac74..6da5ed57998f68 100644
--- a/clang/test/CIR/CodeGen/wide-string.cpp
+++ b/clang/test/CIR/CodeGen/wide-string.cpp
@@ -6,7 +6,7 @@
 // RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
 
 // CIR: cir.global external @some_array = #cir.const_array<[#cir.int<97> : 
!u16i], trailing_zeros> : !cir.array<!u16i x 10>
-// LLVM: @some_array = global [10 x i16] [i16 97, i16 0, i16 0, i16 0, i16 0, 
i16 0, i16 0, i16 0, i16 0, i16 0]
+// LLVM: @some_array = global <{ i16, [9 x i16] }> <{ i16 97, [9 x i16] 
zeroinitializer }>
 // OGCG: @some_array = global [10 x i16] [i16 97, i16 0, i16 0, i16 0, i16 0, 
i16 0, i16 0, i16 0, i16 0, i16 0]
 char16_t some_array[10] = u"a";
 
diff --git a/clang/test/CIR/Lowering/array.cpp 
b/clang/test/CIR/Lowering/array.cpp
index 5594b97a35496e..0225be03906c98 100644
--- a/clang/test/CIR/Lowering/array.cpp
+++ b/clang/test/CIR/Lowering/array.cpp
@@ -25,7 +25,7 @@ int dd[3][2] = {{1, 2}, {3, 4}, {5, 6}};
 // CHECK: [i32 3, i32 4], [2 x i32] [i32 5, i32 6]]
 
 int e[10] = {1, 2};
-// CHECK: @e = global [10 x i32] [i32 1, i32 2, i32 0, i32 0, i32 0, i32 0, 
i32 0, i32 0, i32 0, i32 0]
+// CHECK: @e = global <{ i32, i32, [8 x i32] }> <{ i32 1, i32 2, [8 x i32] 
zeroinitializer }>
 
 int f[5] = {1, 2};
 // CHECK: @f = global [5 x i32] [i32 1, i32 2, i32 0, i32 0, i32 0]
diff --git a/clang/test/CIR/Lowering/const-array-trailing-zeros.cir 
b/clang/test/CIR/Lowering/const-array-trailing-zeros.cir
new file mode 100644
index 00000000000000..a93e0645a7bbd6
--- /dev/null
+++ b/clang/test/CIR/Lowering/const-array-trailing-zeros.cir
@@ -0,0 +1,28 @@
+// RUN: cir-opt %s -cir-to-llvm -o - | FileCheck %s
+
+!s32i = !cir.int<s, 32>
+!rec_S = !cir.struct<"S" {data !cir.array<!s32i x 10>}>
+
+// A global initializer ending in many zeros lowers to a packed struct with a
+// zero-initialized tail, but a cir.const keeps its array type.
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+  cir.global external @g = #cir.const_array<[#cir.int<1> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>
+  // CHECK: llvm.mlir.global external @g([1 : i32, #llvm.zero]) {{.*}} : 
!llvm.struct<packed (i32, array<9 x i32>)>
+
+  cir.func @array(%arg0: !cir.ptr<!cir.array<!s32i x 10>>) {
+    %0 = cir.const #cir.const_array<[#cir.int<1> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>
+    cir.store %0, %arg0 : !cir.array<!s32i x 10>, !cir.ptr<!cir.array<!s32i x 
10>>
+    cir.return
+  }
+  // CHECK-LABEL: llvm.func @array
+  // CHECK: %[[C:.*]] = llvm.mlir.constant(dense<[1, 0, 0, 0, 0, 0, 0, 0, 0, 
0]> : tensor<10xi32>) : !llvm.array<10 x i32>
+  // CHECK: llvm.store %[[C]], %{{.*}} : !llvm.array<10 x i32>, !llvm.ptr
+
+  cir.func @record(%arg0: !cir.ptr<!rec_S>) {
+    %0 = cir.const #cir.const_record<{#cir.const_array<[#cir.int<1> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>}> : !rec_S
+    cir.store %0, %arg0 : !rec_S, !cir.ptr<!rec_S>
+    cir.return
+  }
+  // CHECK-LABEL: llvm.func @record
+  // CHECK: llvm.store %{{.*}}, %{{.*}} : !llvm.struct<"struct.S", (array<10 x 
i32>)>, !llvm.ptr
+}

>From e89c8a66efa440705d515d81e394559910709374 Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <[email protected]>
Date: Fri, 16 Oct 2026 12:05:34 -0700
Subject: [PATCH 2/6] Fix comments

---
 .../lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp  |  4 +++-
 clang/lib/CIR/Lowering/LoweringHelpers.cpp         |  1 -
 .../CIR/CodeGen/cxx11-initializer-aggregate.cpp    | 12 ++++++------
 .../CIR/Lowering/const-array-trailing-zeros.cir    | 14 ++++++++++++++
 4 files changed, 23 insertions(+), 8 deletions(-)

diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 3964881cf23c46..703eb7c56c2724 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -933,7 +933,9 @@ mlir::Value 
CIRAttrToValue::visitCirAttr(cir::ConstArrayAttr attr) {
   // Iteratively lower each constant element of the array.
   if (auto arrayAttr = mlir::dyn_cast<mlir::ArrayAttr>(attr.getElts())) {
     // Fewer struct members than elements: adjustGlobalTypeForInit grouped the
-    // elements of an array ending in many zeros into one leading array.
+    // elements of an array ending in many zeros into one leading array. That
+    // array has no trailing zeros, so it keeps its type and still lowers as
+    // one dense constant.
     auto structTy = mlir::dyn_cast<mlir::LLVM::LLVMStructType>(llvmTy);
     if (structTy && structTy.getBody().size() < arrayAttr.size()) {
       auto arrayTy = mlir::cast<cir::ArrayType>(attr.getType());
diff --git a/clang/lib/CIR/Lowering/LoweringHelpers.cpp 
b/clang/lib/CIR/Lowering/LoweringHelpers.cpp
index f25c66af058869..ef8306f366bf85 100644
--- a/clang/lib/CIR/Lowering/LoweringHelpers.cpp
+++ b/clang/lib/CIR/Lowering/LoweringHelpers.cpp
@@ -829,7 +829,6 @@ static mlir::Type adjustGlobalArrayTypeForInit(
     adjustedElts.push_back(adjusted);
   }
 
-
   if (shouldSplitTrailingZeros(arrayInit)) {
     mlir::Type commonEltTy =
         llvm::all_equal(adjustedElts) ? adjustedElts.front() : mlir::Type{};
diff --git a/clang/test/CIR/CodeGen/cxx11-initializer-aggregate.cpp 
b/clang/test/CIR/CodeGen/cxx11-initializer-aggregate.cpp
index 7c36d3370cf12d..ba346ead9b3270 100644
--- a/clang/test/CIR/CodeGen/cxx11-initializer-aggregate.cpp
+++ b/clang/test/CIR/CodeGen/cxx11-initializer-aggregate.cpp
@@ -144,26 +144,26 @@ namespace ZeroInit {
     long a, b, c;
   };
   // CHECK: define {{.*}}@_ZN8ZeroInit9largeish1Ev(
-  // CHECK-NOT }
+  // CHECK-NOT: }
   // OGCG: call {{.*}}memset
   // LLVM: store i64 0
   Largeish largeish1() { return {}; }
   // CHECK: define {{.*}}@_ZN8ZeroInit9largeish2Ev(
-  // CHECK-NOT }
+  // CHECK-NOT: }
   // CHECK: call {{.*}}memset
   Largeish largeish2() { return Largeish(); }
   // CHECK: define {{.*}}@_ZN8ZeroInit9largeish3Ev(
-  // CHECK-NOT }
+  // CHECK-NOT: }
   // OGCG: call {{.*}}memset
   // LLVM: store i64 0
   Largeish largeish3() { return Largeish{}; }
   // CHECK: define {{.*}}@_ZN8ZeroInit9largeish4Ev(
-  // CHECK-NOT }
+  // CHECK-NOT: }
   // OGCG: call {{.*}}memset
   // LLVM: store i64 0
   Largeish largeish4() { return (Largeish){}; }
   // CHECK: define {{.*}}@_ZN8ZeroInit9largeish5Ev(
-  // CHECK-NOT }
+  // CHECK-NOT: }
   // OGCG: call {{.*}}memset
   // LLVM: store i64 0
   Largeish largeish5() { return {0, 0, 0}; }
@@ -181,7 +181,7 @@ namespace ZeroInit {
     long i;
   };
   // CHECK: define {{.*}}@_ZN8ZeroInit11conversionsEv(
-  // CHECK-NOT }
+  // CHECK-NOT: }
   // OGCG: call {{.*}}memset
   // LLVM: store { i32, i32 } zeroinitializer
   Conversions conversions() {
diff --git a/clang/test/CIR/Lowering/const-array-trailing-zeros.cir 
b/clang/test/CIR/Lowering/const-array-trailing-zeros.cir
index a93e0645a7bbd6..b8466015d6d9ad 100644
--- a/clang/test/CIR/Lowering/const-array-trailing-zeros.cir
+++ b/clang/test/CIR/Lowering/const-array-trailing-zeros.cir
@@ -2,6 +2,7 @@
 
 !s32i = !cir.int<s, 32>
 !rec_S = !cir.struct<"S" {data !cir.array<!s32i x 10>}>
+!rec_P = !cir.struct<"P" {data !cir.ptr<!s32i>, data !cir.array<!s32i x 16>}>
 
 // A global initializer ending in many zeros lowers to a packed struct with a
 // zero-initialized tail, but a cir.const keeps its array type.
@@ -9,6 +10,19 @@ module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
   cir.global external @g = #cir.const_array<[#cir.int<1> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>
   // CHECK: llvm.mlir.global external @g([1 : i32, #llvm.zero]) {{.*}} : 
!llvm.struct<packed (i32, array<9 x i32>)>
 
+  // The offset address cannot be a constant attribute, so @p is built with
+  // insertvalue. The grouped explicit elements still lower as one dense
+  // constant, not element by element.
+  cir.global external @arr = #cir.zero : !cir.array<!s32i x 2>
+  cir.global external @p = #cir.const_record<{#cir.global_view<@arr, [1 : 
i32]> : !cir.ptr<!s32i>, #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : 
!s32i, #cir.int<3> : !s32i, #cir.int<4> : !s32i, #cir.int<5> : !s32i, 
#cir.int<6> : !s32i, #cir.int<7> : !s32i, #cir.int<8> : !s32i], trailing_zeros> 
: !cir.array<!s32i x 16>}> : !rec_P
+  // CHECK-LABEL: llvm.mlir.global external @p()
+  // CHECK-NOT: llvm.insertvalue {{.*}} : !llvm.array<8 x i32>
+  // CHECK: %[[PREFIX:.*]] = llvm.mlir.constant(dense<[1, 2, 3, 4, 5, 6, 7, 
8]> : tensor<8xi32>) : !llvm.array<8 x i32>
+  // CHECK-NOT: llvm.insertvalue {{.*}} : !llvm.array<8 x i32>
+  // CHECK: llvm.insertvalue %[[PREFIX]], %{{.*}}[0] : !llvm.struct<packed 
(array<8 x i32>, array<8 x i32>)>
+  // CHECK-NOT: llvm.insertvalue {{.*}} : !llvm.array<8 x i32>
+  // CHECK: llvm.return
+
   cir.func @array(%arg0: !cir.ptr<!cir.array<!s32i x 10>>) {
     %0 = cir.const #cir.const_array<[#cir.int<1> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>
     cir.store %0, %arg0 : !cir.array<!s32i x 10>, !cir.ptr<!cir.array<!s32i x 
10>>

>From a9cced557c7f23c482a5c007704cdce353cac72a Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <[email protected]>
Date: Fri, 16 Oct 2026 12:16:46 -0700
Subject: [PATCH 3/6] Add large|small cases in test

---
 .../Lowering/const-array-trailing-zeros.cir   | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/clang/test/CIR/Lowering/const-array-trailing-zeros.cir 
b/clang/test/CIR/Lowering/const-array-trailing-zeros.cir
index b8466015d6d9ad..f2b4ffd69916c5 100644
--- a/clang/test/CIR/Lowering/const-array-trailing-zeros.cir
+++ b/clang/test/CIR/Lowering/const-array-trailing-zeros.cir
@@ -3,6 +3,7 @@
 !s32i = !cir.int<s, 32>
 !rec_S = !cir.struct<"S" {data !cir.array<!s32i x 10>}>
 !rec_P = !cir.struct<"P" {data !cir.ptr<!s32i>, data !cir.array<!s32i x 16>}>
+!rec_PLarge = !cir.struct<"PLarge" {data !cir.ptr<!s32i>, data 
!cir.array<!s32i x 108>}>
 
 // A global initializer ending in many zeros lowers to a packed struct with a
 // zero-initialized tail, but a cir.const keeps its array type.
@@ -23,6 +24,24 @@ module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
   // CHECK-NOT: llvm.insertvalue {{.*}} : !llvm.array<8 x i32>
   // CHECK: llvm.return
 
+  // The same with many explicit elements and only 8 trailing zeros.
+  cir.global external @p_large = #cir.const_record<{#cir.global_view<@arr, [1 
: i32]> : !cir.ptr<!s32i>, #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : 
!s32i, #cir.int<3> : !s32i, #cir.int<4> : !s32i, #cir.int<5> : !s32i, 
#cir.int<6> : !s32i, #cir.int<7> : !s32i, #cir.int<8> : !s32i, #cir.int<9> : 
!s32i, #cir.int<10> : !s32i, #cir.int<11> : !s32i, #cir.int<12> : !s32i, 
#cir.int<13> : !s32i, #cir.int<14> : !s32i, #cir.int<15> : !s32i, #cir.int<16> 
: !s32i, #cir.int<17> : !s32i, #cir.int<18> : !s32i, #cir.int<19> : !s32i, 
#cir.int<20> : !s32i, #cir.int<21> : !s32i, #cir.int<22> : !s32i, #cir.int<23> 
: !s32i, #cir.int<24> : !s32i, #cir.int<25> : !s32i, #cir.int<26> : !s32i, 
#cir.int<27> : !s32i, #cir.int<28> : !s32i, #cir.int<29> : !s32i, #cir.int<30> 
: !s32i, #cir.int<31> : !s32i, #cir.int<32> : !s32i, #cir.int<33> : !s32i, 
#cir.int<34> : !s32i, #cir.int<35> : !s32i, #cir.int<36> : !s32i, #cir.int<37> 
: !s32i, #cir.int<38> : !s32i, #cir.int<39> : !s32i, #cir.int<40> : !s32i, 
#cir.int<41> : !s32i, #cir.int<42> : !s32i, #cir.int<43> : !s32i, #cir.int<44> 
: !s32i, #cir.int<45> : !s32i, #cir.int<46> : !s32i, #cir.int<47> : !s32i, 
#cir.int<48> : !s32i, #cir.int<49> : !s32i, #cir.int<50> : !s32i, #cir.int<51> 
: !s32i, #cir.int<52> : !s32i, #cir.int<53> : !s32i, #cir.int<54> : !s32i, 
#cir.int<55> : !s32i, #cir.int<56> : !s32i, #cir.int<57> : !s32i, #cir.int<58> 
: !s32i, #cir.int<59> : !s32i, #cir.int<60> : !s32i, #cir.int<61> : !s32i, 
#cir.int<62> : !s32i, #cir.int<63> : !s32i, #cir.int<64> : !s32i, #cir.int<65> 
: !s32i, #cir.int<66> : !s32i, #cir.int<67> : !s32i, #cir.int<68> : !s32i, 
#cir.int<69> : !s32i, #cir.int<70> : !s32i, #cir.int<71> : !s32i, #cir.int<72> 
: !s32i, #cir.int<73> : !s32i, #cir.int<74> : !s32i, #cir.int<75> : !s32i, 
#cir.int<76> : !s32i, #cir.int<77> : !s32i, #cir.int<78> : !s32i, #cir.int<79> 
: !s32i, #cir.int<80> : !s32i, #cir.int<81> : !s32i, #cir.int<82> : !s32i, 
#cir.int<83> : !s32i, #cir.int<84> : !s32i, #cir.int<85> : !s32i, #cir.int<86> 
: !s32i, #cir.int<87> : !s32i, #cir.int<88> : !s32i, #cir.int<89> : !s32i, 
#cir.int<90> : !s32i, #cir.int<91> : !s32i, #cir.int<92> : !s32i, #cir.int<93> 
: !s32i, #cir.int<94> : !s32i, #cir.int<95> : !s32i, #cir.int<96> : !s32i, 
#cir.int<97> : !s32i, #cir.int<98> : !s32i, #cir.int<99> : !s32i, #cir.int<100> 
: !s32i], trailing_zeros> : !cir.array<!s32i x 108>}> : !rec_PLarge
+  // CHECK-LABEL: llvm.mlir.global external @p_large()
+  // CHECK-NOT: llvm.insertvalue {{.*}} : !llvm.array<100 x i32>
+  // CHECK: %[[PREFIX:.*]] = llvm.mlir.constant(dense<[1, 2, 3, {{.*}}, 99, 
100]> : tensor<100xi32>) : !llvm.array<100 x i32>
+  // CHECK-NOT: llvm.insertvalue {{.*}} : !llvm.array<100 x i32>
+  // CHECK: llvm.insertvalue %[[PREFIX]], %{{.*}}[0] : !llvm.struct<packed 
(array<100 x i32>, array<8 x i32>)>
+  // CHECK-NOT: llvm.insertvalue {{.*}} : !llvm.array<100 x i32>
+  // CHECK: llvm.return
+
+  // Without the address, the initializer is a single attribute, and the
+  // grouped explicit elements are one dense attribute in it.
+  cir.global external @q = #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> 
: !s32i, #cir.int<3> : !s32i, #cir.int<4> : !s32i, #cir.int<5> : !s32i, 
#cir.int<6> : !s32i, #cir.int<7> : !s32i, #cir.int<8> : !s32i], trailing_zeros> 
: !cir.array<!s32i x 16>
+  // CHECK: llvm.mlir.global external @q([dense<[1, 2, 3, 4, 5, 6, 7, 8]> : 
tensor<8xi32>, #llvm.zero]) {{.*}} : !llvm.struct<packed (array<8 x i32>, 
array<8 x i32>)>
+
+  cir.global external @q_large = #cir.const_array<[#cir.int<1> : !s32i, 
#cir.int<2> : !s32i, #cir.int<3> : !s32i, #cir.int<4> : !s32i, #cir.int<5> : 
!s32i, #cir.int<6> : !s32i, #cir.int<7> : !s32i, #cir.int<8> : !s32i, 
#cir.int<9> : !s32i, #cir.int<10> : !s32i, #cir.int<11> : !s32i, #cir.int<12> : 
!s32i, #cir.int<13> : !s32i, #cir.int<14> : !s32i, #cir.int<15> : !s32i, 
#cir.int<16> : !s32i, #cir.int<17> : !s32i, #cir.int<18> : !s32i, #cir.int<19> 
: !s32i, #cir.int<20> : !s32i, #cir.int<21> : !s32i, #cir.int<22> : !s32i, 
#cir.int<23> : !s32i, #cir.int<24> : !s32i, #cir.int<25> : !s32i, #cir.int<26> 
: !s32i, #cir.int<27> : !s32i, #cir.int<28> : !s32i, #cir.int<29> : !s32i, 
#cir.int<30> : !s32i, #cir.int<31> : !s32i, #cir.int<32> : !s32i, #cir.int<33> 
: !s32i, #cir.int<34> : !s32i, #cir.int<35> : !s32i, #cir.int<36> : !s32i, 
#cir.int<37> : !s32i, #cir.int<38> : !s32i, #cir.int<39> : !s32i, #cir.int<40> 
: !s32i, #cir.int<41> : !s32i, #cir.int<42> : !s32i, #cir.int<43> : !s32i, 
#cir.int<44> : !s32i, #cir.int<45> : !s32i, #cir.int<46> : !s32i, #cir.int<47> 
: !s32i, #cir.int<48> : !s32i, #cir.int<49> : !s32i, #cir.int<50> : !s32i, 
#cir.int<51> : !s32i, #cir.int<52> : !s32i, #cir.int<53> : !s32i, #cir.int<54> 
: !s32i, #cir.int<55> : !s32i, #cir.int<56> : !s32i, #cir.int<57> : !s32i, 
#cir.int<58> : !s32i, #cir.int<59> : !s32i, #cir.int<60> : !s32i, #cir.int<61> 
: !s32i, #cir.int<62> : !s32i, #cir.int<63> : !s32i, #cir.int<64> : !s32i, 
#cir.int<65> : !s32i, #cir.int<66> : !s32i, #cir.int<67> : !s32i, #cir.int<68> 
: !s32i, #cir.int<69> : !s32i, #cir.int<70> : !s32i, #cir.int<71> : !s32i, 
#cir.int<72> : !s32i, #cir.int<73> : !s32i, #cir.int<74> : !s32i, #cir.int<75> 
: !s32i, #cir.int<76> : !s32i, #cir.int<77> : !s32i, #cir.int<78> : !s32i, 
#cir.int<79> : !s32i, #cir.int<80> : !s32i, #cir.int<81> : !s32i, #cir.int<82> 
: !s32i, #cir.int<83> : !s32i, #cir.int<84> : !s32i, #cir.int<85> : !s32i, 
#cir.int<86> : !s32i, #cir.int<87> : !s32i, #cir.int<88> : !s32i, #cir.int<89> 
: !s32i, #cir.int<90> : !s32i, #cir.int<91> : !s32i, #cir.int<92> : !s32i, 
#cir.int<93> : !s32i, #cir.int<94> : !s32i, #cir.int<95> : !s32i, #cir.int<96> 
: !s32i, #cir.int<97> : !s32i, #cir.int<98> : !s32i, #cir.int<99> : !s32i, 
#cir.int<100> : !s32i], trailing_zeros> : !cir.array<!s32i x 108>
+  // CHECK: llvm.mlir.global external @q_large([dense<[1, 2, 3, {{.*}}, 99, 
100]> : tensor<100xi32>, #llvm.zero]) {{.*}} : !llvm.struct<packed (array<100 x 
i32>, array<8 x i32>)>
+
   cir.func @array(%arg0: !cir.ptr<!cir.array<!s32i x 10>>) {
     %0 = cir.const #cir.const_array<[#cir.int<1> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>
     cir.store %0, %arg0 : !cir.array<!s32i x 10>, !cir.ptr<!cir.array<!s32i x 
10>>

>From 4dfa8d81d6d8fb01d484ce3a05edbaaafccc532d Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <[email protected]>
Date: Fri, 16 Oct 2026 12:24:05 -0700
Subject: [PATCH 4/6] [CIR] Check the zero tail of split arrays on the
 insertvalue path

Check that the split struct starts from llvm.mlir.zero and that the
prefix is inserted into it, so the trailing elements are zero, not
undef.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
---
 clang/test/CIR/Lowering/const-array-trailing-zeros.cir | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/clang/test/CIR/Lowering/const-array-trailing-zeros.cir 
b/clang/test/CIR/Lowering/const-array-trailing-zeros.cir
index f2b4ffd69916c5..18aafbe41397af 100644
--- a/clang/test/CIR/Lowering/const-array-trailing-zeros.cir
+++ b/clang/test/CIR/Lowering/const-array-trailing-zeros.cir
@@ -18,9 +18,10 @@ module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
   cir.global external @p = #cir.const_record<{#cir.global_view<@arr, [1 : 
i32]> : !cir.ptr<!s32i>, #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : 
!s32i, #cir.int<3> : !s32i, #cir.int<4> : !s32i, #cir.int<5> : !s32i, 
#cir.int<6> : !s32i, #cir.int<7> : !s32i, #cir.int<8> : !s32i], trailing_zeros> 
: !cir.array<!s32i x 16>}> : !rec_P
   // CHECK-LABEL: llvm.mlir.global external @p()
   // CHECK-NOT: llvm.insertvalue {{.*}} : !llvm.array<8 x i32>
+  // CHECK: %[[ZERO:.*]] = llvm.mlir.zero : !llvm.struct<packed (array<8 x 
i32>, array<8 x i32>)>
   // CHECK: %[[PREFIX:.*]] = llvm.mlir.constant(dense<[1, 2, 3, 4, 5, 6, 7, 
8]> : tensor<8xi32>) : !llvm.array<8 x i32>
   // CHECK-NOT: llvm.insertvalue {{.*}} : !llvm.array<8 x i32>
-  // CHECK: llvm.insertvalue %[[PREFIX]], %{{.*}}[0] : !llvm.struct<packed 
(array<8 x i32>, array<8 x i32>)>
+  // CHECK: llvm.insertvalue %[[PREFIX]], %[[ZERO]][0] : !llvm.struct<packed 
(array<8 x i32>, array<8 x i32>)>
   // CHECK-NOT: llvm.insertvalue {{.*}} : !llvm.array<8 x i32>
   // CHECK: llvm.return
 
@@ -28,9 +29,10 @@ module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
   cir.global external @p_large = #cir.const_record<{#cir.global_view<@arr, [1 
: i32]> : !cir.ptr<!s32i>, #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : 
!s32i, #cir.int<3> : !s32i, #cir.int<4> : !s32i, #cir.int<5> : !s32i, 
#cir.int<6> : !s32i, #cir.int<7> : !s32i, #cir.int<8> : !s32i, #cir.int<9> : 
!s32i, #cir.int<10> : !s32i, #cir.int<11> : !s32i, #cir.int<12> : !s32i, 
#cir.int<13> : !s32i, #cir.int<14> : !s32i, #cir.int<15> : !s32i, #cir.int<16> 
: !s32i, #cir.int<17> : !s32i, #cir.int<18> : !s32i, #cir.int<19> : !s32i, 
#cir.int<20> : !s32i, #cir.int<21> : !s32i, #cir.int<22> : !s32i, #cir.int<23> 
: !s32i, #cir.int<24> : !s32i, #cir.int<25> : !s32i, #cir.int<26> : !s32i, 
#cir.int<27> : !s32i, #cir.int<28> : !s32i, #cir.int<29> : !s32i, #cir.int<30> 
: !s32i, #cir.int<31> : !s32i, #cir.int<32> : !s32i, #cir.int<33> : !s32i, 
#cir.int<34> : !s32i, #cir.int<35> : !s32i, #cir.int<36> : !s32i, #cir.int<37> 
: !s32i, #cir.int<38> : !s32i, #cir.int<39> : !s32i, #cir.int<40> : !s32i, 
#cir.int<41> : !s32i, #cir.int<42> : !s32i, #cir.int<43> : !s32i, #cir.int<44> 
: !s32i, #cir.int<45> : !s32i, #cir.int<46> : !s32i, #cir.int<47> : !s32i, 
#cir.int<48> : !s32i, #cir.int<49> : !s32i, #cir.int<50> : !s32i, #cir.int<51> 
: !s32i, #cir.int<52> : !s32i, #cir.int<53> : !s32i, #cir.int<54> : !s32i, 
#cir.int<55> : !s32i, #cir.int<56> : !s32i, #cir.int<57> : !s32i, #cir.int<58> 
: !s32i, #cir.int<59> : !s32i, #cir.int<60> : !s32i, #cir.int<61> : !s32i, 
#cir.int<62> : !s32i, #cir.int<63> : !s32i, #cir.int<64> : !s32i, #cir.int<65> 
: !s32i, #cir.int<66> : !s32i, #cir.int<67> : !s32i, #cir.int<68> : !s32i, 
#cir.int<69> : !s32i, #cir.int<70> : !s32i, #cir.int<71> : !s32i, #cir.int<72> 
: !s32i, #cir.int<73> : !s32i, #cir.int<74> : !s32i, #cir.int<75> : !s32i, 
#cir.int<76> : !s32i, #cir.int<77> : !s32i, #cir.int<78> : !s32i, #cir.int<79> 
: !s32i, #cir.int<80> : !s32i, #cir.int<81> : !s32i, #cir.int<82> : !s32i, 
#cir.int<83> : !s32i, #cir.int<84> : !s32i, #cir.int<85> : !s32i, #cir.int<86> 
: !s32i, #cir.int<87> : !s32i, #cir.int<88> : !s32i, #cir.int<89> : !s32i, 
#cir.int<90> : !s32i, #cir.int<91> : !s32i, #cir.int<92> : !s32i, #cir.int<93> 
: !s32i, #cir.int<94> : !s32i, #cir.int<95> : !s32i, #cir.int<96> : !s32i, 
#cir.int<97> : !s32i, #cir.int<98> : !s32i, #cir.int<99> : !s32i, #cir.int<100> 
: !s32i], trailing_zeros> : !cir.array<!s32i x 108>}> : !rec_PLarge
   // CHECK-LABEL: llvm.mlir.global external @p_large()
   // CHECK-NOT: llvm.insertvalue {{.*}} : !llvm.array<100 x i32>
+  // CHECK: %[[ZERO:.*]] = llvm.mlir.zero : !llvm.struct<packed (array<100 x 
i32>, array<8 x i32>)>
   // CHECK: %[[PREFIX:.*]] = llvm.mlir.constant(dense<[1, 2, 3, {{.*}}, 99, 
100]> : tensor<100xi32>) : !llvm.array<100 x i32>
   // CHECK-NOT: llvm.insertvalue {{.*}} : !llvm.array<100 x i32>
-  // CHECK: llvm.insertvalue %[[PREFIX]], %{{.*}}[0] : !llvm.struct<packed 
(array<100 x i32>, array<8 x i32>)>
+  // CHECK: llvm.insertvalue %[[PREFIX]], %[[ZERO]][0] : !llvm.struct<packed 
(array<100 x i32>, array<8 x i32>)>
   // CHECK-NOT: llvm.insertvalue {{.*}} : !llvm.array<100 x i32>
   // CHECK: llvm.return
 

>From 9569f5997211e5bf7bd0049ea0e2caa205bf410c Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <[email protected]>
Date: Fri, 16 Oct 2026 15:31:11 -0700
Subject: [PATCH 5/6] llvm.mlir.constant rejects only struct-typed constants
 with non-scalar fields; an array of structs with nested element lists is
 fine. So take the shortcut whenever the adjusted type is still an array.

---
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 18 ++++++++-------
 .../Lowering/const-array-trailing-zeros.cir   | 22 +++++++++++++++++++
 2 files changed, 32 insertions(+), 8 deletions(-)

diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 703eb7c56c2724..57e66869a4d0c3 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -911,19 +911,21 @@ mlir::Value 
CIRAttrToValue::visitCirAttr(cir::BlockAddrDiffAttr blockAddrDiff) {
 // ConstArrayAttr visitor
 mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstArrayAttr attr) {
   mlir::Type convertedTy = converter->convertType(attr.getType());
-  mlir::DataLayout dataLayout(parentOp->getParentOfType<mlir::ModuleOp>());
+  auto moduleOp = parentOp->getParentOfType<mlir::ModuleOp>();
+  mlir::DataLayout dataLayout(moduleOp);
   mlir::Type llvmTy =
       adjustGlobalTypeForInit(convertedTy, attr, *converter, dataLayout);
   mlir::Location loc = parentOp->getLoc();
   mlir::Value result;
 
-  // When the array can be represented as a single dense constant, emit one
-  // llvm.mlir.constant instead of a chain of llvm.insertvalue ops. A dense
-  // constant has the converted type, so it cannot take an adjusted shape.
-  if (llvmTy == convertedTy)
-    if (std::optional<mlir::Attribute> denseAttr =
-            lowerConstArrayAttr(attr, symbolTables, converter))
-      return mlir::LLVM::ConstantOp::create(rewriter, loc, llvmTy, *denseAttr);
+  // When the array can be represented as a single constant attribute, emit one
+  // llvm.mlir.constant instead of a chain of llvm.insertvalue ops. That works
+  // for any array type, including an array of split rows, but not for a split
+  // array itself: a struct-typed constant needs scalar fields.
+  if (mlir::isa<mlir::LLVM::LLVMArrayType>(llvmTy))
+    if (std::optional<mlir::Attribute> constAttr =
+            lowerConstArrayAttr(attr, symbolTables, converter, moduleOp))
+      return mlir::LLVM::ConstantOp::create(rewriter, loc, llvmTy, *constAttr);
 
   if (attr.hasTrailingZeros())
     result = mlir::LLVM::ZeroOp::create(rewriter, loc, llvmTy);
diff --git a/clang/test/CIR/Lowering/const-array-trailing-zeros.cir 
b/clang/test/CIR/Lowering/const-array-trailing-zeros.cir
index 18aafbe41397af..3c34831700e999 100644
--- a/clang/test/CIR/Lowering/const-array-trailing-zeros.cir
+++ b/clang/test/CIR/Lowering/const-array-trailing-zeros.cir
@@ -4,6 +4,8 @@
 !rec_S = !cir.struct<"S" {data !cir.array<!s32i x 10>}>
 !rec_P = !cir.struct<"P" {data !cir.ptr<!s32i>, data !cir.array<!s32i x 16>}>
 !rec_PLarge = !cir.struct<"PLarge" {data !cir.ptr<!s32i>, data 
!cir.array<!s32i x 108>}>
+!rec_PRows = !cir.struct<"PRows" {data !cir.ptr<!s32i>, data 
!cir.array<!cir.array<!s32i x 10> x 3>}>
+!rec_PRowsLarge = !cir.struct<"PRowsLarge" {data !cir.ptr<!s32i>, data 
!cir.array<!cir.array<!s32i x 10> x 100>}>
 
 // A global initializer ending in many zeros lowers to a packed struct with a
 // zero-initialized tail, but a cir.const keeps its array type.
@@ -36,6 +38,26 @@ module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
   // CHECK-NOT: llvm.insertvalue {{.*}} : !llvm.array<100 x i32>
   // CHECK: llvm.return
 
+  // Rows that each split make the enclosing array an array of packed structs.
+  // That array is still one constant, not one insertvalue per row.
+  cir.global external @p_rows = #cir.const_record<{#cir.global_view<@arr, [1 : 
i32]> : !cir.ptr<!s32i>, #cir.const_array<[#cir.const_array<[#cir.int<1> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<2> 
: !s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<3> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>]> : !cir.array<!cir.array<!s32i x 10> x 3>}> : !rec_PRows
+  // CHECK-LABEL: llvm.mlir.global external @p_rows()
+  // CHECK-NOT: llvm.insertvalue {{.*}} : !llvm.{{(array<3 x )?}}struct<packed 
(i32, array<9 x i32>)>
+  // CHECK: %[[ROWS:.*]] = llvm.mlir.constant({{\[\[}}1 : i32, #llvm.zero], [2 
: i32, #llvm.zero], [3 : i32, #llvm.zero]]) : !llvm.array<3 x struct<packed 
(i32, array<9 x i32>)>>
+  // CHECK-NOT: llvm.insertvalue {{.*}} : !llvm.{{(array<3 x )?}}struct<packed 
(i32, array<9 x i32>)>
+  // CHECK: llvm.insertvalue %[[ROWS]], %{{.*}}[1] : !llvm.struct<(ptr, 
array<3 x struct<packed (i32, array<9 x i32>)>>)>
+  // CHECK-NOT: llvm.insertvalue {{.*}} : !llvm.{{(array<3 x )?}}struct<packed 
(i32, array<9 x i32>)>
+  // CHECK: llvm.return
+
+  cir.global external @p_rows_large = 
#cir.const_record<{#cir.global_view<@arr, [1 : i32]> : !cir.ptr<!s32i>, 
#cir.const_array<[#cir.const_array<[#cir.int<1> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<2> : !s32i], trailing_zeros> 
: !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<3> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<4> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<5> 
: !s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<6> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<7> : !s32i], trailing_zeros> : !cir.array<!s32i 
x 10>, #cir.const_array<[#cir.int<8> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<9> : !s32i], trailing_zeros> 
: !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<10> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<11> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<12> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<13> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<14> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<15> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<16> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<17> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<18> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<19> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<20> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<21> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<22> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<23> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<24> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<25> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<26> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<27> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<28> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<29> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<30> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<31> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<32> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<33> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<34> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<35> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<36> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<37> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<38> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<39> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<40> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<41> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<42> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<43> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<44> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<45> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<46> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<47> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<48> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<49> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<50> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<51> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<52> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<53> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<54> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<55> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<56> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<57> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<58> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<59> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<60> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<61> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<62> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<63> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<64> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<65> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<66> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<67> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<68> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<69> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<70> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<71> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<72> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<73> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<74> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<75> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<76> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<77> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<78> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<79> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<80> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<81> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<82> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<83> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<84> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<85> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<86> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<87> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<88> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<89> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<90> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<91> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<92> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<93> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<94> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<95> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<96> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>, #cir.const_array<[#cir.int<97> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 10>, #cir.const_array<[#cir.int<98> : !s32i], 
trailing_zeros> : !cir.array<!s32i x 10>, #cir.const_array<[#cir.int<99> : 
!s32i], trailing_zeros> : !cir.array<!s32i x 10>, 
#cir.const_array<[#cir.int<100> : !s32i], trailing_zeros> : !cir.array<!s32i x 
10>]> : !cir.array<!cir.array<!s32i x 10> x 100>}> : !rec_PRowsLarge
+  // CHECK-LABEL: llvm.mlir.global external @p_rows_large()
+  // CHECK-NOT: llvm.insertvalue {{.*}} : !llvm.{{(array<100 x 
)?}}struct<packed (i32, array<9 x i32>)>
+  // CHECK: %[[ROWS:.*]] = llvm.mlir.constant({{\[\[}}1 : i32, #llvm.zero], [2 
: i32, #llvm.zero], {{.*}}, [100 : i32, #llvm.zero]]) : !llvm.array<100 x 
struct<packed (i32, array<9 x i32>)>>
+  // CHECK-NOT: llvm.insertvalue {{.*}} : !llvm.{{(array<100 x 
)?}}struct<packed (i32, array<9 x i32>)>
+  // CHECK: llvm.insertvalue %[[ROWS]], %{{.*}}[1] : !llvm.struct<(ptr, 
array<100 x struct<packed (i32, array<9 x i32>)>>)>
+  // CHECK-NOT: llvm.insertvalue {{.*}} : !llvm.{{(array<100 x 
)?}}struct<packed (i32, array<9 x i32>)>
+  // CHECK: llvm.return
+
   // Without the address, the initializer is a single attribute, and the
   // grouped explicit elements are one dense attribute in it.
   cir.global external @q = #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> 
: !s32i, #cir.int<3> : !s32i, #cir.int<4> : !s32i, #cir.int<5> : !s32i, 
#cir.int<6> : !s32i, #cir.int<7> : !s32i, #cir.int<8> : !s32i], trailing_zeros> 
: !cir.array<!s32i x 16>

>From e7a988f3ec283b892f53908267dabce5c91ae199 Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <[email protected]>
Date: Fri, 16 Oct 2026 18:48:37 -0700
Subject: [PATCH 6/6] Add fixme comment

---
 clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 57e66869a4d0c3..69ea87baf38cca 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -938,6 +938,12 @@ mlir::Value 
CIRAttrToValue::visitCirAttr(cir::ConstArrayAttr attr) {
     // elements of an array ending in many zeros into one leading array. That
     // array has no trailing zeros, so it keeps its type and still lowers as
     // one dense constant.
+    //
+    // FIXME: A split array itself is a struct, which llvm.mlir.constant cannot
+    // hold with aggregate fields, so on this path each split array costs
+    // insertvalue ops. With many split arrays that cannot be folded into an
+    // enclosing constant, that may be slower than lowering them unsplit as
+    // dense arrays; consider a heuristic choosing between the two.
     auto structTy = mlir::dyn_cast<mlir::LLVM::LLVMStructType>(llvmTy);
     if (structTy && structTy.getBody().size() < arrayAttr.size()) {
       auto arrayTy = mlir::cast<cir::ArrayType>(attr.getType());

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to