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 } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
