-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[CIR] Add support for array cleanups #150499
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -649,6 +649,38 @@ void CIRGenFunction::emitNullabilityCheck(LValue lhs, mlir::Value rhs, | |
| assert(!cir::MissingFeatures::sanitizers()); | ||
| } | ||
|
|
||
| /// 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 end - a type* denoting one past the end of the array | ||
| /// \param elementType - the element type of the array | ||
| /// \param destroyer - the function to call to destroy elements | ||
| void CIRGenFunction::emitArrayDestroy(mlir::Value begin, mlir::Value end, | ||
| QualType elementType, | ||
| CharUnits elementAlign, | ||
| Destroyer *destroyer) { | ||
| assert(!elementType->isArrayType()); | ||
|
|
||
| // Differently from LLVM traditional codegen, use a higher level | ||
| // representation instead of lowering directly to a loop. | ||
| mlir::Type cirElementType = convertTypeForMem(elementType); | ||
| cir::PointerType ptrToElmType = builder.getPointerTo(cirElementType); | ||
|
|
||
| // Emit the dtor call that will execute for every array element. | ||
| cir::ArrayDtor::create( | ||
| builder, *currSrcLoc, begin, [&](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); | ||
| }); | ||
| } | ||
|
|
||
| /// Immediately perform the destruction of the given object. | ||
| /// | ||
| /// \param addr - the address of the object; a type* | ||
|
|
@@ -658,10 +690,34 @@ void CIRGenFunction::emitNullabilityCheck(LValue lhs, mlir::Value rhs, | |
| /// elements | ||
| void CIRGenFunction::emitDestroy(Address addr, QualType type, | ||
| Destroyer *destroyer) { | ||
| if (getContext().getAsArrayType(type)) | ||
| cgm.errorNYI("emitDestroy: array type"); | ||
| const ArrayType *arrayType = getContext().getAsArrayType(type); | ||
| if (!arrayType) | ||
| return destroyer(*this, addr, type); | ||
|
|
||
| mlir::Value length = emitArrayLength(arrayType, type, addr); | ||
|
|
||
| CharUnits elementAlign = addr.getAlignment().alignmentOfArrayElement( | ||
| getContext().getTypeSizeInChars(type)); | ||
|
|
||
| auto constantCount = length.getDefiningOp<cir::ConstantOp>(); | ||
| if (!constantCount) { | ||
| assert(!cir::MissingFeatures::vlas()); | ||
| cgm.errorNYI("emitDestroy: variable length array"); | ||
| 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(); | ||
| mlir::Value end; // This will be used for future non-constant counts. | ||
| emitArrayDestroy(begin, end, type, elementAlign, destroyer); | ||
|
|
||
| return destroyer(*this, addr, type); | ||
| // If the array destroy didn't use the length op, we can erase it. | ||
| if (constantCount.use_empty()) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can simplify the control flow a little if you invert the condition up there. Something like: if(!constantCount) {
assert(!cir::MissingFeature::vlas());
cgm.errorNYI("emitDestroy: variable length array");
return;
}
auto constIntAttr = mlir::dyn_cast<cir::IntAttr>(constantCount.getValue());
// ...and if it's constant zero, we can just skip the entire thing.
if (constIntAttr && constIntAttr.getUInt() == 0)
return;
checkZeroLength = false;
mlir::Value begin = addr.getPointer();
mlir::Value end; // This will be used for future non-constant counts.
emitArrayDestroy(begin, end, type, elementAlign, destroyer, checkZeroLength);
if (constantCount.use_empty())
constantCount.erase();
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We will probably need to switch back to the if-else structure when we support VLAs, but for now your suggestion looks better. I'll make the change. |
||
| constantCount.erase(); | ||
| } | ||
|
|
||
| CIRGenFunction::Destroyer * | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,7 +29,8 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> { | |
| void runOnOp(mlir::Operation *op); | ||
| void lowerCastOp(cir::CastOp op); | ||
| void lowerUnaryOp(cir::UnaryOp op); | ||
| void lowerArrayCtor(ArrayCtor op); | ||
| void lowerArrayDtor(cir::ArrayDtor op); | ||
| void lowerArrayCtor(cir::ArrayCtor op); | ||
|
|
||
| /// | ||
| /// AST related | ||
|
|
@@ -172,28 +173,30 @@ void LoweringPreparePass::lowerUnaryOp(cir::UnaryOp op) { | |
| static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder, | ||
| clang::ASTContext *astCtx, | ||
| 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. | ||
| mlir::Location loc = op->getLoc(); | ||
|
|
||
| // TODO: instead of fixed integer size, create alias for PtrDiffTy and unify | ||
| // with CIRGen stuff. | ||
| // TODO: instead of getting the size from the AST context, create alias for | ||
| // PtrDiffTy and unify with CIRGen stuff. | ||
| const unsigned sizeTypeSize = | ||
| astCtx->getTypeSize(astCtx->getSignedSizeType()); | ||
| auto ptrDiffTy = | ||
| cir::IntType::get(builder.getContext(), sizeTypeSize, /*isSigned=*/false); | ||
| mlir::Value numArrayElementsConst = builder.getUnsignedInt(loc, arrayLen, 64); | ||
| uint64_t endOffset = isCtor ? arrayLen : arrayLen - 1; | ||
| mlir::Value endOffsetVal = | ||
| builder.getUnsignedInt(loc, endOffset, sizeTypeSize); | ||
|
|
||
| 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); | ||
| auto begin = cir::CastOp::create(builder, loc, eltTy, | ||
| cir::CastKind::array_to_ptrdecay, arrayAddr); | ||
| mlir::Value end = | ||
| cir::PtrStrideOp::create(builder, loc, eltTy, begin, endOffsetVal); | ||
| mlir::Value start = isCtor ? begin : end; | ||
| mlir::Value stop = isCtor ? end : begin; | ||
|
|
||
| mlir::Value tmpAddr = builder.createAlloca( | ||
| loc, /*addr type*/ builder.getPointerTo(eltTy), | ||
| /*var type*/ eltTy, "__array_idx", builder.getAlignmentAttr(1)); | ||
| builder.createStore(loc, begin, tmpAddr); | ||
| builder.createStore(loc, start, tmpAddr); | ||
|
|
||
| cir::DoWhileOp loop = builder.createDoWhile( | ||
| loc, | ||
|
|
@@ -202,7 +205,7 @@ static void lowerArrayDtorCtorIntoLoop(cir::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=*/ | ||
|
|
@@ -213,15 +216,19 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder, | |
| op->walk([&](cir::CallOp c) { ctorCall = c; }); | ||
| assert(ctorCall && "expected ctor call"); | ||
|
|
||
| auto one = builder.create<cir::ConstantOp>( | ||
| loc, ptrDiffTy, cir::IntAttr::get(ptrDiffTy, 1)); | ||
| // Array elements get constructed in order but destructed in reverse. | ||
| mlir::Value stride; | ||
| if (isCtor) | ||
| stride = builder.getUnsignedInt(loc, 1, sizeTypeSize); | ||
| else | ||
| stride = builder.getSignedInt(loc, -1, sizeTypeSize); | ||
|
|
||
| ctorCall->moveAfter(one); | ||
| ctorCall->moveBefore(stride.getDefiningOp()); | ||
| ctorCall->setOperand(0, currentElement); | ||
| auto nextElement = cir::PtrStrideOp::create(builder, loc, eltTy, | ||
| currentElement, stride); | ||
|
|
||
| // Advance pointer and store them to temporary variable | ||
| auto nextElement = | ||
| builder.create<cir::PtrStrideOp>(loc, eltTy, currentElement, one); | ||
| // Store the element pointer to the temporary variable | ||
| builder.createStore(loc, nextElement, tmpAddr); | ||
| builder.createYield(loc); | ||
| }); | ||
|
|
@@ -230,6 +237,18 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder, | |
| op->erase(); | ||
| } | ||
|
|
||
| void LoweringPreparePass::lowerArrayDtor(cir::ArrayDtor op) { | ||
| CIRBaseBuilderTy builder(getContext()); | ||
| builder.setInsertionPointAfter(op.getOperation()); | ||
|
|
||
| mlir::Type eltTy = op->getRegion(0).getArgument(0).getType(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ArrayCtor asserts here |
||
| assert(!cir::MissingFeatures::vlas()); | ||
| auto arrayLen = | ||
| mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize(); | ||
| lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(), arrayLen, | ||
| false); | ||
| } | ||
|
|
||
| void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) { | ||
| cir::CIRBaseBuilderTy builder(getContext()); | ||
| builder.setInsertionPointAfter(op.getOperation()); | ||
|
|
@@ -238,13 +257,15 @@ void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) { | |
| assert(!cir::MissingFeatures::vlas()); | ||
| auto arrayLen = | ||
| mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize(); | ||
| lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(), | ||
| arrayLen); | ||
| lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(), arrayLen, | ||
| true); | ||
| } | ||
|
|
||
| void LoweringPreparePass::runOnOp(mlir::Operation *op) { | ||
| if (auto arrayCtor = dyn_cast<ArrayCtor>(op)) | ||
| lowerArrayCtor(arrayCtor); | ||
| else if (auto arrayDtor = dyn_cast<cir::ArrayDtor>(op)) | ||
| lowerArrayDtor(arrayDtor); | ||
| else if (auto cast = mlir::dyn_cast<cir::CastOp>(op)) | ||
| lowerCastOp(cast); | ||
| else if (auto unary = mlir::dyn_cast<cir::UnaryOp>(op)) | ||
|
|
@@ -257,7 +278,8 @@ void LoweringPreparePass::runOnOperation() { | |
| llvm::SmallVector<mlir::Operation *> opsToTransform; | ||
|
|
||
| op->walk([&](mlir::Operation *op) { | ||
| if (mlir::isa<cir::ArrayCtor, cir::CastOp, cir::UnaryOp>(op)) | ||
| if (mlir::isa<cir::ArrayCtor, cir::ArrayDtor, cir::CastOp, cir::UnaryOp>( | ||
| op)) | ||
| opsToTransform.push_back(op); | ||
| }); | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a
cir::MissingFeature::vla()here?