https://github.com/andykaylor created 
https://github.com/llvm/llvm-project/pull/190656

This change adds support for array new with variable size. This required 
extending the cir.array.ctor operation to accept a value for the size and a 
direct pointer to the element size instead of a pointer to an array.

Assisted-by: Cursor / claude-4.6-opus-high
Assisted-by: Cursor / composer-2-fast

>From 03b66a546a12a5aec2c88fbce0ca6415a633b29b Mon Sep 17 00:00:00 2001
From: Andy Kaylor <[email protected]>
Date: Thu, 19 Mar 2026 18:10:13 -0700
Subject: [PATCH] [CIR] Add support for variable sized array new.

This change adds support for array new with variable size. This required
extending the cir.array.ctor operation to accept a value for the size
and a direct pointer to the element size instead of a pointer to an array.

Assisted-by: Cursor / claude-4.6-opus-high
Assisted-by: Cursor / composer-2-fast
---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  |  34 +++-
 clang/lib/CIR/CodeGen/CIRGenClass.cpp         |  94 +++++------
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp       |  44 ++++++
 .../Dialect/Transforms/LoweringPrepare.cpp    |  24 ++-
 clang/test/CIR/CodeGen/new.cpp                | 146 ++++++++++++++++++
 clang/test/CIR/IR/array-ctor.cir              |  19 +++
 clang/test/CIR/IR/invalid-array-structor.cir  | 143 +++++++++++++++++
 7 files changed, 452 insertions(+), 52 deletions(-)
 create mode 100644 clang/test/CIR/IR/invalid-array-structor.cir

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 04884beaa5685..74d79db5e11c6 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4563,7 +4563,14 @@ def CIR_ArrayCtor : CIR_Op<"array.ctor"> {
     operation has one region, with one single block. The block has an
     incoming argument for the current array element to initialize.
 
-    Example:
+    When `num_elements` is absent, `addr` must be a pointer to a fixed-size
+    CIR array type and the element count is derived from that array type.
+
+    When `num_elements` is present, `addr` is a pointer to the first element
+    and `num_elements` provides the runtime element count (for example `new
+    T[n]`).
+
+    Examples:
 
     ```mlir
     cir.array.ctor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) {
@@ -4571,19 +4578,28 @@ def CIR_ArrayCtor : CIR_Op<"array.ctor"> {
         cir.call @some_ctor(%arg0) : (!cir.ptr<!rec_S>) -> ()
         cir.yield
     }
+
+    cir.array.ctor(%ptr, %n : !cir.ptr<!rec_S>, !u64i) {
+      ^bb0(%arg0: !cir.ptr<!rec_S>):
+        cir.call @some_ctor(%arg0) : (!cir.ptr<!rec_S>) -> ()
+        cir.yield
+    }
     ```
   }];
 
   let arguments = (ins
-    Arg<CIR_PtrToArray, "array address", [MemWrite, MemRead]>:$addr
+    Arg<CIR_AnyPtrType, "array or element address", [MemWrite, MemRead]>:$addr,
+    Optional<CIR_AnyIntType>:$num_elements
   );
 
   let regions = (region SizedRegion<1>:$body);
   let assemblyFormat = [{
-    $addr `:` qualified(type($addr)) $body attr-dict
+    $addr (`,` $num_elements^)? `:` qualified(type($addr))
+    (`,` type($num_elements)^)? $body attr-dict
   }];
 
   let builders = [
+    // Static form: addr is ptr<array<T x N>>, no num_elements.
     OpBuilder<(ins "mlir::Value":$addr,
       "llvm::function_ref<void(mlir::OpBuilder &, 
mlir::Location)>":$regionBuilder), [{
         assert(regionBuilder && "builder callback expected");
@@ -4592,9 +4608,20 @@ def CIR_ArrayCtor : CIR_Op<"array.ctor"> {
         $_state.addOperands(ValueRange{addr});
         $_builder.createBlock(r);
         regionBuilder($_builder, $_state.location);
+    }]>,
+    // Dynamic form: addr is ptr<T>, num_elements is the runtime count.
+    OpBuilder<(ins "mlir::Value":$addr, "mlir::Value":$num_elements,
+      "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({addr, num_elements});
+        $_builder.createBlock(r);
+        regionBuilder($_builder, $_state.location);
     }]>
   ];
 
+  let hasVerifier = 1;
   let hasLLVMLowering = false;
 }
 
@@ -4668,6 +4695,7 @@ def CIR_ArrayDtor : CIR_Op<"array.dtor"> {
     }]>
   ];
 
