Skip to content

Commit

Permalink
[CIR][NFC] Add cleanup region to ScopeOp
Browse files Browse the repository at this point in the history
This does not change anything in practice, work in that direction should come
next. We also want this to not affect existing tests to isolate upcoming
changes.
  • Loading branch information
bcardosolopes authored and lanza committed Jan 27, 2025
1 parent 8efa1d0 commit 6ea1d14
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 26 deletions.
14 changes: 10 additions & 4 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -1066,23 +1066,29 @@ def ScopeOp : CIR_Op<"scope", [
The blocks can be terminated by `cir.yield`, `cir.return` or `cir.throw`.
If `cir.scope` yields no value, the `cir.yield` can be left out, and
will be inserted implicitly.

The scope might also have an associated `cleanup` region, providing code
that run destruction of automatic variables. Note that in order to lower the
cleanup region while keeping C++ semantics, all immediate control-flow
breaking operations not under a children scope should jump to this cleanup
code.
}];

let results = (outs Optional<CIR_AnyType>:$results);
let regions = (region AnyRegion:$scopeRegion);
let regions = (region AnyRegion:$scopeRegion, AnyRegion:$cleanupRegion);

let hasVerifier = 1;
let skipDefaultBuilders = 1;
let assemblyFormat = [{
custom<OmittedTerminatorRegion>($scopeRegion) (`:` type($results)^)? attr-dict
custom<OmittedTerminatorRegion>($scopeRegion, $cleanupRegion) (`:` type($results)^)? attr-dict
}];

