Skip to content

Commit 13c8a3d

Browse files
committed
[CIR] Initial support for array cleanups
This adds initial support for array cleanups, including the ArrayDtor op.
1 parent 47b5917 commit 13c8a3d

File tree

8 files changed

+274
-28
lines changed

8 files changed

+274
-28
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: 20 additions & 3 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", [
@@ -2229,7 +2229,7 @@ def CIR_TrapOp : CIR_Op<"trap", [Terminator]> {
22292229
}
22302230

22312231
//===----------------------------------------------------------------------===//
2232-
// ArrayCtor
2232+
// ArrayCtor & ArrayDtor
22332233
//===----------------------------------------------------------------------===//
22342234

22352235
class CIR_ArrayInitDestroy<string mnemonic> : CIR_Op<mnemonic> {
@@ -2272,6 +2272,23 @@ def CIR_ArrayCtor : CIR_ArrayInitDestroy<"array.ctor"> {
22722272
}];
22732273
}
22742274

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 index to initialize.
2281+
2282+
```mlir
2283+
cir.array.dtor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) {
2284+
^bb0(%arg0: !cir.ptr<!rec_S>):
2285+
cir.call @some_dtor(%arg0) : (!cir.ptr<!rec_S>) -> ()
2286+
cir.yield
2287+
}
2288+
```
2289+
}];
2290+
}
2291+
22752292
//===----------------------------------------------------------------------===//
22762293
// VecCreate
22772294
//===----------------------------------------------------------------------===//

clang/lib/CIR/CodeGen/CIRGenDecl.cpp

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,41 @@ 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+
bool checkZeroLength) {
664+
assert(!elementType->isArrayType());
665+
if (checkZeroLength)
666+
cgm.errorNYI("emitArrayDestroy: check for zero length");
667+
668+
// Differently from LLVM traditional codegen, use a higher level
669+
// representation instead of lowering directly to a loop.
670+
mlir::Type cirElementType = convertTypeForMem(elementType);
671+
cir::PointerType ptrToElmType = builder.getPointerTo(cirElementType);
672+
673+
// Emit the dtor call that will execute for every array element.
674+
builder.create<cir::ArrayDtor>(
675+
*currSrcLoc, begin, [&](mlir::OpBuilder &b, mlir::Location loc) {
676+
auto arg = b.getInsertionBlock()->addArgument(ptrToElmType, loc);
677+
Address curAddr = Address(arg, cirElementType, elementAlign);
678+
assert(!cir::MissingFeatures::dtorCleanups());
679+
680+
// Perform the actual destruction there.
681+
destroyer(*this, curAddr, elementType);
682+
683+
builder.create<cir::YieldOp>(loc);
684+
});
685+
}
686+
652687
/// Immediately perform the destruction of the given object.
653688
///
654689
/// \param addr - the address of the object; a type*
@@ -658,10 +693,38 @@ void CIRGenFunction::emitNullabilityCheck(LValue lhs, mlir::Value rhs,
658693
/// elements
659694
void CIRGenFunction::emitDestroy(Address addr, QualType type,
660695
Destroyer *destroyer) {
661-
if (getContext().getAsArrayType(type))
662-
cgm.errorNYI("emitDestroy: array type");
696+
const ArrayType *arrayType = getContext().getAsArrayType(type);
697+
if (!arrayType)
698+
return destroyer(*this, addr, type);
699+
700+
mlir::Value length = emitArrayLength(arrayType, type, addr);
701+
702+
CharUnits elementAlign = addr.getAlignment().alignmentOfArrayElement(
703+
getContext().getTypeSizeInChars(type));
704+
705+
// Normally we have to check whether the array is zero-length.
706+
bool checkZeroLength = true;
707+
708+
// But if the array length is constant, we can suppress that.
709+
auto constantCount = dyn_cast<cir::ConstantOp>(length.getDefiningOp());
710+
if (constantCount) {
711+
auto constIntAttr = mlir::dyn_cast<cir::IntAttr>(constantCount.getValue());
712+
// ...and if it's constant zero, we can just skip the entire thing.
713+
if (constIntAttr && constIntAttr.getUInt() == 0)
714+
return;
715+
checkZeroLength = false;
716+
} else {
717+
cgm.errorNYI("emitDestroy: variable length array");
718+
return;
719+
}
720+
721+
mlir::Value begin = addr.getPointer();
722+
mlir::Value end; // This will be used for future non-constant counts.
723+
emitArrayDestroy(begin, end, type, elementAlign, destroyer, checkZeroLength);
663724

664-
return destroyer(*this, addr, type);
725+
// If the array destroy didn't use the length op, we can erase it.
726+
if (constantCount.use_empty())
727+
constantCount.erase();
665728
}
666729

667730
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, bool checkZeroLength);
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 & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ 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 lowerArrayDtor(ArrayDtor op);
3233
void lowerArrayCtor(ArrayCtor op);
3334

3435
///
@@ -172,28 +173,29 @@ 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+
mlir::Value numArrayElementsConst =
186+
builder.getUnsignedInt(loc, arrayLen, sizeTypeSize);
187187

