CIRCT 20.0.0git
Loading...
Searching...
No Matches
InsertMergeBlocks.cpp
Go to the documentation of this file.
1//===- InsertMergeBlocks.cpp - Insert Merge Blocks --------------*- C++ -*-===//
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
10#include "mlir/Analysis/CFGLoopInfo.h"
11#include "mlir/Conversion/LLVMCommon/ConversionTarget.h"
12#include "mlir/Conversion/LLVMCommon/Pattern.h"
13#include "mlir/Dialect/ControlFlow/IR/ControlFlow.h"
14#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
15#include "mlir/Dialect/Func/IR/FuncOps.h"
16#include "mlir/IR/Dominance.h"
17#include "mlir/Pass/Pass.h"
18#include "mlir/Transforms/DialectConversion.h"
19#include "llvm/ADT/TypeSwitch.h"
20
21namespace circt {
22#define GEN_PASS_DEF_INSERTMERGEBLOCKS
23#include "circt/Transforms/Passes.h.inc"
24} // namespace circt
25
26using namespace mlir;
27using namespace circt;
28
29/// Replaces the branching to oldDest of with an equivalent operation that
30/// instead branches to newDest.
31static LogicalResult changeBranchTarget(Block *block, Block *oldDest,
32 Block *newDest,
33 ConversionPatternRewriter &rewriter) {
34 rewriter.setInsertionPointToEnd(block);
35 auto term = block->getTerminator();
36 return llvm::TypeSwitch<Operation *, LogicalResult>(term)
37 .Case<cf::BranchOp>([&](auto branchOp) {
38 rewriter.replaceOpWithNewOp<cf::BranchOp>(branchOp, newDest,
39 branchOp->getOperands());
40 return success();
41 })
42 .Case<cf::CondBranchOp>([&](auto condBr) {
43 auto cond = condBr.getCondition();
44
45 Block *trueDest = condBr.getTrueDest();
46 Block *falseDest = condBr.getFalseDest();
47
48 // Change to the correct destination.
49 if (trueDest == oldDest)
50 trueDest = newDest;
51
52 if (falseDest == oldDest)
53 falseDest = newDest;
54
55 rewriter.replaceOpWithNewOp<cf::CondBranchOp>(
56 condBr, cond, trueDest, condBr.getTrueOperands(), falseDest,
57 condBr.getFalseOperands());
58 return success();
59 })
60 .Default([&](Operation *op) {
61 return op->emitError("Unexpected terminator that cannot be handled.");
62 });
63}
64
65/// Creates a new intermediate block that b1 and b2 branch to. The new block
66/// branches to their common successor oldSucc.
67static FailureOr<Block *> buildMergeBlock(Block *b1, Block *b2, Block *oldSucc,
68 ConversionPatternRewriter &rewriter) {
69 auto blockArgTypes = oldSucc->getArgumentTypes();
70 SmallVector<Location> argLocs(blockArgTypes.size(), rewriter.getUnknownLoc());
71
72 Block *res = rewriter.createBlock(oldSucc, blockArgTypes, argLocs);
73 rewriter.create<cf::BranchOp>(rewriter.getUnknownLoc(), oldSucc,
74 res->getArguments());
75
76 if (failed(changeBranchTarget(b1, oldSucc, res, rewriter)))
77 return failure();
78 if (failed(changeBranchTarget(b2, oldSucc, res, rewriter)))
79 return failure();
80
81 return res;
82}
83
84namespace {
85/// A dual CFG that contracts cycles into single logical blocks.
86struct DualGraph {
87 DualGraph(Region &r, CFGLoopInfo &loopInfo);
88
89 size_t getNumPredecessors(Block *b) { return predCnts.lookup(b); }
90 void getPredecessors(Block *b, SmallVectorImpl<Block *> &res);
91
92 size_t getNumSuccessors(Block *b) { return succMap.lookup(b).size(); }
93 ArrayRef<Block *> getSuccessors(Block *b) {
94 return succMap.find(b)->getSecond();
95 }
96
97 // If the block is part of a contracted block, the header of the contracted
98 // block is returned. Otherwise, the block itself is returned.
99 Block *lookupDualBlock(Block *b);
100 DenseMap<Block *, size_t> getPredCountMapCopy() { return predCnts; }
101
102private:
103 CFGLoopInfo &loopInfo;
104
105 DenseMap<Block *, SmallVector<Block *>> succMap;
106 DenseMap<Block *, size_t> predCnts;
107};
108} // namespace
109
110DualGraph::DualGraph(Region &r, CFGLoopInfo &loopInfo)
111 : loopInfo(loopInfo), succMap(), predCnts() {
112 for (Block &b : r) {
113 CFGLoop *loop = loopInfo.getLoopFor(&b);
114
115 if (loop && loop->getHeader() != &b)
116 continue;
117
118 // Create and get a new succ map entry for the current block.
119 SmallVector<Block *> &succs =
120 succMap.try_emplace(&b, SmallVector<Block *>()).first->getSecond();
121
122 // NOTE: This assumes that there is only one exiting node, i.e., not
123 // two blocks from the same loop can be predecessors of one block.
124 unsigned predCnt = 0;
125 for (auto *pred : b.getPredecessors())
126 if (!loop || !loop->contains(pred))
127 predCnt++;
128
129 if (loop && loop->getHeader() == &b)
130 loop->getExitBlocks(succs);
131 else
132 llvm::copy(b.getSuccessors(), std::back_inserter(succs));
133
134 predCnts.try_emplace(&b, predCnt);
135 }
136}
137
138Block *DualGraph::lookupDualBlock(Block *b) {
139 CFGLoop *loop = loopInfo.getLoopFor(b);
140 if (!loop)
141 return b;
142
143 return loop->getHeader();
144}
145
146void DualGraph::getPredecessors(Block *b, SmallVectorImpl<Block *> &res) {
147 CFGLoop *loop = loopInfo.getLoopFor(b);
148 assert((!loop || loop->getHeader() == b) &&
149 "can only get predecessors of blocks in the graph");
150
151 for (auto *pred : b->getPredecessors()) {
152 if (loop && loop->contains(pred))
153 continue;
154
155 if (CFGLoop *predLoop = loopInfo.getLoopFor(pred)) {
156 assert(predLoop->getExitBlock() &&
157 "multiple exit blocks are not yet supported");
158 res.push_back(predLoop->getHeader());
159 continue;
160 }
161 res.push_back(pred);
162 }
163}
164
165namespace {
166using BlockToBlockMap = DenseMap<Block *, Block *>;
167/// A helper class to store the split block information gathered during analysis
168/// of the CFG.
169struct SplitInfo {
170 /// Points to the last split block that dominates the block.
171 BlockToBlockMap in;
172 /// Either points to the last split block or to itself, if the block itself is
173 /// a split block.
174 BlockToBlockMap out;
175};
176} // namespace
177
178/// Builds a binary merge block tree for the predecessors of currBlock.
179static LogicalResult buildMergeBlocks(Block *currBlock, SplitInfo &splitInfo,
180 Block *predDom,
181 ConversionPatternRewriter &rewriter,
182 DualGraph &graph) {
183 SmallVector<Block *> preds;
184 llvm::copy(currBlock->getPredecessors(), std::back_inserter(preds));
185
186 // Map from split blocks to blocks that descend from it.
187 DenseMap<Block *, Block *> predsToConsider;
188
189 while (!preds.empty()) {
190 Block *pred = preds.pop_back_val();
191 Block *splitBlock = splitInfo.out.lookup(graph.lookupDualBlock(pred));
192 if (splitBlock == predDom)
193 // Needs no additional merge block, as this directly descends from the
194 // correct split block.
195 continue;
196
197 if (predsToConsider.count(splitBlock) == 0) {
198 // No other block with the same split block was found yet, so just store
199 // it and wait for a match.
200 predsToConsider.try_emplace(splitBlock, pred);
201 continue;
202 }
203
204 // Found a pair, so insert a new merge block for them.
205 Block *other = predsToConsider.lookup(splitBlock);
206 predsToConsider.erase(splitBlock);
207
208 FailureOr<Block *> mergeBlock =
209 buildMergeBlock(pred, other, currBlock, rewriter);
210 if (failed(mergeBlock))
211 return failure();
212
213 // Update info for the newly created block.
214 Block *splitIn = splitInfo.in.lookup(splitBlock);
215 splitInfo.in.try_emplace(*mergeBlock, splitIn);
216 // By construction, this block has only one successor, therefore, out == in.
217 splitInfo.out.try_emplace(*mergeBlock, splitIn);
218
219 preds.push_back(*mergeBlock);
220 }
221 if (!predsToConsider.empty())
222 return currBlock->getParentOp()->emitError(
223 "irregular control flow is not yet supported");
224 return success();
225}
226
227/// Checks preconditions of this transformation.
228static LogicalResult preconditionCheck(Region &r, CFGLoopInfo &loopInfo) {
229 for (auto &info : loopInfo.getTopLevelLoops())
230 // Does only return a block if it is the only exit block.
231 if (!info->getExitBlock())
232 return r.getParentOp()->emitError(
233 "multiple exit blocks are not yet supported");
234
235 return success();
236}
237
238/// Insert additional blocks that serve as counterparts to the blocks that
239/// diverged the control flow.
240/// The resulting merge block tree is guaranteed to be a binary tree.
241///
242/// This transformation does not affect any blocks that are part of a loop as it
243/// treats a loop as one logical block.
244/// Irregular control flow is not supported and results in a failed
245/// transformation.
246LogicalResult circt::insertMergeBlocks(Region &r,
247 ConversionPatternRewriter &rewriter) {
248 Block *entry = &r.front();
249 DominanceInfo domInfo(r.getParentOp());
250
251 CFGLoopInfo loopInfo(domInfo.getDomTree(&r));
252 if (failed(preconditionCheck(r, loopInfo)))
253 return failure();
254
255 // Traversing the graph in topological order can be simply done with a stack.
256 SmallVector<Block *> stack;
257 stack.push_back(entry);
258
259 // Holds the graph that contains the relevant blocks. It for example contracts
260 // loops into one block to preserve a DAG structure.
261 DualGraph graph(r, loopInfo);
262
263 // Counts the amount of predecessors remaining.
264 auto predsToVisit = graph.getPredCountMapCopy();
265
266 SplitInfo splitInfo;
267
268 while (!stack.empty()) {
269 Block *currBlock = stack.pop_back_val();
270
271 Block *in = nullptr;
272 Block *out = nullptr;
273
274 bool isMergeBlock = graph.getNumPredecessors(currBlock) > 1;
275 bool isSplitBlock = graph.getNumSuccessors(currBlock) > 1;
276
277 SmallVector<Block *> preds;
278 graph.getPredecessors(currBlock, preds);
279
280 if (isMergeBlock) {
281 Block *predDom = currBlock;
282 for (auto *pred : preds) {
283 predDom = domInfo.findNearestCommonDominator(predDom, pred);
284 }
285
286 if (failed(
287 buildMergeBlocks(currBlock, splitInfo, predDom, rewriter, graph)))
288 return failure();
289
290 // The sub-CFG created by the predDom (split block) and the current merge
291 // block can logically be treated like a single block, thus their "in"s
292 // are the same.
293 in = splitInfo.in.lookup(predDom);
294 } else if (!preds.empty()) {
295 Block *pred = preds.front();
296
297 in = splitInfo.out.lookup(pred);
298 }
299
300 if (isSplitBlock)
301 out = currBlock;
302 else
303 out = in;
304
305 splitInfo.in.try_emplace(currBlock, in);
306 splitInfo.out.try_emplace(currBlock, out);
307
308 for (auto *succ : graph.getSuccessors(currBlock)) {
309 auto it = predsToVisit.find(succ);
310 unsigned predsRemaining = --(it->getSecond());
311 // Pushing the block on the stack once all it's successors were visited
312 // ensures a topological traversal.
313 if (predsRemaining == 0)
314 stack.push_back(succ);
315 }
316 }
317
318 return success();
319}
320
321namespace {
322
323using PtrSet = SmallPtrSet<Operation *, 4>;
324
325struct FuncOpPattern : public OpConversionPattern<func::FuncOp> {
326
327 FuncOpPattern(PtrSet &rewrittenFuncs, MLIRContext *ctx)
328 : OpConversionPattern(ctx), rewrittenFuncs(rewrittenFuncs) {}
329
330 LogicalResult
331 matchAndRewrite(func::FuncOp op, OpAdaptor adaptor,
332 ConversionPatternRewriter &rewriter) const override {
333 rewriter.startOpModification(op);
334
335 if (!op.isExternal())
336 if (failed(insertMergeBlocks(op.getRegion(), rewriter))) {
337 rewriter.cancelOpModification(op);
338 return failure();
339 }
340
341 rewriter.finalizeOpModification(op);
342 rewrittenFuncs.insert(op);
343
344 return success();
345 }
346
347private:
348 PtrSet &rewrittenFuncs;
349};
350
351struct InsertMergeBlocksPass
352 : public circt::impl::InsertMergeBlocksBase<InsertMergeBlocksPass> {
353public:
354 void runOnOperation() override {
355 auto *ctx = &getContext();
356 RewritePatternSet patterns(ctx);
357 // Remembers traversed functions to only apply the conversion once.
358 PtrSet rewrittenFuncs;
359 patterns.add<FuncOpPattern>(rewrittenFuncs, ctx);
360
361 ConversionTarget target(*ctx);
362 target.addDynamicallyLegalOp<func::FuncOp>(
363 [&](func::FuncOp func) { return rewrittenFuncs.contains(func); });
364 target.addLegalDialect<cf::ControlFlowDialect>();
365
366 if (applyPartialConversion(getOperation(), target, std::move(patterns))
367 .failed())
368 signalPassFailure();
369 }
370};
371
372} // namespace
373
374namespace circt {
375std::unique_ptr<mlir::Pass> createInsertMergeBlocksPass() {
376 return std::make_unique<InsertMergeBlocksPass>();
377}
378} // namespace circt
assert(baseType &&"element must be base type")
static void mergeBlock(Block &destination, Block::iterator insertPoint, Block &source)
Move all operations from a source block in to a destination block.
static LogicalResult changeBranchTarget(Block *block, Block *oldDest, Block *newDest, ConversionPatternRewriter &rewriter)
Replaces the branching to oldDest of with an equivalent operation that instead branches to newDest.
static FailureOr< Block * > buildMergeBlock(Block *b1, Block *b2, Block *oldSucc, ConversionPatternRewriter &rewriter)
Creates a new intermediate block that b1 and b2 branch to.
static LogicalResult preconditionCheck(Region &r, CFGLoopInfo &loopInfo)
Checks preconditions of this transformation.
static LogicalResult buildMergeBlocks(Block *currBlock, SplitInfo &splitInfo, Block *predDom, ConversionPatternRewriter &rewriter, DualGraph &graph)
Builds a binary merge block tree for the predecessors of currBlock.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createInsertMergeBlocksPass()
mlir::LogicalResult insertMergeBlocks(mlir::Region &r, mlir::ConversionPatternRewriter &rewriter)
Insert additional blocks that serve as counterparts to the blocks that diverged the control flow.