Skip to content

Commit 88620ae

Browse files
authored
[CIR] Add support for array cleanups (llvm#150499)
This adds support for array cleanups, including the ArrayDtor op.
1 parent 111edfc commit 88620ae

File tree

8 files changed

+272
-31
lines changed

8 files changed

+272
-31
lines changed

clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
7575
return getConstant(loc, cir::IntAttr::get(ty, value));
7676
}
7777

78+
mlir::Value getSignedInt(mlir::Location loc, int64_t val, unsigned numBits) {
79+
auto type = cir::IntType::get(getContext(), numBits, /*isSigned=*/true);
80+
return getConstAPInt(loc, type,
81+
llvm::APInt(numBits, val, /*isSigned=*/true));
82+
}
83+
7884
mlir::Value getUnsignedInt(mlir::Location loc, uint64_t val,
7985
unsigned numBits) {
8086
auto type = cir::IntType::get(getContext(), numBits, /*isSigned=*/false);

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -607,8 +607,8 @@ def CIR_ConditionOp : CIR_Op<"condition", [
607607
//===----------------------------------------------------------------------===//
608608

609609
defvar CIR_YieldableScopes = [
610-
"ArrayCtor", "CaseOp", "DoWhileOp", "ForOp", "IfOp", "ScopeOp", "SwitchOp",
611-
"TernaryOp", "WhileOp"
610+
"ArrayCtor", "ArrayDtor", "CaseOp", "DoWhileOp", "ForOp", "IfOp", "ScopeOp",
611+
"SwitchOp", "TernaryOp", "WhileOp"
612612
];
613613

614614
def CIR_YieldOp : CIR_Op<"yield", [
@@ -2227,7 +2227,7 @@ def CIR_TrapOp : CIR_Op<"trap", [Terminator]> {
22272227
}
22282228

22292229
//===----------------------------------------------------------------------===//
2230-
// ArrayCtor
2230+
// ArrayCtor & ArrayDtor
22312231
//===----------------------------------------------------------------------===//
22322232

22332233
class CIR_ArrayInitDestroy<string mnemonic> : CIR_Op<mnemonic> {
@@ -2258,7 +2258,9 @@ def CIR_ArrayCtor : CIR_ArrayInitDestroy<"array.ctor"> {
22582258
let description = [{
22592259
Initialize each array element using the same C++ constructor. This
22602260
operation has one region, with one single block. The block has an
2261-
incoming argument for the current array index to initialize.
2261+
incoming argument for the current array element to initialize.
2262+
2263+
Example:
22622264

22632265
```mlir
22642266
cir.array.ctor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) {
@@ -2270,6 +2272,25 @@ def CIR_ArrayCtor : CIR_ArrayInitDestroy<"array.ctor"> {
22702272
}];
22712273
}
22722274

2275+
def CIR_ArrayDtor : CIR_ArrayInitDestroy<"array.dtor"> {
2276+
let summary = "Destroy array elements with C++ dtors";
2277+
let description = [{
2278+
Destroy each array element using the same C++ destructor. This
2279+
operation has one region, with one single block. The block has an
2280+
incoming argument for the current array element to destruct.
2281+
2282+
Example:
2283+
2284+
```mlir
2285+
cir.array.dtor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) {
2286+
^bb0(%arg0: !cir.ptr<!rec_S>):
2287+
cir.call @some_dtor(%arg0) : (!cir.ptr<!rec_S>) -> ()
2288+
cir.yield
2289+
}
2290+
```
2291+
}];
2292+
}
2293+
22732294
//===----------------------------------------------------------------------===//
22742295
// VecCreate
22752296
//===----------------------------------------------------------------------===//

clang/lib/CIR/CodeGen/CIRGenDecl.cpp

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,38 @@ void CIRGenFunction::emitNullabilityCheck(LValue lhs, mlir::Value rhs,
649649
assert(!cir::MissingFeatures::sanitizers());
650650
}
651651

652+
/// Destroys all the elements of the given array, beginning from last to first.
653+
/// The array cannot be zero-length.
654+
///
655+
/// \param begin - a type* denoting the first element of the array
656+
/// \param end - a type* denoting one past the end of the array
657+
/// \param elementType - the element type of the array
658+
/// \param destroyer - the function to call to destroy elements
659+
void CIRGenFunction::emitArrayDestroy(mlir::Value begin, mlir::Value end,
660+
QualType elementType,
661+
CharUnits elementAlign,
662+
Destroyer *destroyer) {
663+
assert(!elementType->isArrayType());
664+
665+
// Differently from LLVM traditional codegen, use a higher level
666+
// representation instead of lowering directly to a loop.
667+
mlir::Type cirElementType = convertTypeForMem(elementType);
668+
cir::PointerType ptrToElmType = builder.getPointerTo(cirElementType);
669+
670+
// Emit the dtor call that will execute for every array element.
671+
cir::ArrayDtor::create(
672+
builder, *currSrcLoc, begin, [&](mlir::OpBuilder &b, mlir::Location loc) {
673+
auto arg = b.getInsertionBlock()->addArgument(ptrToElmType, loc);
674+
Address curAddr = Address(arg, cirElementType, elementAlign);
675+
assert(!cir::MissingFeatures::dtorCleanups());
676+
677+
// Perform the actual destruction there.
678+
destroyer(*this, curAddr, elementType);
679+
680+
cir::YieldOp::create(builder, loc);
681+
});
682+
}
683+
652684
/// Immediately perform the destruction of the given object.
653685
///
654686
/// \param addr - the address of the object; a type*
@@ -658,10 +690,34 @@ void CIRGenFunction::emitNullabilityCheck(LValue lhs, mlir::Value rhs,
658690
/// elements
659691
void CIRGenFunction::emitDestroy(Address addr, QualType type,
660692
Destroyer *destroyer) {
661-
if (getContext().getAsArrayType(type))
662-
cgm.errorNYI("emitDestroy: array type");
693+
const ArrayType *arrayType = getContext().getAsArrayType(type);
694+
if (!arrayType)
695+
return destroyer(*this, addr, type);
696+
697+
mlir::Value length = emitArrayLength(arrayType, type, addr);
698+
699+
CharUnits elementAlign = addr.getAlignment().alignmentOfArrayElement(
700+
getContext().getTypeSizeInChars(type));
701+
702+
auto constantCount = length.getDefiningOp<cir::ConstantOp>();
703+
if (!constantCount) {
704+
assert(!cir::MissingFeatures::vlas());
705+
cgm.errorNYI("emitDestroy: variable length array");
706+
return;
707+
}
708+
709+
auto constIntAttr = mlir::dyn_cast<cir::IntAttr>(constantCount.getValue());
710+
// If it's constant zero, we can just skip the entire thing.
711+
if (constIntAttr && constIntAttr.getUInt() == 0)
712+
return;
713+
714+
mlir::Value begin = addr.getPointer();
715+
mlir::Value end; // This will be used for future non-constant counts.
716+
emitArrayDestroy(begin, end, type, elementAlign, destroyer);
663717

664-
return destroyer(*this, addr, type);
718+
// If the array destroy didn't use the length op, we can erase it.
719+
if (constantCount.use_empty())
720+
constantCount.erase();
665721
}
666722

667723
CIRGenFunction::Destroyer *

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,10 @@ class CIRGenFunction : public CIRGenTypeCache {
848848
/// even if no aggregate location is provided.
849849
RValue emitAnyExprToTemp(const clang::Expr *e);
850850

851+
void emitArrayDestroy(mlir::Value begin, mlir::Value end,
852+
QualType elementType, CharUnits elementAlign,
853+
Destroyer *destroyer);
854+
851855
mlir::Value emitArrayLength(const clang::ArrayType *arrayType,
852856
QualType &baseType, Address &addr);
853857
LValue emitArraySubscriptExpr(const clang::ArraySubscriptExpr *e);

clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
2929
void runOnOp(mlir::Operation *op);
3030
void lowerCastOp(cir::CastOp op);
3131
void lowerUnaryOp(cir::UnaryOp op);
32-
void lowerArrayCtor(ArrayCtor op);
32+
void lowerArrayDtor(cir::ArrayDtor op);
33+
void lowerArrayCtor(cir::ArrayCtor op);
3334

3435
///
3536
/// AST related
@@ -172,28 +173,30 @@ void LoweringPreparePass::lowerUnaryOp(cir::UnaryOp op) {
172173
static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
173174
clang::ASTContext *astCtx,
174175
mlir::Operation *op, mlir::Type eltTy,
175-
mlir::Value arrayAddr,
176-
uint64_t arrayLen) {
176+
mlir::Value arrayAddr, uint64_t arrayLen,
177+
bool isCtor) {
177178
// Generate loop to call into ctor/dtor for every element.
178179
mlir::Location loc = op->getLoc();
179180

180-
// TODO: instead of fixed integer size, create alias for PtrDiffTy and unify
181-
// with CIRGen stuff.
181+
// TODO: instead of getting the size from the AST context, create alias for
182+
// PtrDiffTy and unify with CIRGen stuff.
182183
const unsigned sizeTypeSize =
183184
astCtx->getTypeSize(astCtx->getSignedSizeType());
184-
auto ptrDiffTy =
185-
cir::IntType::get(builder.getContext(), sizeTypeSize, /*isSigned=*/false);
186-
mlir::Value numArrayElementsConst = builder.getUnsignedInt(loc, arrayLen, 64);
185+
uint64_t endOffset = isCtor ? arrayLen : arrayLen - 1;
186+
mlir::Value endOffsetVal =
187+
builder.getUnsignedInt(loc, endOffset, sizeTypeSize);
187188

188-
auto begin = builder.create<cir::CastOp>(
189-
loc, eltTy, cir::CastKind::array_to_ptrdecay, arrayAddr);
190-
mlir::Value end = builder.create<cir::PtrStrideOp>(loc, eltTy, begin,
191-
numArrayElementsConst);
189+
auto begin = cir::CastOp::create(builder, loc, eltTy,
190+
cir::CastKind::array_to_ptrdecay, arrayAddr);
191+
mlir::Value end =
192+
cir::PtrStrideOp::create(builder, loc, eltTy, begin, endOffsetVal);
193+
mlir::Value start = isCtor ? begin : end;
194+
mlir::Value stop = isCtor ? end : begin;
192195

193196
mlir::Value tmpAddr = builder.createAlloca(
194197
loc, /*addr type*/ builder.getPointerTo(eltTy),
195198
/*var type*/ eltTy, "__array_idx", builder.getAlignmentAttr(1));
196-
builder.createStore(loc, begin, tmpAddr);
199+
builder.createStore(loc, start, tmpAddr);
197200

198201
cir::DoWhileOp loop = builder.createDoWhile(
199202
loc,
@@ -202,7 +205,7 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
202205
auto currentElement = b.create<cir::LoadOp>(loc, eltTy, tmpAddr);
203206
mlir::Type boolTy = cir::BoolType::get(b.getContext());
204207
auto cmp = builder.create<cir::CmpOp>(loc, boolTy, cir::CmpOpKind::ne,
205-
currentElement, end);
208+
currentElement, stop);
206209
builder.createCondition(cmp);
207210
},
208211
/*bodyBuilder=*/
@@ -213,15 +216,19 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
213216
op->walk([&](cir::CallOp c) { ctorCall = c; });
214217
assert(ctorCall && "expected ctor call");
215218

216-
auto one = builder.create<cir::ConstantOp>(
217-
loc, ptrDiffTy, cir::IntAttr::get(ptrDiffTy, 1));
219+
// Array elements get constructed in order but destructed in reverse.
220+
mlir::Value stride;
221+
if (isCtor)
222+
stride = builder.getUnsignedInt(loc, 1, sizeTypeSize);
223+
else
224+
stride = builder.getSignedInt(loc, -1, sizeTypeSize);
218225

219-
ctorCall->moveAfter(one);
226+
ctorCall->moveBefore(stride.getDefiningOp());
220227
ctorCall->setOperand(0, currentElement);
228+
auto nextElement = cir::PtrStrideOp::create(builder, loc, eltTy,
229+
currentElement, stride);
221230

222-
// Advance pointer and store them to temporary variable
223-
auto nextElement =
224-
builder.create<cir::PtrStrideOp>(loc, eltTy, currentElement, one);
231+
// Store the element pointer to the temporary variable
225232
builder.createStore(loc, nextElement, tmpAddr);
226233
builder.createYield(loc);
227234
});
@@ -230,6 +237,18 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
230237
op->erase();
231238
}
232239

240+
void LoweringPreparePass::lowerArrayDtor(cir::ArrayDtor op) {
241+
CIRBaseBuilderTy builder(getContext());
242+
builder.setInsertionPointAfter(op.getOperation());
243+
244+
mlir::Type eltTy = op->getRegion(0).getArgument(0).getType();
245+
assert(!cir::MissingFeatures::vlas());
246+
auto arrayLen =
247+
mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
248+
lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(), arrayLen,
249+
false);
250+
}
251+
233252
void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) {
234253
cir::CIRBaseBuilderTy builder(getContext());
235254
builder.setInsertionPointAfter(op.getOperation());
@@ -238,13 +257,15 @@ void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) {
238257
assert(!cir::MissingFeatures::vlas());
239258
auto arrayLen =
240259
mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
241-
lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(),
242-
arrayLen);
260+
lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(), arrayLen,
261+
true);
243262
}
244263

245264
void LoweringPreparePass::runOnOp(mlir::Operation *op) {
246265
if (auto arrayCtor = dyn_cast<ArrayCtor>(op))
247266
lowerArrayCtor(arrayCtor);
267+
else if (auto arrayDtor = dyn_cast<cir::ArrayDtor>(op))
268+
lowerArrayDtor(arrayDtor);
248269
else if (auto cast = mlir::dyn_cast<cir::CastOp>(op))
249270
lowerCastOp(cast);
250271
else if (auto unary = mlir::dyn_cast<cir::UnaryOp>(op))
@@ -257,7 +278,8 @@ void LoweringPreparePass::runOnOperation() {
257278
llvm::SmallVector<mlir::Operation *> opsToTransform;
258279

259280
op->walk([&](mlir::Operation *op) {
260-
if (mlir::isa<cir::ArrayCtor, cir::CastOp, cir::UnaryOp>(op))
281+
if (mlir::isa<cir::ArrayCtor, cir::ArrayDtor, cir::CastOp, cir::UnaryOp>(
282+
op))
261283
opsToTransform.push_back(op);
262284
});
263285

clang/test/CIR/CodeGen/array-ctor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ void foo() {
3333
// CIR: cir.store %[[DECAY]], %[[ITER]] : !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>
3434
// CIR: cir.do {
3535
// CIR: %[[CURRENT:.*]] = cir.load %[[ITER]] : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
36-
// CIR: %[[CONST1:.*]] = cir.const #cir.int<1> : !u64i
3736
// CIR: cir.call @_ZN1SC1Ev(%[[CURRENT]]) : (!cir.ptr<!rec_S>) -> ()
37+
// CIR: %[[CONST1:.*]] = cir.const #cir.int<1> : !u64i
3838
// CIR: %[[NEXT:.*]] = cir.ptr_stride(%[[CURRENT]] : !cir.ptr<!rec_S>, %[[CONST1]] : !u64i), !cir.ptr<!rec_S>
3939
// CIR: cir.store %[[NEXT]], %[[ITER]] : !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>
4040
// CIR: cir.yield

0 commit comments

Comments
 (0)