[clang] 5f27de8 - [CIR] Implement variable size array cleanup (#191247)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Apr 9 15:07:56 PDT 2026
Author: Andy Kaylor
Date: 2026-04-09T15:07:50-07:00
New Revision: 5f27de8c4d895f2128f7ca5ce788d997fd3ea99c
URL: https://github.com/llvm/llvm-project/commit/5f27de8c4d895f2128f7ca5ce788d997fd3ea99c
DIFF: https://github.com/llvm/llvm-project/commit/5f27de8c4d895f2128f7ca5ce788d997fd3ea99c.diff
LOG: [CIR] Implement variable size array cleanup (#191247)
This implements partial array destruction for variable sized arrays. The
cir.array.dtor operation already had support for variable length, so
this change only needs to add the variable handling in
`emitArrayDestroy` and `emitArrayLength`.
Assisted-by: Cursor / claude-4.6-opus-high
Added:
Modified:
clang/lib/CIR/CodeGen/CIRGenDecl.cpp
clang/lib/CIR/CodeGen/CIRGenFunction.cpp
clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
clang/test/CIR/CodeGen/partial-array-cleanup.cpp
Removed:
################################################################################
diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index c3c0ec1c1af0d..b96b822609c10 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -1039,7 +1039,6 @@ void CIRGenFunction::pushLifetimeExtendedCleanupToEHStack(
}
/// Destroys all the elements of the given array, beginning from last to first.
-/// The array cannot be zero-length.
///
/// \param begin - a type* denoting the first element of the array
/// \param numElements - the number of elements in the array
@@ -1057,33 +1056,33 @@ void CIRGenFunction::emitArrayDestroy(mlir::Value begin,
mlir::Type cirElementType = convertTypeForMem(elementType);
cir::PointerType ptrToElmType = builder.getPointerTo(cirElementType);
- uint64_t size = 0;
+ auto regionBuilder = [&](mlir::OpBuilder &b, mlir::Location loc) {
+ mlir::BlockArgument arg =
+ b.getInsertionBlock()->addArgument(ptrToElmType, loc);
+ Address curAddr = Address(arg, cirElementType, elementAlign);
+ assert(!cir::MissingFeatures::dtorCleanups());
- // Optimize for a constant array size.
+ // Perform the actual destruction there.
+ destroyer(*this, curAddr, elementType);
+
+ cir::YieldOp::create(b, loc);
+ };
+
+ // For a constant array size, use the static form of ArrayDtor.
if (auto constantCount = numElements.getDefiningOp<cir::ConstantOp>()) {
+ uint64_t size = 0;
if (auto constIntAttr = constantCount.getValueAttr<cir::IntAttr>())
size = constIntAttr.getUInt();
- } else {
- cgm.errorNYI(begin.getDefiningOp()->getLoc(),
- "emitArrayDestroy: dynamic-length array expression");
+ auto arrayTy = cir::ArrayType::get(cirElementType, size);
+ mlir::Value arrayOp = builder.createPtrBitcast(begin, arrayTy);
+ cir::ArrayDtor::create(builder, *currSrcLoc, arrayOp, regionBuilder);
+ return;
}
- auto arrayTy = cir::ArrayType::get(cirElementType, size);
- mlir::Value arrayOp = builder.createPtrBitcast(begin, arrayTy);
-
- // Emit the dtor call that will execute for every array element.
- cir::ArrayDtor::create(
- builder, *currSrcLoc, arrayOp,
- [&](mlir::OpBuilder &b, mlir::Location loc) {
- auto arg = b.getInsertionBlock()->addArgument(ptrToElmType, loc);
- Address curAddr = Address(arg, cirElementType, elementAlign);
- assert(!cir::MissingFeatures::dtorCleanups());
-
- // Perform the actual destruction there.
- destroyer(*this, curAddr, elementType);
-
- cir::YieldOp::create(builder, loc);
- });
+ // For a dynamic array size (VLA), use the dynamic form of ArrayDtor.
+ mlir::Value elemBegin = builder.createPtrBitcast(begin, cirElementType);
+ cir::ArrayDtor::create(builder, *currSrcLoc, elemBegin, numElements,
+ regionBuilder);
}
/// Immediately perform the destruction of the given object.
@@ -1104,24 +1103,20 @@ void CIRGenFunction::emitDestroy(Address addr, QualType type,
CharUnits elementAlign = addr.getAlignment().alignmentOfArrayElement(
getContext().getTypeSizeInChars(type));
+ // If the array length is constant, we can check for zero at compile time.
auto constantCount = length.getDefiningOp<cir::ConstantOp>();
- if (!constantCount) {
- assert(!cir::MissingFeatures::vlas());
- cgm.errorNYI("emitDestroy: variable length array");
- return;
+ if (constantCount) {
+ auto constIntAttr = mlir::dyn_cast<cir::IntAttr>(constantCount.getValue());
+ if (constIntAttr && constIntAttr.getUInt() == 0)
+ return;
}
- auto constIntAttr = mlir::dyn_cast<cir::IntAttr>(constantCount.getValue());
- // If it's constant zero, we can just skip the entire thing.
- if (constIntAttr && constIntAttr.getUInt() == 0)
- return;
-
mlir::Value begin = addr.getPointer();
assert(!cir::MissingFeatures::useEHCleanupForArray());
emitArrayDestroy(begin, length, type, elementAlign, destroyer);
// If the array destroy didn't use the length op, we can erase it.
- if (constantCount.use_empty())
+ if (constantCount && constantCount.use_empty())
constantCount.erase();
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 572addda77cec..f7f9331060956 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -1231,12 +1231,32 @@ CIRGenFunction::emitArrayLength(const clang::ArrayType *origArrayType,
// If it's a VLA, we have to load the stored size. Note that
// this is the size of the VLA in bytes, not its size in elements.
+ mlir::Value numVLAElements = nullptr;
if (isa<VariableArrayType>(arrayType)) {
- assert(!cir::MissingFeatures::vlas());
- cgm.errorNYI(*currSrcLoc, "VLAs");
- return builder.getConstInt(*currSrcLoc, sizeTy, 0);
+ numVLAElements = getVLASize(cast<VariableArrayType>(arrayType)).numElts;
+
+ // Walk into all VLAs. This doesn't require changes to addr,
+ // which has type T* where T is the first non-VLA element type.
+ do {
+ QualType elementType = arrayType->getElementType();
+ arrayType = getContext().getAsArrayType(elementType);
+
+ // If we only have VLA components, 'addr' requires no adjustment.
+ if (!arrayType) {
+ baseType = elementType;
+ return numVLAElements;
+ }
+ } while (isa<VariableArrayType>(arrayType));
+
+ // We get out here only if we find a constant array type
+ // inside the VLA.
}
+ // Classic codegen emits an all-zero inbounds GEP to convert addr from
+ // [M x [N x T]]* to T*. CIR doesn't need this because callers handle
+ // the array-to-element pointer conversion themselves (via array_to_ptrdecay
+ // casts, ptr_bitcast, or manual array type peeling).
+
uint64_t countFromCLAs = 1;
QualType eltType;
@@ -1263,7 +1283,17 @@ CIRGenFunction::emitArrayLength(const clang::ArrayType *origArrayType,
}
baseType = eltType;
- return builder.getConstInt(*currSrcLoc, sizeTy, countFromCLAs);
+
+ mlir::Value numElements =
+ builder.getConstInt(*currSrcLoc, sizeTy, countFromCLAs);
+
+ // If we had any VLA dimensions, factor them in.
+ if (numVLAElements)
+ numElements =
+ builder.createMul(numVLAElements.getLoc(), numVLAElements, numElements,
+ cir::OverflowBehavior::NoUnsignedWrap);
+
+ return numElements;
}
void CIRGenFunction::instantiateIndirectGotoBlock() {
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 482e73f9f3050..8a9bfbf81d453 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -1547,7 +1547,6 @@ void LoweringPreparePass::lowerArrayDtor(cir::ArrayDtor op) {
return;
}
- assert(!cir::MissingFeatures::vlas());
auto arrayLen =
mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(),
@@ -1568,7 +1567,6 @@ void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) {
return;
}
- assert(!cir::MissingFeatures::vlas());
auto arrayLen =
mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(),
diff --git a/clang/test/CIR/CodeGen/partial-array-cleanup.cpp b/clang/test/CIR/CodeGen/partial-array-cleanup.cpp
index 7c260a6f13cb0..520170246eb8e 100644
--- a/clang/test/CIR/CodeGen/partial-array-cleanup.cpp
+++ b/clang/test/CIR/CodeGen/partial-array-cleanup.cpp
@@ -159,3 +159,502 @@ void test_partial_array_cleanup() {
//
// OGCG: [[EH_RESUME]]:
// OGCG: resume { ptr, i32 }
+
+void test_variable_size_partial_array_cleanup(int n) {
+ S s[n];
+}
+
+// CIR-BEFORE-LPP: cir.func {{.*}} @_Z40test_variable_size_partial_array_cleanupi
+// CIR-BEFORE-LPP: %[[N_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["n", init]
+// CIR-BEFORE-LPP: %[[N_VAL:.*]] = cir.load {{.*}} %[[N_ADDR]] : !cir.ptr<!s32i>, !s32i
+// CIR-BEFORE-LPP: %[[N:.*]] = cir.cast integral %[[N_VAL]] : !s32i -> !u64i
+// CIR-BEFORE-LPP: cir.stacksave
+// CIR-BEFORE-LPP: cir.cleanup.scope {
+// CIR-BEFORE-LPP: %[[VLA:.*]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, %[[N]] : !u64i, ["s", init]
+// CIR-BEFORE-LPP: cir.array.ctor %[[VLA]], %[[N]] : !cir.ptr<!rec_S>, !u64i {
+// 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: } 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-BEFORE-LPP: cir.cleanup.scope {
+// CIR-BEFORE-LPP: cir.yield
+// CIR-BEFORE-LPP: } cleanup all {
+// CIR-BEFORE-LPP: cir.array.dtor %[[VLA]], %[[N]] : !cir.ptr<!rec_S>, !u64i {
+// CIR-BEFORE-LPP: ^bb0(%[[DTOR_ARG2:.*]]: !cir.ptr<!rec_S>):
+// CIR-BEFORE-LPP: cir.call @_ZN1SD1Ev(%[[DTOR_ARG2]]){{.*}} : (!cir.ptr<!rec_S>{{.*}}) -> ()
+// CIR-BEFORE-LPP: }
+// CIR-BEFORE-LPP: } cleanup normal {
+// CIR-BEFORE-LPP: cir.stackrestore
+// CIR-BEFORE-LPP: }
+
+// CIR: cir.func {{.*}} @_Z40test_variable_size_partial_array_cleanupi
+// CIR: %[[N_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["n", init]
+// CIR: %[[SAVED_STACK:.*]] = cir.alloca !cir.ptr<!u8i>, !cir.ptr<!cir.ptr<!u8i>>, ["saved_stack"]
+// CIR: %[[N_VAL:.*]] = cir.load {{.*}} %[[N_ADDR]] : !cir.ptr<!s32i>, !s32i
+// CIR: %[[N:.*]] = cir.cast integral %[[N_VAL]] : !s32i -> !u64i
+// CIR: %[[STACK:.*]] = cir.stacksave : !cir.ptr<!u8i>
+// CIR: cir.store {{.*}} %[[STACK]], %[[SAVED_STACK]] : !cir.ptr<!u8i>, !cir.ptr<!cir.ptr<!u8i>>
+// CIR: cir.cleanup.scope {
+// CIR: %[[BEGIN:.*]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, %[[N]] : !u64i, ["s", init]
+// CIR: %[[END:.*]] = cir.ptr_stride %[[BEGIN]], %[[N]] : (!cir.ptr<!rec_S>, !u64i) -> !cir.ptr<!rec_S>
+// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !u64i
+// CIR: %[[IS_NONZERO:.*]] = cir.cmp ne %[[N]], %[[ZERO]] : !u64i
+// CIR: cir.if %[[IS_NONZERO]] {
+// 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: }
+// CIR: }
+//
+// --- normal dtor ---
+// CIR: } cleanup all {
+// CIR: %[[LAST:.*]] = cir.ptr_stride %[[BEGIN]], %[[N]] : (!cir.ptr<!rec_S>, !u64i) -> !cir.ptr<!rec_S>
+// CIR: %[[DTOR_NE:.*]] = cir.cmp ne %[[LAST]], %[[BEGIN]] : !cir.ptr<!rec_S>
+// CIR: cir.if %[[DTOR_NE]] {
+// CIR: %[[DTOR_ITER:.*]] = cir.alloca !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>, ["__array_idx"]
+// CIR: cir.store %[[LAST]], %[[DTOR_ITER]] : !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>
+// CIR: cir.do {
+// CIR: %[[DTOR_CUR:.*]] = cir.load %[[DTOR_ITER]] : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
+// CIR: %[[DTOR_NEG1:.*]] = cir.const #cir.int<-1> : !s64i
+// CIR: %[[DTOR_PREV:.*]] = cir.ptr_stride %[[DTOR_CUR]], %[[DTOR_NEG1]] : (!cir.ptr<!rec_S>, !s64i) -> !cir.ptr<!rec_S>
+// CIR: cir.store %[[DTOR_PREV]], %[[DTOR_ITER]] : !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>
+// CIR: cir.call @_ZN1SD1Ev(%[[DTOR_PREV]]){{.*}} : (!cir.ptr<!rec_S>{{.*}}) -> ()
+// CIR: cir.yield
+// CIR: } while {
+// CIR: %[[DTOR_CUR2:.*]] = cir.load %[[DTOR_ITER]] : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
+// CIR: %[[DTOR_NE2:.*]] = cir.cmp ne %[[DTOR_CUR2]], %[[BEGIN]] : !cir.ptr<!rec_S>
+// CIR: cir.condition(%[[DTOR_NE2]])
+// CIR: }
+// CIR: }
+// CIR: cir.yield
+// CIR: }
+// CIR: cir.yield
+// CIR: } cleanup normal {
+// CIR: cir.stackrestore {{.*}} : !cir.ptr<!u8i>
+// CIR: cir.yield
+// CIR: }
+
+// LLVM: define dso_local void @_Z40test_variable_size_partial_array_cleanupi
+// LLVM: %[[N_I32:.*]] = load i32, ptr %{{.*}}
+// LLVM: %[[N:.*]] = sext i32 %[[N_I32]] to i64
+// LLVM: call ptr @llvm.stacksave.p0()
+//
+// --- VLA alloca + zero check ---
+// LLVM: %[[BEGIN:.*]] = alloca %struct.S, i64 %[[N]]
+// LLVM: %[[END:.*]] = getelementptr %struct.S, ptr %[[BEGIN]], i64 %[[N]]
+// LLVM: %[[IS_NONZERO:.*]] = icmp ne i64 %[[N]], 0
+// LLVM: br i1 %[[IS_NONZERO]], label %[[CTOR_SETUP:.*]], label %[[AFTER_CTOR:.*]]
+//
+// --- ctor init ---
+// LLVM: [[CTOR_SETUP]]:
+// LLVM: store ptr %[[BEGIN]], ptr %[[CTOR_ITER:.*]]
+// LLVM: br label %[[CTOR_ENTRY:.*]]
+//
+// LLVM: [[CTOR_ENTRY]]:
+// LLVM: br label %[[CTOR_BODY:.*]]
+//
+// --- ctor loop condition (back-edge) ---
+// LLVM: [[CTOR_LOOP_COND:.*]]:
+// LLVM: %[[COND_CUR:.*]] = load ptr, ptr %[[CTOR_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 %[[CTOR_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 %[[CTOR_ITER]]
+// LLVM: br label %[[CTOR_LOOP_COND]]
+//
+// LLVM: [[CTOR_EXIT]]:
+// LLVM: br label %[[CTOR_JOIN:.*]]
+//
+// --- landing pad + cleanup guard ---
+// LLVM: [[LPAD]]:
+// LLVM: landingpad { ptr, i32 }
+// LLVM: cleanup
+// LLVM: %[[PAD_CUR:.*]] = load ptr, ptr %[[CTOR_ITER]]
+// LLVM: %[[GUARD:.*]] = icmp ne ptr %[[PAD_CUR]], %[[BEGIN]]
+// LLVM: br i1 %[[GUARD]], label %[[PDTOR_ENTRY:.*]], label %[[EH_RESUME:.*]]
+//
+// --- partial dtor do-while entry ---
+// LLVM: [[PDTOR_ENTRY]]:
+// LLVM: br label %[[PDTOR_BODY:.*]]
+//
+// --- partial dtor loop condition (back-edge) ---
+// LLVM: [[PDTOR_LOOP_COND:.*]]:
+// LLVM: %[[PDTOR_CUR:.*]] = load ptr, ptr %[[CTOR_ITER]]
+// LLVM: %[[PDTOR_CONT:.*]] = icmp ne ptr %[[PDTOR_CUR]], %[[BEGIN]]
+// LLVM: br i1 %[[PDTOR_CONT]], label %[[PDTOR_BODY]], label %[[PDTOR_DONE:.*]]
+//
+// --- partial dtor loop body ---
+// LLVM: [[PDTOR_BODY]]:
+// LLVM: %[[PDCUR:.*]] = load ptr, ptr %[[CTOR_ITER]]
+// LLVM: %[[PPREV:.*]] = getelementptr %struct.S, ptr %[[PDCUR]], i64 -1
+// LLVM: store ptr %[[PPREV]], ptr %[[CTOR_ITER]]
+// LLVM: call void @_ZN1SD1Ev(ptr{{.*}} %[[PPREV]])
+// LLVM: br label %[[PDTOR_LOOP_COND]]
+//
+// LLVM: [[PDTOR_DONE]]:
+// LLVM: br label %[[EH_RESUME]]
+//
+// LLVM: [[EH_RESUME]]:
+// LLVM: resume { ptr, i32 }
+//
+// --- ctor done join ---
+// LLVM: [[CTOR_JOIN]]:
+// LLVM: br label %[[AFTER_CTOR]]
+//
+// --- normal dtor setup ---
+// LLVM: [[AFTER_CTOR]]:
+// LLVM: %[[LAST:.*]] = getelementptr %struct.S, ptr %[[BEGIN]], i64 %[[N]]
+// LLVM: %[[DTOR_NE:.*]] = icmp ne ptr %[[LAST]], %[[BEGIN]]
+// LLVM: br i1 %[[DTOR_NE]], label %[[NDTOR_ENTRY:.*]], label %[[NDTOR_DONE:.*]]
+//
+// LLVM: [[NDTOR_ENTRY]]:
+// LLVM: store ptr %[[LAST]], ptr %[[DTOR_ITER:.*]]
+// LLVM: br label %[[NDTOR_BODY:.*]]
+//
+// --- normal dtor loop condition (back-edge) ---
+// LLVM: [[NDTOR_LOOP_COND:.*]]:
+// LLVM: %[[NDCUR_CHECK:.*]] = load ptr, ptr %[[DTOR_ITER]]
+// LLVM: %[[NDTOR_CONT:.*]] = icmp ne ptr %[[NDCUR_CHECK]], %[[BEGIN]]
+// LLVM: br i1 %[[NDTOR_CONT]], label %[[NDTOR_BODY]], label %[[NDTOR_EXIT:.*]]
+//
+// --- normal dtor loop body ---
+// LLVM: [[NDTOR_BODY]]:
+// LLVM: %[[NDCUR:.*]] = load ptr, ptr %[[DTOR_ITER]]
+// LLVM: %[[NDPREV:.*]] = getelementptr %struct.S, ptr %[[NDCUR]], i64 -1
+// LLVM: store ptr %[[NDPREV]], ptr %[[DTOR_ITER]]
+// LLVM: call void @_ZN1SD1Ev(ptr{{.*}} %[[NDPREV]])
+// LLVM: br label %[[NDTOR_LOOP_COND]]
+//
+// LLVM: [[NDTOR_EXIT]]:
+// LLVM: br label %[[NDTOR_DONE]]
+//
+// LLVM: [[NDTOR_DONE]]:
+// LLVM: call void @llvm.stackrestore.p0
+// LLVM: ret void
+
+// OGCG: define dso_local void @_Z40test_variable_size_partial_array_cleanupi
+// OGCG: [[ENTRY:.*]]:
+// OGCG: %[[N:.*]] = zext i32 %{{.*}} to i64
+// OGCG: %[[VLA:.*]] = alloca %struct.S, i64 %[[N]]
+// OGCG: %[[ISEMPTY:.*]] = icmp eq i64 %[[N]], 0
+// OGCG: br i1 %[[ISEMPTY]], label %[[CTOR_CONT:.*]], label %[[CTOR_LOOP_ENTRY:.*]]
+//
+// --- ctor loop ---
+// OGCG: [[CTOR_LOOP_ENTRY]]:
+// OGCG: %[[CTOR_END:.*]] = getelementptr inbounds %struct.S, ptr %[[VLA]], i64 %[[N]]
+// OGCG: br label %[[CTOR_LOOP:.*]]
+//
+// OGCG: [[CTOR_LOOP]]:
+// OGCG: %[[CUR:.*]] = phi ptr [ %[[VLA]], %[[CTOR_LOOP_ENTRY]] ], [ %[[NEXT:.*]], %[[INVOKE_CONT:.*]] ]
+// OGCG: invoke void @_ZN1SC1Ev(ptr{{.*}})
+// OGCG: to label %[[INVOKE_CONT]] unwind label %[[LPAD:.*]]
+//
+// OGCG: [[INVOKE_CONT]]:
+// OGCG: %[[NEXT]] = getelementptr inbounds %struct.S, ptr %[[CUR]], i64 1
+// OGCG: %[[DONE:.*]] = icmp eq ptr %[[NEXT]], %[[CTOR_END]]
+// OGCG: br i1 %[[DONE]], label %[[CTOR_CONT]], label %[[CTOR_LOOP]]
+//
+// --- normal dtor ---
+// OGCG: [[CTOR_CONT]]:
+// OGCG: %[[DTOR_END:.*]] = getelementptr inbounds %struct.S, ptr %[[VLA]], i64 %[[N]]
+// OGCG: %[[DTOR_ISEMPTY:.*]] = icmp eq ptr %[[VLA]], %[[DTOR_END]]
+// OGCG: br i1 %[[DTOR_ISEMPTY]], label %[[DTOR_DONE:.*]], label %[[DTOR_LOOP:.*]]
+//
+// OGCG: [[DTOR_LOOP]]:
+// OGCG: %[[PAST:.*]] = phi ptr [ %[[DTOR_END]], %[[CTOR_CONT]] ], [ %[[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]], %[[VLA]]
+// OGCG: br i1 %[[DDONE]], label %[[DTOR_DONE]], label %[[DTOR_LOOP]]
+//
+// OGCG: [[DTOR_DONE]]:
+// OGCG: call void @llvm.stackrestore.p0(ptr %{{.*}})
+// OGCG: ret void
+//
+// --- landing pad + cleanup guard ---
+// OGCG: [[LPAD]]:
+// OGCG: landingpad { ptr, i32 }
+// OGCG: cleanup
+// OGCG: %[[PISEMPTY:.*]] = icmp eq ptr %[[VLA]], %[[CUR]]
+// OGCG: br i1 %[[PISEMPTY]], label %[[PDTOR_DONE:.*]], label %[[PDTOR_LOOP:.*]]
+//
+// --- partial dtor loop ---
+// OGCG: [[PDTOR_LOOP]]:
+// OGCG: %[[PPAST:.*]] = phi ptr [ %[[CUR]], %[[LPAD]] ], [ %[[PPREV:.*]], %[[PDTOR_LOOP]] ]
+// OGCG: %[[PPREV]] = getelementptr inbounds %struct.S, ptr %[[PPAST]], i64 -1
+// OGCG: call void @_ZN1SD1Ev(ptr{{.*}} %[[PPREV]])
+// OGCG: %[[PDDONE:.*]] = icmp eq ptr %[[PPREV]], %[[VLA]]
+// OGCG: br i1 %[[PDDONE]], label %[[PDTOR_DONE]], label %[[PDTOR_LOOP]]
+//
+// OGCG: [[PDTOR_DONE]]:
+// OGCG: br label %[[EH_RESUME:.*]]
+//
+// OGCG: [[EH_RESUME]]:
+// OGCG: resume { ptr, i32 }
+
+void test_multi_dim_vla(int n, int m) {
+ S s[n][m];
+}
+
+// CIR-BEFORE-LPP: cir.func {{.*}} @_Z18test_multi_dim_vlaii
+// CIR-BEFORE-LPP: %[[N_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["n", init]
+// CIR-BEFORE-LPP: %[[M_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["m", init]
+// CIR-BEFORE-LPP: %[[N_VAL:.*]] = cir.load {{.*}} %[[N_ADDR]] : !cir.ptr<!s32i>, !s32i
+// CIR-BEFORE-LPP: %[[N:.*]] = cir.cast integral %[[N_VAL]] : !s32i -> !u64i
+// CIR-BEFORE-LPP: %[[M_VAL:.*]] = cir.load {{.*}} %[[M_ADDR]] : !cir.ptr<!s32i>, !s32i
+// CIR-BEFORE-LPP: %[[M:.*]] = cir.cast integral %[[M_VAL]] : !s32i -> !u64i
+// CIR-BEFORE-LPP: cir.stacksave
+// CIR-BEFORE-LPP: cir.cleanup.scope {
+// CIR-BEFORE-LPP: %[[NM:.*]] = cir.mul nuw %[[N]], %[[M]] : !u64i
+// CIR-BEFORE-LPP: %[[VLA:.*]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, %[[NM]] : !u64i, ["s", init]
+// CIR-BEFORE-LPP: cir.array.ctor %[[VLA]], {{.*}} : !cir.ptr<!rec_S>, !u64i {
+// 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: } 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-BEFORE-LPP: cir.cleanup.scope {
+// CIR-BEFORE-LPP: cir.yield
+// CIR-BEFORE-LPP: } cleanup all {
+// CIR-BEFORE-LPP: cir.array.dtor %[[VLA]], {{.*}} : !cir.ptr<!rec_S>, !u64i {
+// CIR-BEFORE-LPP: ^bb0(%[[DTOR_ARG2:.*]]: !cir.ptr<!rec_S>):
+// CIR-BEFORE-LPP: cir.call @_ZN1SD1Ev(%[[DTOR_ARG2]]){{.*}} : (!cir.ptr<!rec_S>{{.*}}) -> ()
+// CIR-BEFORE-LPP: }
+// CIR-BEFORE-LPP: } cleanup normal {
+// CIR-BEFORE-LPP: cir.stackrestore
+// CIR-BEFORE-LPP: }
+
+// CIR: cir.func {{.*}} @_Z18test_multi_dim_vlaii
+// CIR: %[[N_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["n", init]
+// CIR: %[[M_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["m", init]
+// CIR: %[[SAVED_STACK:.*]] = cir.alloca !cir.ptr<!u8i>, !cir.ptr<!cir.ptr<!u8i>>, ["saved_stack"]
+// CIR: %[[N_VAL:.*]] = cir.load {{.*}} %[[N_ADDR]] : !cir.ptr<!s32i>, !s32i
+// CIR: %[[N:.*]] = cir.cast integral %[[N_VAL]] : !s32i -> !u64i
+// CIR: %[[M_VAL:.*]] = cir.load {{.*}} %[[M_ADDR]] : !cir.ptr<!s32i>, !s32i
+// CIR: %[[M:.*]] = cir.cast integral %[[M_VAL]] : !s32i -> !u64i
+// CIR: cir.cleanup.scope {
+// CIR: %[[NM:.*]] = cir.mul nuw %[[N]], %[[M]] : !u64i
+// CIR: %[[BEGIN:.*]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, %[[NM]] : !u64i, ["s", init]
+// CIR: cir.call @_ZN1SC1Ev
+// CIR: } cleanup eh {
+// CIR: cir.call @_ZN1SD1Ev
+// CIR: } cleanup all {
+// CIR: %[[NM3:.*]] = cir.mul nuw %[[N]], %[[M]] : !u64i
+// CIR: cir.call @_ZN1SD1Ev
+// CIR: } cleanup normal {
+// CIR: cir.stackrestore {{.*}} : !cir.ptr<!u8i>
+// CIR: }
+
+// LLVM: define dso_local void @_Z18test_multi_dim_vlaii
+// LLVM: %[[N_I32:.*]] = load i32, ptr %{{.*}}
+// LLVM: %[[N:.*]] = sext i32 %[[N_I32]] to i64
+// LLVM: %[[M_I32:.*]] = load i32, ptr %{{.*}}
+// LLVM: %[[M:.*]] = sext i32 %[[M_I32]] to i64
+// LLVM: call ptr @llvm.stacksave.p0()
+//
+// --- VLA alloca with n*m ---
+// LLVM: %[[NM:.*]] = mul nuw i64 %[[N]], %[[M]]
+// LLVM: %[[BEGIN:.*]] = alloca %struct.S, i64 %[[NM]]
+//
+// --- ctor loop ---
+// LLVM: invoke void @_ZN1SC1Ev(ptr{{.*}})
+// LLVM: to label %{{.*}} unwind label %[[LPAD:.*]]
+//
+// --- landing pad + partial dtor ---
+// LLVM: [[LPAD]]:
+// LLVM: landingpad { ptr, i32 }
+// LLVM: cleanup
+// LLVM: call void @_ZN1SD1Ev
+// LLVM: resume { ptr, i32 }
+//
+// --- normal dtor ---
+// LLVM: %[[NM2:.*]] = mul nuw i64 %[[N]], %[[M]]
+// LLVM: %[[LAST:.*]] = getelementptr %struct.S, ptr %[[BEGIN]], i64 %[[NM2]]
+// LLVM: %[[DTOR_NE:.*]] = icmp ne ptr %[[LAST]], %[[BEGIN]]
+// LLVM: call void @_ZN1SD1Ev
+// LLVM: call void @llvm.stackrestore.p0
+// LLVM: ret void
+
+// OGCG: define dso_local void @_Z18test_multi_dim_vlaii
+// OGCG: [[ENTRY:.*]]:
+// OGCG: %[[N:.*]] = zext i32 %{{.*}} to i64
+// OGCG: %[[M:.*]] = zext i32 %{{.*}} to i64
+// OGCG: %[[NM:.*]] = mul nuw i64 %[[N]], %[[M]]
+// OGCG: %[[VLA:.*]] = alloca %struct.S, i64 %[[NM]]
+// OGCG: %[[ISEMPTY:.*]] = icmp eq i64 %{{.*}}, 0
+// OGCG: br i1 %[[ISEMPTY]], label %[[CTOR_CONT:.*]], label %[[CTOR_LOOP_ENTRY:.*]]
+//
+// --- ctor loop ---
+// OGCG: [[CTOR_LOOP_ENTRY]]:
+// OGCG: invoke void @_ZN1SC1Ev(ptr{{.*}})
+// OGCG: to label %{{.*}} unwind label %[[LPAD:.*]]
+//
+// --- normal dtor ---
+// OGCG: [[CTOR_CONT]]:
+// OGCG: %[[NM2:.*]] = mul nuw i64 %[[N]], %[[M]]
+// OGCG: call void @_ZN1SD1Ev
+//
+// OGCG: call void @llvm.stackrestore.p0(ptr %{{.*}})
+// OGCG: ret void
+//
+// --- landing pad + partial dtor ---
+// OGCG: [[LPAD]]:
+// OGCG: landingpad { ptr, i32 }
+// OGCG: cleanup
+// OGCG: call void @_ZN1SD1Ev
+//
+// OGCG: resume { ptr, i32 }
+
+void test_vla_of_constant_array(int n) {
+ S s[n][4];
+}
+
+// CIR-BEFORE-LPP: cir.func {{.*}} @_Z26test_vla_of_constant_arrayi
+// CIR-BEFORE-LPP: %[[N_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["n", init]
+// CIR-BEFORE-LPP: %[[N_VAL:.*]] = cir.load {{.*}} %[[N_ADDR]] : !cir.ptr<!s32i>, !s32i
+// CIR-BEFORE-LPP: %[[N:.*]] = cir.cast integral %[[N_VAL]] : !s32i -> !u64i
+// CIR-BEFORE-LPP: cir.stacksave
+// CIR-BEFORE-LPP: cir.cleanup.scope {
+// CIR-BEFORE-LPP: %[[VLA:.*]] = cir.alloca !cir.array<!rec_S x 4>, !cir.ptr<!cir.array<!rec_S x 4>>, %[[N]] : !u64i, ["s", init]
+// CIR-BEFORE-LPP: %[[FOUR:.*]] = cir.const #cir.int<4> : !u64i
+// CIR-BEFORE-LPP: %[[TOTAL:.*]] = cir.mul nuw %[[N]], %[[FOUR]] : !u64i
+// CIR-BEFORE-LPP: %[[ELEM_PTR:.*]] = cir.cast bitcast %[[VLA]] : !cir.ptr<!cir.array<!rec_S x 4>> -> !cir.ptr<!rec_S>
+// CIR-BEFORE-LPP: cir.array.ctor %[[ELEM_PTR]], %[[TOTAL]] : !cir.ptr<!rec_S>, !u64i {
+// 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: } 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-BEFORE-LPP: cir.cleanup.scope {
+// CIR-BEFORE-LPP: cir.yield
+// CIR-BEFORE-LPP: } cleanup all {
+// CIR-BEFORE-LPP: %{{.*}} = cir.const #cir.int<4> : !u64i
+// CIR-BEFORE-LPP: %{{.*}} = cir.mul nuw %[[N]], %{{.*}} : !u64i
+// CIR-BEFORE-LPP: %{{.*}} = cir.cast bitcast %[[VLA]] : !cir.ptr<!cir.array<!rec_S x 4>> -> !cir.ptr<!rec_S>
+// CIR-BEFORE-LPP: cir.array.dtor {{.*}} : !cir.ptr<!rec_S>, !u64i {
+// CIR-BEFORE-LPP: ^bb0(%[[DTOR_ARG2:.*]]: !cir.ptr<!rec_S>):
+// CIR-BEFORE-LPP: cir.call @_ZN1SD1Ev(%[[DTOR_ARG2]]){{.*}} : (!cir.ptr<!rec_S>{{.*}}) -> ()
+// CIR-BEFORE-LPP: }
+// CIR-BEFORE-LPP: } cleanup normal {
+// CIR-BEFORE-LPP: cir.stackrestore
+// CIR-BEFORE-LPP: }
+
+// CIR: cir.func {{.*}} @_Z26test_vla_of_constant_arrayi
+// CIR: %[[N_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["n", init]
+// CIR: %[[N_VAL:.*]] = cir.load {{.*}} %[[N_ADDR]] : !cir.ptr<!s32i>, !s32i
+// CIR: %[[N:.*]] = cir.cast integral %[[N_VAL]] : !s32i -> !u64i
+// CIR: cir.cleanup.scope {
+// CIR: %[[VLA:.*]] = cir.alloca !cir.array<!rec_S x 4>, !cir.ptr<!cir.array<!rec_S x 4>>, %[[N]] : !u64i, ["s", init]
+// CIR: %[[FOUR:.*]] = cir.const #cir.int<4> : !u64i
+// CIR: %[[TOTAL:.*]] = cir.mul nuw %[[N]], %[[FOUR]] : !u64i
+// CIR: %[[ELEM_PTR:.*]] = cir.cast bitcast %[[VLA]] : !cir.ptr<!cir.array<!rec_S x 4>> -> !cir.ptr<!rec_S>
+// CIR: cir.call @_ZN1SC1Ev
+// CIR: } cleanup eh {
+// CIR: cir.call @_ZN1SD1Ev
+// CIR: } cleanup all {
+// CIR: %{{.*}} = cir.const #cir.int<4> : !u64i
+// CIR: %{{.*}} = cir.mul nuw %[[N]], %{{.*}} : !u64i
+// CIR: %{{.*}} = cir.cast bitcast %[[VLA]] : !cir.ptr<!cir.array<!rec_S x 4>> -> !cir.ptr<!rec_S>
+// CIR: cir.call @_ZN1SD1Ev
+// CIR: } cleanup normal {
+// CIR: cir.stackrestore {{.*}} : !cir.ptr<!u8i>
+// CIR: }
+
+// LLVM: define dso_local void @_Z26test_vla_of_constant_arrayi
+// LLVM: %[[N_I32:.*]] = load i32, ptr %{{.*}}
+// LLVM: %[[N:.*]] = sext i32 %[[N_I32]] to i64
+// LLVM: call ptr @llvm.stacksave.p0()
+//
+// --- VLA alloca with [4 x %struct.S] ---
+// LLVM: %[[BEGIN:.*]] = alloca [4 x %struct.S], i64 %[[N]]
+// LLVM: %[[TOTAL:.*]] = mul nuw i64 %[[N]], 4
+//
+// --- ctor loop ---
+// LLVM: invoke void @_ZN1SC1Ev(ptr{{.*}})
+// LLVM: to label %{{.*}} unwind label %[[LPAD:.*]]
+//
+// --- landing pad + partial dtor ---
+// LLVM: [[LPAD]]:
+// LLVM: landingpad { ptr, i32 }
+// LLVM: cleanup
+// LLVM: call void @_ZN1SD1Ev
+// LLVM: resume { ptr, i32 }
+//
+// --- normal dtor ---
+// LLVM: %[[TOTAL2:.*]] = mul nuw i64 %[[N]], 4
+// LLVM: call void @_ZN1SD1Ev
+// LLVM: call void @llvm.stackrestore.p0
+// LLVM: ret void
+
+// OGCG: define dso_local void @_Z26test_vla_of_constant_arrayi
+// OGCG: [[ENTRY:.*]]:
+// OGCG: %[[N:.*]] = zext i32 %{{.*}} to i64
+// OGCG: %[[VLA:.*]] = alloca [4 x %struct.S], i64 %[[N]]
+// OGCG: %[[ARRAY_BEGIN:.*]] = getelementptr inbounds [4 x %struct.S], ptr %[[VLA]], i32 0, i32 0
+// OGCG: %[[TOTAL:.*]] = mul nuw i64 %[[N]], 4
+// OGCG: %[[ISEMPTY:.*]] = icmp eq i64 %[[TOTAL]], 0
+// OGCG: br i1 %[[ISEMPTY]], label %[[CTOR_CONT:.*]], label %[[CTOR_LOOP_ENTRY:.*]]
+//
+// --- ctor loop ---
+// OGCG: [[CTOR_LOOP_ENTRY]]:
+// OGCG: invoke void @_ZN1SC1Ev(ptr{{.*}})
+// OGCG: to label %{{.*}} unwind label %[[LPAD:.*]]
+//
+// --- normal dtor ---
+// OGCG: [[CTOR_CONT]]:
+// OGCG: %[[TOTAL2:.*]] = mul nuw i64 %[[N]], 4
+// OGCG: call void @_ZN1SD1Ev
+//
+// OGCG: call void @llvm.stackrestore.p0(ptr %{{.*}})
+// OGCG: ret void
+//
+// --- landing pad + partial dtor ---
+// OGCG: [[LPAD]]:
+// OGCG: landingpad { ptr, i32 }
+// OGCG: cleanup
+// OGCG: call void @_ZN1SD1Ev
+//
+// OGCG: resume { ptr, i32 }
More information about the cfe-commits
mailing list