Skip to content

Commit 60620d0

Browse files
authored
[CIR] Canonicalization: leverage MLIR traits and folding (#173305)
Replace custom rewrite patterns with dedicated fold implementations for ScopeOp, or rely on DCE in cases of effect-less SwitchOp.
1 parent 7068497 commit 60620d0

File tree

4 files changed

+54
-42
lines changed

4 files changed

+54
-42
lines changed

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,7 @@ defvar CIR_YieldableScopes = [
842842
];
843843

844844
def CIR_YieldOp : CIR_Op<"yield", [
845-
ReturnLike, Terminator, ParentOneOf<CIR_YieldableScopes>
845+
ReturnLike, Terminator, ParentOneOf<CIR_YieldableScopes>, NoMemoryEffect
846846
]> {
847847
let summary = "Represents the default branching behaviour of a region";
848848
let description = [{
@@ -999,7 +999,8 @@ def CIR_ResumeFlatOp : CIR_Op<"resume.flat", [
999999

10001000
def CIR_ScopeOp : CIR_Op<"scope", [
10011001
DeclareOpInterfaceMethods<RegionBranchOpInterface>,
1002-
RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments
1002+
RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments,
1003+
RecursiveMemoryEffects
10031004
]> {
10041005
let summary = "Represents a C/C++ scope";
10051006
let description = [{
@@ -1026,6 +1027,7 @@ def CIR_ScopeOp : CIR_Op<"scope", [
10261027
let results = (outs Optional<CIR_AnyType>:$results);
10271028
let regions = (region AnyRegion:$scopeRegion);
10281029

1030+
let hasFolder = 1;
10291031
let hasVerifier = 1;
10301032
let skipDefaultBuilders = 1;
10311033
let assemblyFormat = [{
@@ -1104,7 +1106,8 @@ def CIR_CaseOp : CIR_Op<"case", [
11041106
def CIR_SwitchOp : CIR_Op<"switch", [
11051107
SameVariadicOperandSize,
11061108
DeclareOpInterfaceMethods<RegionBranchOpInterface>,
1107-
RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments
1109+
RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments,
1110+
RecursiveMemoryEffects
11081111
]> {
11091112
let summary = "Switch operation";
11101113
let description = [{

clang/lib/CIR/Dialect/IR/CIRDialect.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,27 @@ LogicalResult cir::ScopeOp::verify() {
12401240
return success();
12411241
}
12421242

1243+
LogicalResult cir::ScopeOp::fold(FoldAdaptor /*adaptor*/,
1244+
SmallVectorImpl<OpFoldResult> &results) {
1245+
// Only fold "trivial" scopes: a single block containing only a `cir.yield`.
1246+
if (!getRegion().hasOneBlock())
1247+
return failure();
1248+
Block &block = getRegion().front();
1249+
if (block.getOperations().size() != 1)
1250+
return failure();
1251+
1252+
auto yield = dyn_cast<cir::YieldOp>(block.front());
1253+
if (!yield)
1254+
return failure();
1255+
1256+
// Only fold when the scope produces a value.
1257+
if (getNumResults() != 1 || yield.getNumOperands() != 1)
1258+
return failure();
1259+
1260+
results.push_back(yield.getOperand(0));
1261+
return success();
1262+
}
1263+
12431264
//===----------------------------------------------------------------------===//
12441265
// BrOp
12451266
//===----------------------------------------------------------------------===//

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

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -66,42 +66,6 @@ struct RemoveRedundantBranches : public OpRewritePattern<BrOp> {
6666
}
6767
};
6868

69-
struct RemoveEmptyScope : public OpRewritePattern<ScopeOp> {
70-
using OpRewritePattern<ScopeOp>::OpRewritePattern;
71-
72-
LogicalResult matchAndRewrite(ScopeOp op,
73-
PatternRewriter &rewriter) const final {
74-
// TODO: Remove this logic once CIR uses MLIR infrastructure to remove
75-
// trivially dead operations
76-
if (op.isEmpty()) {
77-
rewriter.eraseOp(op);
78-
return success();
79-
}
80-
81-
Region &region = op.getScopeRegion();
82-
if (region.getBlocks().front().getOperations().size() == 1 &&
83-
isa<YieldOp>(region.getBlocks().front().front())) {
84-
rewriter.eraseOp(op);
85-
return success();
86-
}
87-
88-
return failure();
89-
}
90-
};
91-
92-
struct RemoveEmptySwitch : public OpRewritePattern<SwitchOp> {
93-
using OpRewritePattern<SwitchOp>::OpRewritePattern;
94-
95-
LogicalResult matchAndRewrite(SwitchOp op,
96-
PatternRewriter &rewriter) const final {
97-
if (!(op.getBody().empty() || isa<YieldOp>(op.getBody().front().front())))
98-
return failure();
99-
100-
rewriter.eraseOp(op);
101-
return success();
102-
}
103-
};
104-
10569
//===----------------------------------------------------------------------===//
10670
// CIRCanonicalizePass
10771
//===----------------------------------------------------------------------===//
@@ -124,8 +88,7 @@ struct CIRCanonicalizePass
12488
void populateCIRCanonicalizePatterns(RewritePatternSet &patterns) {
12589
// clang-format off
12690
patterns.add<
127-
RemoveRedundantBranches,
128-
RemoveEmptyScope
91+
RemoveRedundantBranches
12992
>(patterns.getContext());
13093
// clang-format on
13194
}
@@ -138,7 +101,6 @@ void CIRCanonicalizePass::runOnOperation() {
138101
// Collect operations to apply patterns.
139102
llvm::SmallVector<Operation *, 16> ops;
140103
getOperation()->walk([&](Operation *op) {
141-
assert(!cir::MissingFeatures::switchOp());
142104
assert(!cir::MissingFeatures::tryOp());
143105
assert(!cir::MissingFeatures::callOp());
144106

clang/test/CIR/Transforms/canonicalize.cir

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,19 @@ module {
2222
// CHECK-NEXT: cir.return
2323
// CHECK-NEXT: }
2424

25+
cir.func @scope_yield_value_fold() -> !u32i {
26+
%0 = cir.const #cir.int<7> : !u32i
27+
%1 = cir.scope {
28+
cir.yield %0 : !u32i
29+
} : !u32i
30+
cir.return %1 : !u32i
31+
}
32+
// CHECK: cir.func{{.*}} @scope_yield_value_fold() -> !u32i {
33+
// CHECK-NEXT: %[[C:.*]] = cir.const #cir.int<7> : !u32i
34+
// CHECK-NOT: cir.scope
35+
// CHECK: cir.return %[[C]] : !u32i
36+
// CHECK-NEXT: }
37+
2538
cir.func @empty_scope() {
2639
cir.scope {
2740
}
@@ -31,6 +44,19 @@ module {
3144
// CHECK-NEXT: cir.return
3245
// CHECK-NEXT: }
3346

47+
cir.func @dead_switch() {
48+
%0 = cir.const #cir.int<0> : !s32i
49+
cir.switch (%0 : !s32i) {
50+
cir.yield
51+
}
52+
cir.return
53+
}
54+
// CHECK: cir.func{{.*}} @dead_switch() {
55+
// CHECK-NOT: %[[Z:.*]] = cir.const #cir.int<0> : !s32i
56+
// CHECK-NOT: cir.switch
57+
// CHECK-NEXT: cir.return
58+
// CHECK-NEXT: }
59+
3460
cir.func @unary_not(%arg0: !cir.bool) -> !cir.bool {
3561
%0 = cir.unary(not, %arg0) : !cir.bool, !cir.bool
3662
%1 = cir.unary(not, %0) : !cir.bool, !cir.bool

0 commit comments

Comments
 (0)