+  let hasVerifier = 1;
   let hasLLVMLowering = false;
 }
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp 
b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index e54514950e00f..f8d65fa9a6d33 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -735,31 +735,22 @@ void CIRGenFunction::emitCXXAggrConstructorCall(
   // are probably legitimate places where we could assume that this
   // doesn't happen, but it's not clear that it's worth it.
 
-  auto arrayTy = mlir::cast<cir::ArrayType>(arrayBase.getElementType());
-  mlir::Type elementType = arrayTy.getElementType();
-
-  // This might be a multi-dimensional array. Find the innermost element type.
+  // Peel any array types wrapped in the address element type down to the CIR
+  // type of a single constructed object.
+  mlir::Type elementType = arrayBase.getElementType();
   while (auto maybeArrayTy = mlir::dyn_cast<cir::ArrayType>(elementType))
     elementType = maybeArrayTy.getElementType();
   cir::PointerType ptrToElmType = builder.getPointerTo(elementType);
 
-  // Optimize for a constant count.
-  if (auto constantCount = numElements.getDefiningOp<cir::ConstantOp>()) {
-    if (auto constIntAttr = constantCount.getValueAttr<cir::IntAttr>()) {
-      // Just skip out if the constant count is zero.
-      if (constIntAttr.getUInt() == 0)
-        return;
-
-      arrayTy = cir::ArrayType::get(elementType, constIntAttr.getUInt());
-      // Otherwise, emit the check.
-    }
-
-    if (constantCount.use_empty())
-      constantCount.erase();
-  } else {
-    // Otherwise, emit the check.
-    cgm.errorNYI(e->getSourceRange(),
-                 "emitCXXAggrConstructorCall: dynamic-length array 
expression");
+  bool useDynamicArrayCtor = true;
+  uint64_t constElementCount = 0;
+  if (auto constantOp = numElements.getDefiningOp<cir::ConstantOp>()) {
+    constElementCount = CIRGenFunction::getZExtIntValueFromConstOp(constantOp);
+    if (constElementCount == 0)
+      return;
+    if (constantOp.use_empty())
+      constantOp.erase();
+    useDynamicArrayCtor = false;
   }
 
   // Traditional LLVM codegen emits a loop here. CIR lowers to a loop as part 
of
@@ -775,6 +766,13 @@ void CIRGenFunction::emitCXXAggrConstructorCall(
   CharUnits eltAlignment = arrayBase.getAlignment().alignmentOfArrayElement(
       getContext().getTypeSizeInChars(type));
 
+  mlir::Location loc = *currSrcLoc;
+
+  mlir::Value dynamicElPtr;
+  if (useDynamicArrayCtor)
+    dynamicElPtr =
+        builder.createPtrBitcast(arrayBase.getPointer(), elementType);
+
   // 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
@@ -792,30 +790,36 @@ void CIRGenFunction::emitCXXAggrConstructorCall(
       cgm.errorNYI(e->getSourceRange(), "partial array cleanups");
     }
 
-    // Emit the constructor call that will execute for every array element.
-    mlir::Value arrayOp =
-        builder.createPtrBitcast(arrayBase.getPointer(), arrayTy);
-    cir::ArrayCtor::create(
-        builder, *currSrcLoc, arrayOp,
-        [&](mlir::OpBuilder &b, mlir::Location loc) {
-          mlir::BlockArgument arg =
-              b.getInsertionBlock()->addArgument(ptrToElmType, loc);
-          Address curAddr = Address(arg, elementType, eltAlignment);
-          assert(!cir::MissingFeatures::sanitizers());
-          // Zero-initialize each element before invoking its constructor,
-          // matching CGClass::EmitCXXAggrConstructorCall which does 
per-element
-          // zero-init inside the array ctor loop.
-          if (zeroInitialize)
-            emitNullInitialization(loc, curAddr, type);
-          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);
-          cir::YieldOp::create(b, loc);
-        });
+    auto emitCtorBody = [&](mlir::OpBuilder &b, mlir::Location l) {
+      mlir::BlockArgument arg =
+          b.getInsertionBlock()->addArgument(ptrToElmType, l);
+      Address curAddr = Address(arg, elementType, eltAlignment);
+      assert(!cir::MissingFeatures::sanitizers());
+      // Match CGClass::EmitCXXAggrConstructorCall: zero-initialize each 
element
+      // in the array-ctor loop before invoking the constructor for that slot.
+      if (zeroInitialize)
+        emitNullInitialization(l, curAddr, type);
+      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);
+      cir::YieldOp::create(b, l);
+    };
+
+    // Emit the per-element initialization.
+    if (useDynamicArrayCtor) {
+      cir::ArrayCtor::create(builder, loc, dynamicElPtr, numElements,
+                             emitCtorBody);
+    } else {
+      cir::ArrayType arrayTy =
+          cir::ArrayType::get(elementType, constElementCount);
+      mlir::Value arrayOp =
+          builder.createPtrBitcast(arrayBase.getPointer(), arrayTy);
+      cir::ArrayCtor::create(builder, loc, arrayOp, emitCtorBody);
+    }
   }
 }
 
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp 
b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index a0b11f7f46b97..9dad642344cc0 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -289,6 +289,50 @@ void cir::AllocaOp::build(mlir::OpBuilder &odsBuilder,
   odsState.addTypes(addr);
 }
 
