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

>From 9814dc290952184c3a1080ea49ddd29b603ecdae Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Wed, 16 Jul 2025 18:48:52 +0200
Subject: [PATCH 1/6] [CIR] Add support for array constructors

This patch upstreams support for creating arrays of classes that require 
calling a constructor.

* Adds the ArrayCtor operation
* New lowering pass for lowering ArrayCtor to a loop
---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  |  38 +++++-
 .../CIR/Dialect/IR/CIRTypeConstraints.td      |   8 ++
 clang/lib/CIR/CodeGen/CIRGenClass.cpp         | 110 ++++++++++++++++++
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp          |  59 +++++-----
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp      |  45 +++++++
 clang/lib/CIR/CodeGen/CIRGenFunction.h        |  12 ++
 .../Dialect/Transforms/LoweringPrepare.cpp    |  78 ++++++++++++-
 clang/test/CIR/CodeGen/array-ctor.cpp         |  70 +++++++++++
 clang/test/CIR/IR/array-ctor.cir              |  29 +++++
 9 files changed, 417 insertions(+), 32 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/array-ctor.cpp
 create mode 100644 clang/test/CIR/IR/array-ctor.cir

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 694e3691c9361..096537fc78839 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -607,7 +607,7 @@ def CIR_ConditionOp : CIR_Op<"condition", [
 
//===----------------------------------------------------------------------===//
 
 defvar CIR_YieldableScopes = [
-  "CaseOp", "DoWhileOp", "ForOp", "IfOp", "ScopeOp", "SwitchOp",
+  "ArrayCtor", "CaseOp", "DoWhileOp", "ForOp", "IfOp", "ScopeOp", "SwitchOp",
   "TernaryOp", "WhileOp"
 ];
 
@@ -2228,6 +2228,42 @@ def CIR_TrapOp : CIR_Op<"trap", [Terminator]> {
   let assemblyFormat = "attr-dict";
 }
 
+//===----------------------------------------------------------------------===//
+// ArrayCtor
+//===----------------------------------------------------------------------===//
+
+class CIR_ArrayInitDestroy<string mnemonic> : CIR_Op<mnemonic> {
+  let arguments = (ins
+    Arg<CIR_PtrToArray, "array address", [MemWrite, MemRead]>:$addr
+  );
+
+  let regions = (region SizedRegion<1>:$body);
+  let assemblyFormat = [{
+    `(` $addr `:` qualified(type($addr)) `)` $body attr-dict
+  }];
+
+  let builders = [
+    OpBuilder<(ins "mlir::Value":$addr,
+      "llvm::function_ref<void(mlir::OpBuilder &, 
mlir::Location)>":$regionBuilder), [{
+        assert(regionBuilder && "builder callback expected");
+        mlir::OpBuilder::InsertionGuard guard($_builder);
+        mlir::Region *r = $_state.addRegion();
+        $_state.addOperands(ValueRange{addr});
+        $_builder.createBlock(r);
+        regionBuilder($_builder, $_state.location);
+    }]>
+  ];
+}
+
+def CIR_ArrayCtor : CIR_ArrayInitDestroy<"array.ctor"> {
+  let summary = "Initialize array elements with C++ constructors";
+  let description = [{
+    Initialize each array element using the same C++ constructor. This
+    operation has one region, with one single block. The block has an
+    incoming argument for the current array index to initialize.
+  }];
+}
+
 
//===----------------------------------------------------------------------===//
 // VecCreate
 
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td 
b/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
index 2bf77583465a6..d7d55dfbc0654 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
@@ -165,6 +165,12 @@ def CIR_AnyIntOrFloatType : AnyTypeOf<[CIR_AnyFloatType, 
CIR_AnyIntType],
 
 def CIR_AnyComplexType : CIR_TypeBase<"::cir::ComplexType", "complex type">;
 
+//===----------------------------------------------------------------------===//
+// Array Type predicates
+//===----------------------------------------------------------------------===//
+
+def CIR_AnyArrayType : CIR_TypeBase<"::cir::ArrayType", "array type">;
+
 
//===----------------------------------------------------------------------===//
 // Pointer Type predicates
 
//===----------------------------------------------------------------------===//
@@ -216,6 +222,8 @@ def CIR_PtrToIntOrFloatType : 
CIR_PtrToType<CIR_AnyIntOrFloatType>;
 
 def CIR_PtrToComplexType : CIR_PtrToType<CIR_AnyComplexType>;
 
+def CIR_PtrToArray : CIR_PtrToType<CIR_AnyArrayType>;
+
 
//===----------------------------------------------------------------------===//
 // Vector Type predicates
 
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp 
b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index fbf53dbdf385b..5a661bf11b114 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -12,6 +12,7 @@
 
 #include "CIRGenCXXABI.h"
 #include "CIRGenFunction.h"
+#include "CIRGenValue.h"
 
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/RecordLayout.h"
@@ -311,6 +312,115 @@ void CIRGenFunction::emitInitializerForField(FieldDecl 
*field, LValue lhs,
   assert(!cir::MissingFeatures::requiresCleanups());
 }
 
