[clang] [CIR] Canonicalization: leverage MLIR traits and folding (PR #173305)
Henrich Lauko via cfe-commits
cfe-commits at lists.llvm.org
Mon Dec 22 12:27:59 PST 2025
https://github.com/xlauko created https://github.com/llvm/llvm-project/pull/173305
Replace custom rewrite patterns with dedicated fold implementations for ScopeOp, or rely on DCE in cases of effect-less SwitchOp.
>From 1f58d2d84a8eaea5e77dca65ed391e791f4d5abe Mon Sep 17 00:00:00 2001
From: xlauko <xlauko at mail.muni.cz>
Date: Mon, 22 Dec 2025 20:56:59 +0100
Subject: [PATCH] [CIR] Canonicalization: leverage MLIR traits and folding
Replace custom rewrite patterns with dedicated fold implementations for ScopeOp, or rely on DCE in cases of effect-less SwitchOp.
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 9 +++--
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 21 ++++++++++
.../Dialect/Transforms/CIRCanonicalize.cpp | 40 +------------------
clang/test/CIR/Transforms/canonicalize.cir | 26 ++++++++++++
4 files changed, 54 insertions(+), 42 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 86ea5fca75200..8358b076ee7b6 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -842,7 +842,7 @@ defvar CIR_YieldableScopes = [
];
def CIR_YieldOp : CIR_Op<"yield", [
- ReturnLike, Terminator, ParentOneOf<CIR_YieldableScopes>
+ ReturnLike, Terminator, ParentOneOf<CIR_YieldableScopes>, NoMemoryEffect
]> {
let summary = "Represents the default branching behaviour of a region";
let description = [{
@@ -999,7 +999,8 @@ def CIR_ResumeFlatOp : CIR_Op<"resume.flat", [
def CIR_ScopeOp : CIR_Op<"scope", [
DeclareOpInterfaceMethods<RegionBranchOpInterface>,
- RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments
+ RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments,
+ RecursiveMemoryEffects
]> {
let summary = "Represents a C/C++ scope";
let description = [{
@@ -1026,6 +1027,7 @@ def CIR_ScopeOp : CIR_Op<"scope", [
let results = (outs Optional<CIR_AnyType>:$results);
let regions = (region AnyRegion:$scopeRegion);
+ let hasFolder = 1;
let hasVerifier = 1;
let skipDefaultBuilders = 1;
let assemblyFormat = [{
@@ -1104,7 +1106,8 @@ def CIR_CaseOp : CIR_Op<"case", [
def CIR_SwitchOp : CIR_Op<"switch", [
SameVariadicOperandSize,
DeclareOpInterfaceMethods<RegionBranchOpInterface>,
- RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments
+ RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments,
+ RecursiveMemoryEffects
]> {
let summary = "Switch operation";
let description = [{
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index cca13a243c419..8f0dc705181e6 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -1240,6 +1240,27 @@ LogicalResult cir::ScopeOp::verify() {
return success();
}
+LogicalResult cir::ScopeOp::fold(FoldAdaptor /*adaptor*/,
+ SmallVectorImpl<OpFoldResult> &results) {
+ // Only fold "trivial" scopes: a single block containing only a `cir.yield`.
+ if (!getRegion().hasOneBlock())
+ return failure();
+ Block &block = getRegion().front();
+ if (block.getOperations().size() != 1)
+ return failure();
+
+ auto yield = dyn_cast<cir::YieldOp>(block.front());
+ if (!yield)
+ return failure();
+
+ // Only fold when the scope produces a value.
+ if (getNumResults() != 1 || yield.getNumOperands() != 1)
+ return failure();
+
+ results.push_back(yield.getOperand(0));
+ return success();
+}
+
//===----------------------------------------------------------------------===//
// BrOp
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp
index 0749c5e79e3ff..6a0c46e546cd5 100644
--- a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp
@@ -66,42 +66,6 @@ struct RemoveRedundantBranches : public OpRewritePattern<BrOp> {
}
};
-struct RemoveEmptyScope : public OpRewritePattern<ScopeOp> {
- using OpRewritePattern<ScopeOp>::OpRewritePattern;
-
- LogicalResult matchAndRewrite(ScopeOp op,
- PatternRewriter &rewriter) const final {
- // TODO: Remove this logic once CIR uses MLIR infrastructure to remove
- // trivially dead operations
- if (op.isEmpty()) {
- rewriter.eraseOp(op);
- return success();
- }
-
- Region ®ion = op.getScopeRegion();
- if (region.getBlocks().front().getOperations().size() == 1 &&
- isa<YieldOp>(region.getBlocks().front().front())) {
- rewriter.eraseOp(op);
- return success();
- }
-
- return failure();
- }
-};
-
-struct RemoveEmptySwitch : public OpRewritePattern<SwitchOp> {
- using OpRewritePattern<SwitchOp>::OpRewritePattern;
-
- LogicalResult matchAndRewrite(SwitchOp op,
- PatternRewriter &rewriter) const final {
- if (!(op.getBody().empty() || isa<YieldOp>(op.getBody().front().front())))
- return failure();
-
- rewriter.eraseOp(op);
- return success();
- }
-};
-
//===----------------------------------------------------------------------===//
// CIRCanonicalizePass
//===----------------------------------------------------------------------===//
@@ -124,8 +88,7 @@ struct CIRCanonicalizePass
void populateCIRCanonicalizePatterns(RewritePatternSet &patterns) {
// clang-format off
patterns.add<
- RemoveRedundantBranches,
- RemoveEmptyScope
+ RemoveRedundantBranches
>(patterns.getContext());
// clang-format on
}
@@ -138,7 +101,6 @@ void CIRCanonicalizePass::runOnOperation() {
// Collect operations to apply patterns.
llvm::SmallVector<Operation *, 16> ops;
getOperation()->walk([&](Operation *op) {
- assert(!cir::MissingFeatures::switchOp());
assert(!cir::MissingFeatures::tryOp());
assert(!cir::MissingFeatures::callOp());
diff --git a/clang/test/CIR/Transforms/canonicalize.cir b/clang/test/CIR/Transforms/canonicalize.cir
index 5606f9e16a690..de7c90f716398 100644
--- a/clang/test/CIR/Transforms/canonicalize.cir
+++ b/clang/test/CIR/Transforms/canonicalize.cir
@@ -22,6 +22,19 @@ module {
// CHECK-NEXT: cir.return
// CHECK-NEXT: }
+ cir.func @scope_yield_value_fold() -> !u32i {
+ %0 = cir.const #cir.int<7> : !u32i
+ %1 = cir.scope {
+ cir.yield %0 : !u32i
+ } : !u32i
+ cir.return %1 : !u32i
+ }
+ // CHECK: cir.func{{.*}} @scope_yield_value_fold() -> !u32i {
+ // CHECK-NEXT: %[[C:.*]] = cir.const #cir.int<7> : !u32i
+ // CHECK-NOT: cir.scope
+ // CHECK: cir.return %[[C]] : !u32i
+ // CHECK-NEXT: }
+
cir.func @empty_scope() {
cir.scope {
}
@@ -31,6 +44,19 @@ module {
// CHECK-NEXT: cir.return
// CHECK-NEXT: }
+ cir.func @dead_switch() {
+ %0 = cir.const #cir.int<0> : !s32i
+ cir.switch (%0 : !s32i) {
+ cir.yield
+ }
+ cir.return
+ }
+ // CHECK: cir.func{{.*}} @dead_switch() {
+ // CHECK-NOT: %[[Z:.*]] = cir.const #cir.int<0> : !s32i
+ // CHECK-NOT: cir.switch
+ // CHECK-NEXT: cir.return
+ // CHECK-NEXT: }
+
cir.func @unary_not(%arg0: !cir.bool) -> !cir.bool {
%0 = cir.unary(not, %arg0) : !cir.bool, !cir.bool
%1 = cir.unary(not, %0) : !cir.bool, !cir.bool
More information about the cfe-commits
mailing list