188188
auto begin = builder.create<cir::CastOp>(
189189
loc, eltTy, cir::CastKind::array_to_ptrdecay, arrayAddr);
190190
mlir::Value end = builder.create<cir::PtrStrideOp>(loc, eltTy, begin,
191191
numArrayElementsConst);
192+
mlir::Value start = isCtor ? begin : end;
193+
mlir::Value stop = isCtor ? end : begin;
192194

193195
mlir::Value tmpAddr = builder.createAlloca(
194196
loc, /*addr type*/ builder.getPointerTo(eltTy),
195197
/*var type*/ eltTy, "__array_idx", builder.getAlignmentAttr(1));
196-
builder.createStore(loc, begin, tmpAddr);
198+
builder.createStore(loc, start, tmpAddr);
197199

198200
cir::DoWhileOp loop = builder.createDoWhile(
199201
loc,
@@ -202,7 +204,7 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
202204
auto currentElement = b.create<cir::LoadOp>(loc, eltTy, tmpAddr);
203205
mlir::Type boolTy = cir::BoolType::get(b.getContext());
204206
auto cmp = builder.create<cir::CmpOp>(loc, boolTy, cir::CmpOpKind::ne,
205-
currentElement, end);
207+
currentElement, stop);
206208
builder.createCondition(cmp);
207209
},
208210
/*bodyBuilder=*/
@@ -213,15 +215,23 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
213215
op->walk([&](cir::CallOp c) { ctorCall = c; });
214216
assert(ctorCall && "expected ctor call");
215217

216-
auto one = builder.create<cir::ConstantOp>(
217-
loc, ptrDiffTy, cir::IntAttr::get(ptrDiffTy, 1));
218-
219-
ctorCall->moveAfter(one);
220-
ctorCall->setOperand(0, currentElement);
221-
222-
// Advance pointer and store them to temporary variable
223-
auto nextElement =
224-
builder.create<cir::PtrStrideOp>(loc, eltTy, currentElement, one);
218+
// Array elements get constructed in order but destructed in reverse.
219+
cir::PtrStrideOp nextElement;
220+
if (isCtor) {
221+
mlir::Value stride = builder.getUnsignedInt(loc, 1, sizeTypeSize);
222+
ctorCall->moveBefore(stride.getDefiningOp());
223+
ctorCall->setOperand(0, currentElement);
224+
nextElement = builder.create<cir::PtrStrideOp>(
225+
loc, eltTy, currentElement, stride);
226+
} else {
227+
mlir::Value stride = builder.getSignedInt(loc, -1, sizeTypeSize);
228+
nextElement = builder.create<cir::PtrStrideOp>(
229+
loc, eltTy, currentElement, stride);
230+
ctorCall->moveAfter(nextElement);
231+
ctorCall->setOperand(0, nextElement);
232+
}
233+
234+
// Store the element pointer to the temporary variable
225235
builder.createStore(loc, nextElement, tmpAddr);
226236
builder.createYield(loc);
227237
});
@@ -230,6 +240,17 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
230240
op->erase();
231241
}
232242

243+
void LoweringPreparePass::lowerArrayDtor(ArrayDtor op) {
244+
CIRBaseBuilderTy builder(getContext());
245+
builder.setInsertionPointAfter(op.getOperation());
246+
247+
mlir::Type eltTy = op->getRegion(0).getArgument(0).getType();
248+
auto arrayLen =
249+
mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
250+
lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(), arrayLen,
251+
false);
252+
}
253+
233254
void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) {
234255
cir::CIRBaseBuilderTy builder(getContext());
235256
builder.setInsertionPointAfter(op.getOperation());
@@ -238,8 +259,8 @@ void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) {
238259
assert(!cir::MissingFeatures::vlas());
239260
auto arrayLen =
240261
mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
241-
lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(),
242-
arrayLen);
262+
lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(), arrayLen,
263+
true);
243264
}
244265

245266
void LoweringPreparePass::runOnOp(mlir::Operation *op) {
@@ -249,6 +270,8 @@ void LoweringPreparePass::runOnOp(mlir::Operation *op) {
249270
lowerCastOp(cast);
250271
else if (auto unary = mlir::dyn_cast<cir::UnaryOp>(op))
251272
lowerUnaryOp(unary);
273+
else if (auto arrayDtor = dyn_cast<ArrayDtor>(op))
274+
lowerArrayDtor(arrayDtor);
252275
}
253276

254277
void LoweringPreparePass::runOnOperation() {
@@ -257,7 +280,8 @@ void LoweringPreparePass::runOnOperation() {
257280
llvm::SmallVector<mlir::Operation *> opsToTransform;
258281

259282
op->walk([&](mlir::Operation *op) {
260-
if (mlir::isa<cir::ArrayCtor, cir::CastOp, cir::UnaryOp>(op))
283+
if (mlir::isa<cir::ArrayCtor, cir::ArrayDtor, cir::CastOp, cir::UnaryOp>(
284+
op))
261285
opsToTransform.push_back(op);
262286
});
263287

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)