+/// Emit a loop to call a particular constructor for each of several members
+/// of an array.
+///
+/// \param ctor the constructor to call for each element
+/// \param arrayType the type of the array to initialize
+/// \param arrayBegin an arrayType*
+/// \param zeroInitialize true if each element should be
+///   zero-initialized before it is constructed
+void CIRGenFunction::emitCXXAggrConstructorCall(
+    const CXXConstructorDecl *ctor, const clang::ArrayType *arrayType,
+    Address arrayBegin, const CXXConstructExpr *e, bool newPointerIsChecked,
+    bool zeroInitialize) {
+  QualType elementType;
+  mlir::Value numElements = emitArrayLength(arrayType, elementType, 
arrayBegin);
+  emitCXXAggrConstructorCall(ctor, numElements, arrayBegin, e,
+                             newPointerIsChecked, zeroInitialize);
+}
+
+/// Emit a loop to call a particular constructor for each of several members
+/// of an array.
+///
+/// \param ctor the constructor to call for each element
+/// \param numElements the number of elements in the array;
+///   may be zero
+/// \param arrayBase a T*, where T is the type constructed by ctor
+/// \param zeroInitialize true if each element should be
+///   zero-initialized before it is constructed
+void CIRGenFunction::emitCXXAggrConstructorCall(
+    const CXXConstructorDecl *ctor, mlir::Value numElements, Address arrayBase,
+    const CXXConstructExpr *e, bool newPointerIsChecked, bool zeroInitialize) {
+  // It's legal for numElements to be zero.  This can happen both
+  // dynamically, because x can be zero in 'new A[x]', and statically,
+  // because of GCC extensions that permit zero-length arrays.  There
+  // are probably legitimate places where we could assume that this
+  // doesn't happen, but it's not clear that it's worth it.
+
+  // Optimize for a constant count.
+  auto constantCount = dyn_cast<cir::ConstantOp>(numElements.getDefiningOp());
+  if (constantCount) {
+    auto constIntAttr = mlir::dyn_cast<cir::IntAttr>(constantCount.getValue());
+    // Just skip out if the constant count is zero.
+    if (constIntAttr && constIntAttr.getUInt() == 0)
+      return;
+    // Otherwise, emit the check.
+  } else {
+    cgm.errorNYI(e->getSourceRange(), "dynamic-length array expression");
+  }
+
+  auto arrayTy = mlir::dyn_cast<cir::ArrayType>(arrayBase.getElementType());
+  assert(arrayTy && "expected array type");
+  mlir::Type elementType = arrayTy.getElementType();
+  cir::PointerType ptrToElmType = builder.getPointerTo(elementType);
+
+  // Tradional LLVM codegen emits a loop here. CIR lowers to a loop as part of
+  // LoweringPrepare.
+
+  // The alignment of the base, adjusted by the size of a single element,
+  // provides a conservative estimate of the alignment of every element.
+  // (This assumes we never start tracking offsetted alignments.)
+  //
+  // Note that these are complete objects and so we don't need to
+  // use the non-virtual size or alignment.
+  QualType type = getContext().getTypeDeclType(ctor->getParent());
+  CharUnits eltAlignment = arrayBase.getAlignment().alignmentOfArrayElement(
+      getContext().getTypeSizeInChars(type));
+
+  // Zero initialize the storage, if requested.
+  if (zeroInitialize)
+    emitNullInitialization(*currSrcLoc, arrayBase, type);
+
+  // C++ [class.temporary]p4:
+  // There are two contexts in which temporaries are destroyed at a different
+  // point than the end of the full-expression. The first context is when a
+  // default constructor is called to initialize an element of an array.
+  // If the constructor has one or more default arguments, the destruction of
+  // every temporary created in a default argument expression is sequenced
+  // before the construction of the next array element, if any.
+  {
+    assert(!cir::MissingFeatures::runCleanupsScope());
+
+    // Evaluate the constructor and its arguments in a regular
+    // partial-destroy cleanup.
+    if (getLangOpts().Exceptions &&
+        !ctor->getParent()->hasTrivialDestructor()) {
+      cgm.errorNYI(e->getSourceRange(), "partial array cleanups");
+    }
+
+    // Emit the constructor call that will execute for every array element.
+    auto arrayOp = builder.createPtrBitcast(arrayBase.getPointer(), arrayTy);
+    builder.create<cir::ArrayCtor>(
+        *currSrcLoc, arrayOp, [&](mlir::OpBuilder &b, mlir::Location loc) {
+          auto arg = b.getInsertionBlock()->addArgument(ptrToElmType, loc);
+          Address curAddr = Address(arg, elementType, eltAlignment);
+          assert(!cir::MissingFeatures::sanitizers());
+          auto currAVS = AggValueSlot::forAddr(
+              curAddr, type.getQualifiers(), AggValueSlot::IsDestructed,
+              AggValueSlot::IsNotAliased, AggValueSlot::DoesNotOverlap,
+              AggValueSlot::IsNotZeroed);
+          emitCXXConstructorCall(ctor, Ctor_Complete,
+                                 /*ForVirtualBase=*/false,
+                                 /*Delegating=*/false, currAVS, e);
+          builder.create<cir::YieldOp>(loc);
+        });
+  }
+
+  if (constantCount.use_empty())
+    constantCount.erase();
+}
+
 void CIRGenFunction::emitDelegateCXXConstructorCall(
     const CXXConstructorDecl *ctor, CXXCtorType ctorType,
     const FunctionArgList &args, SourceLocation loc) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 1f64801926887..7ff5f26be21b4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1657,37 +1657,38 @@ void CIRGenFunction::emitCXXConstructExpr(const 
CXXConstructExpr *e,
     return;
   }
 
-  if (getContext().getAsArrayType(e->getType())) {
-    cgm.errorNYI(e->getSourceRange(), "emitCXXConstructExpr: array type");
-    return;
-  }
+  if (const ArrayType *arrayType = getContext().getAsArrayType(e->getType())) {
+    assert(!cir::MissingFeatures::sanitizers());
+    emitCXXAggrConstructorCall(cd, arrayType, dest.getAddress(), e, false);
+  } else {
 
-  clang::CXXCtorType type = Ctor_Complete;
-  bool forVirtualBase = false;
-  bool delegating = false;
-
-  switch (e->getConstructionKind()) {
-  case CXXConstructionKind::Complete:
-    type = Ctor_Complete;
-    break;
-  case CXXConstructionKind::Delegating:
-    // We should be emitting a constructor; GlobalDecl will assert this
-    type = curGD.getCtorType();
-    delegating = true;
-    break;
-  case CXXConstructionKind::VirtualBase:
-    // This should just set 'forVirtualBase' to true and fall through, but
-    // virtual base class support is otherwise missing, so this needs to wait
-    // until it can be tested.
-    cgm.errorNYI(e->getSourceRange(),
-                 "emitCXXConstructExpr: virtual base constructor");
-    return;
-  case CXXConstructionKind::NonVirtualBase:
-    type = Ctor_Base;
-    break;
-  }
+    clang::CXXCtorType type = Ctor_Complete;
+    bool forVirtualBase = false;
+    bool delegating = false;
 
-  emitCXXConstructorCall(cd, type, forVirtualBase, delegating, dest, e);
+    switch (e->getConstructionKind()) {
+    case CXXConstructionKind::Complete:
+      type = Ctor_Complete;
+      break;
+    case CXXConstructionKind::Delegating:
+      // We should be emitting a constructor; GlobalDecl will assert this
+      type = curGD.getCtorType();
+      delegating = true;
+      break;
+    case CXXConstructionKind::VirtualBase:
+      // This should just set 'forVirtualBase' to true and fall through, but
+      // virtual base class support is otherwise missing, so this needs to wait
+      // until it can be tested.
+      cgm.errorNYI(e->getSourceRange(),
+                   "emitCXXConstructExpr: virtual base constructor");
+      return;
+    case CXXConstructionKind::NonVirtualBase:
+      type = Ctor_Base;
+      break;
+    }
+
+    emitCXXConstructorCall(cd, type, forVirtualBase, delegating, dest, e);
+  }
 }
 
 RValue CIRGenFunction::emitReferenceBindingToExpr(const Expr *e) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp 
b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 3e69e5673dd86..8e073ef22cc4e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -808,4 +808,49 @@ bool CIRGenFunction::shouldNullCheckClassCastValue(const 
CastExpr *ce) {
   return true;
 }
 
