Skip to content

[CIR] Backport a fix for array element destruction order. #1823

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 23 additions & 15 deletions clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1333,27 +1333,30 @@ void LoweringPreparePass::lowerDynamicCastOp(DynamicCastOp op) {

static void lowerArrayDtorCtorIntoLoop(CIRBaseBuilderTy &builder,
mlir::Operation *op, mlir::Type eltTy,
mlir::Value arrayAddr,
uint64_t arrayLen) {
mlir::Value arrayAddr, uint64_t arrayLen,
bool isCtor) {
// Generate loop to call into ctor/dtor for every element.
auto loc = op->getLoc();

// TODO: instead of fixed integer size, create alias for PtrDiffTy and unify
// with CIRGen stuff.
auto ptrDiffTy =
cir::IntType::get(builder.getContext(), 64, /*signed=*/false);
auto numArrayElementsConst = builder.create<cir::ConstantOp>(
loc, ptrDiffTy, cir::IntAttr::get(ptrDiffTy, arrayLen));
uint64_t endOffset = isCtor ? arrayLen : arrayLen - 1;
mlir::Value endOffsetVal = builder.create<cir::ConstantOp>(
loc, ptrDiffTy, cir::IntAttr::get(ptrDiffTy, endOffset));

auto begin = builder.create<cir::CastOp>(
loc, eltTy, cir::CastKind::array_to_ptrdecay, arrayAddr);
mlir::Value end = builder.create<cir::PtrStrideOp>(loc, eltTy, begin,
numArrayElementsConst);
mlir::Value end =
builder.create<cir::PtrStrideOp>(loc, eltTy, begin, endOffsetVal);
mlir::Value start = isCtor ? begin : end;
mlir::Value stop = isCtor ? end : begin;

auto tmpAddr = builder.createAlloca(
loc, /*addr type*/ builder.getPointerTo(eltTy),
/*var type*/ eltTy, "__array_idx", clang::CharUnits::One());
builder.createStore(loc, begin, tmpAddr);
builder.createStore(loc, start, tmpAddr);

auto loop = builder.createDoWhile(
loc,
Expand All @@ -1362,7 +1365,7 @@ static void lowerArrayDtorCtorIntoLoop(CIRBaseBuilderTy &builder,
auto currentElement = b.create<cir::LoadOp>(loc, eltTy, tmpAddr);
mlir::Type boolTy = cir::BoolType::get(b.getContext());
auto cmp = builder.create<cir::CmpOp>(loc, boolTy, cir::CmpOpKind::ne,
currentElement, end);
currentElement, stop);
builder.createCondition(cmp);
},
/*bodyBuilder=*/
Expand All @@ -1373,15 +1376,20 @@ static void lowerArrayDtorCtorIntoLoop(CIRBaseBuilderTy &builder,
op->walk([&](CallOp c) { ctorCall = c; });
assert(ctorCall && "expected ctor call");

auto one = builder.create<cir::ConstantOp>(
loc, ptrDiffTy, cir::IntAttr::get(ptrDiffTy, 1));
cir::ConstantOp stride;
if (isCtor)
stride = builder.create<cir::ConstantOp>(
loc, ptrDiffTy, cir::IntAttr::get(ptrDiffTy, 1));
else
stride = builder.create<cir::ConstantOp>(
loc, ptrDiffTy, cir::IntAttr::get(ptrDiffTy, -1));

ctorCall->moveAfter(one);
ctorCall->moveBefore(stride);
ctorCall->setOperand(0, currentElement);

// Advance pointer and store them to temporary variable
auto nextElement =
builder.create<cir::PtrStrideOp>(loc, eltTy, currentElement, one);
auto nextElement = builder.create<cir::PtrStrideOp>(
loc, eltTy, currentElement, stride);
builder.createStore(loc, nextElement, tmpAddr);
builder.createYield(loc);
});
Expand All @@ -1397,7 +1405,7 @@ void LoweringPreparePass::lowerArrayDtor(ArrayDtor op) {
auto eltTy = op->getRegion(0).getArgument(0).getType();
auto arrayLen =
mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
lowerArrayDtorCtorIntoLoop(builder, op, eltTy, op.getAddr(), arrayLen);
lowerArrayDtorCtorIntoLoop(builder, op, eltTy, op.getAddr(), arrayLen, false);
}

static std::string getGlobalVarNameForConstString(cir::StoreOp op,
Expand Down Expand Up @@ -1460,7 +1468,7 @@ void LoweringPreparePass::lowerArrayCtor(ArrayCtor op) {
auto eltTy = op->getRegion(0).getArgument(0).getType();
auto arrayLen =
mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
lowerArrayDtorCtorIntoLoop(builder, op, eltTy, op.getAddr(), arrayLen);
lowerArrayDtorCtorIntoLoop(builder, op, eltTy, op.getAddr(), arrayLen, true);
}

void LoweringPreparePass::lowerStdFindOp(StdFindOp op) {
Expand Down
34 changes: 24 additions & 10 deletions clang/test/CIR/CodeGen/array-init-destroy.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t1.cir 2>&1 | FileCheck -check-prefix=BEFORE %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir --mlir-print-ir-after=cir-lowering-prepare %s -o %t2.cir 2>&1 | FileCheck -check-prefix=AFTER %s

// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir &> %t1.cir
// RUN: FileCheck --input-file=%t1.cir -check-prefix=BEFORE %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir --mlir-print-ir-after=cir-lowering-prepare %s -o %t.cir &> %t2.cir
// RUN: FileCheck --input-file=%t2.cir -check-prefix=AFTER %s
// Note: The run lines above send the final CIR to %t.cir, but that's ignored.
// The test checks the CIR before and after the cir-lowering-prepare pass.
void foo() noexcept;

class xpto {
Expand Down Expand Up @@ -43,8 +46,8 @@ void x() {
// AFTER: cir.store %[[ArrayBegin]], %[[TmpIdx]] : !cir.ptr<!rec_xpto>, !cir.ptr<!cir.ptr<!rec_xpto>>
// AFTER: cir.do {
// AFTER: %[[ArrayElt:.*]] = cir.load %[[TmpIdx]] : !cir.ptr<!cir.ptr<!rec_xpto>>, !cir.ptr<!rec_xpto>
// AFTER: %[[ConstOne:.*]] = cir.const #cir.int<1> : !u64i
// AFTER: cir.call @_ZN4xptoC1Ev(%[[ArrayElt]]) : (!cir.ptr<!rec_xpto>) -> ()
// AFTER: %[[ConstOne:.*]] = cir.const #cir.int<1> : !u64i
// AFTER: %[[NextElt:.*]] = cir.ptr_stride(%[[ArrayElt]] : !cir.ptr<!rec_xpto>, %[[ConstOne]] : !u64i), !cir.ptr<!rec_xpto>
// AFTER: cir.store %[[NextElt]], %[[TmpIdx]] : !cir.ptr<!rec_xpto>, !cir.ptr<!cir.ptr<!rec_xpto>>
// AFTER: cir.yield
Expand All @@ -53,10 +56,21 @@ void x() {
// AFTER: %[[ExitCond:.*]] = cir.cmp(ne, %[[ArrayElt]], %[[ArrayPastEnd]]) : !cir.ptr<!rec_xpto>, !cir.bool
// AFTER: cir.condition(%[[ExitCond]])
// AFTER: }

// AFTER: cir.do {
// AFTER: cir.call @_ZN4xptoD1Ev({{.*}}) : (!cir.ptr<!rec_xpto>) -> ()
// AFTER: } while {
// AFTER: }

// AFTER: %[[ConstOne:.*]] = cir.const #cir.int<1> : !u64i
// AFTER: %[[ArrayBegin:.*]] = cir.cast(array_to_ptrdecay, %[[ArrayAddr0]] : !cir.ptr<!cir.array<!rec_xpto x 2>>), !cir.ptr<!rec_xpto>
// AFTER: %[[ArrayEnd:.*]] = cir.ptr_stride(%[[ArrayBegin]] : !cir.ptr<!rec_xpto>, %[[ConstOne]] : !u64i), !cir.ptr<!rec_xpto>
// AFTER: %[[TmpIdx:.*]] = cir.alloca !cir.ptr<!rec_xpto>, !cir.ptr<!cir.ptr<!rec_xpto>>, ["__array_idx"] {alignment = 1 : i64}
// AFTER: cir.store %[[ArrayEnd]], %[[TmpIdx]] : !cir.ptr<!rec_xpto>, !cir.ptr<!cir.ptr<!rec_xpto>>
// AFTER cir.do {
// AFTER %[[ArrElt:.*]] = cir.load{{.*}} %[[TmpIdx]]
// AFTER cir.call @_ZN13array_elementD1Ev(%[[ArrElt]]) : (!cir.ptr<!rec_xpto>) -> ()
// AFTER %[[ConstNegOne:.*]] = cir.const #cir.int<-1> : !s64i
// AFTER %[[NextElt:.*]] = cir.ptr_stride(%[[ARR_CUR]] : !cir.ptr<!rec_xpto>, %[[ConstNegOne]] : !s64i), !cir.ptr<!rec_xpto>
// AFTER cir.store %[[NextElt]], %[[TmpIdx]] : !cir.ptr<!rec_xpto>, !cir.ptr<!cir.ptr<!rec_xpto>>
// AFTER cir.yield
// AFTER } while {
// AFTER %[[ArrElt:.*]] = cir.load %[[TmpIdx]] : !cir.ptr<!cir.ptr<!rec_xpto>>, !cir.ptr<!rec_xpto>
// AFTER: %[[ExitCond:.*]] = cir.cmp(ne, %[[ArrayElt]], %[[ArrayBegin]]) : !cir.ptr<!rec_xpto>, !cir.bool
// AFTER cir.condition(%[[ExitCond]])
// AFTER }
// AFTER: cir.return
2 changes: 1 addition & 1 deletion clang/test/CIR/CodeGen/array-new-init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ void t_new_constant_size_constructor() {
// AFTER: cir.store{{.*}} %[[ELEM_PTR]], %[[CUR_ELEM_ALLOCA]] : !cir.ptr<!rec_E>, !cir.ptr<!cir.ptr<!rec_E>>
// AFTER: cir.do {
// AFTER: %[[CUR_ELEM_PTR:.*]] = cir.load %[[CUR_ELEM_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_E>>, !cir.ptr<!rec_E>
// AFTER: %[[OFFSET:.*]] = cir.const #cir.int<1> : !u64i
// AFTER: cir.call @_ZN1EC1Ev(%[[CUR_ELEM_PTR]]) : (!cir.ptr<!rec_E>) -> ()
// AFTER: %[[OFFSET:.*]] = cir.const #cir.int<1> : !u64i
// AFTER: %[[NEXT_PTR:.*]] = cir.ptr_stride(%[[CUR_ELEM_PTR]] : !cir.ptr<!rec_E>, %[[OFFSET]] : !u64i), !cir.ptr<!rec_E>
// AFTER: cir.store{{.*}} %[[NEXT_PTR]], %[[CUR_ELEM_ALLOCA]] : !cir.ptr<!rec_E>, !cir.ptr<!cir.ptr<!rec_E>>
// AFTER: cir.yield
Expand Down
Loading