let extraClassDeclaration = [{
/// Determine whether the scope is empty, meaning it contains a single block
/// terminated by a cir.yield.
bool isEmpty() {
auto &entry = getRegion().front();
return getRegion().hasOneBlock() &&
auto &entry = getScopeRegion().front();
return getScopeRegion().hasOneBlock() &&
llvm::isa<YieldOp>(entry.front());
}
}];
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2842,7 +2842,7 @@ mlir::Value CIRGenFunction::emitAlloca(StringRef name, mlir::Type ty,
if (auto tryOp =
llvm::dyn_cast_if_present<cir::TryOp>(entryBlock->getParentOp())) {
if (auto scopeOp = llvm::dyn_cast<cir::ScopeOp>(tryOp->getParentOp()))
entryBlock = &scopeOp.getRegion().front();
entryBlock = &scopeOp.getScopeRegion().front();
}

return emitAlloca(name, ty, loc, alignment,
Expand Down
15 changes: 9 additions & 6 deletions clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -826,13 +826,16 @@ void CIRGenItaniumCXXABI::emitBeginCatch(CIRGenFunction &CGF,
auto getCatchParamAllocaIP = [&]() {
auto currIns = CGF.getBuilder().saveInsertionPoint();
auto currParent = currIns.getBlock()->getParentOp();
mlir::Operation *scopeLikeOp = currParent->getParentOfType<cir::ScopeOp>();
if (!scopeLikeOp)
scopeLikeOp = currParent->getParentOfType<cir::FuncOp>();
assert(scopeLikeOp && "unknown outermost scope-like parent");
assert(scopeLikeOp->getNumRegions() == 1 && "expected single region");

auto *insertBlock = &scopeLikeOp->getRegion(0).getBlocks().back();
mlir::Block *insertBlock = nullptr;
if (auto scopeOp = currParent->getParentOfType<cir::ScopeOp>()) {
insertBlock = &scopeOp.getScopeRegion().getBlocks().back();
} else if (auto fnOp = currParent->getParentOfType<cir::FuncOp>()) {
insertBlock = &fnOp.getRegion().getBlocks().back();
} else {
llvm_unreachable("unknown outermost scope-like parent");
}

return CGF.getBuilder().getBestAllocaInsertPoint(insertBlock);
};

Expand Down
47 changes: 37 additions & 10 deletions clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,22 +276,41 @@ void parseVisibilityAttr(OpAsmParser &parser, cir::VisibilityAttr &visibility) {
// CIR Custom Parsers/Printers
//===----------------------------------------------------------------------===//

static mlir::ParseResult parseOmittedTerminatorRegion(mlir::OpAsmParser &parser,
mlir::Region &region) {
static mlir::ParseResult
parseOmittedTerminatorRegion(mlir::OpAsmParser &parser,
mlir::Region &scopeRegion,
mlir::Region &cleanupRegion) {
auto regionLoc = parser.getCurrentLocation();
if (parser.parseRegion(region))
if (parser.parseRegion(scopeRegion))
return failure();
if (ensureRegionTerm(parser, region, regionLoc).failed())
if (ensureRegionTerm(parser, scopeRegion, regionLoc).failed())
return failure();

// Parse optional cleanup region.
if (parser.parseOptionalKeyword("cleanup").succeeded()) {
if (parser.parseRegion(cleanupRegion, /*arguments=*/{}, /*argTypes=*/{}))
return failure();
if (ensureRegionTerm(parser, cleanupRegion, regionLoc).failed())
return failure();
}

return success();
}

static void printOmittedTerminatorRegion(mlir::OpAsmPrinter &printer,
cir::ScopeOp &op,
mlir::Region &region) {
printer.printRegion(region,
mlir::Region &scopeRegion,
mlir::Region &cleanupRegion) {
printer.printRegion(scopeRegion,
/*printEntryBlockArgs=*/false,
/*printBlockTerminators=*/!omitRegionTerm(region));
/*printBlockTerminators=*/!omitRegionTerm(scopeRegion));
if (!op.getCleanupRegion().empty()) {
printer << " cleanup ";
printer.printRegion(
cleanupRegion,
/*printEntryBlockArgs=*/false,
/*printBlockTerminators=*/!omitRegionTerm(cleanupRegion));
}
}

//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -1251,6 +1270,7 @@ void cir::ScopeOp::getSuccessorRegions(

// If the condition isn't constant, both regions may be executed.
regions.push_back(RegionSuccessor(&getScopeRegion()));
regions.push_back(RegionSuccessor(&getCleanupRegion()));
}

void cir::ScopeOp::build(
Expand All @@ -1261,6 +1281,8 @@ void cir::ScopeOp::build(
OpBuilder::InsertionGuard guard(builder);
Region *scopeRegion = result.addRegion();
builder.createBlock(scopeRegion);
// cleanup region, do not eagarly create blocks, do it upon demand.
(void)result.addRegion();

mlir::Type yieldTy;
scopeBuilder(builder, yieldTy, result.location);
Expand All @@ -1276,17 +1298,22 @@ void cir::ScopeOp::build(
OpBuilder::InsertionGuard guard(builder);
Region *scopeRegion = result.addRegion();
builder.createBlock(scopeRegion);
// cleanup region, do not eagarly create blocks, do it upon demand.
(void)result.addRegion();
scopeBuilder(builder, result.location);
}

LogicalResult cir::ScopeOp::verify() {
if (getRegion().empty()) {
if (getScopeRegion().empty()) {
return emitOpError() << "cir.scope must not be empty since it should "
"include at least an implicit cir.yield ";
}

if (getRegion().back().empty() ||
!getRegion().back().getTerminator()->hasTrait<OpTrait::IsTerminator>())
if (getScopeRegion().back().empty() ||
!getScopeRegion()
.back()
.getTerminator()
->hasTrait<OpTrait::IsTerminator>())
return emitOpError() << "last block of cir.scope must be terminated";
return success();
}
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,9 @@ class CIRScopeOpFlattening : public mlir::OpRewritePattern<cir::ScopeOp> {
continueBlock->addArguments(scopeOp.getResultTypes(), loc);

// Inline body region.
auto *beforeBody = &scopeOp.getRegion().front();
auto *afterBody = &scopeOp.getRegion().back();
rewriter.inlineRegionBefore(scopeOp.getRegion(), continueBlock);
auto *beforeBody = &scopeOp.getScopeRegion().front();
auto *afterBody = &scopeOp.getScopeRegion().back();
rewriter.inlineRegionBefore(scopeOp.getScopeRegion(), continueBlock);

// Save stack and then branch into the body of the region.
rewriter.setInsertionPointToEnd(currentBlock);
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CIR/Lowering/ThroughMLIR/LowerCIRToMLIR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -853,7 +853,7 @@ class CIRScopeOpLowering : public mlir::OpConversionPattern<cir::ScopeOp> {
return mlir::success();
}

for (auto &block : scopeOp.getRegion()) {
for (auto &block : scopeOp.getScopeRegion()) {
rewriter.setInsertionPointToEnd(&block);
auto *terminator = block.getTerminator();
rewriter.replaceOpWithNewOp<mlir::memref::AllocaScopeReturnOp>(
Expand Down
32 changes: 31 additions & 1 deletion clang/test/CIR/IR/scope.cir
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@

module {
// Should properly print/parse scope with implicit empty yield.
// CHECK-LABEL: implicit_yield
cir.func @implicit_yield() {
cir.scope {
}
// CHECK: cir.scope {
// CHECK: }
// CHECK-NEXT: }
// CHECK-NEXT: cir.return
cir.return
}

// Should properly print/parse scope with explicit yield.
// CHECK-LABEL: explicit_yield
cir.func @explicit_yield() {
%0 = cir.scope {
%1 = cir.alloca !u32i, !cir.ptr<!u32i>, ["a", init] {alignment = 4 : i64}
Expand All @@ -24,4 +27,31 @@ module {
// CHECK: } : !cir.ptr<!u32i>
cir.return
}

// Handle optional cleanup presence
// CHECK-LABEL: empty_cleanup
cir.func @empty_cleanup() {
cir.scope {
} cleanup {
}
// CHECK: cir.scope {
// CHECK-NEXT: } cleanup {
// CHECK-NEXT: }
// CHECK-NEXT: cir.return
cir.return
}

// Handle optional cleanup presence
// CHECK-LABEL: some_cleanup
cir.func @some_cleanup() {
cir.scope {
} cleanup {
%1 = cir.alloca !u32i, !cir.ptr<!u32i>, ["a", init] {alignment = 4 : i64}
}
// CHECK: cir.scope {
// CHECK: } cleanup {
// CHECK: cir.alloca
// CHECK: }
cir.return
}
}

0 comments on commit 6ea1d14

Please sign in to comment.