[clang] 88a78f6 - [CIR] Add support for variable sized array new. (#190656)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 7 12:21:40 PDT 2026
Author: Andy Kaylor
Date: 2026-04-07T12:21:34-07:00
New Revision: 88a78f651c5e6b89bf6377e06d5fe5e92cd1ae2a
URL: https://github.com/llvm/llvm-project/commit/88a78f651c5e6b89bf6377e06d5fe5e92cd1ae2a
DIFF: https://github.com/llvm/llvm-project/commit/88a78f651c5e6b89bf6377e06d5fe5e92cd1ae2a.diff
LOG: [CIR] Add support for variable sized array new. (#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
Added:
clang/test/CIR/IR/invalid-array-structor.cir
Modified:
clang/include/clang/CIR/Dialect/IR/CIROps.td
clang/lib/CIR/CodeGen/CIRGenClass.cpp
clang/lib/CIR/Dialect/IR/CIRDialect.cpp
clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
clang/test/CIR/CodeGen/new.cpp
clang/test/CIR/IR/array-ctor.cir
Removed:
################################################################################
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 97b5fee6d6f7e..d2fd2df7ab7d1 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4581,7 +4581,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>>) {
@@ -4589,19 +4596,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");
@@ -4610,9 +4626,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;
}
@@ -4686,6 +4713,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
diff erent
// 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 3f7cdd7758ad5..ab1c0b453ac18 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -289,6 +289,61 @@ 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()) {
+ auto recTy = mlir::dyn_cast<cir::RecordType>(pointeeTy);
+ if (!recTy)
+ return op.emitOpError(
+ "when 'num_elements' is present, 'addr' must be a pointer to a "
+ "!cir.record type");
+
+ 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();
+
+ auto recTy = mlir::dyn_cast<cir::RecordType>(innerEltTy);
+ if (!recTy)
+ return op.emitOpError(
+ "the block argument type must be a pointer to a !cir.record type");
+
+ 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..0ab030231f703
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-array-structor.cir
@@ -0,0 +1,218 @@
+// 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 dso_local @bad_static_array_ctor_with_num_elements(%p: !cir.ptr<!cir.array<!rec_S x 10>>, %n: !u64i) {
+ // expected-error at +1 {{'cir.array.ctor' op when 'num_elements' is present, 'addr' must be a pointer to a !cir.record type}}
+ cir.array.ctor %p, %n : !cir.ptr<!cir.array<!rec_S x 10>>, !u64i {
+ ^bb0(%e: !cir.ptr<!cir.array<!rec_S x 10>>):
+ cir.yield
+ }
+ cir.return
+ }
+}
+
+// -----
+
+!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 at +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>
+!rec_S = !cir.record<struct "S" padded {!u8i}>
+
+module {
+ cir.func private @construct_int(!cir.ptr<!u8i>)
+
+ // Static form: addr must be a pointer to an array type.
+ cir.func @bad_static_array_not_record_ctor(%p: !cir.ptr<!cir.array<!u8i x 4>>) {
+ // expected-error at +1 {{'cir.array.ctor' op the block argument type must be a pointer to a !cir.record type}}
+ cir.array.ctor %p : !cir.ptr<!cir.array<!u8i x 4>> {
+ ^bb0(%e: !cir.ptr<!u8i>):
+ cir.call @construct_int(%e) : (!cir.ptr<!u8i>) -> ()
+ 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 at +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 at +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>
+!u64i = !cir.int<u, 64>
+!rec_S = !cir.record<struct "S" padded {!u8i}>
+
+module {
+ cir.func dso_local @bad_static_array_dtor_with_num_elements(%p: !cir.ptr<!cir.array<!rec_S x 10>>, %n: !u64i) {
+ // expected-error at +1 {{'cir.array.dtor' op when 'num_elements' is present, 'addr' must be a pointer to a !cir.record type}}
+ cir.array.dtor %p, %n : !cir.ptr<!cir.array<!rec_S x 10>>, !u64i {
+ ^bb0(%e: !cir.ptr<!cir.array<!rec_S x 10>>):
+ 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 at +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}>
+
+module {
+ cir.func private @destroy_int(!cir.ptr<!u8i>)
+
+ // Static form: addr must be a pointer to an array type.
+ cir.func @bad_static_array_not_record_dtor(%p: !cir.ptr<!cir.array<!u8i x 4>>) {
+ // expected-error at +1 {{'cir.array.dtor' op the block argument type must be a pointer to a !cir.record type}}
+ cir.array.dtor %p : !cir.ptr<!cir.array<!u8i x 4>> {
+ ^bb0(%e: !cir.ptr<!u8i>):
+ cir.call @destroy_int(%e) : (!cir.ptr<!u8i>) -> ()
+ 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 at +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 at +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 at +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
+ }
+}
More information about the cfe-commits
mailing list