+/// Computes the length of an array in elements, as well as the base
+/// element type and a properly-typed first element pointer.
+mlir::Value
+CIRGenFunction::emitArrayLength(const clang::ArrayType *origArrayType,
+                                QualType &baseType, Address &addr) {
+  const clang::ArrayType *arrayType = origArrayType;
+
+  // If it's a VLA, we have to load the stored size.  Note that
+  // this is the size of the VLA in bytes, not its size in elements.
+  if (isa<VariableArrayType>(arrayType)) {
+    cgm.errorNYI(*currSrcLoc, "VLAs");
+    return builder.getConstInt(*currSrcLoc, SizeTy, 0);
+  }
+
+  uint64_t countFromCLAs = 1;
+  QualType eltType;
+
+  auto cirArrayType = mlir::dyn_cast<cir::ArrayType>(addr.getElementType());
+
+  while (cirArrayType) {
+    assert(isa<ConstantArrayType>(arrayType));
+    countFromCLAs *= cirArrayType.getSize();
+    eltType = arrayType->getElementType();
+
+    cirArrayType =
+        mlir::dyn_cast<cir::ArrayType>(cirArrayType.getElementType());
+
+    arrayType = getContext().getAsArrayType(arrayType->getElementType());
+    assert((!cirArrayType || arrayType) &&
+           "CIR and Clang types are out-of-sync");
+  }
+
+  if (arrayType) {
+    // From this point onwards, the Clang array type has been emitted
+    // as some other type (probably a packed struct). Compute the array
+    // size, and just emit the 'begin' expression as a bitcast.
+    cgm.errorNYI(*currSrcLoc, "length for non-array underlying types");
+  }
+
+  baseType = eltType;
+  auto numElements = builder.getConstInt(*currSrcLoc, SizeTy, countFromCLAs);
+
+  return numElements;
+}
+
 } // namespace clang::CIRGen
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h 
b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 2aceeef793385..83e7f63773c3c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -766,6 +766,8 @@ class CIRGenFunction : public CIRGenTypeCache {
   /// even if no aggregate location is provided.
   RValue emitAnyExprToTemp(const clang::Expr *e);
 
+  mlir::Value emitArrayLength(const clang::ArrayType *arrayType,
+                              QualType &baseType, Address &addr);
   LValue emitArraySubscriptExpr(const clang::ArraySubscriptExpr *e);
 
   Address emitArrayToPointerDecay(const Expr *array);
@@ -843,6 +845,16 @@ class CIRGenFunction : public CIRGenTypeCache {
   void emitCXXConstructExpr(const clang::CXXConstructExpr *e,
                             AggValueSlot dest);
 
+  void emitCXXAggrConstructorCall(const CXXConstructorDecl *ctor,
+                                  const clang::ArrayType *arrayType,
+                                  Address arrayBegin, const CXXConstructExpr 
*e,
+                                  bool newPointerIsChecked,
+                                  bool zeroInitialize = false);
+  void emitCXXAggrConstructorCall(const CXXConstructorDecl *ctor,
+                                  mlir::Value numElements, Address arrayBase,
+                                  const CXXConstructExpr *e,
+                                  bool newPointerIsChecked,
+                                  bool zeroInitialize);
   void emitCXXConstructorCall(const clang::CXXConstructorDecl *d,
                               clang::CXXCtorType type, bool forVirtualBase,
                               bool delegating, AggValueSlot thisAVS,
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp 
b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 8f848c7345610..3f0291729c0f5 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -8,6 +8,7 @@
 
 #include "PassDetail.h"
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/CharUnits.h"
 #include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
 #include "clang/CIR/Dialect/IR/CIROpsEnums.h"
@@ -25,6 +26,7 @@ struct LoweringPreparePass : public 
LoweringPrepareBase<LoweringPreparePass> {
 
   void runOnOp(mlir::Operation *op);
   void lowerUnaryOp(cir::UnaryOp op);
+  void lowerArrayCtor(ArrayCtor op);
 };
 
 } // namespace
@@ -71,9 +73,81 @@ void LoweringPreparePass::lowerUnaryOp(cir::UnaryOp op) {
   op.erase();
 }
 
+static void lowerArrayDtorCtorIntoLoop(CIRBaseBuilderTy &builder,
+                                       mlir::Operation *op, mlir::Type eltTy,
+                                       mlir::Value arrayAddr,
+                                       uint64_t arrayLen) {
+  // Generate loop to call into ctor/dtor for every element.
+  Location loc = op->getLoc();
+
+  // TODO: instead of fixed integer size, create alias for PtrDiffTy and unify
+  // with CIRGen stuff.
+  auto ptrDiffTy =
+      cir::IntType::get(builder.getContext(), 64, /*isSigned=*/false);
+  auto numArrayElementsConst = builder.create<cir::ConstantOp>(
+      loc, ptrDiffTy, cir::IntAttr::get(ptrDiffTy, arrayLen));
+
+  auto begin = builder.create<cir::CastOp>(
+      loc, eltTy, cir::CastKind::array_to_ptrdecay, arrayAddr);
+  mlir::Value end = builder.create<cir::PtrStrideOp>(loc, eltTy, begin,
+                                                     numArrayElementsConst);
+
+  mlir::Value tmpAddr = builder.createAlloca(
+      loc, /*addr type*/ builder.getPointerTo(eltTy),
+      /*var type*/ eltTy, "__array_idx", builder.getAlignmentAttr(1));
+  builder.createStore(loc, begin, tmpAddr);
+
+  cir::DoWhileOp loop = builder.createDoWhile(
+      loc,
+      /*condBuilder=*/
+      [&](mlir::OpBuilder &b, mlir::Location loc) {
+        auto currentElement = b.create<cir::LoadOp>(loc, eltTy, tmpAddr);
+        mlir::Type boolTy = cir::BoolType::get(b.getContext());
+        auto cmp = builder.create<cir::CmpOp>(loc, boolTy, cir::CmpOpKind::eq,
+                                              currentElement, end);
+        builder.createCondition(cmp);
+      },
+      /*bodyBuilder=*/
+      [&](mlir::OpBuilder &b, mlir::Location loc) {
+        auto currentElement = b.create<cir::LoadOp>(loc, eltTy, tmpAddr);
+
+        CallOp ctorCall;
+        op->walk([&](CallOp c) { ctorCall = c; });
+        assert(ctorCall && "expected ctor call");
+
+        auto one = builder.create<cir::ConstantOp>(
+            loc, ptrDiffTy, cir::IntAttr::get(ptrDiffTy, 1));
+
+        ctorCall->moveAfter(one);
+        ctorCall->setOperand(0, currentElement);
+
+        // Advance pointer and store them to temporary variable
+        auto nextElement =
+            builder.create<cir::PtrStrideOp>(loc, eltTy, currentElement, one);
+        builder.createStore(loc, nextElement, tmpAddr);
+        builder.createYield(loc);
+      });
+
+  op->replaceAllUsesWith(loop);
+  op->erase();
+}
+
+void LoweringPreparePass::lowerArrayCtor(ArrayCtor op) {
+  CIRBaseBuilderTy builder(getContext());
+  builder.setInsertionPointAfter(op.getOperation());
+
+  Type eltTy = op->getRegion(0).getArgument(0).getType();
+  auto arrayLen =
+      
mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
+  lowerArrayDtorCtorIntoLoop(builder, op, eltTy, op.getAddr(), arrayLen);
+}
+
 void LoweringPreparePass::runOnOp(mlir::Operation *op) {
-  if (auto unary = dyn_cast<cir::UnaryOp>(op))
+  if (auto unary = dyn_cast<cir::UnaryOp>(op)) {
     lowerUnaryOp(unary);
+  } else if (auto arrayCtor = dyn_cast<ArrayCtor>(op)) {
+    lowerArrayCtor(arrayCtor);
+  }
 }
 
 void LoweringPreparePass::runOnOperation() {
@@ -82,7 +156,7 @@ void LoweringPreparePass::runOnOperation() {
   llvm::SmallVector<mlir::Operation *> opsToTransform;
 
   op->walk([&](mlir::Operation *op) {
-    if (mlir::isa<cir::UnaryOp>(op))
+    if (mlir::isa<cir::UnaryOp, ArrayCtor>(op))
       opsToTransform.push_back(op);
   });
 
diff --git a/clang/test/CIR/CodeGen/array-ctor.cpp 
b/clang/test/CIR/CodeGen/array-ctor.cpp
new file mode 100644
index 0000000000000..0a2661248f908
--- /dev/null
+++ b/clang/test/CIR/CodeGen/array-ctor.cpp
@@ -0,0 +1,70 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -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 -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 S {
+    S();
+};
+
+void foo() {
+    S s[42];
+}
+
+// CIR: cir.func dso_local @_Z3foov()
+// CIR:   %[[ARRAY:.*]] = cir.alloca !cir.array<!rec_S x 42>, 
!cir.ptr<!cir.array<!rec_S x 42>>, ["s", init]
+// CIR:   %[[CONST42:.*]] = cir.const #cir.int<42> : !u64i
+// CIR:   %[[DECAY:.*]] = cir.cast(array_to_ptrdecay, %[[ARRAY]] : 
!cir.ptr<!cir.array<!rec_S x 42>>), !cir.ptr<!rec_S>
+// CIR:   %[[END_PTR:.*]] = cir.ptr_stride(%[[DECAY]] : !cir.ptr<!rec_S>, 
%[[CONST42]] : !u64i), !cir.ptr<!rec_S>
+// CIR:   %[[ITER:.*]] = cir.alloca !cir.ptr<!rec_S>, 
!cir.ptr<!cir.ptr<!rec_S>>, ["__array_idx"]
+// CIR:   cir.store %[[DECAY]], %[[ITER]] : !cir.ptr<!rec_S>, 
!cir.ptr<!cir.ptr<!rec_S>>
+// CIR:   cir.do {
+// CIR:     %[[CURRENT:.*]] = cir.load %[[ITER]] : !cir.ptr<!cir.ptr<!rec_S>>, 
!cir.ptr<!rec_S>
+// CIR:     %[[CONST1:.*]] = cir.const #cir.int<1> : !u64i
+// CIR:     cir.call @_ZN1SC1Ev(%[[CURRENT]]) : (!cir.ptr<!rec_S>) -> ()
+// CIR:     %[[NEXT:.*]] = cir.ptr_stride(%[[CURRENT]] : !cir.ptr<!rec_S>, 
%[[CONST1]] : !u64i), !cir.ptr<!rec_S>
+// CIR:     cir.store %[[NEXT]], %[[ITER]] : !cir.ptr<!rec_S>, 
!cir.ptr<!cir.ptr<!rec_S>>
+// CIR:     cir.yield
+// CIR:   } while {
+// CIR:     %[[CURRENT2:.*]] = cir.load %[[ITER]] : 
!cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
+// CIR:     %[[CMP:.*]] = cir.cmp(eq, %[[CURRENT2]], %[[END_PTR]]) : 
!cir.ptr<!rec_S>, !cir.bool
+// CIR:     cir.condition(%[[CMP]])
+// CIR:   }
+// CIR:   cir.return
+// CIR: }
+
+// LLVM: define dso_local void @_Z3foov()
+// LLVM: %[[ARRAY:.*]] = alloca [42 x %struct.S]
+// LLVM: %[[START:.*]] = getelementptr %struct.S, ptr %[[ARRAY]], i32 0
+// LLVM: %[[END:.*]] = getelementptr %struct.S, ptr %[[START]], i64 42
+// LLVM: %[[ITER:.*]] = alloca ptr
+// LLVM: store ptr %[[START]], ptr %[[ITER]]
+// LLVM: br label %[[LOOP:.*]]
+// LLVM: [[COND:.*]]:
+// LLVM: %[[CURRENT_CHECK:.*]] = load ptr, ptr %[[ITER]]
+// LLVM: %[[DONE:.*]] = icmp eq ptr %[[CURRENT_CHECK]], %[[END]]
+// LLVM: br i1 %[[DONE]], label %[[LOOP]], label %[[EXIT:.*]]
+// LLVM: [[LOOP]]:
+// LLVM: %[[CURRENT:.*]] = load ptr, ptr %[[ITER]]
+// LLVM: call void @_ZN1SC1Ev(ptr %[[CURRENT]])
+// LLVM: %[[NEXT:.*]] = getelementptr %struct.S, ptr %[[CURRENT]], i64 1
+// LLVM: store ptr %[[NEXT]], ptr %[[ITER]]
+// LLVM: br label %[[COND]]
+// LLVM: [[EXIT]]:
+// LLVM: ret void
+
+// OGCG: define dso_local void @_Z3foov()
+// OGCG: %[[ARRAY:.*]] = alloca [42 x %struct.S]
+// OGCG: %[[START:.*]] = getelementptr{{.*}} %struct.S{{.*}}
+// OGCG: %[[END:.*]] = getelementptr{{.*}} %struct.S{{.*}} i64 42
+// OGCG: br label %[[LOOP:.*]]
+// OGCG: [[LOOP]]:
+// OGCG: %[[CURRENT:.*]] = phi ptr [ %[[START]], %{{.*}} ], [ %[[NEXT:.*]], 
%[[LOOP]] ]
+// OGCG: call void @_ZN1SC1Ev(ptr{{.*}})
+// OGCG: %[[NEXT]] = getelementptr{{.*}} %struct.S{{.*}} i64 1
+// OGCG: %[[DONE:.*]] = icmp eq ptr %[[NEXT]], %[[END]]
+// OGCG: br i1 %[[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
+// OGCG: [[EXIT]]:
+// OGCG: ret void
diff --git a/clang/test/CIR/IR/array-ctor.cir b/clang/test/CIR/IR/array-ctor.cir
new file mode 100644
index 0000000000000..a039d7d2ca6b0
--- /dev/null
+++ b/clang/test/CIR/IR/array-ctor.cir
@@ -0,0 +1,29 @@
+
+// RUN: cir-opt %s | FileCheck %s
+
+!u8i = !cir.int<u, 8>
+!rec_S = !cir.record<struct "S" padded {!u8i}>
+
+module {
+  cir.func private @_ZN1SC1Ev(!cir.ptr<!rec_S>)
+  cir.func dso_local @_Z3foov() {
+    %0 = cir.alloca !cir.array<!rec_S x 42>, !cir.ptr<!cir.array<!rec_S x 
42>>, ["s", init] {alignment = 16 : i64}
+    cir.array.ctor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) {
+    ^bb0(%arg0: !cir.ptr<!rec_S>):
+      cir.call @_ZN1SC1Ev(%arg0) : (!cir.ptr<!rec_S>) -> ()
+      cir.yield
+    }
+    cir.return
+  }
+
+  // CHECK: cir.func private @_ZN1SC1Ev(!cir.ptr<!rec_S>)
+  // CHECK: cir.func dso_local @_Z3foov() {
+  // CHECK:   %0 = cir.alloca !cir.array<!rec_S x 42>, 
!cir.ptr<!cir.array<!rec_S x 42>>, ["s", init] {alignment = 16 : i64}
+  // CHECK:   cir.array.ctor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) {
+  // CHECK:   ^bb0(%arg0: !cir.ptr<!rec_S>):
+  // CHECK:     cir.call @_ZN1SC1Ev(%arg0) : (!cir.ptr<!rec_S>) -> ()
+  // CHECK:     cir.yield
+  // CHECK:   }
+  // CHECK:   cir.return
+  // CHECK: }
+}

