CIRCT 24.0.0git
Loading...
Searching...
No Matches
UnrollLoops.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
14#include "mlir/Analysis/CFGLoopInfo.h"
15#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
16#include "mlir/IR/Dominance.h"
17#include "mlir/IR/IRMapping.h"
18#include "mlir/IR/Matchers.h"
19#include "mlir/Pass/Pass.h"
20#include "llvm/ADT/PostOrderIterator.h"
21#include "llvm/Support/Debug.h"
22
23#define DEBUG_TYPE "llhd-unroll-loops"
24
25namespace circt {
26namespace llhd {
27#define GEN_PASS_DEF_UNROLLLOOPSPASS
28#include "circt/Dialect/LLHD/LLHDPasses.h.inc"
29} // namespace llhd
30} // namespace circt
31
32using namespace mlir;
33using namespace circt;
34using namespace llhd;
35using llvm::SmallDenseSet;
37
38//===----------------------------------------------------------------------===//
39// Utilities
40//===----------------------------------------------------------------------===//
41
42/// Clone a list of blocks into a region before the given block.
43///
44/// See `Region::cloneInto` for the original code that clones an entire region.
45static void cloneBlocks(ArrayRef<Block *> blocks, Region &region,
46 Region::iterator before, IRMapping &mapper) {
47 // If the list is empty there is nothing to clone.
48 if (blocks.empty())
49 return;
50
51 // First clone all the blocks and block arguments and map them, but don't yet
52 // clone the operations, as they may otherwise add a use to a block that has
53 // not yet been mapped
54 SmallVector<Block *> newBlocks;
55 newBlocks.reserve(blocks.size());
56 for (auto *block : blocks) {
57 auto *newBlock = new Block();
58 mapper.map(block, newBlock);
59 for (auto arg : block->getArguments())
60 mapper.map(arg, newBlock->addArgument(arg.getType(), arg.getLoc()));
61 region.getBlocks().insert(before, newBlock);
62 newBlocks.push_back(newBlock);
63 }
64
65 // Now follow up with creating the operations, but don't yet clone their
66 // regions, nor set their operands. Setting the successors is safe as all have
67 // already been mapped. We are essentially just creating the operation results
68 // to be able to map them. Cloning the operands and region as well would lead
69 // to uses of operations not yet mapped.
70 auto cloneOptions =
71 Operation::CloneOptions::all().cloneRegions(false).cloneOperands(false);
72 for (auto [oldBlock, newBlock] : llvm::zip(blocks, newBlocks))
73 for (auto &op : *oldBlock)
74 newBlock->push_back(op.clone(mapper, cloneOptions));
75
76 // Finally now that all operation results have been mapped, set the operands
77 // and clone the regions.
78 SmallVector<Value> operands;
79 for (auto [oldBlock, newBlock] : llvm::zip(blocks, newBlocks)) {
80 for (auto [oldOp, newOp] : llvm::zip(*oldBlock, *newBlock)) {
81 operands.resize(oldOp.getNumOperands());
82 llvm::transform(
83 oldOp.getOperands(), operands.begin(),
84 [&](Value operand) { return mapper.lookupOrDefault(operand); });
85 newOp.setOperands(operands);
86 for (auto [oldRegion, newRegion] :
87 llvm::zip(oldOp.getRegions(), newOp.getRegions()))
88 oldRegion.cloneInto(&newRegion, mapper);
89 }
90 }
91}
92
93//===----------------------------------------------------------------------===//
94// Loop Unroller
95//===----------------------------------------------------------------------===//
96
97namespace {
98/// A data structure tracking information on a single loop.
99struct Loop {
100 Loop(unsigned loopId, CFGLoop &cfgLoop) : loopId(loopId), cfgLoop(cfgLoop) {}
101 bool failMatch(const Twine &msg) const;
102 bool match();
103 bool matchExit(BlockOperand *edge);
104 bool routeLiveOutsThroughArguments();
105 void unroll(CFGLoopInfo &cfgLoopInfo);
106
107 /// A numeric identifier for debugging purposes.
108 unsigned loopId;
109 /// Loop analysis information about this specific loop.
110 CFGLoop &cfgLoop;
111 /// The CFG edge exiting the loop.
112 BlockOperand *exitEdge = nullptr;
113 /// The SSA value holding the exit condition.
114 Value exitCondition;
115 /// Whether the exit condition is inverted, i.e. the contination condition.
116 bool exitInverted;
117 /// The induction variable.
118 Value indVar;
119 /// The updated induction variable passed into the next loop iteration.
120 Value indVarNext;
121 /// Whether the exit condition tests the updated induction variable (a
122 /// `do ... while` loop) instead of the header block argument.
123 bool comparesNext = false;
124 /// The continuation predicate. The loop continues until the induction
125 /// variable compared against the end bound no longer matches this predicate.
126 comb::ICmpPredicate predicate;
127 /// The induction variable increment.
128 APInt indVarIncrement;
129 /// The initial value for the induction variable.
130 APInt beginBound;
131 /// The final value for the induction variable.
132 APInt endBound;
133 /// The number of iterations of the loop.
134 unsigned tripCount = 0;
135};
136} // namespace
137
138static llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Loop &loop) {
139 os << "#" << loop.loopId << " from ";
140 loop.cfgLoop.getHeader()->printAsOperand(os);
141 os << " to ";
142 loop.cfgLoop.getLoopLatch()->printAsOperand(os);
143 return os;
144}
145
146/// Helper to print a debug message on match failure and return false.
147bool Loop::failMatch(const Twine &msg) const {
148 LLVM_DEBUG(llvm::dbgs() << "- Ignoring loop " << *this << ": " << msg
149 << "\n");
150 return false;
151}
152
153/// Evaluate a comparison predicate on two constants. Returns `std::nullopt` for
154/// the four-valued predicates, which never control a loop we unroll.
155static std::optional<bool> evaluatePredicate(comb::ICmpPredicate predicate,
156 const APInt &lhs,
157 const APInt &rhs) {
158 switch (predicate) {
159 case comb::ICmpPredicate::eq:
160 return lhs == rhs;
161 case comb::ICmpPredicate::ne:
162 return lhs != rhs;
163 case comb::ICmpPredicate::slt:
164 return lhs.slt(rhs);
165 case comb::ICmpPredicate::sle:
166 return lhs.sle(rhs);
167 case comb::ICmpPredicate::sgt:
168 return lhs.sgt(rhs);
169 case comb::ICmpPredicate::sge:
170 return lhs.sge(rhs);
171 case comb::ICmpPredicate::ult:
172 return lhs.ult(rhs);
173 case comb::ICmpPredicate::ule:
174 return lhs.ule(rhs);
175 case comb::ICmpPredicate::ugt:
176 return lhs.ugt(rhs);
177 case comb::ICmpPredicate::uge:
178 return lhs.uge(rhs);
179 default:
180 return std::nullopt;
181 }
182}
183
184/// The largest number of iterations the unroller copies a loop body for.
185static constexpr unsigned kMaxTripCount = 1024;
186
187/// Check that the loop matches the specific pattern we understand, and extract
188/// the loop condition and induction variable.
189///
190/// The loop may have more than one exit (a `break`, or a `return` from a
191/// function inlined into the process): exactly one of them must be the
192/// counting exit that compares the induction variable against a constant, and
193/// the others stay in every unrolled copy of the body as ordinary branches out
194/// of the loop. Such early exits make the exit blocks reachable from every
195/// copy, so the loop's values must leave it only through branch operands
196/// (see routeLiveOutsThroughArguments).
197bool Loop::match() {
198 SmallVector<BlockOperand *> exits;
199 for (auto *block : cfgLoop.getBlocks())
200 for (auto &edge : block->getTerminator()->getBlockOperands())
201 if (!cfgLoop.contains(edge.get()))
202 exits.push_back(&edge);
203 if (exits.empty())
204 return failMatch("no exit");
205
206 // A single exit may be anywhere in the loop. With several, the counting exit
207 // must be in the header, which every iteration runs: that is where a `for`
208 // loop tests its bound, and a `break` leaves through a later block. An exit
209 // test that an iteration can skip would make the trip count wrong.
210 if (exits.size() == 1)
211 return matchExit(exits.front());
212 auto *header = cfgLoop.getHeader();
213 bool found = false;
214 for (auto *edge : exits)
215 if (edge->getOwner()->getBlock() == header && matchExit(edge)) {
216 found = true;
217 break;
218 }
219 if (!found)
220 return failMatch("no counting exit in the header of a loop with several "
221 "exits");
222 if (!routeLiveOutsThroughArguments())
223 return failMatch("a value of the loop is used after it where it cannot be "
224 "passed as a block argument");
225 return true;
226}
227
228/// Make every use of a loop value outside the loop go through a block
229/// argument. Unrolling a loop with several exits clones the exiting blocks, so
230/// a block after the loop gets one predecessor per copy and a loop value it
231/// uses directly would no longer dominate it. A `break` block (outside the
232/// loop, entered from one loop block) commonly reads the induction variable;
233/// such a block, and any chain of single-predecessor blocks after it, gets the
234/// value as a new argument from its predecessor instead; so does a block whose
235/// predecessors are all in the loop (where a `break` from an inner loop joins
236/// its normal exit). A use in a block also entered from outside the loop
237/// cannot be rewritten this way and fails the match. The rewrite does not
238/// change what the region computes, so a failure part-way leaves valid,
239/// equivalent IR.
240bool Loop::routeLiveOutsThroughArguments() {
241 Region *region = cfgLoop.getHeader()->getParent();
242
243 // A header argument that the loop passes back unchanged and that enters with
244 // a single value is that value. Replacing it keeps a use after the loop (a
245 // variable the loop does not write, read where the exits merge) from
246 // counting as a loop value.
247 auto *header = cfgLoop.getHeader();
248 for (auto arg : header->getArguments()) {
249 Value incoming;
250 bool invariant = true;
251 for (auto &pred : header->getUses()) {
252 auto branch = dyn_cast<BranchOpInterface>(pred.getOwner());
253 if (!branch) {
254 invariant = false;
255 break;
256 }
257 Value value = branch.getSuccessorOperands(
258 pred.getOperandNumber())[arg.getArgNumber()];
259 if (value == arg)
260 continue;
261 if (!value || (incoming && incoming != value)) {
262 invariant = false;
263 break;
264 }
265 incoming = value;
266 }
267 if (invariant && incoming)
268 arg.replaceAllUsesWith(incoming);
269 }
270
271 SmallVector<Value> worklist;
272 for (auto *block : cfgLoop.getBlocks()) {
273 for (auto arg : block->getArguments())
274 worklist.push_back(arg);
275 block->walk([&](Operation *op) {
276 for (auto result : op->getResults())
277 worklist.push_back(result);
278 });
279 }
280 unsigned budget = 100000;
281 while (!worklist.empty()) {
282 Value value = worklist.pop_back_val();
283 for (auto &use : value.getUses()) {
284 auto *block =
285 region->findAncestorBlockInRegion(*use.getOwner()->getBlock());
286 if (!block)
287 return false;
288 if (cfgLoop.contains(block))
289 continue;
290 // The block must be entered by a single edge, or only from inside the
291 // loop: the value then dominates every predecessor (it dominated the
292 // block), so each edge can pass it.
293 if (block->use_empty() || --budget == 0)
294 return false;
295 SmallVector<std::pair<BranchOpInterface, unsigned>> edges;
296 for (auto &edge : block->getUses()) {
297 auto branch = dyn_cast<BranchOpInterface>(edge.getOwner());
298 auto *pred = edge.getOwner()->getBlock();
299 if (!branch || pred == block ||
300 (!block->hasOneUse() && !cfgLoop.contains(pred)))
301 return false;
302 edges.push_back({branch, edge.getOperandNumber()});
303 }
304 auto arg = block->addArgument(value.getType(), value.getLoc());
305 value.replaceUsesWithIf(arg, [&](OpOperand &other) {
306 return region->findAncestorBlockInRegion(
307 *other.getOwner()->getBlock()) == block;
308 });
309 for (auto [branch, index] : edges)
310 branch.getSuccessorOperands(index).append(value);
311 // The value is now used by the predecessor's branch; if that block is
312 // outside the loop too, the next round moves the use up once more.
313 worklist.push_back(value);
314 break;
315 }
316 }
317 return true;
318}
319
320/// Check whether `edge` is a counting exit: a conditional branch on a
321/// comparison of a header block argument against a constant, where the argument
322/// starts at a constant and advances by a constant step. Compute the trip count
323/// by stepping the induction variable from its initial value.
324bool Loop::matchExit(BlockOperand *edge) {
325 exitEdge = edge;
326 indVarNext = {};
327 comparesNext = false;
328
329 // The terminator doing the exit must be a conditional branch whose other
330 // successor stays in the loop.
331 auto exitBranch = dyn_cast<cf::CondBranchOp>(exitEdge->getOwner());
332 if (!exitBranch)
333 return failMatch("unsupported exit branch");
334 if (!cfgLoop.contains(
335 exitBranch->getSuccessor(1 - exitEdge->getOperandNumber())))
336 return failMatch("both successors of the exit branch leave the loop");
337 exitCondition = exitBranch.getCondition();
338 exitInverted = exitEdge->getOperandNumber() == 1;
339
340 // Determine one of the loop bounds and the induction variable based on the
341 // exit condition.
342 if (auto icmpOp = exitCondition.getDefiningOp<comb::ICmpOp>()) {
343 IntegerAttr boundAttr;
344 if (!matchPattern(icmpOp.getRhs(), m_Constant(&boundAttr)))
345 return failMatch("non-constant loop bound");
346 indVar = icmpOp.getLhs();
347 predicate = icmpOp.getPredicate();
348 endBound = boundAttr.getValue();
349 } else {
350 return failMatch("unsupported exit condition");
351 }
352
353 // If the exit condition is not inverted, the predicate is the exit predicate.
354 // Negate it such that we have a continuation predicate.
355 if (!exitInverted)
356 predicate = comb::ICmpOp::getNegatedPredicate(predicate);
357
358 // Determine the initial and next value of the induction variable.
359 auto *header = cfgLoop.getHeader();
360 auto *latch = cfgLoop.getLoopLatch();
361 auto indVarArg = dyn_cast<BlockArgument>(indVar);
362 if (!indVarArg) {
363 // A `do ... while` loop tests the stepped value, `i + step < bound`; the
364 // add is checked against the back-edge value below.
365 if (auto addOp = indVar.getDefiningOp<comb::AddOp>();
366 addOp && addOp.getNumOperands() == 2) {
367 indVarArg = dyn_cast<BlockArgument>(addOp.getOperand(0));
368 comparesNext = true;
369 }
370 }
371 if (!indVarArg || indVarArg.getOwner() != header)
372 return failMatch("induction variable is not a header block argument");
373 Value compared = indVar;
374 indVar = indVarArg;
375 IntegerAttr beginBoundAttr;
376 for (auto &pred : header->getUses()) {
377 auto branchOp = dyn_cast<BranchOpInterface>(pred.getOwner());
378 if (!branchOp)
379 return failMatch("header predecessor terminator is not a branch op");
380 auto indVarValue = branchOp.getSuccessorOperands(
381 pred.getOperandNumber())[indVarArg.getArgNumber()];
382 IntegerAttr boundAttr;
383 if (pred.getOwner()->getBlock() == latch) {
384 indVarNext = indVarValue;
385 } else if (matchPattern(indVarValue, m_Constant(&boundAttr))) {
386 if (!beginBoundAttr)
387 beginBoundAttr = boundAttr;
388 else if (boundAttr != beginBoundAttr)
389 return failMatch("multiple initial bounds");
390 } else {
391 return failMatch("unsupported induction variable value");
392 }
393 }
394 if (!beginBoundAttr)
395 return failMatch("no initial bound");
396 beginBound = beginBoundAttr.getValue();
397
398 // Pattern match the increment operation on the induction variable: an add
399 // of a constant (a down-counting loop adds -1), or a subtract of one.
400 if (!indVarNext)
401 return failMatch("no back-edge value for the induction variable");
402 IntegerAttr incAttr;
403 if (auto addOp = indVarNext.getDefiningOp<comb::AddOp>();
404 addOp && addOp.getNumOperands() == 2) {
405 if (addOp.getOperand(0) != indVarArg)
406 return failMatch("increment LHS not the induction variable");
407 if (!matchPattern(addOp.getOperand(1), m_Constant(&incAttr)))
408 return failMatch("increment RHS non-constant");
409 indVarIncrement = incAttr.getValue();
410 } else if (auto subOp = indVarNext.getDefiningOp<comb::SubOp>()) {
411 if (subOp.getLhs() != indVarArg)
412 return failMatch("decrement LHS not the induction variable");
413 if (!matchPattern(subOp.getRhs(), m_Constant(&incAttr)))
414 return failMatch("decrement RHS non-constant");
415 indVarIncrement = -incAttr.getValue();
416 } else {
417 return failMatch("unsupported increment");
418 }
419 if (indVarIncrement.isZero())
420 return failMatch("zero increment");
421 if (comparesNext && compared != indVarNext)
422 return failMatch("exit condition tests a value other than the next "
423 "induction variable");
424
425 // Determine the trip count by stepping the induction variable from its
426 // initial value until the continuation predicate fails (tested on the
427 // stepped value for a `do ... while`). This covers up- and down-counting
428 // loops with any constant start, step and comparison, also across the
429 // signed and unsigned wrap-around points. A loop that runs longer than
430 // kMaxTripCount iterations, or never ends, is left alone.
431 if (beginBound.getBitWidth() != endBound.getBitWidth() ||
432 beginBound.getBitWidth() != indVarIncrement.getBitWidth())
433 return failMatch("mismatched induction variable widths");
434 APInt value = beginBound;
435 unsigned trips = 0;
436 while (true) {
437 auto proceed = evaluatePredicate(
438 predicate, comparesNext ? value + indVarIncrement : value, endBound);
439 if (!proceed)
440 return failMatch("unsupported loop predicate");
441 if (!*proceed)
442 break;
443 if (++trips > kMaxTripCount)
444 return failMatch("unsupported loop bounds");
445 value += indVarIncrement;
446 }
447 tripCount = trips;
448 return true;
449}
450
451/// Unroll the loop by cloning its body blocks and replacing the induction
452/// variable with constant iteration indices.
453void Loop::unroll(CFGLoopInfo &cfgLoopInfo) {
454 LLVM_DEBUG(llvm::dbgs() << "- Unrolling loop " << *this << "\n");
455 UnusedOpPruner pruner;
456
457 // Sort the blocks in the body. This is not strictly necessary, but makes the
458 // pass a lot easier to reason about in tests.
459 auto *header = cfgLoop.getHeader();
460 SmallVector<Block *> orderedBody;
461 for (auto &block : *header->getParent())
462 if (cfgLoop.contains(&block))
463 orderedBody.push_back(&block);
464
465 // Copy the loop body for every iteration of the loop.
466 auto *latch = cfgLoop.getLoopLatch();
467 OpBuilder builder(indVar.getContext());
468 auto indValue = beginBound;
469 for (unsigned trip = 0; trip < tripCount; ++trip) {
470 // Clone the loop body.
471 IRMapping mapper;
472 cloneBlocks(orderedBody, *header->getParent(), header->getIterator(),
473 mapper);
474 auto *clonedHeader = mapper.lookup(header);
475 auto *clonedTail = mapper.lookup(latch);
476
477 // Replace the induction variable with the concrete value.
478 auto iterIndVar = mapper.lookup(indVar);
479 pruner.eraseLaterIfUnused(iterIndVar);
480 builder.setInsertionPointAfterValue(iterIndVar);
481 iterIndVar.replaceAllUsesWith(
482 hw::ConstantOp::create(builder, iterIndVar.getLoc(), indValue));
483
484 // Update all edges to the original loop header to point to the cloned loop
485 // header. Leave the original back-edge untouched.
486 for (auto &blockOperand : llvm::make_early_inc_range(header->getUses()))
487 if (blockOperand.getOwner()->getBlock() != latch)
488 blockOperand.set(clonedHeader);
489
490 // Update the back-edge in the cloned latch to point to the original loop
491 // header, i.e. the next iteration, instead of the cloned loop header.
492 for (auto &blockOperand : clonedTail->getTerminator()->getBlockOperands())
493 if (blockOperand.get() == clonedHeader)
494 blockOperand.set(header);
495
496 // Remove the exit edge in the cloned body, since we statically know that
497 // the loop will continue.
498 auto exitBranchOp =
499 cast<cf::CondBranchOp>(mapper.lookup(exitEdge->getOwner()));
500 Block *continueDest = exitBranchOp.getTrueDest();
501 ValueRange continueDestOperands = exitBranchOp.getTrueDestOperands();
502 if (exitEdge->getOperandNumber() == 0) {
503 continueDest = exitBranchOp.getFalseDest();
504 continueDestOperands = exitBranchOp.getFalseDestOperands();
505 }
506 builder.setInsertionPoint(exitBranchOp);
507 cf::BranchOp::create(builder, exitBranchOp.getLoc(), continueDest,
508 continueDestOperands);
509 pruner.eraseLaterIfUnused(exitBranchOp.getOperands());
510 exitBranchOp.erase();
511
512 // Add the new blocks to the loop body.
513 for (auto *block : orderedBody) {
514 auto *newBlock = mapper.lookup(block);
515 cfgLoop.addBasicBlockToLoop(newBlock, cfgLoopInfo);
516 }
517
518 // Increment the induction variable value.
519 indValue += indVarIncrement;
520 }
521
522 // Now that the loop body has been cloned once for each trip throughout the
523 // loop, we can clean up the final iteration by always breaking out of the
524 // loop. Start by replacing the induction variable with the final value.
525 pruner.eraseLaterIfUnused(indVar);
526 builder.setInsertionPointAfterValue(indVar);
527 indVar.replaceAllUsesWith(
528 hw::ConstantOp::create(builder, indVar.getLoc(), indValue));
529 indVar = {};
530
531 // Remove the continue edge of the exit branch in the loop body, since we
532 // statically know that the loop will exit.
533 auto exitBranchOp = cast<cf::CondBranchOp>(exitEdge->getOwner());
534 Block *exitDest = exitBranchOp.getTrueDest();
535 ValueRange exitDestOperands = exitBranchOp.getTrueDestOperands();
536 if (exitEdge->getOperandNumber() == 1) {
537 exitDest = exitBranchOp.getFalseDest();
538 exitDestOperands = exitBranchOp.getFalseDestOperands();
539 }
540 builder.setInsertionPoint(exitBranchOp);
541 cf::BranchOp::create(builder, exitBranchOp.getLoc(), exitDest,
542 exitDestOperands);
543 pruner.eraseLaterIfUnused(exitBranchOp.getOperands());
544 exitBranchOp.erase();
545 exitEdge = nullptr;
546
547 // Prune any body blocks that have become unreachable.
548 auto &region = *header->getParent();
549 SmallPtrSet<Block *, 8> reachable;
550 SmallVector<Block *> worklist;
551 reachable.insert(&region.front());
552 worklist.push_back(&region.front());
553 while (!worklist.empty())
554 for (auto *succ : worklist.pop_back_val()->getSuccessors())
555 if (reachable.insert(succ).second)
556 worklist.push_back(succ);
557
558 SmallVector<Block *> deadBlocks;
559 for (auto &block : region) {
560 if (reachable.contains(&block))
561 continue;
562 block.dropAllDefinedValueUses();
563 deadBlocks.push_back(&block);
564 }
565
566 for (auto *block : deadBlocks) {
567 cfgLoopInfo.removeBlock(block);
568 block->erase();
569 }
570
571 // Remove any unused operations and block arguments.
572 pruner.eraseNow();
573
574 // Collapse trivial branches to avoid carrying a lot of useless blocks around
575 // especially when unrolling nested loops.
576 for (auto &block : *header->getParent()) {
577 if (!cfgLoop.contains(&block))
578 continue;
579 while (true) {
580 auto branchOp = dyn_cast<cf::BranchOp>(block.getTerminator());
581 if (!branchOp)
582 break;
583 auto *otherBlock = branchOp.getDest();
584 if (!cfgLoop.contains(otherBlock) || !otherBlock->getSinglePredecessor())
585 break;
586 for (auto [blockArg, branchArg] :
587 llvm::zip(otherBlock->getArguments(), branchOp.getDestOperands()))
588 blockArg.replaceAllUsesWith(branchArg);
589 block.getOperations().splice(branchOp->getIterator(),
590 otherBlock->getOperations());
591 branchOp.erase();
592 cfgLoopInfo.removeBlock(otherBlock);
593 otherBlock->erase();
594 }
595 }
596}
597
598//===----------------------------------------------------------------------===//
599// Pass Infrastructure
600//===----------------------------------------------------------------------===//
601
602namespace {
603struct UnrollLoopsPass
604 : public llhd::impl::UnrollLoopsPassBase<UnrollLoopsPass> {
605 void runOnOperation() override;
606 void runOnOperation(CombinationalOp op);
607 bool unrollLoops(CombinationalOp op);
608};
609} // namespace
610
611void UnrollLoopsPass::runOnOperation() {
612 for (auto op : getOperation().getOps<CombinationalOp>())
613 runOnOperation(op);
614}
615
616void UnrollLoopsPass::runOnOperation(CombinationalOp op) {
617 // Unrolling a loop may open up opportunities to unroll its nested loops.
618 // Iterate until all possible loops are unrolled or the limit is reached.
619 for (unsigned i = 0; i < 16; ++i)
620 if (!unrollLoops(op))
621 break;
622}
623
624bool UnrollLoopsPass::unrollLoops(CombinationalOp op) {
625 // There's nothing to do if we only have a single block. MLIR even refuses to
626 // compute a dominator tree in that case.
627 if (op.getBody().hasOneBlock())
628 return false;
629
630 // Find the loops.
631 LLVM_DEBUG(llvm::dbgs() << "Unrolling loops in " << op.getLoc() << "\n");
632 DominanceInfo domInfo(op);
633 CFGLoopInfo cfgLoopInfo(domInfo.getDomTree(&op.getBody()));
634
635 // We only support simple loops where there is a single back-edge to the
636 // header, and the latch block has a back-edge to a single header. Create a
637 // data structure for each loop we can potentially unroll. The loops are in
638 // preorder, with outer loops appearing before their child loops.
639 SmallVector<Loop> loops;
640 bool retry = false;
641 for (auto *cfgLoop : cfgLoopInfo.getLoopsInPreorder()) {
642 // To simplify unrolling we need a unique latch block branching back to the
643 // header.
644 auto *header = cfgLoop->getHeader();
645 auto *latch = cfgLoop->getLoopLatch();
646 if (!latch)
647 continue;
648
649 LLVM_DEBUG({
650 llvm::dbgs() << "- ";
651 cfgLoop->print(llvm::dbgs(), false, false);
652 llvm::dbgs() << "\n";
653 });
654 Loop loop(loops.size(), *cfgLoop);
655
656 // Ensure that the header block is only a header for the current loop. This
657 // simplifies unrolling.
658 auto *parent = cfgLoop->getParentLoop();
659 while (parent && parent->getHeader() != header)
660 parent = parent->getParentLoop();
661 if (parent) {
662 loop.failMatch("header block shared across multiple loops");
663 continue;
664 }
665
666 // Ensure that the latch block is only a latch for the current loop. This
667 // simplifies unrolling.
668 parent = cfgLoop->getParentLoop();
669 while (parent && !parent->isLoopLatch(latch))
670 parent = parent->getParentLoop();
671 if (parent) {
672 loop.failMatch("latch block shared across multiple loops");
673 continue;
674 }
675
676 // Check if the loop body matches the pattern we can unroll.
677 if (loop.match()) {
678 loops.push_back(std::move(loop));
679 continue;
680 }
681
682 // If an enclosing loop is unrolled, we retry the unrolling.
683 retry |= llvm::any_of(loops, [&](const Loop &other) {
684 return other.cfgLoop.contains(cfgLoop);
685 });
686 }
687
688 if (loops.empty())
689 return false;
690
691 // Dump some debugging information about the loops we've found.
692 LLVM_DEBUG({
693 auto &os = llvm::dbgs();
694 for (auto &loop : loops) {
695 os << "- Loop " << loop << ":\n";
696 os << " - ";
697 loop.cfgLoop.print(os, false, false);
698 os << "\n";
699 os << " - Exit: ";
700 loop.exitEdge->get()->printAsOperand(os);
701 os << " if ";
702 if (loop.exitInverted)
703 os << "not ";
704 os << loop.exitCondition;
705 os << "\n";
706 os << " - Induction variable: ";
707 loop.indVar.printAsOperand(os, OpPrintingFlags().useLocalScope());
708 os << ", from " << loop.beginBound << ", while " << loop.predicate << " "
709 << loop.endBound << ", increment " << loop.indVarIncrement << "\n";
710 os << " - Trip count: " << loop.tripCount << "\n";
711 }
712 });
713
714 // Unroll the loops. Handling the loops in reverse unrolls inner loops before
715 // their parent loops.
716 for (auto &loop : llvm::reverse(loops))
717 loop.unroll(cfgLoopInfo);
718
719 return retry;
720}
static constexpr unsigned kMaxTripCount
The largest number of iterations the unroller copies a loop body for.
static void cloneBlocks(ArrayRef< Block * > blocks, Region &region, Region::iterator before, IRMapping &mapper)
Clone a list of blocks into a region before the given block.
static std::optional< bool > evaluatePredicate(comb::ICmpPredicate predicate, const APInt &lhs, const APInt &rhs)
Evaluate a comparison predicate on two constants.
create(data_type, value)
Definition hw.py:433
OS & operator<<(OS &os, const InnerSymTarget &target)
Printing InnerSymTarget's.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
void eraseLaterIfUnused(Operation *op)
Mark an op the be erased later if it is unused at that point.
void eraseNow(Operation *op)
Erase an operation immediately, and remove it from the set of ops to be removed later.