+//===----------------------------------------------------------------------===//
+// ArrayCtor & ArrayDtor
+//===----------------------------------------------------------------------===//
+
+template <typename Op> static LogicalResult verifyArrayCtorDtor(Op op) {
+  auto ptrTy = mlir::cast<cir::PointerType>(op.getAddr().getType());
+  mlir::Type pointeeTy = ptrTy.getPointee();
+
+  mlir::Block &body = op.getBody().front();
+  if (body.getNumArguments() != 1)
+    return op.emitOpError("body must have exactly one block argument");
+
+  auto expectedEltPtrTy =
+      mlir::dyn_cast<cir::PointerType>(body.getArgument(0).getType());
+  if (!expectedEltPtrTy)
+    return op.emitOpError("block argument must be a !cir.ptr type");
+
+  if (op.getNumElements()) {
+    if (expectedEltPtrTy != ptrTy)
+      return op.emitOpError("when 'num_elements' is present, 'addr' type must "
+                            "match the block argument type");
+  } else {
+    auto arrayTy = mlir::dyn_cast<cir::ArrayType>(pointeeTy);
+    if (!arrayTy)
+      return op.emitOpError(
+          "when 'num_elements' is absent, 'addr' must be a pointer to a "
+          "!cir.array type");
+
+    mlir::Type innerEltTy = arrayTy.getElementType();
+    while (auto nested = mlir::dyn_cast<cir::ArrayType>(innerEltTy))
+      innerEltTy = nested.getElementType();
+
+    if (expectedEltPtrTy.getPointee() != innerEltTy)
+      return op.emitOpError(
+          "block argument pointee type must match the innermost array "
+          "element type");
+  }
+
+  return success();
+}
+
+LogicalResult cir::ArrayCtor::verify() { return verifyArrayCtorDtor(*this); }
+LogicalResult cir::ArrayDtor::verify() { return verifyArrayCtorDtor(*this); }
+
 