>From e3cd6304d87755b31c927e9fa10a79d04f39ba3a Mon Sep 17 00:00:00 2001
From: Morris Hafner <m...@users.noreply.github.com>
Date: Thu, 17 Jul 2025 15:06:16 +0200
Subject: [PATCH 2/6] Apply suggestions from code review

Co-authored-by: Andy Kaylor <akay...@nvidia.com>
Co-authored-by: Henrich Lauko <xla...@mail.muni.cz>
---
 clang/lib/CIR/CodeGen/CIRGenClass.cpp         |  5 ++---
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp      |  2 +-
 .../Dialect/Transforms/LoweringPrepare.cpp    | 19 ++++++++-----------
 3 files changed, 11 insertions(+), 15 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp 
b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 5a661bf11b114..6351ad418dd74 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -360,8 +360,7 @@ void CIRGenFunction::emitCXXAggrConstructorCall(
     cgm.errorNYI(e->getSourceRange(), "dynamic-length array expression");
   }
 
-  auto arrayTy = mlir::dyn_cast<cir::ArrayType>(arrayBase.getElementType());
-  assert(arrayTy && "expected array type");
+  auto arrayTy = mlir::cast<cir::ArrayType>(arrayBase.getElementType());
   mlir::Type elementType = arrayTy.getElementType();
   cir::PointerType ptrToElmType = builder.getPointerTo(elementType);
 
