clang 22.0.0git
CIRCanonicalize.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements pass that canonicalizes CIR operations, eliminating
10// redundant branches, empty scopes, and other unnecessary operations.
11//
12//===----------------------------------------------------------------------===//
13
14#include "PassDetail.h"
15#include "mlir/Dialect/Func/IR/FuncOps.h"
16#include "mlir/IR/Block.h"
17#include "mlir/IR/Operation.h"
18#include "mlir/IR/PatternMatch.h"
19#include "mlir/IR/Region.h"
20#include "mlir/Support/LogicalResult.h"
21#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
25
26using namespace mlir;
27using namespace cir;
28
29namespace mlir {
30#define GEN_PASS_DEF_CIRCANONICALIZE
31#include "clang/CIR/Dialect/Passes.h.inc"
32} // namespace mlir
33
34namespace {
35
36/// Removes branches between two blocks if it is the only branch.
37///
38/// From:
39/// ^bb0:
40/// cir.br ^bb1
41/// ^bb1: // pred: ^bb0
42/// cir.return
43///
44/// To:
45/// ^bb0:
46/// cir.return
47struct RemoveRedundantBranches : public OpRewritePattern<BrOp> {
48 using OpRewritePattern<BrOp>::OpRewritePattern;
49
50 LogicalResult matchAndRewrite(BrOp op,
51 PatternRewriter &rewriter) const final {
52 Block *block = op.getOperation()->getBlock();
53 Block *dest = op.getDest();
54
55 if (isa<cir::LabelOp>(dest->front()))
56 return failure();
57 // Single edge between blocks: merge it.
58 if (block->getNumSuccessors() == 1 &&
59 dest->getSinglePredecessor() == block) {
60 rewriter.eraseOp(op);
61 rewriter.mergeBlocks(dest, block);
62 return success();
63 }
64
65 return failure();
66 }
67};
68
69struct 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
92struct 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
105//===----------------------------------------------------------------------===//
106// CIRCanonicalizePass
107//===----------------------------------------------------------------------===//
108
109struct CIRCanonicalizePass
110 : public impl::CIRCanonicalizeBase<CIRCanonicalizePass> {
111 using CIRCanonicalizeBase::CIRCanonicalizeBase;
112
113 // The same operation rewriting done here could have been performed
114 // by CanonicalizerPass (adding hasCanonicalizer for target Ops and
115 // implementing the same from above in CIRDialects.cpp). However, it's
116 // currently too aggressive for static analysis purposes, since it might
117 // remove things where a diagnostic can be generated.
118 //
119 // FIXME: perhaps we can add one more mode to GreedyRewriteConfig to
120 // disable this behavior.
121 void runOnOperation() override;
122};
123
124void populateCIRCanonicalizePatterns(RewritePatternSet &patterns) {
125 // clang-format off
126 patterns.add<
127 RemoveRedundantBranches,
128 RemoveEmptyScope
129 >(patterns.getContext());
130 // clang-format on
131}
132
133void CIRCanonicalizePass::runOnOperation() {
134 // Collect rewrite patterns.
135 RewritePatternSet patterns(&getContext());
136 populateCIRCanonicalizePatterns(patterns);
137
138 // Collect operations to apply patterns.
139 llvm::SmallVector<Operation *, 16> ops;
140 getOperation()->walk([&](Operation *op) {
144
145 // Many operations are here to perform a manual `fold` in
146 // applyOpPatternsGreedily.
147 if (isa<BrOp, BrCondOp, CastOp, ScopeOp, SwitchOp, SelectOp, UnaryOp,
148 ComplexCreateOp, ComplexImagOp, ComplexRealOp, VecCmpOp,
149 VecCreateOp, VecExtractOp, VecShuffleOp, VecShuffleDynamicOp,
150 VecTernaryOp, BitClrsbOp, BitClzOp, BitCtzOp, BitFfsOp, BitParityOp,
151 BitPopcountOp, BitReverseOp, ByteSwapOp, RotateOp>(op))
152 ops.push_back(op);
153 });
154
155 // Apply patterns.
156 if (applyOpPatternsGreedily(ops, std::move(patterns)).failed())
157 signalPassFailure();
158}
159
160} // namespace
161
162std::unique_ptr<Pass> mlir::createCIRCanonicalizePass() {
163 return std::make_unique<CIRCanonicalizePass>();
164}
std::unique_ptr< Pass > createCIRCanonicalizePass()