//===----------------------------------------------------------------------===//
 // BreakOp
 
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp 
b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 4d0c7b179f260..91f082725711f 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -1389,7 +1389,6 @@ static void 
lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
   // from end, decrementing before calling the destructor on each element.
   mlir::Value begin, end;
   if (isDynamic) {
-    assert(!isCtor && "Unexpected dynamic ctor loop");
     begin = addr;
     end = cir::PtrStrideOp::create(builder, loc, eltTy, begin, numElements);
   } else {
@@ -1407,9 +1406,18 @@ static void 
lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
   // This places the destructor loop emitted below inside the if block.
   cir::IfOp ifOp;
   if (isDynamic) {
-    mlir::Value isEmpty =
-        cir::CmpOp::create(builder, loc, cir::CmpOpKind::ne, start, stop);
-    ifOp = cir::IfOp::create(builder, loc, isEmpty,
+    mlir::Value guardCond;
+    if (isCtor) {
+      mlir::Value zero = builder.getUnsignedInt(loc, 0, sizeTypeSize);
+      guardCond = cir::CmpOp::create(builder, loc, cir::CmpOpKind::ne,
+                                     numElements, zero);
+    } else {
+      // We could check for numElements != 0 in this case too, but this matches
+      // what classic codegen does.
+      guardCond =
+          cir::CmpOp::create(builder, loc, cir::CmpOpKind::ne, start, stop);
+    }
+    ifOp = cir::IfOp::create(builder, loc, guardCond,
                              /*withElseRegion=*/false,
                              [&](mlir::OpBuilder &, mlir::Location) {});
     builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
@@ -1496,6 +1504,14 @@ void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor 
op) {
   builder.setInsertionPointAfter(op.getOperation());
 
   mlir::Type eltTy = op->getRegion(0).getArgument(0).getType();
+
+  if (op.getNumElements()) {
+    lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(),
+                               op.getNumElements(), /*arrayLen=*/0,
+                               /*isCtor=*/true);
+    return;
+  }
+
   assert(!cir::MissingFeatures::vlas());
   auto arrayLen =
       
mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
diff --git a/clang/test/CIR/CodeGen/new.cpp b/clang/test/CIR/CodeGen/new.cpp
index 55c7507346500..7da943de355ef 100644
--- a/clang/test/CIR/CodeGen/new.cpp
+++ b/clang/test/CIR/CodeGen/new.cpp
@@ -806,6 +806,43 @@ void test_array_new_with_ctor_init() {
 // OGCG:   ret void
 //
 
+void test_array_new_var_sized_with_ctor_init(int size) {
+  auto p = new F[size];
+}
+
+// CIR-BEFORE-LPP: cir.func{{.*}} @_Z39test_array_new_var_sized_with_ctor_initi
+// CIR-BEFORE-LPP:    %[[N64:.*]] = cir.cast integral{{.*}} : !s32i -> !u64i
+// CIR-BEFORE-LPP:    %[[RAW:.*]] = cir.call @_Znam(%[[N64]])
+// CIR-BEFORE-LPP:    cir.cast bitcast %[[RAW]] : !cir.ptr<!void> -> 
!cir.ptr<!rec_F>
+// CIR-BEFORE-LPP:    cir.array.ctor %{{.*}}, %[[N64]] : !cir.ptr<!rec_F>, 
!u64i {
+// CIR-BEFORE-LPP:    ^bb0({{.*}}: !cir.ptr<!rec_F>):
+// CIR-BEFORE-LPP:      cir.call @_ZN1FC1Ev({{.*}}) : (!cir.ptr<!rec_F>
+
+// CHECK:  cir.func{{.*}} @_Z39test_array_new_var_sized_with_ctor_initi
+// CHECK:    %[[N64:.*]] = cir.cast integral{{.*}} : !s32i -> !u64i
+// CHECK:    %[[RAW:.*]] = cir.call @_Znam(%[[N64]])
+// CHECK:    %[[BEGIN:.*]] = cir.cast bitcast %[[RAW]] : !cir.ptr<!void> -> 
!cir.ptr<!rec_F>
+// CHECK:    %[[END:.*]] = cir.ptr_stride %{{.*}}, %[[N64]] : 
(!cir.ptr<!rec_F>, !u64i) -> !cir.ptr<!rec_F>
+// CHECK:    cir.cmp ne %[[N64]], %{{.*}} : !u64i
+// CHECK:    cir.if
+// CHECK:      cir.call @_ZN1FC1Ev(
+// CHECK:    cir.store{{.*}} %[[BEGIN]],
+// CHECK:    cir.return
+
+// LLVM: define{{.*}} void @_Z39test_array_new_var_sized_with_ctor_initi
+// LLVM:   %[[N:.*]] = load i32, ptr %{{.+}}
+// LLVM:   %[[N64:.*]] = sext i32 %[[N]] to i64
+// LLVM:   %[[RAW:.*]] = call noundef ptr @_Znam(i64 {{.*}} %[[N64]])
+// LLVM:   %[[CMP:.*]] = icmp ne i64 %[[N64]], 0
+// LLVM:   br i1 %[[CMP]]
+
+// OGCG: define{{.*}} void @_Z39test_array_new_var_sized_with_ctor_initi
+// OGCG:   %[[N:.*]] = load i32, ptr %{{.+}}
+// OGCG:   %[[N64:.*]] = sext i32 %[[N]] to i64
+// OGCG:   %[[RAW:.*]] = call{{.*}} ptr @_Znam(i64 {{.*}} %[[N64]])
+// OGCG:   %[[CMP:.*]] = icmp eq i64 %[[N64]], 0
+// OGCG:   br i1 %[[CMP]]
+
 // Non-trivial member => non-trivial implicit OuterZero default constructor.
 // Value-initialization `new OuterZero[3]()` needs zero-init before each ctor
 // (CXXConstructExpr::requiresZeroInitialization).
@@ -890,6 +927,56 @@ void test_const_array_new_value_init() {
 // OGCG:   store ptr %[[RAW]], ptr %{{.*}}, align 8
 // OGCG:   ret void
 
+void test_var_array_new_value_init(int n) {
+  auto p = new OuterZero[n]();
+}
+
+// --- Per-element zero-init before ctor (dynamic array new + value-init):
+// OG emits @llvm.memset of i64 1 then C1Ev inside its arrayctor.loop. CIR
+// stores #cir.zero per element in the cir.array.ctor region, then the same
+// ctor call. There must be no bulk clear of the whole allocation before the
+// lowered loop (no libc.memset / memset scaled by element count in the
+// lowering-prepare input).
+
+// CIR-BEFORE-LPP-LABEL: cir.func{{.*}} @_Z29test_var_array_new_value_initi
+// CIR-BEFORE-LPP:         %[[N:.*]] = cir.cast integral{{.*}} : !s32i -> !u64i
+// CIR-BEFORE-LPP-NEXT:    %{{.*}} = cir.call @_Znam(%[[N]]){{.*}}
+// CIR-BEFORE-LPP-NEXT:    cir.cast bitcast %{{.*}} : !cir.ptr<!void> -> 
!cir.ptr<!rec_OuterZero>
+// CIR-BEFORE-LPP-NEXT:    cir.cast bitcast %{{.*}} : !cir.ptr<!rec_OuterZero> 
-> !cir.ptr<!cir.array<!rec_OuterZero x 0>>
+// CIR-BEFORE-LPP-NEXT:    cir.cast bitcast %{{.*}} : 
!cir.ptr<!cir.array<!rec_OuterZero x 0>> -> !cir.ptr<!rec_OuterZero>
+// CIR-BEFORE-LPP-NEXT:    cir.array.ctor %{{.*}}, %[[N]] : 
!cir.ptr<!rec_OuterZero>, !u64i {
+// CIR-BEFORE-LPP-NEXT:  ^bb0(%[[EL:.*]]: !cir.ptr<!rec_OuterZero>):
+// CIR-BEFORE-LPP-NEXT:    cir.const #cir.zero : !rec_OuterZero
+// CIR-BEFORE-LPP-NEXT:    cir.store{{.*}} %{{.*}}, %[[EL]] : !rec_OuterZero, 
!cir.ptr<!rec_OuterZero>
+// CIR-BEFORE-LPP-NEXT:    cir.call @_ZN9OuterZeroC1Ev(%[[EL]]) : 
(!cir.ptr<!rec_OuterZero> {llvm.align = 1 : i64, llvm.dereferenceable = 1 : 
i64, llvm.nonnull, llvm.noundef}) -> ()
+// CIR-BEFORE-LPP-NEXT:    cir.yield
+// CIR-BEFORE-LPP-NEXT:  }
+
+// CHECK-LABEL: cir.func{{.*}} @_Z29test_var_array_new_value_initi
+// CHECK:       cir.do {
+// CHECK:         cir.load{{.*}} : !cir.ptr<!cir.ptr<!rec_OuterZero>>, 
!cir.ptr<!rec_OuterZero>
+// CHECK:         cir.const #cir.zero : !rec_OuterZero
+// CHECK:         cir.store{{.*}} : !rec_OuterZero, !cir.ptr<!rec_OuterZero>
+// CHECK:         cir.call @_ZN9OuterZeroC1Ev(
+// CHECK:         cir.ptr_stride
+// CHECK:         cir.store{{.*}} : !cir.ptr<!rec_OuterZero>, 
!cir.ptr<!cir.ptr<!rec_OuterZero>>
+// CHECK:         cir.yield
+// CHECK:       } while {
+// CHECK:         cir.load{{.*}} : !cir.ptr<!cir.ptr<!rec_OuterZero>>, 
!cir.ptr<!rec_OuterZero>
+// CHECK:         cir.cmp ne
+// CHECK:         cir.condition
+// CHECK:       }
+// CHECK:       cir.return
+
+// LLVM-LABEL: define{{.*}} void @_Z29test_var_array_new_value_initi
+// LLVM-NOT: call void @llvm.memset.p0.i64
+// LLVM: store %class.OuterZero zeroinitializer, ptr %[[CUR:.*]], align 1
+// LLVM-NEXT: call void @_ZN9OuterZeroC1Ev(ptr noundef nonnull align 1 
dereferenceable(1) %[[CUR]])
+
+// OGCG-LABEL: define{{.*}} void @_Z29test_var_array_new_value_initi
+// OGCG: call void @llvm.memset.p0.i64(ptr align 1 %[[CUR:.*]], i8 0, i64 1, 
i1 false)
+// OGCG-NEXT: call void @_ZN9OuterZeroC1Ev(ptr noundef nonnull align 1 
dereferenceable(1) %[[CUR]])
+
 void test_multidim_array_new_with_ctor() {
   auto p = new F[2][3];
 }
@@ -956,6 +1043,65 @@ void test_multidim_array_new_with_ctor() {
 // OGCG:   store ptr %[[RAW]], ptr %{{.*}}, align 8
 // OGCG:   ret void
 
+void test_multidim_var_array_new_with_ctor(int n) {
+  auto p = new F[n][3];
+}
+
+// CIR-BEFORE-LPP-LABEL: cir.func{{.*}} 
@_Z37test_multidim_var_array_new_with_ctori
+// CIR-BEFORE-LPP:    %[[N64:.*]] = cir.cast integral %{{.*}} : !s32i -> !u64i
+// CIR-BEFORE-LPP:    %[[THREE:.*]] = cir.const #cir.int<3> : !u64i
+// CIR-BEFORE-LPP:    %[[TOTAL:.*]], %{{.*}} = cir.mul.overflow %[[N64]], 
%[[THREE]] : !u64i
+// CIR-BEFORE-LPP:    cir.select
+// CIR-BEFORE-LPP:    cir.call @_Znam
+// CIR-BEFORE-LPP:    cir.cast bitcast %{{.*}} : !cir.ptr<!void> -> 
!cir.ptr<!cir.array<!rec_F x 3>>
+// CIR-BEFORE-LPP:    cir.cast bitcast %{{.*}} : !cir.ptr<!cir.array<!rec_F x 
3>> -> !cir.ptr<!cir.array<!cir.array<!rec_F x 3> x 0>>
+// CIR-BEFORE-LPP:    %[[ELPTR:.*]] = cir.cast bitcast %{{.*}} : 
!cir.ptr<!cir.array<!cir.array<!rec_F x 3> x 0>> -> !cir.ptr<!rec_F>
+// CIR-BEFORE-LPP:    cir.array.ctor %[[ELPTR]], %[[TOTAL]] : 
!cir.ptr<!rec_F>, !u64i {
+// CIR-BEFORE-LPP:    ^bb0(%[[ARG:.*]]: !cir.ptr<!rec_F>):
+// CIR-BEFORE-LPP:      cir.call @_ZN1FC1Ev(%[[ARG]])
+// CIR-BEFORE-LPP:      cir.yield
+// CIR-BEFORE-LPP:    }
+
+// CHECK-LABEL: cir.func{{.*}} @_Z37test_multidim_var_array_new_with_ctori
+// CHECK:    %[[N64:.*]] = cir.cast integral %{{.*}} : !s32i -> !u64i
+// CHECK:    %[[TOTAL:.*]], %{{.*}} = cir.mul.overflow %[[N64]], %{{.*}} : 
!u64i
+// CHECK:    cir.call @_Znam
+// CHECK:    cir.ptr_stride %{{.*}}, %[[TOTAL]] : (!cir.ptr<!rec_F>, !u64i) -> 
!cir.ptr<!rec_F>
+// CHECK:    cir.cmp ne %[[TOTAL]], %{{.*}} : !u64i
+// CHECK:    cir.if %{{.*}} {
+// CHECK:      cir.do {
+// CHECK:        cir.call @_ZN1FC1Ev(
+// CHECK:        cir.ptr_stride
+// CHECK:        cir.yield
+// CHECK:      } while {
+// CHECK:        cir.cmp ne
+// CHECK:        cir.condition
+// CHECK:      }
+// CHECK:    }
+
+// LLVM-LABEL: define{{.*}} void @_Z37test_multidim_var_array_new_with_ctori
+// LLVM:   %[[N:.*]] = load i32, ptr %{{.*}}
+// LLVM:   %[[N64:.*]] = sext i32 %[[N]] to i64
+// LLVM:   %[[MUL:.*]] = call { i64, i1 } @llvm.umul.with.overflow.i64(i64 
%[[N64]], i64 3)
+// LLVM:   %[[TOTAL:.*]] = extractvalue { i64, i1 } %[[MUL]], 0
+// LLVM:   %[[RAW:.*]] = call noundef ptr @_Znam(i64 noundef %{{.*}})
+// LLVM:   %[[END:.*]] = getelementptr %class.F, ptr %[[RAW]], i64 %[[TOTAL]]
+// LLVM:   %[[CMP:.*]] = icmp ne i64 %[[TOTAL]], 0
+// LLVM:   br i1 %[[CMP]]
+// LLVM:   call void @_ZN1FC1Ev(
+// LLVM:   getelementptr %class.F, ptr %{{.*}}, i64 1
+
+// OGCG-LABEL: define{{.*}} void @_Z37test_multidim_var_array_new_with_ctori
+// OGCG:   %[[N:.*]] = load i32, ptr %{{.*}}
+// OGCG:   %[[N64:.*]] = sext i32 %[[N]] to i64
+// OGCG:   call { i64, i1 } @llvm.umul.with.overflow.i64(i64 %[[N64]], i64 3)
+// OGCG:   %[[TOTAL:.*]] = extractvalue { i64, i1 } %{{.*}}, 0
+// OGCG:   %[[RAW:.*]] = call{{.*}} ptr @_Znam(i64 noundef %{{.*}})
+// OGCG:   %[[END:.*]] = getelementptr inbounds %class.F, ptr %[[RAW]], i64 
%[[TOTAL]]
+// OGCG:   call void @_ZN1FC1Ev(
+// OGCG:   getelementptr inbounds %class.F, ptr %{{.*}}, i64 1
+// OGCG:   icmp eq ptr %{{.*}}, %[[END]]
+
 class G {
 public:
   G();
diff --git a/clang/test/CIR/IR/array-ctor.cir b/clang/test/CIR/IR/array-ctor.cir
index fd2ec7eb93c23..0a71b1e4dee6d 100644
--- a/clang/test/CIR/IR/array-ctor.cir
+++ b/clang/test/CIR/IR/array-ctor.cir
@@ -2,6 +2,7 @@
 // RUN: cir-opt %s --verify-roundtrip | FileCheck %s
 
 !u8i = !cir.int<u, 8>
+!u64i = !cir.int<u, 64>
 !rec_S = !cir.record<struct "S" padded {!u8i}>
 
 module {
@@ -26,4 +27,22 @@ module {
   // CHECK:   }
   // CHECK:   cir.return
   // CHECK: }
+
+  cir.func dso_local @dynamic_array_ctor(%p: !cir.ptr<!rec_S>, %n: !u64i) {
+    cir.array.ctor %p, %n : !cir.ptr<!rec_S>, !u64i {
+    ^bb0(%e: !cir.ptr<!rec_S>):
+      cir.call @_ZN1SC1Ev(%e) : (!cir.ptr<!rec_S>) -> ()
+      cir.yield
+    }
+    cir.return
+  }
+
+  // CHECK: cir.func dso_local @dynamic_array_ctor
+  // CHECK:   cir.array.ctor %{{.*}}, %{{.*}} : !cir.ptr<!rec_S>, !u64i {
+  // CHECK:   ^bb0(%{{.*}}: !cir.ptr<!rec_S>):
+  // CHECK:     cir.call @_ZN1SC1Ev(%{{.*}}) : (!cir.ptr<!rec_S>) -> ()
+  // CHECK:     cir.yield
+  // CHECK:   }
+  // CHECK:   cir.return
+  // CHECK: }
 }
diff --git a/clang/test/CIR/IR/invalid-array-structor.cir 
b/clang/test/CIR/IR/invalid-array-structor.cir
new file mode 100644
index 0000000000000..3a33846ddd648
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-array-structor.cir
@@ -0,0 +1,143 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+!u8i = !cir.int<u, 8>
+!u64i = !cir.int<u, 64>
+!rec_S = !cir.record<struct "S" padded {!u8i}>
+
+module {
+  cir.func private @_ZN1SC1Ev(!cir.ptr<!rec_S>)
+
+  // Static form: addr must be a pointer to an array type.
+  cir.func @bad_static_not_array_ctor(%p: !cir.ptr<!rec_S>) {
+    // expected-error@+1 {{'cir.array.ctor' op when 'num_elements' is absent, 
'addr' must be a pointer to a !cir.array type}}
+    cir.array.ctor %p : !cir.ptr<!rec_S> {
+    ^bb0(%e: !cir.ptr<!rec_S>):
+      cir.call @_ZN1SC1Ev(%e) : (!cir.ptr<!rec_S>) -> ()
+      cir.yield
+    }
+    cir.return
+  }
+}
+
+// -----
+
+!u8i = !cir.int<u, 8>
+!u64i = !cir.int<u, 64>
+!rec_S = !cir.record<struct "S" padded {!u8i}>
+!rec_T = !cir.record<struct "T" padded {!u8i}>
+
+module {
+  cir.func private @_ZN1SC1Ev(!cir.ptr<!rec_S>)
+
+  // Static form: block argument element type must match innermost array 
element.
+  cir.func @bad_static_element_mismatch_ctor(%p: !cir.ptr<!cir.array<!rec_S x 
10>>) {
+    // expected-error@+1 {{'cir.array.ctor' op block argument pointee type 
must match the innermost array element type}}
+    cir.array.ctor %p : !cir.ptr<!cir.array<!rec_S x 10>> {
+    ^bb0(%e: !cir.ptr<!rec_T>):
+      cir.yield
+    }
+    cir.return
+  }
+}
+
+// -----
+
+!u8i = !cir.int<u, 8>
+!u64i = !cir.int<u, 64>
+!rec_S = !cir.record<struct "S" padded {!u8i}>
+!rec_T = !cir.record<struct "T" padded {!u8i}>
+
+module {
+  cir.func private @_ZN1SC1Ev(!cir.ptr<!rec_S>)
+
+  // Dynamic form: addr type must match block argument type.
+  cir.func @bad_dynamic_type_mismatch_ctor(%p: !cir.ptr<!rec_T>, %n: !u64i) {
+    // expected-error@+1 {{'cir.array.ctor' op when 'num_elements' is present, 
'addr' type must match the block argument type}}
+    cir.array.ctor %p, %n : !cir.ptr<!rec_T>, !u64i {
+    ^bb0(%e: !cir.ptr<!rec_S>):
+      cir.call @_ZN1SC1Ev(%e) : (!cir.ptr<!rec_S>) -> ()
+      cir.yield
+    }
+    cir.return
+  }
+}
+
+// -----
+
+!u8i = !cir.int<u, 8>
+!rec_S = !cir.record<struct "S" padded {!u8i}>
+
+module {
+  cir.func private @_ZN1SD1Ev(!cir.ptr<!rec_S>)
+  // Static form: addr must be a pointer to an array type.
+  cir.func @bad_static_not_array_dtor(%p: !cir.ptr<!rec_S>) {
+    // expected-error@+1 {{'cir.array.dtor' op when 'num_elements' is absent, 
'addr' must be a pointer to a !cir.array type}}
+    cir.array.dtor %p : !cir.ptr<!rec_S> {
+    ^bb0(%e: !cir.ptr<!rec_S>):
+      cir.yield
+    }
+    cir.return
+  }
+}
+
+// -----
+
+!u8i = !cir.int<u, 8>
+!rec_S = !cir.record<struct "S" padded {!u8i}>
+!rec_T = !cir.record<struct "T" padded {!u8i}>
+
+module {
+  cir.func private @_ZN1SD1Ev(!cir.ptr<!rec_S>)
+  // Static form: addr must be a pointer to an array type.
+  cir.func @bad_static_dtor_not_array_ctor(%p: !cir.ptr<!cir.array<!rec_S x 
10>>) {
+    // expected-error@+1 {{'cir.array.dtor' op block argument pointee type 
must match the innermost array element type}}
+    cir.array.dtor %p: !cir.ptr<!cir.array<!rec_S x 10>> {
+    ^bb0(%e: !cir.ptr<!rec_T>):
+      cir.yield
+    }
+    cir.return
+  }
+}
+
+// -----
+
+!u8i = !cir.int<u, 8>
+!u64i = !cir.int<u, 64>
+!rec_S = !cir.record<struct "S" padded {!u8i}>
+!rec_T = !cir.record<struct "T" padded {!u8i}>
+
+module {
+  cir.func private @_ZN1SD1Ev(!cir.ptr<!rec_S>)
+
+  // Static form: block argument element type must match innermost array 
element.
+  cir.func @bad_static_element_mismatch_dtor(%p: !cir.ptr<!cir.array<!rec_S x 
10>>) {
+    // expected-error@+1 {{'cir.array.dtor' op block argument pointee type 
must match the innermost array element type}}
+    cir.array.dtor %p : !cir.ptr<!cir.array<!rec_S x 10>> {
+    ^bb0(%e: !cir.ptr<!rec_T>):
+      cir.yield
+    }
+    cir.return
+  }
+}
+
+// -----
+
+!u8i = !cir.int<u, 8>
+!u64i = !cir.int<u, 64>
+!rec_S = !cir.record<struct "S" padded {!u8i}>
+!rec_T = !cir.record<struct "T" padded {!u8i}>
+
+module {
+  cir.func private @_ZN1SD1Ev(!cir.ptr<!rec_S>)
+
+  // Dynamic form: addr type must match block argument type.
+  cir.func @bad_dynamic_type_mismatch_dtor(%p: !cir.ptr<!rec_T>, %n: !u64i) {
+    // expected-error@+1 {{'cir.array.dtor' op when 'num_elements' is present, 
'addr' type must match the block argument type}}
+    cir.array.dtor %p, %n : !cir.ptr<!rec_T>, !u64i {
+    ^bb0(%e: !cir.ptr<!rec_S>):
+      cir.call @_ZN1SD1Ev(%e) : (!cir.ptr<!rec_S>) -> ()
+      cir.yield
+    }
+    cir.return
+  }
+}

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

Reply via email to