@@ -400,7 +399,7 @@ void CIRGenFunction::emitCXXAggrConstructorCall(
     }
 
     // Emit the constructor call that will execute for every array element.
-    auto arrayOp = builder.createPtrBitcast(arrayBase.getPointer(), arrayTy);
+    mlir::Value arrayOp = builder.createPtrBitcast(arrayBase.getPointer(), 
arrayTy);
     builder.create<cir::ArrayCtor>(
         *currSrcLoc, arrayOp, [&](mlir::OpBuilder &b, mlir::Location loc) {
           auto arg = b.getInsertionBlock()->addArgument(ptrToElmType, loc);
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp 
b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 8e073ef22cc4e..4abbf65cbfe01 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -848,7 +848,7 @@ CIRGenFunction::emitArrayLength(const clang::ArrayType 
*origArrayType,
   }
 
   baseType = eltType;
-  auto numElements = builder.getConstInt(*currSrcLoc, SizeTy, countFromCLAs);
+  cir::ConstantOp numElements = builder.getConstInt(*currSrcLoc, SizeTy, 
countFromCLAs);
 
   return numElements;
 }
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp 
b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 3f0291729c0f5..6a436b1841f05 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -73,19 +73,16 @@ void LoweringPreparePass::lowerUnaryOp(cir::UnaryOp op) {
   op.erase();
 }
 
-static void lowerArrayDtorCtorIntoLoop(CIRBaseBuilderTy &builder,
+static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
                                        mlir::Operation *op, mlir::Type eltTy,
                                        mlir::Value arrayAddr,
                                        uint64_t arrayLen) {
   // Generate loop to call into ctor/dtor for every element.
-  Location loc = op->getLoc();
+  mlir::Location loc = op->getLoc();
 
   // TODO: instead of fixed integer size, create alias for PtrDiffTy and unify
   // with CIRGen stuff.
-  auto ptrDiffTy =
-      cir::IntType::get(builder.getContext(), 64, /*isSigned=*/false);
-  auto numArrayElementsConst = builder.create<cir::ConstantOp>(
-      loc, ptrDiffTy, cir::IntAttr::get(ptrDiffTy, arrayLen));
+  cir::ConstantOp numArrayElementsConst = builder.getUnsignedInt(loc, 64, 
arrayLen);
 
   auto begin = builder.create<cir::CastOp>(
       loc, eltTy, cir::CastKind::array_to_ptrdecay, arrayAddr);
@@ -111,8 +108,8 @@ static void lowerArrayDtorCtorIntoLoop(CIRBaseBuilderTy 
&builder,
       [&](mlir::OpBuilder &b, mlir::Location loc) {
         auto currentElement = b.create<cir::LoadOp>(loc, eltTy, tmpAddr);
 
-        CallOp ctorCall;
-        op->walk([&](CallOp c) { ctorCall = c; });
+        cir::CallOp ctorCall;
+        op->walk([&](cir::CallOp c) { ctorCall = c; });
         assert(ctorCall && "expected ctor call");
 
         auto one = builder.create<cir::ConstantOp>(
@@ -132,11 +129,11 @@ static void lowerArrayDtorCtorIntoLoop(CIRBaseBuilderTy 
&builder,
   op->erase();
 }
 
-void LoweringPreparePass::lowerArrayCtor(ArrayCtor op) {
-  CIRBaseBuilderTy builder(getContext());
+void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) {
+  cir::CIRBaseBuilderTy builder(getContext());
   builder.setInsertionPointAfter(op.getOperation());
 
-  Type eltTy = op->getRegion(0).getArgument(0).getType();
+  mlir::Type eltTy = op->getRegion(0).getArgument(0).getType();
   auto arrayLen =
       
mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
   lowerArrayDtorCtorIntoLoop(builder, op, eltTy, op.getAddr(), arrayLen);

>From 6cb9af033d1ad4fff0d7218540ec26c0faef7c3b Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Thu, 17 Jul 2025 15:34:27 +0200
Subject: [PATCH 3/6] Fix compilation errors after applying suggestions

---
 .../CIR/Dialect/Builder/CIRBaseBuilder.h      |  6 +++++
 clang/include/clang/CIR/Dialect/Passes.h      |  1 +
 .../Dialect/Transforms/LoweringPrepare.cpp    | 25 +++++++++++++++++--
 clang/lib/CIR/Lowering/CIRPasses.cpp          |  2 +-
 4 files changed, 31 insertions(+), 3 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h 
b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index 25baf278bba38..5c04d59475b6a 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -75,6 +75,12 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
     return getConstant(loc, cir::IntAttr::get(ty, value));
   }
 
+  mlir::Value getUnsignedInt(mlir::Location loc, uint64_t val,
+                             unsigned numBits) {
+    auto type = cir::IntType::get(getContext(), numBits, /*isSigned=*/false);
+    return getConstAPInt(loc, type, llvm::APInt(numBits, val));
+  }
+
   // Creates constant null value for integral type ty.
   cir::ConstantOp getNullValue(mlir::Type ty, mlir::Location loc) {
     return getConstant(loc, getZeroInitAttr(ty));
diff --git a/clang/include/clang/CIR/Dialect/Passes.h 
b/clang/include/clang/CIR/Dialect/Passes.h
index 02210ec0a8336..7a202b1e04ef9 100644
--- a/clang/include/clang/CIR/Dialect/Passes.h
+++ b/clang/include/clang/CIR/Dialect/Passes.h
@@ -25,6 +25,7 @@ std::unique_ptr<Pass> createCIRFlattenCFGPass();
 std::unique_ptr<Pass> createCIRSimplifyPass();
 std::unique_ptr<Pass> createHoistAllocasPass();
 std::unique_ptr<Pass> createLoweringPreparePass();
+std::unique_ptr<Pass> createLoweringPreparePass(clang::ASTContext *astCtx);
 
 void populateCIRPreLoweringPasses(mlir::OpPassManager &pm);
 
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp 
b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 6a436b1841f05..30d3060013f67 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -27,6 +27,14 @@ struct LoweringPreparePass : public 
LoweringPrepareBase<LoweringPreparePass> {
   void runOnOp(mlir::Operation *op);
   void lowerUnaryOp(cir::UnaryOp op);
   void lowerArrayCtor(ArrayCtor op);
+
+  ///
+  /// AST related
+  /// -----------
+
+  clang::ASTContext *astCtx;
+
+  void setASTContext(clang::ASTContext *c) { astCtx = c; }
 };
 
 } // namespace
@@ -74,6 +82,7 @@ void LoweringPreparePass::lowerUnaryOp(cir::UnaryOp op) {
 }
 
 static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
+                                       clang::ASTContext *astCtx,
                                        mlir::Operation *op, mlir::Type eltTy,
                                        mlir::Value arrayAddr,
                                        uint64_t arrayLen) {
@@ -82,7 +91,11 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy 
&builder,
 
   // TODO: instead of fixed integer size, create alias for PtrDiffTy and unify
   // with CIRGen stuff.
-  cir::ConstantOp numArrayElementsConst = builder.getUnsignedInt(loc, 64, 
arrayLen);
+  const unsigned sizeTypeSize =
+      astCtx->getTypeSize(astCtx->getSignedSizeType());
+  auto ptrDiffTy =
+      cir::IntType::get(builder.getContext(), sizeTypeSize, 
/*isSigned=*/false);
+  mlir::Value numArrayElementsConst = builder.getUnsignedInt(loc, arrayLen, 
64);
 
   auto begin = builder.create<cir::CastOp>(
       loc, eltTy, cir::CastKind::array_to_ptrdecay, arrayAddr);
@@ -136,7 +149,8 @@ void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) 
{
   mlir::Type eltTy = op->getRegion(0).getArgument(0).getType();
   auto arrayLen =
       
mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
-  lowerArrayDtorCtorIntoLoop(builder, op, eltTy, op.getAddr(), arrayLen);
+  lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(),
+                             arrayLen);
 }
 
 void LoweringPreparePass::runOnOp(mlir::Operation *op) {
@@ -164,3 +178,10 @@ void LoweringPreparePass::runOnOperation() {
 std::unique_ptr<Pass> mlir::createLoweringPreparePass() {
   return std::make_unique<LoweringPreparePass>();
 }
+
+std::unique_ptr<Pass>
+mlir::createLoweringPreparePass(clang::ASTContext *astCtx) {
+  auto pass = std::make_unique<LoweringPreparePass>();
+  pass->setASTContext(astCtx);
+  return std::move(pass);
+}
diff --git a/clang/lib/CIR/Lowering/CIRPasses.cpp 
b/clang/lib/CIR/Lowering/CIRPasses.cpp
index 5607abc98e319..bb9781be897eb 100644
--- a/clang/lib/CIR/Lowering/CIRPasses.cpp
+++ b/clang/lib/CIR/Lowering/CIRPasses.cpp
@@ -31,7 +31,7 @@ mlir::LogicalResult runCIRToCIRPasses(mlir::ModuleOp 
theModule,
   if (enableCIRSimplify)
     pm.addPass(mlir::createCIRSimplifyPass());
 
-  pm.addPass(mlir::createLoweringPreparePass());
+  pm.addPass(mlir::createLoweringPreparePass(&astContext));
 
   pm.enableVerifier(enableVerifier);
   (void)mlir::applyPassManagerCLOptions(pm);

>From e1aaaf53a46079068e3d6cdd323e5c87cded6278 Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Thu, 17 Jul 2025 17:35:06 +0200
Subject: [PATCH 4/6] Address some review feedback

---
 clang/include/clang/CIR/Dialect/IR/CIROps.td         | 8 ++++++++
 clang/include/clang/CIR/MissingFeatures.h            | 1 +
 clang/lib/CIR/CodeGen/CIRGenClass.cpp                | 5 +++--
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp             | 1 +
 clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp | 2 ++
 clang/test/CIR/CodeGen/array-ctor.cpp                | 8 ++++++++
 6 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 096537fc78839..5f43b257554e0 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2261,6 +2261,14 @@ def CIR_ArrayCtor : CIR_ArrayInitDestroy<"array.ctor"> {
     Initialize each array element using the same C++ constructor. This
     operation has one region, with one single block. The block has an
     incoming argument for the current array index to initialize.
+
+    ```mlir
+    cir.array.ctor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) {
+      ^bb0(%arg0: !cir.ptr<!rec_S>):
+        cir.call @_ZN1SC1Ev(%arg0) : (!cir.ptr<!rec_S>) -> ()
+        cir.yield
+    }
+    ```
   }];
 }
 
diff --git a/clang/include/clang/CIR/MissingFeatures.h 
b/clang/include/clang/CIR/MissingFeatures.h
index 37e0a4c8c1b6b..098c2cad46f67 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -254,6 +254,7 @@ struct MissingFeatures {
   static bool dtorCleanups() { return false; }
   static bool vtableInitialization() { return false; }
   static bool msvcBuiltins() { return false; }
+  static bool vlas() { return false; }
 
   // Missing types
   static bool dataMemberType() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp 
b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 6351ad418dd74..46a5b2a1a7379 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -355,8 +355,8 @@ void CIRGenFunction::emitCXXAggrConstructorCall(
     // Just skip out if the constant count is zero.
     if (constIntAttr && constIntAttr.getUInt() == 0)
       return;
-    // Otherwise, emit the check.
   } else {
+    // Otherwise, emit the check.
     cgm.errorNYI(e->getSourceRange(), "dynamic-length array expression");
   }
 
@@ -399,7 +399,8 @@ void CIRGenFunction::emitCXXAggrConstructorCall(
     }
 
     // Emit the constructor call that will execute for every array element.
-    mlir::Value arrayOp = builder.createPtrBitcast(arrayBase.getPointer(), 
arrayTy);
+    mlir::Value arrayOp =
+        builder.createPtrBitcast(arrayBase.getPointer(), arrayTy);
     builder.create<cir::ArrayCtor>(
         *currSrcLoc, arrayOp, [&](mlir::OpBuilder &b, mlir::Location loc) {
           auto arg = b.getInsertionBlock()->addArgument(ptrToElmType, loc);
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp 
b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 4abbf65cbfe01..97ba226745d40 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -818,6 +818,7 @@ CIRGenFunction::emitArrayLength(const clang::ArrayType 
*origArrayType,
   // If it's a VLA, we have to load the stored size.  Note that
   // this is the size of the VLA in bytes, not its size in elements.
   if (isa<VariableArrayType>(arrayType)) {
+    assert(cir::MissingFeatures::vlas());
     cgm.errorNYI(*currSrcLoc, "VLAs");
     return builder.getConstInt(*currSrcLoc, SizeTy, 0);
   }
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp 
b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 30d3060013f67..c7d6f008e7c93 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -13,6 +13,7 @@
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
 #include "clang/CIR/Dialect/IR/CIROpsEnums.h"
 #include "clang/CIR/Dialect/Passes.h"
+#include "clang/CIR/MissingFeatures.h"
 
 #include <memory>
 
@@ -147,6 +148,7 @@ void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) 
{
   builder.setInsertionPointAfter(op.getOperation());
 
   mlir::Type eltTy = op->getRegion(0).getArgument(0).getType();
+  assert(!cir::MissingFeatures::vlas());
   auto arrayLen =
       
mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
   lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(),
diff --git a/clang/test/CIR/CodeGen/array-ctor.cpp 
b/clang/test/CIR/CodeGen/array-ctor.cpp
index 0a2661248f908..d927db3189270 100644
--- a/clang/test/CIR/CodeGen/array-ctor.cpp
+++ b/clang/test/CIR/CodeGen/array-ctor.cpp
@@ -68,3 +68,11 @@ void foo() {
 // OGCG: br i1 %[[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
 // OGCG: [[EXIT]]:
 // OGCG: ret void
+
+void zero_sized() {
+    int s[0];
+}
+
+// CIR:     cir.func dso_local @_Z10zero_sizedv()
+// CIR-NOT:   cir.do
+// CIR:       cir.return

>From 7331eb4ff98de1d99708b8ac35e439833ad23db3 Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Tue, 22 Jul 2025 18:21:26 +0200
Subject: [PATCH 5/6] more review feedback

---
 clang/include/clang/CIR/Dialect/IR/CIROps.td |  4 ++--
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp     |  4 +---
 clang/test/CIR/CodeGen/array-ctor.cpp        | 15 +++++++++++++++
 clang/test/CIR/IR/array-ctor.cir             |  4 ++--
 4 files changed, 20 insertions(+), 7 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 5f43b257554e0..177382c360e3f 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2239,7 +2239,7 @@ class CIR_ArrayInitDestroy<string mnemonic> : 
CIR_Op<mnemonic> {
 
   let regions = (region SizedRegion<1>:$body);
   let assemblyFormat = [{
-    `(` $addr `:` qualified(type($addr)) `)` $body attr-dict
+    $addr `:` qualified(type($addr)) $body attr-dict
   }];
 
   let builders = [
@@ -2265,7 +2265,7 @@ def CIR_ArrayCtor : CIR_ArrayInitDestroy<"array.ctor"> {
     ```mlir
     cir.array.ctor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) {
       ^bb0(%arg0: !cir.ptr<!rec_S>):
-        cir.call @_ZN1SC1Ev(%arg0) : (!cir.ptr<!rec_S>) -> ()
+        cir.call @some_ctor(%arg0) : (!cir.ptr<!rec_S>) -> ()
         cir.yield
     }
     ```
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp 
b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 97ba226745d40..afdb1927f952e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -849,9 +849,7 @@ CIRGenFunction::emitArrayLength(const clang::ArrayType 
*origArrayType,
   }
 
   baseType = eltType;
-  cir::ConstantOp numElements = builder.getConstInt(*currSrcLoc, SizeTy, 
countFromCLAs);
-
-  return numElements;
+  return builder.getConstInt(*currSrcLoc, SizeTy, countFromCLAs);
 }
 
 } // namespace clang::CIRGen
diff --git a/clang/test/CIR/CodeGen/array-ctor.cpp 
b/clang/test/CIR/CodeGen/array-ctor.cpp
index d927db3189270..788bc8cfcba78 100644
--- a/clang/test/CIR/CodeGen/array-ctor.cpp
+++ b/clang/test/CIR/CodeGen/array-ctor.cpp
@@ -1,3 +1,4 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o -  
2>&1 | FileCheck --check-prefixes=CIR-BEFORE-LPP %s
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-fclangir -emit-cir %s -o %t.cir
 // RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-fclangir -emit-llvm %s -o %t-cir.ll
@@ -13,6 +14,16 @@ void foo() {
     S s[42];
 }
 
+// CIR-BEFORE-LPP: cir.func dso_local @_Z3foov()
+// CIR-BEFORE-LPP:   %[[ARRAY:.*]] = cir.alloca !cir.array<!rec_S x 42>, 
!cir.ptr<!cir.array<!rec_S x 42>>, ["s", init]
+// CIR-BEFORE-LPP:   cir.array.ctor %[[ARRAY]] : !cir.ptr<!cir.array<!rec_S x 
42>> {
+// CIR-BEFORE-LPP:    ^bb0(%[[ARG:.*]]: !cir.ptr<!rec_S>):
+// CIR-BEFORE-LPP:      cir.call @_ZN1SC1Ev(%[[ARG]]) : (!cir.ptr<!rec_S>) -> 
()
+// CIR-BEFORE-LPP:      cir.yield
+// CIR-BEFORE-LPP:    }
+// CIR-BEFORE-LPP:   cir.return
+// CIR-BEFORE-LPP: }
+
 // CIR: cir.func dso_local @_Z3foov()
 // CIR:   %[[ARRAY:.*]] = cir.alloca !cir.array<!rec_S x 42>, 
!cir.ptr<!cir.array<!rec_S x 42>>, ["s", init]
 // CIR:   %[[CONST42:.*]] = cir.const #cir.int<42> : !u64i
@@ -73,6 +84,10 @@ void zero_sized() {
     int s[0];
 }
 
+// CIR-BEFORE-LPP:     cir.func dso_local @_Z10zero_sizedv()
+// CIR-BEFORE-LPP-NOT:   cir.array.ctor
+// CIR-BEFORE-LPP:       cir.return
+
 // CIR:     cir.func dso_local @_Z10zero_sizedv()
 // CIR-NOT:   cir.do
 // CIR:       cir.return
diff --git a/clang/test/CIR/IR/array-ctor.cir b/clang/test/CIR/IR/array-ctor.cir
index a039d7d2ca6b0..2378992bbd9fc 100644
--- a/clang/test/CIR/IR/array-ctor.cir
+++ b/clang/test/CIR/IR/array-ctor.cir
@@ -8,7 +8,7 @@ module {
   cir.func private @_ZN1SC1Ev(!cir.ptr<!rec_S>)
   cir.func dso_local @_Z3foov() {
     %0 = cir.alloca !cir.array<!rec_S x 42>, !cir.ptr<!cir.array<!rec_S x 
42>>, ["s", init] {alignment = 16 : i64}
-    cir.array.ctor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) {
+    cir.array.ctor %0 : !cir.ptr<!cir.array<!rec_S x 42>> {
     ^bb0(%arg0: !cir.ptr<!rec_S>):
       cir.call @_ZN1SC1Ev(%arg0) : (!cir.ptr<!rec_S>) -> ()
       cir.yield
@@ -19,7 +19,7 @@ module {
   // CHECK: cir.func private @_ZN1SC1Ev(!cir.ptr<!rec_S>)
   // CHECK: cir.func dso_local @_Z3foov() {
   // CHECK:   %0 = cir.alloca !cir.array<!rec_S x 42>, 
!cir.ptr<!cir.array<!rec_S x 42>>, ["s", init] {alignment = 16 : i64}
-  // CHECK:   cir.array.ctor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) {
+  // CHECK:   cir.array.ctor %0 : !cir.ptr<!cir.array<!rec_S x 42>> {
   // CHECK:   ^bb0(%arg0: !cir.ptr<!rec_S>):
   // CHECK:     cir.call @_ZN1SC1Ev(%arg0) : (!cir.ptr<!rec_S>) -> ()
   // CHECK:     cir.yield

>From 91a24b7526b6998dd8aa63b86303c188139f5528 Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Wed, 23 Jul 2025 17:31:24 +0200
Subject: [PATCH 6/6] * Expand zero sized test case * Remove use of auto

---
 clang/lib/CIR/CodeGen/CIRGenClass.cpp |  3 ++-
 clang/test/CIR/CodeGen/array-ctor.cpp | 15 ++++++++++++++-
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp 
b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 46a5b2a1a7379..bf42ff7738b8c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -403,7 +403,8 @@ void CIRGenFunction::emitCXXAggrConstructorCall(
         builder.createPtrBitcast(arrayBase.getPointer(), arrayTy);
     builder.create<cir::ArrayCtor>(
         *currSrcLoc, arrayOp, [&](mlir::OpBuilder &b, mlir::Location loc) {
-          auto arg = b.getInsertionBlock()->addArgument(ptrToElmType, loc);
+          mlir::BlockArgument arg =
+              b.getInsertionBlock()->addArgument(ptrToElmType, loc);
           Address curAddr = Address(arg, elementType, eltAlignment);
           assert(!cir::MissingFeatures::sanitizers());
           auto currAVS = AggValueSlot::forAddr(
diff --git a/clang/test/CIR/CodeGen/array-ctor.cpp 
b/clang/test/CIR/CodeGen/array-ctor.cpp
index 788bc8cfcba78..42ef4effafb57 100644
--- a/clang/test/CIR/CodeGen/array-ctor.cpp
+++ b/clang/test/CIR/CodeGen/array-ctor.cpp
@@ -81,13 +81,26 @@ void foo() {
 // OGCG: ret void
 
 void zero_sized() {
-    int s[0];
+    S s[0];
 }
 
 // CIR-BEFORE-LPP:     cir.func dso_local @_Z10zero_sizedv()
+// CIR-BEFORE-LPP:       cir.alloca !cir.array<!rec_S x 0>, 
!cir.ptr<!cir.array<!rec_S x 0>>, ["s"]
 // CIR-BEFORE-LPP-NOT:   cir.array.ctor
 // CIR-BEFORE-LPP:       cir.return
 
 // CIR:     cir.func dso_local @_Z10zero_sizedv()
+// CIR:       cir.alloca !cir.array<!rec_S x 0>, !cir.ptr<!cir.array<!rec_S x 
0>>, ["s"]
 // CIR-NOT:   cir.do
+// CIR-NOT:   cir.call @_ZN1SC1Ev
 // CIR:       cir.return
+
+// LLVM:     define dso_local void @_Z10zero_sizedv()
+// LLVM:       alloca [0 x %struct.S]
+// LLVM-NOT:   call void @_ZN1SC1Ev
+// LLVM:       ret void
+
+// OGCG:     define dso_local void @_Z10zero_sizedv()
+// OGCG:       alloca [0 x %struct.S]
+// OGCG-NOT:   call void @_ZN1SC1Ev
+// OGCG:       ret void

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

Reply via email to