[clang] [CIR] Implement partial array destroy handling (PR #190834)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 7 16:50:21 PDT 2026
https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/190834
>From aa9c3f2d26d513575648ff317f5508ab5f5b305d Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Wed, 1 Apr 2026 15:07:42 -0700
Subject: [PATCH 1/4] [CIR] Implement partial array destroy handling
This implements EH cleanup handling that performs regular partial array
destruction for array constructor loops with a destructed type. Because
CIR represents array construction using an abstract operation, we do
not go through the normal EH stack mechanism. Instead, we add a
partial_dtor region to the cir.array.ctor operation indicating how a
single element should be destructed. When the cir.array.ctor operation
is expanded during LoweringPrepare, we create a cleanup scope with a
partial array dtor loop in an EH cleanup region. This gets further
expanded during CFG flattening to produce a control flow equivalent to
that generated by classic codegen.
Assisted-by: Cursor / claude-4.6-opus-high
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 65 +++++--
clang/lib/CIR/CodeGen/CIRGenClass.cpp | 31 ++--
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 89 +++++++++-
.../Dialect/Transforms/LoweringPrepare.cpp | 118 +++++++++----
clang/test/CIR/CodeGen/array-ctor.cpp | 13 ++
.../CIR/CodeGen/partial-array-cleanup.cpp | 163 ++++++++++++++++++
6 files changed, 420 insertions(+), 59 deletions(-)
create mode 100644 clang/test/CIR/CodeGen/partial-array-cleanup.cpp
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index d2fd2df7ab7d1..bbe1ff71da9f3 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4578,8 +4578,17 @@ def CIR_ArrayCtor : CIR_Op<"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 element to initialize.
+ operation has a `body` region and an optional `partial_dtor` region.
+ Both regions have a single block whose argument is a pointer to the
+ current array element.
+
+ The `body` region contains the constructor call for one element.
+
+ The `partial_dtor` region, when non-empty, contains the destructor call
+ for one element. During lowering, it is used to build a cleanup that
+ destroys already-constructed elements if a constructor throws. When the
+ element type has a trivial destructor or exceptions are disabled, the
+ `partial_dtor` region is left empty.
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.
@@ -4591,17 +4600,30 @@ def CIR_ArrayCtor : CIR_Op<"array.ctor"> {
Examples:
```mlir
+ // Fixed size without partial destructor:
cir.array.ctor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) {
^bb0(%arg0: !cir.ptr<!rec_S>):
cir.call @some_ctor(%arg0) : (!cir.ptr<!rec_S>) -> ()
cir.yield
}
+ // Variable size without partial destructor:
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
}
+
+ // Fixed size with partial destructor:
+ cir.array.ctor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) {
+ ^bb0(%arg0: !cir.ptr<!rec_S>):
+ cir.call @some_ctor(%arg0) : (!cir.ptr<!rec_S>) -> ()
+ cir.yield
+ } partial_dtor {
+ ^bb0(%arg0: !cir.ptr<!rec_S>):
+ cir.call @some_dtor(%arg0) : (!cir.ptr<!rec_S>) -> ()
+ cir.yield
+ }
```
}];
@@ -4610,32 +4632,41 @@ def CIR_ArrayCtor : CIR_Op<"array.ctor"> {
Optional<CIR_AnyIntType>:$num_elements
);
- let regions = (region SizedRegion<1>:$body);
- let assemblyFormat = [{
- $addr (`,` $num_elements^)? `:` qualified(type($addr))
- (`,` type($num_elements)^)? $body attr-dict
- }];
+ let regions = (region SizedRegion<1>:$body, AnyRegion:$partial_dtor);
+ let hasCustomAssemblyFormat = 1;
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");
+ "llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>":$bodyBuilder,
+ "llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>":$partialDtorBuilder), [{
+ assert(bodyBuilder && "builder callback expected");
mlir::OpBuilder::InsertionGuard guard($_builder);
- mlir::Region *r = $_state.addRegion();
$_state.addOperands(ValueRange{addr});
- $_builder.createBlock(r);
- regionBuilder($_builder, $_state.location);
+ mlir::Region *body = $_state.addRegion();
+ $_builder.createBlock(body);
+ bodyBuilder($_builder, $_state.location);
+ mlir::Region *partialDtor = $_state.addRegion();
+ if (partialDtorBuilder) {
+ $_builder.createBlock(partialDtor);
+ partialDtorBuilder($_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");
+ "llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>":$bodyBuilder,
+ "llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>":$partialDtorBuilder), [{
+ assert(bodyBuilder && "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);
+ mlir::Region *body = $_state.addRegion();
+ $_builder.createBlock(body);
+ bodyBuilder($_builder, $_state.location);
+ mlir::Region *partialDtor = $_state.addRegion();
+ if (partialDtorBuilder) {
+ $_builder.createBlock(partialDtor);
+ partialDtorBuilder($_builder, $_state.location);
+ }
}]>
];
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index f8d65fa9a6d33..02e074200204d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -783,20 +783,15 @@ void CIRGenFunction::emitCXXAggrConstructorCall(
{
RunCleanupsScope scope(*this);
- // 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");
- }
+ bool needsPartialArrayCleanup =
+ getLangOpts().Exceptions &&
+ !ctor->getParent()->hasTrivialDestructor();
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(
@@ -809,16 +804,30 @@ void CIRGenFunction::emitCXXAggrConstructorCall(
cir::YieldOp::create(b, l);
};
- // Emit the per-element initialization.
+ llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>
+ emitPartialDtorBody = nullptr;
+ auto partialDtorBuilder = [&](mlir::OpBuilder &b, mlir::Location l) {
+ mlir::BlockArgument arg =
+ b.getInsertionBlock()->addArgument(ptrToElmType, l);
+ Address curAddr = Address(arg, elementType, eltAlignment);
+ emitCXXDestructorCall(ctor->getParent()->getDestructor(), Dtor_Complete,
+ /*forVirtualBase=*/false,
+ /*delegating=*/false, curAddr, type);
+ cir::YieldOp::create(b, l);
+ };
+ if (needsPartialArrayCleanup)
+ emitPartialDtorBody = partialDtorBuilder;
+
if (useDynamicArrayCtor) {
cir::ArrayCtor::create(builder, loc, dynamicElPtr, numElements,
- emitCtorBody);
+ emitCtorBody, emitPartialDtorBody);
} else {
cir::ArrayType arrayTy =
cir::ArrayType::get(elementType, constElementCount);
mlir::Value arrayOp =
builder.createPtrBitcast(arrayBase.getPointer(), arrayTy);
- cir::ArrayCtor::create(builder, loc, arrayOp, emitCtorBody);
+ cir::ArrayCtor::create(builder, loc, arrayOp, emitCtorBody,
+ emitPartialDtorBody);
}
}
}
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index ab1c0b453ac18..3e020e52ebece 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -341,7 +341,94 @@ template <typename Op> static LogicalResult verifyArrayCtorDtor(Op op) {
return success();
}
-LogicalResult cir::ArrayCtor::verify() { return verifyArrayCtorDtor(*this); }
+ParseResult cir::ArrayCtor::parse(OpAsmParser &parser, OperationState &result) {
+ OpAsmParser::UnresolvedOperand addrOperand;
+ OpAsmParser::UnresolvedOperand numElementsOperand;
+ bool hasNumElements = false;
+
+ if (parser.parseOperand(addrOperand))
+ return failure();
+
+ if (succeeded(parser.parseOptionalComma())) {
+ if (parser.parseOperand(numElementsOperand))
+ return failure();
+ hasNumElements = true;
+ }
+
+ if (parser.parseColon())
+ return failure();
+
+ mlir::Type addrType;
+ if (parser.parseType(addrType))
+ return failure();
+ if (parser.resolveOperand(addrOperand, addrType, result.operands))
+ return failure();
+
+ if (hasNumElements) {
+ if (parser.parseComma())
+ return failure();
+ mlir::Type numElementsType;
+ if (parser.parseType(numElementsType))
+ return failure();
+ if (parser.resolveOperand(numElementsOperand, numElementsType,
+ result.operands))
+ return failure();
+ }
+
+ Region *body = result.addRegion();
+ if (parser.parseRegion(*body))
+ return failure();
+
+ Region *partialDtor = result.addRegion();
+ if (!parser.parseOptionalKeyword("partial_dtor")) {
+ if (parser.parseRegion(*partialDtor))
+ return failure();
+ }
+
+ if (parser.parseOptionalAttrDict(result.attributes))
+ return failure();
+
+ return success();
+}
+
+void cir::ArrayCtor::print(OpAsmPrinter &p) {
+ p << " " << getAddr();
+ if (getNumElements())
+ p << ", " << getNumElements();
+
+ p << " : " << getAddr().getType();
+ if (getNumElements())
+ p << ", " << getNumElements().getType();
+
+ p << " ";
+ p.printRegion(getBody());
+
+ if (!getPartialDtor().empty()) {
+ p << " partial_dtor ";
+ p.printRegion(getPartialDtor());
+ }
+
+ p.printOptionalAttrDict(getOperation()->getAttrs());
+}
+
+LogicalResult cir::ArrayCtor::verify() {
+ if (failed(verifyArrayCtorDtor(*this)))
+ return failure();
+
+ mlir::Region &partialDtor = getPartialDtor();
+ if (!partialDtor.empty()) {
+ mlir::Block &dtorBlock = partialDtor.front();
+ if (dtorBlock.getNumArguments() != 1)
+ return emitOpError(
+ "partial_dtor must have exactly one block argument");
+
+ auto bodyArgTy = getBody().front().getArgument(0).getType();
+ if (dtorBlock.getArgument(0).getType() != bodyArgTy)
+ return emitOpError("partial_dtor block argument type must match "
+ "the body block argument type");
+ }
+ return success();
+}
LogicalResult cir::ArrayDtor::verify() { return verifyArrayCtorDtor(*this); }
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 91f082725711f..f5ab406789882 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -1369,13 +1369,16 @@ void LoweringPreparePass::buildCXXGlobalInitFunc() {
cir::ReturnOp::create(builder, f.getLoc());
}
+/// Lower a cir.array.ctor or cir.array.dtor into a do-while loop that
+/// iterates over every element. For cir.array.ctor ops whose partial_dtor
+/// region is non-empty, the ctor loop is wrapped in a cir.cleanup.scope whose
+/// EH cleanup performs a reverse destruction loop using the partial dtor body.
static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
clang::ASTContext *astCtx,
mlir::Operation *op, mlir::Type eltTy,
mlir::Value addr,
mlir::Value numElements,
uint64_t arrayLen, bool isCtor) {
- // Generate loop to call into ctor/dtor for every element.
mlir::Location loc = op->getLoc();
bool isDynamic = numElements != nullptr;
@@ -1432,7 +1435,7 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
// Clone the region body (ctor/dtor call and any setup ops like per-element
// zero-init) into the loop, remapping the block argument to the current
- // element pointer.
+ // element pointer.
auto cloneRegionBodyInto = [&](mlir::Block *srcBlock,
mlir::Value replacement) {
mlir::IRMapping map;
@@ -1443,34 +1446,89 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
}
};
- builder.createDoWhile(
- loc,
- /*condBuilder=*/
- [&](mlir::OpBuilder &b, mlir::Location loc) {
- auto currentElement = cir::LoadOp::create(b, loc, eltTy, tmpAddr);
- auto cmp = cir::CmpOp::create(builder, loc, cir::CmpOpKind::ne,
- currentElement, stop);
- builder.createCondition(cmp);
- },
- /*bodyBuilder=*/
- [&](mlir::OpBuilder &b, mlir::Location loc) {
- auto currentElement = cir::LoadOp::create(b, loc, eltTy, tmpAddr);
- if (isCtor) {
- cloneRegionBodyInto(bodyBlock, currentElement);
- mlir::Value stride = builder.getUnsignedInt(loc, 1, sizeTypeSize);
- auto nextElement = cir::PtrStrideOp::create(builder, loc, eltTy,
- currentElement, stride);
- builder.createStore(loc, nextElement, tmpAddr);
- } else {
- mlir::Value stride = builder.getSignedInt(loc, -1, sizeTypeSize);
- auto prevElement = cir::PtrStrideOp::create(builder, loc, eltTy,
- currentElement, stride);
- builder.createStore(loc, prevElement, tmpAddr);
- cloneRegionBodyInto(bodyBlock, prevElement);
- }
-
- builder.createYield(loc);
- });
+ mlir::Block *partialDtorBlock = nullptr;
+ if (auto arrayCtor = mlir::dyn_cast<cir::ArrayCtor>(op)) {
+ mlir::Region &partialDtor = arrayCtor.getPartialDtor();
+ if (!partialDtor.empty())
+ partialDtorBlock = &partialDtor.front();
+ }
+
+ auto emitCtorDtorLoop = [&]() {
+ builder.createDoWhile(
+ loc,
+ /*condBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ auto currentElement = cir::LoadOp::create(b, loc, eltTy, tmpAddr);
+ auto cmp = cir::CmpOp::create(builder, loc, cir::CmpOpKind::ne,
+ currentElement, stop);
+ builder.createCondition(cmp);
+ },
+ /*bodyBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ auto currentElement = cir::LoadOp::create(b, loc, eltTy, tmpAddr);
+ if (isCtor) {
+ cloneRegionBodyInto(bodyBlock, currentElement);
+ mlir::Value stride = builder.getUnsignedInt(loc, 1, sizeTypeSize);
+ auto nextElement = cir::PtrStrideOp::create(builder, loc, eltTy,
+ currentElement, stride);
+ builder.createStore(loc, nextElement, tmpAddr);
+ } else {
+ mlir::Value stride = builder.getSignedInt(loc, -1, sizeTypeSize);
+ auto prevElement = cir::PtrStrideOp::create(builder, loc, eltTy,
+ currentElement, stride);
+ builder.createStore(loc, prevElement, tmpAddr);
+ cloneRegionBodyInto(bodyBlock, prevElement);
+ }
+
+ builder.createYield(loc);
+ });
+ };
+
+ if (partialDtorBlock) {
+ cir::CleanupScopeOp::create(
+ builder, loc, cir::CleanupKind::EH,
+ /*bodyBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ emitCtorDtorLoop();
+ builder.createYield(loc);
+ },
+ /*cleanupBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ auto cur = cir::LoadOp::create(b, loc, eltTy, tmpAddr);
+ auto cmp = cir::CmpOp::create(builder, loc, cir::CmpOpKind::ne,
+ cur, begin);
+ cir::IfOp::create(
+ builder, loc, cmp, /*withElseRegion=*/false,
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ builder.createDoWhile(
+ loc,
+ /*condBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ auto el =
+ cir::LoadOp::create(b, loc, eltTy, tmpAddr);
+ auto neq = cir::CmpOp::create(
+ builder, loc, cir::CmpOpKind::ne, el, begin);
+ builder.createCondition(neq);
+ },
+ /*bodyBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ auto el =
+ cir::LoadOp::create(b, loc, eltTy, tmpAddr);
+ mlir::Value negOne =
+ builder.getSignedInt(loc, -1, sizeTypeSize);
+ auto prev = cir::PtrStrideOp::create(
+ builder, loc, eltTy, el, negOne);
+ builder.createStore(loc, prev, tmpAddr);
+ cloneRegionBodyInto(partialDtorBlock, prev);
+ builder.createYield(loc);
+ });
+ cir::YieldOp::create(builder, loc);
+ });
+ builder.createYield(loc);
+ });
+ } else {
+ emitCtorDtorLoop();
+ }
if (ifOp)
cir::YieldOp::create(builder, loc);
diff --git a/clang/test/CIR/CodeGen/array-ctor.cpp b/clang/test/CIR/CodeGen/array-ctor.cpp
index 2233529a192c8..f58a46d4afc49 100644
--- a/clang/test/CIR/CodeGen/array-ctor.cpp
+++ b/clang/test/CIR/CodeGen/array-ctor.cpp
@@ -173,3 +173,16 @@ void multi_dimensional() {
// OGCG: br i1 %[[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
// OGCG: [[EXIT]]:
// OGCG: ret void
+
+struct HasDtor {
+ HasDtor();
+ ~HasDtor();
+};
+
+void test_partial_array_cleanup() {
+ HasDtor s[4];
+}
+
+// CIR-BEFORE-LPP: cir.func {{.*}} @_Z26test_partial_array_cleanupv()
+
+// CIR: cir.func {{.*}} @_Z26test_partial_array_cleanupv()
diff --git a/clang/test/CIR/CodeGen/partial-array-cleanup.cpp b/clang/test/CIR/CodeGen/partial-array-cleanup.cpp
new file mode 100644
index 0000000000000..f41f80ab601e0
--- /dev/null
+++ b/clang/test/CIR/CodeGen/partial-array-cleanup.cpp
@@ -0,0 +1,163 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -fexceptions -fcxx-exceptions -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2> %t-before-lp.cir
+// RUN: FileCheck --input-file=%t-before-lp.cir %s -check-prefix=CIR-BEFORE-LPP
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fexceptions -fcxx-exceptions -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 -fexceptions -fcxx-exceptions -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+struct S {
+ S();
+ ~S();
+};
+
+void test_partial_array_cleanup() {
+ S s[4];
+}
+
+// CIR-BEFORE-LPP: cir.func {{.*}} @_Z26test_partial_array_cleanupv()
+// CIR-BEFORE-LPP: %[[ARRAY:.*]] = cir.alloca !cir.array<!rec_S x 4>, !cir.ptr<!cir.array<!rec_S x 4>>, ["s", init]
+// CIR-BEFORE-LPP: cir.array.ctor %[[ARRAY]] : !cir.ptr<!cir.array<!rec_S x 4>> {
+// CIR-BEFORE-LPP: ^bb0(%[[CTOR_ARG:.*]]: !cir.ptr<!rec_S>):
+// CIR-BEFORE-LPP: cir.call @_ZN1SC1Ev(%[[CTOR_ARG]]) : (!cir.ptr<!rec_S>{{.*}}) -> ()
+// CIR-BEFORE-LPP: cir.yield
+// CIR-BEFORE-LPP: } partial_dtor {
+// CIR-BEFORE-LPP: ^bb0(%[[DTOR_ARG:.*]]: !cir.ptr<!rec_S>):
+// CIR-BEFORE-LPP: cir.call @_ZN1SD1Ev(%[[DTOR_ARG]]){{.*}} : (!cir.ptr<!rec_S>{{.*}}) -> ()
+// CIR-BEFORE-LPP: cir.yield
+// CIR-BEFORE-LPP: }
+
+// CIR: cir.func {{.*}} @_Z26test_partial_array_cleanupv()
+// CIR: %[[ARRAY:.*]] = cir.alloca !cir.array<!rec_S x 4>, !cir.ptr<!cir.array<!rec_S x 4>>, ["s", init]
+// CIR: %[[CONST4:.*]] = cir.const #cir.int<4> : !u64i
+// CIR: %[[BEGIN:.*]] = cir.cast array_to_ptrdecay %[[ARRAY]] : !cir.ptr<!cir.array<!rec_S x 4>> -> !cir.ptr<!rec_S>
+// CIR: %[[END:.*]] = cir.ptr_stride %[[BEGIN]], %[[CONST4]] : (!cir.ptr<!rec_S>, !u64i) -> !cir.ptr<!rec_S>
+// CIR: %[[ITER:.*]] = cir.alloca !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>, ["__array_idx"]
+// CIR: cir.store %[[BEGIN]], %[[ITER]] : !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>
+// CIR: cir.cleanup.scope {
+// CIR: cir.do {
+// CIR: %[[CUR:.*]] = cir.load %[[ITER]] : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
+// CIR: cir.call @_ZN1SC1Ev(%[[CUR]]) : (!cir.ptr<!rec_S>{{.*}}) -> ()
+// CIR: %[[CONST1:.*]] = cir.const #cir.int<1> : !u64i
+// CIR: %[[NEXT:.*]] = cir.ptr_stride %[[CUR]], %[[CONST1]] : (!cir.ptr<!rec_S>, !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: %[[CUR2:.*]] = cir.load %[[ITER]] : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
+// CIR: %[[CMP:.*]] = cir.cmp ne %[[CUR2]], %[[END]] : !cir.ptr<!rec_S>
+// CIR: cir.condition(%[[CMP]])
+// CIR: }
+// CIR: cir.yield
+// CIR: } cleanup eh {
+// CIR: %[[CUR3:.*]] = cir.load %[[ITER]] : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
+// CIR: %[[NE:.*]] = cir.cmp ne %[[CUR3]], %[[BEGIN]] : !cir.ptr<!rec_S>
+// CIR: cir.if %[[NE]] {
+// CIR: cir.do {
+// CIR: %[[EL:.*]] = cir.load %[[ITER]] : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
+// CIR: %[[NEG1:.*]] = cir.const #cir.int<-1> : !s64i
+// CIR: %[[PREV:.*]] = cir.ptr_stride %[[EL]], %[[NEG1]] : (!cir.ptr<!rec_S>, !s64i) -> !cir.ptr<!rec_S>
+// CIR: cir.store %[[PREV]], %[[ITER]] : !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>
+// CIR: cir.call @_ZN1SD1Ev(%[[PREV]]){{.*}} : (!cir.ptr<!rec_S>{{.*}}) -> ()
+// CIR: cir.yield
+// CIR: } while {
+// CIR: %[[EL2:.*]] = cir.load %[[ITER]] : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
+// CIR: %[[NE2:.*]] = cir.cmp ne %[[EL2]], %[[BEGIN]] : !cir.ptr<!rec_S>
+// CIR: cir.condition(%[[NE2]])
+// CIR: }
+// CIR: }
+// CIR: cir.yield
+// CIR: }
+
+// LLVM: define dso_local void @_Z26test_partial_array_cleanupv()
+// LLVM: %[[ARRAY:.*]] = alloca [4 x %struct.S]
+// LLVM: %[[BEGIN:.*]] = getelementptr %struct.S, ptr %[[ARRAY]], i32 0
+// LLVM: %[[END:.*]] = getelementptr %struct.S, ptr %[[BEGIN]], i64 4
+// LLVM: %[[ITER:.*]] = alloca ptr
+// LLVM: store ptr %[[BEGIN]], ptr %[[ITER]]
+//
+// --- ctor loop condition ---
+// LLVM: [[CLEANUP_SCOPE:.*]]:
+// LLVM: br label %[[CTOR_LOOP_COND:.*]]
+//
+// LLVM: [[CTOR_LOOP_COND:]]:
+// LLVM: %[[COND_CUR:.*]] = load ptr, ptr %[[ITER]]
+// LLVM: %[[CTOR_DONE:.*]] = icmp ne ptr %[[COND_CUR]], %[[END]]
+// LLVM: br i1 %[[CTOR_DONE]], label %[[CTOR_BODY:.*]], label %[[CTOR_EXIT:.*]]
+//
+// --- ctor loop body ---
+// LLVM: [[CTOR_BODY]]:
+// LLVM: %[[CUR:.*]] = load ptr, ptr %[[ITER]]
+// LLVM: invoke void @_ZN1SC1Ev(ptr{{.*}} %[[CUR]])
+// LLVM: to label %[[CTOR_CONT:.*]] unwind label %[[LPAD:.*]]
+//
+// LLVM: [[CTOR_CONT]]:
+// LLVM: %[[NEXT:.*]] = getelementptr %struct.S, ptr %[[CUR]], i64 1
+// LLVM: store ptr %[[NEXT]], ptr %[[ITER]]
+// LLVM: br label %[[CTOR_LOOP_COND]]
+//
+// --- landing pad + cleanup guard ---
+// LLVM: [[LPAD]]:
+// LLVM: landingpad { ptr, i32 }
+// LLVM: cleanup
+// LLVM: %[[PAD_CUR:.*]] = load ptr, ptr %[[ITER]]
+// LLVM: %[[GUARD:.*]] = icmp ne ptr %[[PAD_CUR]], %[[BEGIN]]
+// LLVM: br i1 %[[GUARD]], label %[[DTOR_ENTRY:.*]], label %[[EH_RESUME:.*]]
+//
+// --- partial dtor do-while entry ---
+// LLVM: [[DTOR_ENTRY]]:
+// LLVM: br label %[[DTOR_BODY:.*]]
+//
+// --- partial dtor loop condition (back-edge) ---
+// LLVM: [[DTOR_LOOP_COND:.*]]:
+// LLVM: %[[DTOR_CUR:.*]] = load ptr, ptr %[[ITER]]
+// LLVM: %[[DTOR_CONT:.*]] = icmp ne ptr %[[DTOR_CUR]], %[[BEGIN]]
+// LLVM: br i1 %[[DTOR_CONT]], label %[[DTOR_BODY]], label %[[DTOR_DONE:.*]]
+//
+// --- partial dtor loop body ---
+// LLVM: [[DTOR_BODY]]:
+// LLVM: %[[DCUR:.*]] = load ptr, ptr %[[ITER]]
+// LLVM: %[[PREV:.*]] = getelementptr %struct.S, ptr %[[DCUR]], i64 -1
+// LLVM: store ptr %[[PREV]], ptr %[[ITER]]
+// LLVM: call void @_ZN1SD1Ev(ptr{{.*}} %[[PREV]])
+// LLVM: br label %[[DTOR_LOOP_COND]]
+//
+// LLVM: [[DTOR_DONE]]:
+// LLVM: br label %[[EH_RESUME]]
+//
+// LLVM: [[EH_RESUME]]:
+// LLVM: resume { ptr, i32 }
+
+// OGCG: define dso_local void @_Z26test_partial_array_cleanupv()
+// OGCG: [[ENTRY:.*]]:
+// OGCG: %[[ARRAY:.*]] = alloca [4 x %struct.S]
+// OGCG: %[[BEGIN:.*]] = getelementptr inbounds [4 x %struct.S], ptr %[[ARRAY]], i32 0, i32 0
+// OGCG: %[[END:.*]] = getelementptr inbounds %struct.S, ptr %[[BEGIN]], i64 4
+//
+// --- ctor loop ---
+// OGCG: [[CTOR_BODY:.*]]:
+// OGCG: %[[CUR:.*]] = phi ptr [ %[[BEGIN]], %[[ENTRY]] ], [ %[[NEXT:.*]], %[[CONT:.*]] ]
+// OGCG: invoke void @_ZN1SC1Ev(ptr{{.*}})
+// OGCG: to label %[[CONT]] unwind label %[[LPAD:.*]]
+//
+// OGCG: [[CONT]]:
+// OGCG: %[[NEXT]] = getelementptr inbounds %struct.S, ptr %[[CUR]], i64 1
+// OGCG: %[[DONE:.*]] = icmp eq ptr %[[NEXT]], %[[END]]
+// OGCG: br i1 %[[DONE]], label %[[CTOR_EXIT:.*]], label %[[CTOR_BODY:.*]]
+//
+// --- landing pad + cleanup guard ---
+// OGCG: [[LPAD]]:
+// OGCG: landingpad { ptr, i32 }
+// OGCG: cleanup
+// OGCG: %[[ISEMPTY:.*]] = icmp eq ptr %[[BEGIN]], %[[CUR]]
+// OGCG: br i1 %[[ISEMPTY]], label %[[EH_RESUME:.*]], label %[[DTOR_LOOP:.*]]
+//
+// --- partial dtor loop ---
+// OGCG: [[DTOR_LOOP]]:
+// OGCG: %[[PAST:.*]] = phi ptr [ %[[CUR]], %[[LPAD]] ], [ %[[PREV:.*]], %[[DTOR_LOOP]] ]
+// OGCG: %[[PREV]] = getelementptr inbounds %struct.S, ptr %[[PAST]], i64 -1
+// OGCG: call void @_ZN1SD1Ev(ptr{{.*}} %[[PREV]])
+// OGCG: %[[DDONE:.*]] = icmp eq ptr %[[PREV]], %[[BEGIN]]
+// OGCG: br i1 %[[DDONE]], label %[[EH_RESUME]], label %[[DTOR_LOOP]]
+//
+// OGCG: [[EH_RESUME]]:
+// OGCG: resume { ptr, i32 }
>From 6fe2848c813506c0b4e182f568b7b0fe8d90b06d Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Tue, 7 Apr 2026 13:07:44 -0700
Subject: [PATCH 2/4] Fix formatting
---
clang/lib/CIR/CodeGen/CIRGenClass.cpp | 3 +--
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 3 +--
.../CIR/Dialect/Transforms/LoweringPrepare.cpp | 16 +++++++---------
3 files changed, 9 insertions(+), 13 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 02e074200204d..748d450d6dc10 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -784,8 +784,7 @@ void CIRGenFunction::emitCXXAggrConstructorCall(
RunCleanupsScope scope(*this);
bool needsPartialArrayCleanup =
- getLangOpts().Exceptions &&
- !ctor->getParent()->hasTrivialDestructor();
+ getLangOpts().Exceptions && !ctor->getParent()->hasTrivialDestructor();
auto emitCtorBody = [&](mlir::OpBuilder &b, mlir::Location l) {
mlir::BlockArgument arg =
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 3e020e52ebece..61f61466c7e2e 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -419,8 +419,7 @@ LogicalResult cir::ArrayCtor::verify() {
if (!partialDtor.empty()) {
mlir::Block &dtorBlock = partialDtor.front();
if (dtorBlock.getNumArguments() != 1)
- return emitOpError(
- "partial_dtor must have exactly one block argument");
+ return emitOpError("partial_dtor must have exactly one block argument");
auto bodyArgTy = getBody().front().getArgument(0).getType();
if (dtorBlock.getArgument(0).getType() != bodyArgTy)
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index f5ab406789882..5e00e0c8fb146 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -1435,7 +1435,7 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
// Clone the region body (ctor/dtor call and any setup ops like per-element
// zero-init) into the loop, remapping the block argument to the current
- // element pointer.
+ // element pointer.
auto cloneRegionBodyInto = [&](mlir::Block *srcBlock,
mlir::Value replacement) {
mlir::IRMapping map;
@@ -1495,8 +1495,8 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
/*cleanupBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
auto cur = cir::LoadOp::create(b, loc, eltTy, tmpAddr);
- auto cmp = cir::CmpOp::create(builder, loc, cir::CmpOpKind::ne,
- cur, begin);
+ auto cmp =
+ cir::CmpOp::create(builder, loc, cir::CmpOpKind::ne, cur, begin);
cir::IfOp::create(
builder, loc, cmp, /*withElseRegion=*/false,
[&](mlir::OpBuilder &b, mlir::Location loc) {
@@ -1504,20 +1504,18 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
loc,
/*condBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
- auto el =
- cir::LoadOp::create(b, loc, eltTy, tmpAddr);
+ auto el = cir::LoadOp::create(b, loc, eltTy, tmpAddr);
auto neq = cir::CmpOp::create(
builder, loc, cir::CmpOpKind::ne, el, begin);
builder.createCondition(neq);
},
/*bodyBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
- auto el =
- cir::LoadOp::create(b, loc, eltTy, tmpAddr);
+ auto el = cir::LoadOp::create(b, loc, eltTy, tmpAddr);
mlir::Value negOne =
builder.getSignedInt(loc, -1, sizeTypeSize);
- auto prev = cir::PtrStrideOp::create(
- builder, loc, eltTy, el, negOne);
+ auto prev = cir::PtrStrideOp::create(builder, loc, eltTy,
+ el, negOne);
builder.createStore(loc, prev, tmpAddr);
cloneRegionBodyInto(partialDtorBlock, prev);
builder.createYield(loc);
>From f76854ddc3106d8ca7d5af2e9a7b92dbc5637063 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Tue, 7 Apr 2026 16:37:18 -0700
Subject: [PATCH 3/4] Address review feedback
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 21 +++---
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 70 -------------------
.../Dialect/Transforms/LoweringPrepare.cpp | 6 +-
clang/test/CIR/CodeGen/array-ctor.cpp | 2 -
clang/test/CIR/CodeGen/array-dtor.cpp | 2 -
clang/test/CIR/CodeGen/global-array-dtor.cpp | 1 -
clang/test/CIR/CodeGen/global-init.cpp | 1 -
clang/test/CIR/CodeGen/new.cpp | 6 --
.../CIR/CodeGen/partial-array-cleanup.cpp | 2 -
clang/test/CIR/IR/array-ctor.cir | 4 --
clang/test/CIR/IR/array-dtor.cir | 2 -
clang/test/CIR/IR/invalid-array-structor.cir | 11 ---
12 files changed, 15 insertions(+), 113 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index bbe1ff71da9f3..93ca0172b2a7f 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4574,7 +4574,9 @@ def CIR_TrapOp : CIR_Op<"trap", [Terminator]> {
// ArrayCtor & ArrayDtor
//===----------------------------------------------------------------------===//
-def CIR_ArrayCtor : CIR_Op<"array.ctor"> {
+def CIR_ArrayCtor : CIR_Op<"array.ctor", [
+ SingleBlockImplicitTerminator<"cir::YieldOp">
+]> {
let summary = "Initialize array elements with C++ constructors";
let description = [{
Initialize each array element using the same C++ constructor. This
@@ -4604,25 +4606,21 @@ def CIR_ArrayCtor : CIR_Op<"array.ctor"> {
cir.array.ctor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) {
^bb0(%arg0: !cir.ptr<!rec_S>):
cir.call @some_ctor(%arg0) : (!cir.ptr<!rec_S>) -> ()
- cir.yield
}
// Variable size without partial destructor:
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
}
// Fixed size with partial destructor:
cir.array.ctor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) {
^bb0(%arg0: !cir.ptr<!rec_S>):
cir.call @some_ctor(%arg0) : (!cir.ptr<!rec_S>) -> ()
- cir.yield
} partial_dtor {
^bb0(%arg0: !cir.ptr<!rec_S>):
cir.call @some_dtor(%arg0) : (!cir.ptr<!rec_S>) -> ()
- cir.yield
}
```
}];
@@ -4633,7 +4631,12 @@ def CIR_ArrayCtor : CIR_Op<"array.ctor"> {
);
let regions = (region SizedRegion<1>:$body, AnyRegion:$partial_dtor);
- let hasCustomAssemblyFormat = 1;
+ let assemblyFormat = [{
+ $addr (`,` $num_elements^)? `:` qualified(type($addr))
+ (`,` type($num_elements)^)? $body
+ (`partial_dtor` $partial_dtor^)?
+ attr-dict
+ }];
let builders = [
// Static form: addr is ptr<array<T x N>>, no num_elements.
@@ -4674,7 +4677,9 @@ def CIR_ArrayCtor : CIR_Op<"array.ctor"> {
let hasLLVMLowering = false;
}
-def CIR_ArrayDtor : CIR_Op<"array.dtor"> {
+def CIR_ArrayDtor : CIR_Op<"array.dtor", [
+ SingleBlockImplicitTerminator<"cir::YieldOp">
+]> {
let summary = "Destroy array elements with C++ destructors";
let description = [{
Destroy each array element using the same C++ destructor. This operation
@@ -4697,14 +4702,12 @@ def CIR_ArrayDtor : CIR_Op<"array.dtor"> {
cir.array.dtor %0 : !cir.ptr<!cir.array<!rec_S x 42>> {
^bb0(%arg0: !cir.ptr<!rec_S>):
cir.call @_ZN1SD1Ev(%arg0) : (!cir.ptr<!rec_S>) -> ()
- cir.yield
}
// Dynamic count (delete[] with destructor):
cir.array.dtor %ptr, %n : !cir.ptr<!rec_S>, !u64i {
^bb0(%arg0: !cir.ptr<!rec_S>):
cir.call @_ZN1SD1Ev(%arg0) : (!cir.ptr<!rec_S>) -> ()
- cir.yield
}
```
}];
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 61f61466c7e2e..e99faf840c15a 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -341,76 +341,6 @@ template <typename Op> static LogicalResult verifyArrayCtorDtor(Op op) {
return success();
}
-ParseResult cir::ArrayCtor::parse(OpAsmParser &parser, OperationState &result) {
- OpAsmParser::UnresolvedOperand addrOperand;
- OpAsmParser::UnresolvedOperand numElementsOperand;
- bool hasNumElements = false;
-
- if (parser.parseOperand(addrOperand))
- return failure();
-
- if (succeeded(parser.parseOptionalComma())) {
- if (parser.parseOperand(numElementsOperand))
- return failure();
- hasNumElements = true;
- }
-
- if (parser.parseColon())
- return failure();
-
- mlir::Type addrType;
- if (parser.parseType(addrType))
- return failure();
- if (parser.resolveOperand(addrOperand, addrType, result.operands))
- return failure();
-
- if (hasNumElements) {
- if (parser.parseComma())
- return failure();
- mlir::Type numElementsType;
- if (parser.parseType(numElementsType))
- return failure();
- if (parser.resolveOperand(numElementsOperand, numElementsType,
- result.operands))
- return failure();
- }
-
- Region *body = result.addRegion();
- if (parser.parseRegion(*body))
- return failure();
-
- Region *partialDtor = result.addRegion();
- if (!parser.parseOptionalKeyword("partial_dtor")) {
- if (parser.parseRegion(*partialDtor))
- return failure();
- }
-
- if (parser.parseOptionalAttrDict(result.attributes))
- return failure();
-
- return success();
-}
-
-void cir::ArrayCtor::print(OpAsmPrinter &p) {
- p << " " << getAddr();
- if (getNumElements())
- p << ", " << getNumElements();
-
- p << " : " << getAddr().getType();
- if (getNumElements())
- p << ", " << getNumElements().getType();
-
- p << " ";
- p.printRegion(getBody());
-
- if (!getPartialDtor().empty()) {
- p << " partial_dtor ";
- p.printRegion(getPartialDtor());
- }
-
- p.printOptionalAttrDict(getOperation()->getAttrs());
-}
-
LogicalResult cir::ArrayCtor::verify() {
if (failed(verifyArrayCtorDtor(*this)))
return failure();
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 5e00e0c8fb146..482e73f9f3050 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -1480,7 +1480,7 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
cloneRegionBodyInto(bodyBlock, prevElement);
}
- builder.createYield(loc);
+ cir::YieldOp::create(b, loc);
});
};
@@ -1490,7 +1490,7 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
/*bodyBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
emitCtorDtorLoop();
- builder.createYield(loc);
+ cir::YieldOp::create(b, loc);
},
/*cleanupBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
@@ -1522,7 +1522,7 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
});
cir::YieldOp::create(builder, loc);
});
- builder.createYield(loc);
+ cir::YieldOp::create(b, loc);
});
} else {
emitCtorDtorLoop();
diff --git a/clang/test/CIR/CodeGen/array-ctor.cpp b/clang/test/CIR/CodeGen/array-ctor.cpp
index f58a46d4afc49..be5856456ed90 100644
--- a/clang/test/CIR/CodeGen/array-ctor.cpp
+++ b/clang/test/CIR/CodeGen/array-ctor.cpp
@@ -19,7 +19,6 @@ void foo() {
// 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: }
@@ -115,7 +114,6 @@ void multi_dimensional() {
// CIR-BEFORE-LPP: cir.array.ctor %[[FLAT]] : !cir.ptr<!cir.array<!rec_S x 15>> {
// 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
diff --git a/clang/test/CIR/CodeGen/array-dtor.cpp b/clang/test/CIR/CodeGen/array-dtor.cpp
index d37b6323575d6..5bfe9ac897cb7 100644
--- a/clang/test/CIR/CodeGen/array-dtor.cpp
+++ b/clang/test/CIR/CodeGen/array-dtor.cpp
@@ -19,7 +19,6 @@ void test_cleanup_array() {
// CIR-BEFORE-LPP: cir.array.dtor %[[S]] : !cir.ptr<!cir.array<!rec_S x 42>> {
// CIR-BEFORE-LPP: ^bb0(%arg0: !cir.ptr<!rec_S>
// CIR-BEFORE-LPP: cir.call @_ZN1SD1Ev(%arg0) nothrow : (!cir.ptr<!rec_S> {{.*}}) -> ()
-// CIR-BEFORE-LPP: cir.yield
// CIR-BEFORE-LPP: }
// CIR-BEFORE-LPP: cir.return
@@ -113,7 +112,6 @@ void multi_dimensional() {
// CIR-BEFORE-LPP: cir.array.dtor %[[FLAT]] : !cir.ptr<!cir.array<!rec_S x 15>> {
// CIR-BEFORE-LPP: ^bb0(%[[ARG:.*]]: !cir.ptr<!rec_S>):
// CIR-BEFORE-LPP: cir.call @_ZN1SD1Ev(%[[ARG]]) nothrow : (!cir.ptr<!rec_S> {{.*}}) -> ()
-// CIR-BEFORE-LPP: cir.yield
// CIR-BEFORE-LPP: }
// CIR-BEFORE-LPP: cir.return
diff --git a/clang/test/CIR/CodeGen/global-array-dtor.cpp b/clang/test/CIR/CodeGen/global-array-dtor.cpp
index 61b1717b156fb..49c8e279c2678 100644
--- a/clang/test/CIR/CodeGen/global-array-dtor.cpp
+++ b/clang/test/CIR/CodeGen/global-array-dtor.cpp
@@ -22,7 +22,6 @@ ArrayDtor arrDtor[16];
// CIR-BEFORE-LPP: cir.array.dtor %[[THIS]] : !cir.ptr<!cir.array<!rec_ArrayDtor x 16>> {
// CIR-BEFORE-LPP: ^bb0(%[[ELEM:.*]]: !cir.ptr<!rec_ArrayDtor>):
// CIR-BEFORE-LPP: cir.call @_ZN9ArrayDtorD1Ev(%[[ELEM]]) nothrow : (!cir.ptr<!rec_ArrayDtor> {{.*}}) -> ()
-// CIR-BEFORE-LPP: cir.yield
// CIR-BEFORE-LPP: }
// CIR-BEFORE-LPP: }
diff --git a/clang/test/CIR/CodeGen/global-init.cpp b/clang/test/CIR/CodeGen/global-init.cpp
index 7c6405543b3fe..7ef21cab24940 100644
--- a/clang/test/CIR/CodeGen/global-init.cpp
+++ b/clang/test/CIR/CodeGen/global-init.cpp
@@ -183,7 +183,6 @@ ArrayDtor arrDtor[16];
// CIR-BEFORE-LPP: cir.array.dtor %[[THIS]] : !cir.ptr<!cir.array<!rec_ArrayDtor x 16>> {
// CIR-BEFORE-LPP: ^bb0(%[[ELEM:.*]]: !cir.ptr<!rec_ArrayDtor>):
// CIR-BEFORE-LPP: cir.call @_ZN9ArrayDtorD1Ev(%[[ELEM]]) nothrow : (!cir.ptr<!rec_ArrayDtor> {{.*}}) -> ()
-// CIR-BEFORE-LPP: cir.yield
// CIR-BEFORE-LPP: }
// CIR-BEFORE-LPP: }
diff --git a/clang/test/CIR/CodeGen/new.cpp b/clang/test/CIR/CodeGen/new.cpp
index 7da943de355ef..503b395244bd8 100644
--- a/clang/test/CIR/CodeGen/new.cpp
+++ b/clang/test/CIR/CodeGen/new.cpp
@@ -742,7 +742,6 @@ void test_array_new_with_ctor_init() {
// CIR-BEFORE-LPP: cir.array.ctor %[[ARRAY_PTR]] : !cir.ptr<!cir.array<!rec_F x 3>> {
// CIR-BEFORE-LPP: ^bb0(%[[ARG:.*]]: !cir.ptr<!rec_F>):
// CIR-BEFORE-LPP: cir.call @_ZN1FC1Ev(%[[ARG]]) : (!cir.ptr<!rec_F> {{.*}}) -> ()
-// CIR-BEFORE-LPP: cir.yield
// CIR-BEFORE-LPP: }
// CIR-BEFORE-LPP: cir.store{{.*}} %[[BEGIN]], %[[P_ADDR]] : !cir.ptr<!rec_F>, !cir.ptr<!cir.ptr<!rec_F>>
// CIR-BEFORE-LPP: cir.return
@@ -868,7 +867,6 @@ void test_const_array_new_value_init() {
// CIR-BEFORE-LPP: cir.const #cir.zero : !rec_OuterZero
// CIR-BEFORE-LPP: cir.store{{.*}} %{{.*}}, %[[EL]] : !rec_OuterZero, !cir.ptr<!rec_OuterZero>
// CIR-BEFORE-LPP: cir.call @_ZN9OuterZeroC1Ev(%[[EL]])
-// CIR-BEFORE-LPP: cir.yield
// CIR-BEFORE-LPP: }
// CHECK: cir.func{{.*}} @_Z31test_const_array_new_value_initv
@@ -949,7 +947,6 @@ void test_var_array_new_value_init(int n) {
// 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
@@ -990,7 +987,6 @@ void test_multidim_array_new_with_ctor() {
// CIR-BEFORE-LPP: cir.array.ctor %[[FLAT]] : !cir.ptr<!cir.array<!rec_F x 6>> {
// CIR-BEFORE-LPP: ^bb0(%[[ARG:.*]]: !cir.ptr<!rec_F>):
// CIR-BEFORE-LPP: cir.call @_ZN1FC1Ev(%[[ARG]])
-// CIR-BEFORE-LPP: cir.yield
// CIR-BEFORE-LPP: }
// CIR-BEFORE-LPP: cir.store{{.*}} %[[PTR3]],
@@ -1059,7 +1055,6 @@ void test_multidim_var_array_new_with_ctor(int n) {
// 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
@@ -1129,7 +1124,6 @@ void test_array_new_with_ctor_partial_init_list() {
// CIR-BEFORE-LPP: cir.array.ctor %[[TAIL_ARRAY]] : !cir.ptr<!cir.array<!rec_G x 6>> {
// CIR-BEFORE-LPP: ^bb0(%[[ELEM:.*]]: !cir.ptr<!rec_G>):
// CIR-BEFORE-LPP: cir.call @_ZN1GC1Ev(%[[ELEM]]) : (!cir.ptr<!rec_G> {{.*}}) -> ()
-// CIR-BEFORE-LPP: cir.yield
// CIR-BEFORE-LPP: }
// CIR-BEFORE-LPP: cir.store{{.*}} %[[BEGIN]], %[[P_ADDR]]
// CIR-BEFORE-LPP: cir.return
diff --git a/clang/test/CIR/CodeGen/partial-array-cleanup.cpp b/clang/test/CIR/CodeGen/partial-array-cleanup.cpp
index f41f80ab601e0..7c260a6f13cb0 100644
--- a/clang/test/CIR/CodeGen/partial-array-cleanup.cpp
+++ b/clang/test/CIR/CodeGen/partial-array-cleanup.cpp
@@ -20,11 +20,9 @@ void test_partial_array_cleanup() {
// CIR-BEFORE-LPP: cir.array.ctor %[[ARRAY]] : !cir.ptr<!cir.array<!rec_S x 4>> {
// CIR-BEFORE-LPP: ^bb0(%[[CTOR_ARG:.*]]: !cir.ptr<!rec_S>):
// CIR-BEFORE-LPP: cir.call @_ZN1SC1Ev(%[[CTOR_ARG]]) : (!cir.ptr<!rec_S>{{.*}}) -> ()
-// CIR-BEFORE-LPP: cir.yield
// CIR-BEFORE-LPP: } partial_dtor {
// CIR-BEFORE-LPP: ^bb0(%[[DTOR_ARG:.*]]: !cir.ptr<!rec_S>):
// CIR-BEFORE-LPP: cir.call @_ZN1SD1Ev(%[[DTOR_ARG]]){{.*}} : (!cir.ptr<!rec_S>{{.*}}) -> ()
-// CIR-BEFORE-LPP: cir.yield
// CIR-BEFORE-LPP: }
// CIR: cir.func {{.*}} @_Z26test_partial_array_cleanupv()
diff --git a/clang/test/CIR/IR/array-ctor.cir b/clang/test/CIR/IR/array-ctor.cir
index 0a71b1e4dee6d..b68108b1763eb 100644
--- a/clang/test/CIR/IR/array-ctor.cir
+++ b/clang/test/CIR/IR/array-ctor.cir
@@ -12,7 +12,6 @@ module {
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
}
@@ -23,7 +22,6 @@ module {
// 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: }
@@ -32,7 +30,6 @@ module {
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
}
@@ -41,7 +38,6 @@ module {
// 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/array-dtor.cir b/clang/test/CIR/IR/array-dtor.cir
index 1bb9ff9169a9d..37515f98626be 100644
--- a/clang/test/CIR/IR/array-dtor.cir
+++ b/clang/test/CIR/IR/array-dtor.cir
@@ -10,7 +10,6 @@ module {
cir.array.dtor %0 : !cir.ptr<!cir.array<!rec_S x 42>> {
^bb0(%arg0: !cir.ptr<!rec_S>):
cir.call @_ZN1SD1Ev(%arg0) : (!cir.ptr<!rec_S>) -> ()
- cir.yield
}
cir.return
}
@@ -21,7 +20,6 @@ module {
// CHECK: cir.array.dtor %0 : !cir.ptr<!cir.array<!rec_S x 42>> {
// CHECK: ^bb0(%arg0: !cir.ptr<!rec_S>):
// CHECK: cir.call @_ZN1SD1Ev(%arg0) : (!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
index 0ab030231f703..233b3524a2369 100644
--- a/clang/test/CIR/IR/invalid-array-structor.cir
+++ b/clang/test/CIR/IR/invalid-array-structor.cir
@@ -10,7 +10,6 @@ module {
// 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
}
@@ -31,7 +30,6 @@ module {
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
}
@@ -51,7 +49,6 @@ module {
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
}
@@ -72,7 +69,6 @@ module {
// 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
}
@@ -94,7 +90,6 @@ module {
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
}
@@ -111,7 +106,6 @@ module {
// 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
}
@@ -129,7 +123,6 @@ module {
// 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
}
@@ -149,7 +142,6 @@ module {
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
}
@@ -168,7 +160,6 @@ module {
// 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
}
@@ -189,7 +180,6 @@ module {
// 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
}
@@ -211,7 +201,6 @@ module {
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
}
>From f9b25b24bf1bb9c6b50b665263da5ba179d8052a Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Tue, 7 Apr 2026 16:48:47 -0700
Subject: [PATCH 4/4] Remove stray test
I inadvertantly left a test case in array-ctor.cpp that was part of an
exploration for the next PR in this series. It wasn't actually testing
anything useful here.
---
clang/test/CIR/CodeGen/array-ctor.cpp | 13 -------------
1 file changed, 13 deletions(-)
diff --git a/clang/test/CIR/CodeGen/array-ctor.cpp b/clang/test/CIR/CodeGen/array-ctor.cpp
index be5856456ed90..4a3a4f51db13b 100644
--- a/clang/test/CIR/CodeGen/array-ctor.cpp
+++ b/clang/test/CIR/CodeGen/array-ctor.cpp
@@ -171,16 +171,3 @@ void multi_dimensional() {
// OGCG: br i1 %[[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
// OGCG: [[EXIT]]:
// OGCG: ret void
-
-struct HasDtor {
- HasDtor();
- ~HasDtor();
-};
-
-void test_partial_array_cleanup() {
- HasDtor s[4];
-}
-
-// CIR-BEFORE-LPP: cir.func {{.*}} @_Z26test_partial_array_cleanupv()
-
-// CIR: cir.func {{.*}} @_Z26test_partial_array_cleanupv()
More information about the cfe-commits
mailing list