MLIR 24.0.0git
RegionUtils.cpp
Go to the documentation of this file.
1//===- RegionUtils.cpp - Region-related transformation utilities ----------===//
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
13#include "mlir/IR/Block.h"
14#include "mlir/IR/Dominance.h"
15#include "mlir/IR/IRMapping.h"
16#include "mlir/IR/Operation.h"
18#include "mlir/IR/Value.h"
22#include "llvm/ADT/DenseSet.h"
23#include "llvm/ADT/DepthFirstIterator.h"
24#include "llvm/ADT/PostOrderIterator.h"
25#include "llvm/ADT/STLExtras.h"
26#include "llvm/ADT/SmallVectorExtras.h"
27#include "llvm/Support/DebugLog.h"
28
29#include <deque>
30#include <iterator>
31
32using namespace mlir;
33
34#define DEBUG_TYPE "region-utils"
35
37 Region &region) {
38 for (auto &use : llvm::make_early_inc_range(orig.getUses())) {
39 if (region.isAncestor(use.getOwner()->getParentRegion()))
40 use.set(replacement);
41 }
42}
43
45 Region &region, Region &limit, function_ref<void(OpOperand *)> callback) {
46 assert(limit.isAncestor(&region) &&
47 "expected isolation limit to be an ancestor of the given region");
48
49 // Collect proper ancestors of `limit` upfront to avoid traversing the region
50 // tree for every value.
51 SmallPtrSet<Region *, 4> properAncestors;
52 for (auto *reg = limit.getParentRegion(); reg != nullptr;
53 reg = reg->getParentRegion()) {
54 properAncestors.insert(reg);
55 }
56
57 region.walk([callback, &properAncestors](Operation *op) {
58 for (OpOperand &operand : op->getOpOperands())
59 // Callback on values defined in a proper ancestor of region.
60 if (properAncestors.count(operand.get().getParentRegion()))
61 callback(&operand);
62 });
63}
64
66 MutableArrayRef<Region> regions, function_ref<void(OpOperand *)> callback) {
67 for (Region &region : regions)
68 visitUsedValuesDefinedAbove(region, region, callback);
69}
70
72 SetVector<Value> &values) {
73 visitUsedValuesDefinedAbove(region, limit, [&](OpOperand *operand) {
74 values.insert(operand->get());
75 });
76}
77
79 SetVector<Value> &values) {
80 for (Region &region : regions)
81 getUsedValuesDefinedAbove(region, region, values);
82}
83
84//===----------------------------------------------------------------------===//
85// Make block isolated from above.
86//===----------------------------------------------------------------------===//
87
89 RewriterBase &rewriter, Region &region,
90 llvm::function_ref<bool(Operation *)> cloneOperationIntoRegion) {
91
92 // Get initial list of values used within region but defined above.
93 llvm::SetVector<Value> initialCapturedValues;
94 mlir::getUsedValuesDefinedAbove(region, initialCapturedValues);
95
96 std::deque<Value> worklist(initialCapturedValues.begin(),
97 initialCapturedValues.end());
100
101 llvm::SetVector<Value> finalCapturedValues;
102 SmallVector<Operation *> clonedOperations;
103 while (!worklist.empty()) {
104 Value currValue = worklist.front();
105 worklist.pop_front();
106 if (visited.count(currValue))
107 continue;
108 visited.insert(currValue);
109
110 Operation *definingOp = currValue.getDefiningOp();
111 if (!definingOp || visitedOps.count(definingOp)) {
112 finalCapturedValues.insert(currValue);
113 continue;
114 }
115 visitedOps.insert(definingOp);
116
117 if (!cloneOperationIntoRegion(definingOp)) {
118 // Defining operation isnt cloned, so add the current value to final
119 // captured values list.
120 finalCapturedValues.insert(currValue);
121 continue;
122 }
123
124 // Add all operands of the operation to the worklist and mark the op as to
125 // be cloned.
126 for (Value operand : definingOp->getOperands()) {
127 if (visited.count(operand))
128 continue;
129 worklist.push_back(operand);
130 }
131 clonedOperations.push_back(definingOp);
132 }
133
134 // The operations to be cloned need to be ordered in topological order
135 // so that they can be cloned into the region without violating use-def
136 // chains.
137 mlir::computeTopologicalSorting(clonedOperations);
138
139 OpBuilder::InsertionGuard g(rewriter);
140 // Collect types of existing block
141 Block *entryBlock = &region.front();
142 SmallVector<Type> newArgTypes =
143 llvm::to_vector(entryBlock->getArgumentTypes());
144 SmallVector<Location> newArgLocs = llvm::map_to_vector(
145 entryBlock->getArguments(), [](BlockArgument b) { return b.getLoc(); });
146
147 // Append the types of the captured values.
148 for (auto value : finalCapturedValues) {
149 newArgTypes.push_back(value.getType());
150 newArgLocs.push_back(value.getLoc());
151 }
152
153 // Create a new entry block.
154 Block *newEntryBlock =
155 rewriter.createBlock(&region, region.begin(), newArgTypes, newArgLocs);
156 auto newEntryBlockArgs = newEntryBlock->getArguments();
157
158 // Create a mapping between the captured values and the new arguments added.
159 IRMapping map;
160 auto replaceIfFn = [&](OpOperand &use) {
161 return region.isAncestor(use.getOwner()->getParentRegion());
162 };
163
164 for (auto [arg, capturedVal] :
165 llvm::zip(newEntryBlockArgs.take_back(finalCapturedValues.size()),
166 finalCapturedValues)) {
167 map.map(capturedVal, arg);
168 rewriter.replaceUsesWithIf(capturedVal, arg, replaceIfFn);
169 }
170 rewriter.setInsertionPointToStart(newEntryBlock);
171 for (auto *clonedOp : clonedOperations) {
172 Operation *newOp = rewriter.clone(*clonedOp, map);
173 rewriter.replaceOpUsesWithIf(clonedOp, newOp->getResults(), replaceIfFn);
174 }
175 rewriter.mergeBlocks(
176 entryBlock, newEntryBlock,
177 newEntryBlock->getArguments().take_front(entryBlock->getNumArguments()));
178 return llvm::to_vector(finalCapturedValues);
179}
180
181//===----------------------------------------------------------------------===//
182// Unreachable Block Elimination
183//===----------------------------------------------------------------------===//
184
185/// Erase the unreachable blocks within the provided regions. Returns success
186/// if any blocks were erased, failure otherwise.
187// TODO: We could likely merge this with the DCE algorithm below.
190 bool recurse) {
191 LDBG() << "Starting eraseUnreachableBlocks with " << regions.size()
192 << " regions";
193
194 // Set of blocks found to be reachable within a given region.
195 llvm::df_iterator_default_set<Block *, 16> reachable;
196 // If any blocks were found to be dead.
197 int erasedDeadBlocks = 0;
198
200 worklist.reserve(regions.size());
201 for (Region &region : regions)
202 worklist.push_back(&region);
203
204 LDBG(2) << "Initial worklist size: " << worklist.size();
205
206 while (!worklist.empty()) {
207 Region *region = worklist.pop_back_val();
208 if (region->empty()) {
209 LDBG(2) << "Skipping empty region";
210 continue;
211 }
212
213 LDBG(2) << "Processing region with " << region->getBlocks().size()
214 << " blocks";
215 if (region->getParentOp())
216 LDBG(2) << " -> for operation: "
217 << OpWithFlags(region->getParentOp(),
218 OpPrintingFlags().skipRegions());
219
220 // If this is a single block region, just collect the nested regions.
221 if (region->hasOneBlock()) {
222 if (recurse)
223 for (Operation &op : region->front())
224 for (Region &region : op.getRegions())
225 worklist.push_back(&region);
226 continue;
227 }
228
229 // Mark all reachable blocks.
230 reachable.clear();
231 for (Block *block : depth_first_ext(&region->front(), reachable))
232 (void)block /* Mark all reachable blocks */;
233
234 LDBG(2) << "Found " << reachable.size() << " reachable blocks out of "
235 << region->getBlocks().size() << " total blocks";
236
237 // Collect all of the dead blocks and push the live regions onto the
238 // worklist.
239 for (Block &block : llvm::make_early_inc_range(*region)) {
240 if (!reachable.count(&block)) {
241 LDBG() << "Erasing unreachable block: " << &block;
242 block.dropAllDefinedValueUses();
243 rewriter.eraseBlock(&block);
244 ++erasedDeadBlocks;
245 continue;
246 }
247
248 // Walk any regions within this block.
249 if (recurse)
250 for (Operation &op : block)
251 for (Region &region : op.getRegions())
252 worklist.push_back(&region);
253 }
254 }
255
256 LDBG() << "Finished eraseUnreachableBlocks, erased " << erasedDeadBlocks
257 << " dead blocks";
258
259 return success(erasedDeadBlocks > 0);
260}
261
262//===----------------------------------------------------------------------===//
263// Dead Code Elimination
264//===----------------------------------------------------------------------===//
265
266namespace {
267/// Data structure used to track which values have already been proved live.
268///
269/// Because Operation's can have multiple results, this data structure tracks
270/// liveness for both Value's and Operation's to avoid having to look through
271/// all Operation results when analyzing a use.
272///
273/// This data structure essentially tracks the dataflow lattice.
274/// The set of values/ops proved live increases monotonically to a fixed-point.
275class LiveMap {
276public:
277 /// Value methods.
278 bool wasProvenLive(Value value) {
279 // TODO: For results that are removable, e.g. for region based control flow,
280 // we could allow for these values to be tracked independently.
281 if (OpResult result = dyn_cast<OpResult>(value))
282 return wasProvenLive(result.getOwner());
283 return wasProvenLive(cast<BlockArgument>(value));
284 }
285 bool wasProvenLive(BlockArgument arg) { return liveValues.count(arg); }
286 void setProvedLive(Value value) {
287 // TODO: For results that are removable, e.g. for region based control flow,
288 // we could allow for these values to be tracked independently.
289 if (OpResult result = dyn_cast<OpResult>(value))
290 return setProvedLive(result.getOwner());
291 setProvedLive(cast<BlockArgument>(value));
292 }
293 void setProvedLive(BlockArgument arg) {
294 changed |= liveValues.insert(arg).second;
295 }
296
297 /// Operation methods.
298 bool wasProvenLive(Operation *op) { return liveOps.count(op); }
299 void setProvedLive(Operation *op) { changed |= liveOps.insert(op).second; }
300
301 /// Methods for tracking if we have reached a fixed-point.
302 void resetChanged() { changed = false; }
303 bool hasChanged() { return changed; }
304
305private:
306 bool changed = false;
307 DenseSet<Value> liveValues;
308 DenseSet<Operation *> liveOps;
309};
310} // namespace
311
312static bool isUseSpeciallyKnownDead(OpOperand &use, LiveMap &liveMap) {
313 Operation *owner = use.getOwner();
314 unsigned operandIndex = use.getOperandNumber();
315 // This pass generally treats all uses of an op as live if the op itself is
316 // considered live. However, for successor operands to terminators we need a
317 // finer-grained notion where we deduce liveness for operands individually.
318 // The reason for this is easiest to think about in terms of a classical phi
319 // node based SSA IR, where each successor operand is really an operand to a
320 // *separate* phi node, rather than all operands to the branch itself as with
321 // the block argument representation that MLIR uses.
322 //
323 // And similarly, because each successor operand is really an operand to a phi
324 // node, rather than to the terminator op itself, a terminator op can't e.g.
325 // "print" the value of a successor operand.
326 if (owner->hasTrait<OpTrait::IsTerminator>()) {
327 if (BranchOpInterface branchInterface = dyn_cast<BranchOpInterface>(owner))
328 if (auto arg = branchInterface.getSuccessorBlockArgument(operandIndex))
329 return !liveMap.wasProvenLive(*arg);
330 return false;
331 }
332 return false;
333}
334
335static void processValue(Value value, LiveMap &liveMap) {
336 bool provedLive = llvm::any_of(value.getUses(), [&](OpOperand &use) {
337 if (isUseSpeciallyKnownDead(use, liveMap))
338 return false;
339 return liveMap.wasProvenLive(use.getOwner());
340 });
341 if (provedLive)
342 liveMap.setProvedLive(value);
343}
344
345static void propagateLiveness(Region &region, LiveMap &liveMap);
346
347static void propagateTerminatorLiveness(Operation *op, LiveMap &liveMap) {
348 // Terminators are always live.
349 liveMap.setProvedLive(op);
350
351 // Check to see if we can reason about the successor operands and mutate them.
352 BranchOpInterface branchInterface = dyn_cast<BranchOpInterface>(op);
353 if (!branchInterface) {
354 for (Block *successor : op->getSuccessors())
355 for (BlockArgument arg : successor->getArguments())
356 liveMap.setProvedLive(arg);
357 return;
358 }
359
360 // If we can't reason about the operand to a successor, conservatively mark
361 // it as live.
362 for (unsigned i = 0, e = op->getNumSuccessors(); i != e; ++i) {
363 SuccessorOperands successorOperands =
364 branchInterface.getSuccessorOperands(i);
365 for (unsigned opI = 0, opE = successorOperands.getProducedOperandCount();
366 opI != opE; ++opI)
367 liveMap.setProvedLive(op->getSuccessor(i)->getArgument(opI));
368 }
369}
370
371static void propagateLiveness(Operation *op, LiveMap &liveMap) {
372 // Recurse on any regions the op has.
373 for (Region &region : op->getRegions())
374 propagateLiveness(region, liveMap);
375
376 // Process terminator operations.
378 return propagateTerminatorLiveness(op, liveMap);
379
380 // Don't reprocess live operations.
381 if (liveMap.wasProvenLive(op))
382 return;
383
384 // Process the op itself.
385 if (!wouldOpBeTriviallyDead(op))
386 return liveMap.setProvedLive(op);
387
388 // If the op isn't intrinsically alive, check it's results.
389 for (Value value : op->getResults())
390 processValue(value, liveMap);
391}
392
393static void propagateLiveness(Region &region, LiveMap &liveMap) {
394 if (region.empty())
395 return;
396
397 for (Block *block : llvm::post_order(&region.front())) {
398 // We process block arguments after the ops in the block, to promote
399 // faster convergence to a fixed point (we try to visit uses before defs).
400 for (Operation &op : llvm::reverse(block->getOperations()))
401 propagateLiveness(&op, liveMap);
402
403 // We currently do not remove entry block arguments, so there is no need to
404 // track their liveness.
405 // TODO: We could track these and enable removing dead operands/arguments
406 // from region control flow operations.
407 if (block->isEntryBlock())
408 continue;
409
410 for (Value value : block->getArguments()) {
411 if (!liveMap.wasProvenLive(value))
412 processValue(value, liveMap);
413 }
414 }
415}
416
418 LiveMap &liveMap) {
419 BranchOpInterface branchOp = dyn_cast<BranchOpInterface>(terminator);
420 if (!branchOp)
421 return;
422
423 for (unsigned succI = 0, succE = terminator->getNumSuccessors();
424 succI < succE; succI++) {
425 // Iterating successors in reverse is not strictly needed, since we
426 // aren't erasing any successors. But it is slightly more efficient
427 // since it will promote later operands of the terminator being erased
428 // first, reducing the quadratic-ness.
429 unsigned succ = succE - succI - 1;
430 SuccessorOperands succOperands = branchOp.getSuccessorOperands(succ);
431 Block *successor = terminator->getSuccessor(succ);
432
433 for (unsigned argI = 0, argE = succOperands.size(); argI < argE; ++argI) {
434 // Iterating args in reverse is needed for correctness, to avoid
435 // shifting later args when earlier args are erased.
436 unsigned arg = argE - argI - 1;
437 if (!liveMap.wasProvenLive(successor->getArgument(arg)))
438 succOperands.erase(arg);
439 }
440 }
441}
442
443static LogicalResult deleteDeadness(RewriterBase &rewriter,
445 LiveMap &liveMap) {
446 bool erasedAnything = false;
447 for (Region &region : regions) {
448 if (region.empty())
449 continue;
450 bool hasSingleBlock = region.hasOneBlock();
451
452 // Delete every operation that is not live. Graph regions may have cycles
453 // in the use-def graph, so we must explicitly dropAllUses() from each
454 // operation as we erase it. Visiting the operations in post-order
455 // guarantees that in SSA CFG regions value uses are removed before defs,
456 // which makes dropAllUses() a no-op.
457 for (Block *block : llvm::post_order(&region.front())) {
458 if (!hasSingleBlock)
459 eraseTerminatorSuccessorOperands(block->getTerminator(), liveMap);
460 for (Operation &childOp :
461 llvm::make_early_inc_range(llvm::reverse(block->getOperations()))) {
462 if (!liveMap.wasProvenLive(&childOp)) {
463 erasedAnything = true;
464 childOp.dropAllUses();
465 rewriter.eraseOp(&childOp);
466 } else {
467 erasedAnything |= succeeded(
468 deleteDeadness(rewriter, childOp.getRegions(), liveMap));
469 }
470 }
471 }
472 // Delete block arguments.
473 // The entry block has an unknown contract with their enclosing block, so
474 // skip it.
475 for (Block &block : llvm::drop_begin(region.getBlocks(), 1)) {
476 block.eraseArguments(
477 [&](BlockArgument arg) { return !liveMap.wasProvenLive(arg); });
478 }
479 }
480 return success(erasedAnything);
481}
482
483// This function performs a simple dead code elimination algorithm over the
484// given regions.
485//
486// The overall goal is to prove that Values are dead, which allows deleting ops
487// and block arguments.
488//
489// This uses an optimistic algorithm that assumes everything is dead until
490// proved otherwise, allowing it to delete recursively dead cycles.
491//
492// This is a simple fixed-point dataflow analysis algorithm on a lattice
493// {Dead,Alive}. Because liveness flows backward, we generally try to
494// iterate everything backward to speed up convergence to the fixed-point. This
495// allows for being able to delete recursively dead cycles of the use-def graph,
496// including block arguments.
497//
498// This function returns success if any operations or arguments were deleted,
499// failure otherwise.
500LogicalResult mlir::runRegionDCE(RewriterBase &rewriter,
501 MutableArrayRef<Region> regions) {
502 LiveMap liveMap;
503 do {
504 liveMap.resetChanged();
505
506 for (Region &region : regions)
507 propagateLiveness(region, liveMap);
508 } while (liveMap.hasChanged());
509
510 return deleteDeadness(rewriter, regions, liveMap);
511}
512
514 bool includeNestedRegions) {
515 LDBG() << "Starting eliminateTriviallyDeadOps with "
516 << region.getBlocks().size()
517 << " blocks, includeNestedRegions=" << includeNestedRegions;
518 if (Operation *parentOp = region.getParentOp())
519 LDBG(2) << " -> parent operation: "
520 << OpWithFlags(parentOp, OpPrintingFlags().skipRegions());
521
522 bool changed = false;
523 unsigned erasedOps = 0;
524 unsigned seededOps = 0;
525 unsigned enqueuedDefs = 0;
526
527 // Step 1: walk each op in reverse program order. If the op is already
528 // trivially dead, erase it outright — there's no point recursing into
529 // regions that will be destroyed with it. Otherwise, if
530 // `includeNestedRegions` is set, recurse into its nested regions so values
531 // defined in `region` may lose their last user and show up as dead in
532 // step 2's seed. Reverse iteration lets dead chains propagate within this
533 // single pass.
534 for (Block &block : llvm::reverse(region)) {
535 LDBG(2) << "Scanning block " << &block << " with "
536 << block.getOperations().size() << " operations";
537 for (Operation &op :
538 llvm::make_early_inc_range(llvm::reverse(block.getOperations()))) {
539 LDBG(3) << "Visiting operation: "
540 << OpWithFlags(&op, OpPrintingFlags().skipRegions());
541 if (isOpTriviallyDead(&op)) {
542 LDBG() << "Erasing trivially dead operation: "
543 << OpWithFlags(&op, OpPrintingFlags().skipRegions());
544 rewriter.eraseOp(&op);
545 changed = true;
546 ++erasedOps;
547 continue;
548 }
549 if (includeNestedRegions) {
550 unsigned regionIdx = 0;
551 for (Region &nested : op.getRegions()) {
552 LDBG(2) << "Recursing into nested region #" << regionIdx
553 << " of operation " << op.getName();
554 bool nestedChanged =
555 eliminateTriviallyDeadOps(rewriter, nested, includeNestedRegions);
556 LDBG(2) << "Finished nested region #" << regionIdx << " of operation "
557 << op.getName() << ", changed=" << nestedChanged;
558 changed |= nestedChanged;
559 ++regionIdx;
560 }
561 }
562 }
563 }
564
565 // Step 2: worklist over ops in this region only.
566 //
567 // Worklist invariant: an op is pushed only once we have verified it is
568 // trivially dead. No speculative enqueues: every op on the worklist will
569 // be erased when popped. Two things enforce this:
570 // - the initial seed below calls isOpTriviallyDead before enqueueing,
571 // - the propagation inside the loop drops the erasing op's use of
572 // `defOp` *before* re-checking isOpTriviallyDead(defOp), so the check
573 // sees the post-erase use count and only enqueues when actually dead.
574 // Deadness is monotonic within this pass (we never add users, only remove
575 // them), so an op that was dead at enqueue time is still dead at pop time.
577
578 LDBG(2) << "Stage 2: Seeding trivially dead operation worklist";
579 for (Operation &op : region.getOps()) {
580 if (isOpTriviallyDead(&op)) {
581 LDBG(2) << "Seeded worklist with operation: "
582 << OpWithFlags(&op, OpPrintingFlags().skipRegions());
583 worklist.push_back(&op);
584 changed = true;
585 ++seededOps;
586 }
587 }
588 LDBG(2) << "Initial worklist size: " << worklist.size();
589
590 while (!worklist.empty()) {
591 Operation *op = worklist.pop_back_val();
592 LDBG(2) << "Popped operation from worklist: "
593 << OpWithFlags(op, OpPrintingFlags().skipRegions());
594 /// Erase each operand to drop its use count before checking its defining
595 /// op: by the time we call isOpTriviallyDead on defOp, the
596 /// about-to-be-erased `op` is no longer counted as a user. Only
597 /// actually-dead ops enter the worklist.
598 ///
599 /// Walk nested operations as well because erasing `op` also implicitly
600 /// erases every operation nested under it and therefore drops their operand
601 /// uses.
602 op->walk([&](Operation *erasedOp) {
603 LDBG(3) << "Processing operands of operation erased: "
604 << OpWithFlags(erasedOp, OpPrintingFlags().skipRegions());
605 for (OpOperand &opOperand : erasedOp->getOpOperands()) {
606 Operation *defOp = opOperand.get().getDefiningOp();
607 if (!defOp) {
608 LDBG(4) << "Skipping operand #" << opOperand.getOperandNumber()
609 << ": value has no defining operation";
610 continue;
611 }
612 if (defOp->getParentRegion() != &region) {
613 LDBG(4) << "Skipping operand #" << opOperand.getOperandNumber()
614 << ": defining operation is outside the current region";
615 continue;
616 }
617 LDBG(4) << "Dropping operand #" << opOperand.getOperandNumber()
618 << " from defining operation: "
619 << OpWithFlags(defOp, OpPrintingFlags().skipRegions());
620 opOperand.drop();
621 if (isOpTriviallyDead(defOp)) {
622 LDBG(2) << "Enqueued newly trivially dead defining operation: "
623 << OpWithFlags(defOp, OpPrintingFlags().skipRegions());
624 worklist.push_back(defOp);
625 ++enqueuedDefs;
626 } else {
627 LDBG(4) << "Defining operation is still not trivially dead: "
628 << OpWithFlags(defOp, OpPrintingFlags().skipRegions());
629 }
630 }
631 });
632 LDBG() << "Erasing trivially dead worklist operation: "
633 << OpWithFlags(op, OpPrintingFlags().skipRegions());
634 rewriter.eraseOp(op);
635 ++erasedOps;
636 }
637 LDBG() << "Finished eliminateTriviallyDeadOps, erased " << erasedOps
638 << " operations, seeded " << seededOps << " operations, enqueued "
639 << enqueuedDefs << " defining operations, changed=" << changed;
640 return changed;
641}
642
643//===----------------------------------------------------------------------===//
644// Block Merging
645//===----------------------------------------------------------------------===//
646
647//===----------------------------------------------------------------------===//
648// BlockEquivalenceData
649//===----------------------------------------------------------------------===//
650
651namespace {
652/// This class contains the information for comparing the equivalencies of two
653/// blocks. Blocks are considered equivalent if they contain the same operations
654/// in the same order. The only allowed divergence is for operands that come
655/// from sources outside of the parent block, i.e. the uses of values produced
656/// within the block must be equivalent.
657/// e.g.,
658/// Equivalent:
659/// ^bb1(%arg0: i32)
660/// return %arg0, %foo : i32, i32
661/// ^bb2(%arg1: i32)
662/// return %arg1, %bar : i32, i32
663/// Not Equivalent:
664/// ^bb1(%arg0: i32)
665/// return %foo, %arg0 : i32, i32
666/// ^bb2(%arg1: i32)
667/// return %arg1, %bar : i32, i32
668struct BlockEquivalenceData {
669 BlockEquivalenceData(Block *block);
670
671 /// Return the order index for the given value that is within the block of
672 /// this data.
673 unsigned getOrderOf(Value value) const;
674
675 /// The block this data refers to.
676 Block *block;
677 /// A hash value for this block.
678 llvm::hash_code hash;
679 /// A map of result producing operations to their relative orders within this
680 /// block. The order of an operation is the number of defined values that are
681 /// produced within the block before this operation.
683};
684} // namespace
685
686BlockEquivalenceData::BlockEquivalenceData(Block *block)
687 : block(block), hash(0) {
688 unsigned orderIt = block->getNumArguments();
689 for (Operation &op : *block) {
690 if (unsigned numResults = op.getNumResults()) {
691 opOrderIndex.try_emplace(&op, orderIt);
692 orderIt += numResults;
693 }
698 hash = llvm::hash_combine(hash, opHash);
699 }
700}
701
702unsigned BlockEquivalenceData::getOrderOf(Value value) const {
703 assert(value.getParentBlock() == block && "expected value of this block");
704
705 // Arguments use the argument number as the order index.
706 if (BlockArgument arg = dyn_cast<BlockArgument>(value))
707 return arg.getArgNumber();
708
709 // Otherwise, the result order is offset from the parent op's order.
710 OpResult result = cast<OpResult>(value);
711 auto opOrderIt = opOrderIndex.find(result.getDefiningOp());
712 assert(opOrderIt != opOrderIndex.end() && "expected op to have an order");
713 return opOrderIt->second + result.getResultNumber();
714}
715
716//===----------------------------------------------------------------------===//
717// BlockMergeCluster
718//===----------------------------------------------------------------------===//
719
720namespace {
721/// This class represents a cluster of blocks to be merged together.
722class BlockMergeCluster {
723public:
724 BlockMergeCluster(BlockEquivalenceData &&leaderData)
725 : leaderData(std::move(leaderData)) {}
726
727 /// Attempt to add the given block to this cluster. Returns success if the
728 /// block was merged, failure otherwise.
729 LogicalResult addToCluster(BlockEquivalenceData &blockData);
730
731 /// Try to merge all of the blocks within this cluster into the leader block.
732 LogicalResult merge(RewriterBase &rewriter);
733
734private:
735 /// The equivalence data for the leader of the cluster.
736 BlockEquivalenceData leaderData;
737
738 /// The set of blocks that can be merged into the leader.
739 llvm::SmallSetVector<Block *, 1> blocksToMerge;
740
741 /// A set of operand+index pairs that correspond to operands that need to be
742 /// replaced by arguments when the cluster gets merged.
743 std::set<std::pair<int, int>> operandsToMerge;
744};
745} // namespace
746
747LogicalResult BlockMergeCluster::addToCluster(BlockEquivalenceData &blockData) {
748 if (leaderData.hash != blockData.hash)
749 return failure();
750 Block *leaderBlock = leaderData.block, *mergeBlock = blockData.block;
751 if (leaderBlock->getArgumentTypes() != mergeBlock->getArgumentTypes())
752 return failure();
753
754 // A set of operands that mismatch between the leader and the new block.
755 SmallVector<std::pair<int, int>, 8> mismatchedOperands;
756 auto lhsIt = leaderBlock->begin(), lhsE = leaderBlock->end();
757 auto rhsIt = blockData.block->begin(), rhsE = blockData.block->end();
758 for (int opI = 0; lhsIt != lhsE && rhsIt != rhsE; ++lhsIt, ++rhsIt, ++opI) {
759 // Check that the operations are equivalent.
762 /*markEquivalent=*/nullptr,
763 OperationEquivalence::Flags::IgnoreLocations))
764 return failure();
765
766 // Compare the operands of the two operations. If the operand is within
767 // the block, it must refer to the same operation.
768 auto lhsOperands = lhsIt->getOperands(), rhsOperands = rhsIt->getOperands();
769 for (int operand : llvm::seq<int>(0, lhsIt->getNumOperands())) {
770 Value lhsOperand = lhsOperands[operand];
771 Value rhsOperand = rhsOperands[operand];
772 if (lhsOperand == rhsOperand)
773 continue;
774 // Check that the types of the operands match.
775 if (lhsOperand.getType() != rhsOperand.getType())
776 return failure();
777
778 // Check that these uses are both external, or both internal.
779 bool lhsIsInBlock = lhsOperand.getParentBlock() == leaderBlock;
780 bool rhsIsInBlock = rhsOperand.getParentBlock() == mergeBlock;
781 if (lhsIsInBlock != rhsIsInBlock)
782 return failure();
783 // Let the operands differ if they are defined in a different block. These
784 // will become new arguments if the blocks get merged.
785 if (!lhsIsInBlock) {
786
787 // Check whether the operands aren't the result of an immediate
788 // predecessors terminator. In that case we are not able to use it as a
789 // successor operand when branching to the merged block as it does not
790 // dominate its producing operation.
791 auto isValidSuccessorArg = [](Block *block, Value operand) {
792 if (operand.getDefiningOp() !=
793 operand.getParentBlock()->getTerminator())
794 return true;
795 return !llvm::is_contained(block->getPredecessors(),
796 operand.getParentBlock());
797 };
798
799 if (!isValidSuccessorArg(leaderBlock, lhsOperand) ||
800 !isValidSuccessorArg(mergeBlock, rhsOperand))
801 return failure();
802
803 mismatchedOperands.emplace_back(opI, operand);
804 continue;
805 }
806
807 // Otherwise, these operands must have the same logical order within the
808 // parent block.
809 if (leaderData.getOrderOf(lhsOperand) != blockData.getOrderOf(rhsOperand))
810 return failure();
811 }
812
813 // If the lhs or rhs has external uses, the blocks cannot be merged as the
814 // merged version of this operation will not be either the lhs or rhs
815 // alone (thus semantically incorrect), but some mix dependending on which
816 // block preceeded this.
817 // TODO allow merging of operations when one block does not dominate the
818 // other
819 if (rhsIt->isUsedOutsideOfBlock(mergeBlock) ||
820 lhsIt->isUsedOutsideOfBlock(leaderBlock)) {
821 return failure();
822 }
823 }
824 // Make sure that the block sizes are equivalent.
825 if (lhsIt != lhsE || rhsIt != rhsE)
826 return failure();
827
828 // If we get here, the blocks are equivalent and can be merged.
829 operandsToMerge.insert(mismatchedOperands.begin(), mismatchedOperands.end());
830 blocksToMerge.insert(blockData.block);
831 return success();
832}
833
834/// Returns true if the predecessor terminators of the given block can have
835/// their operands updated by appending values of the given types: each must
836/// implement BranchOpInterface and be willing to forward every one of the
837/// types to the block (`areTypesCompatible(T, T)`).
839 for (auto it = block->pred_begin(), e = block->pred_end(); it != e; ++it) {
840 auto branch = dyn_cast<BranchOpInterface>((*it)->getTerminator());
841 if (!branch)
842 return false;
843 for (Type type : types)
844 if (!branch.areTypesCompatible(type, type))
845 return false;
846 }
847 return true;
848}
849
850/// Prunes the redundant list of new arguments. E.g., if we are passing an
851/// argument list like [x, y, z, x] this would return [x, y, z] and it would
852/// update the `block` (to whom the argument are passed to) accordingly. The new
853/// arguments are passed as arguments at the back of the block, hence we need to
854/// know how many `numOldArguments` were before, in order to correctly replace
855/// the new arguments in the block
857 const SmallVector<SmallVector<Value, 8>, 2> &newArguments,
858 RewriterBase &rewriter, unsigned numOldArguments, Block *block) {
859
860 SmallVector<SmallVector<Value, 8>, 2> newArgumentsPruned(
861 newArguments.size(), SmallVector<Value, 8>());
862
863 if (newArguments.empty())
864 return newArguments;
865
866 // `newArguments` is a 2D array of size `numLists` x `numArgs`
867 unsigned numLists = newArguments.size();
868 unsigned numArgs = newArguments[0].size();
869
870 // Map that for each arg index contains the index that we can use in place of
871 // the original index. E.g., if we have newArgs = [x, y, z, x], we will have
872 // idxToReplacement[3] = 0
873 llvm::DenseMap<unsigned, unsigned> idxToReplacement;
874
875 // This is a useful data structure to track the first appearance of a Value
876 // on a given list of arguments
877 DenseMap<Value, unsigned> firstValueToIdx;
878 for (unsigned j = 0; j < numArgs; ++j) {
879 Value newArg = newArguments[0][j];
880 firstValueToIdx.try_emplace(newArg, j);
881 }
882
883 // Go through the first list of arguments (list 0).
884 for (unsigned j = 0; j < numArgs; ++j) {
885 // Look back to see if there are possible redundancies in list 0. Please
886 // note that we are using a map to annotate when an argument was seen first
887 // to avoid a O(N^2) algorithm. This has the drawback that if we have two
888 // lists like:
889 // list0: [%a, %a, %a]
890 // list1: [%c, %b, %b]
891 // We cannot simplify it, because firstValueToIdx[%a] = 0, but we cannot
892 // point list1[1](==%b) or list1[2](==%b) to list1[0](==%c). However, since
893 // the number of arguments can be potentially unbounded we cannot afford a
894 // O(N^2) algorithm (to search to all the possible pairs) and we need to
895 // accept the trade-off.
896 unsigned k = firstValueToIdx[newArguments[0][j]];
897 if (k == j)
898 continue;
899
900 bool shouldReplaceJ = true;
901 unsigned replacement = k;
902 // If a possible redundancy is found, then scan the other lists: we
903 // can prune the arguments if and only if they are redundant in every
904 // list.
905 for (unsigned i = 1; i < numLists; ++i)
906 shouldReplaceJ =
907 shouldReplaceJ && (newArguments[i][k] == newArguments[i][j]);
908 // Save the replacement.
909 if (shouldReplaceJ)
910 idxToReplacement[j] = replacement;
911 }
912
913 // Populate the pruned argument list.
914 for (unsigned i = 0; i < numLists; ++i)
915 for (unsigned j = 0; j < numArgs; ++j)
916 if (!idxToReplacement.contains(j))
917 newArgumentsPruned[i].push_back(newArguments[i][j]);
918
919 // Replace the block's redundant arguments.
920 SmallVector<unsigned> toErase;
921 for (auto [idx, arg] : llvm::enumerate(block->getArguments())) {
922 if (idxToReplacement.contains(idx)) {
923 Value oldArg = block->getArgument(numOldArguments + idx);
924 Value newArg =
925 block->getArgument(numOldArguments + idxToReplacement[idx]);
926 rewriter.replaceAllUsesWith(oldArg, newArg);
927 toErase.push_back(numOldArguments + idx);
928 }
929 }
930
931 // Erase the block's redundant arguments.
932 for (unsigned idxToErase : llvm::reverse(toErase))
933 block->eraseArgument(idxToErase);
934 return newArgumentsPruned;
935}
936
937LogicalResult BlockMergeCluster::merge(RewriterBase &rewriter) {
938 // Don't consider clusters that don't have blocks to merge.
939 if (blocksToMerge.empty())
940 return failure();
941
942 Block *leaderBlock = leaderData.block;
943 if (!operandsToMerge.empty()) {
944 // If the cluster has operands to merge, verify that the predecessor
945 // terminators of each of the blocks can have their successor operands
946 // updated: merging threads the mismatched values through them as new
947 // successor operands, so each terminator must be able to forward values
948 // of those types. The types are read off the leader block; addToCluster
949 // already required every block's mismatched operand types to match.
950 // TODO: We could try and sub-partition this cluster if only some blocks
951 // cause the mismatch.
952 SmallVector<Type> operandTypes;
953 operandTypes.reserve(operandsToMerge.size());
954 {
955 unsigned curOpIndex = 0;
956 Block::iterator opIt = leaderBlock->begin();
957 for (const auto &it : operandsToMerge) {
958 std::advance(opIt, it.first - curOpIndex);
959 curOpIndex = it.first;
960 operandTypes.push_back(opIt->getOperand(it.second).getType());
961 }
962 }
963 if (!ableToUpdatePredOperands(leaderBlock, operandTypes) ||
964 !llvm::all_of(blocksToMerge, [&](Block *block) {
965 return ableToUpdatePredOperands(block, operandTypes);
966 }))
967 return failure();
968
969 // Collect the iterators for each of the blocks to merge. We will walk all
970 // of the iterators at once to avoid operand index invalidation.
971 SmallVector<Block::iterator, 2> blockIterators;
972 blockIterators.reserve(blocksToMerge.size() + 1);
973 blockIterators.push_back(leaderBlock->begin());
974 for (Block *mergeBlock : blocksToMerge)
975 blockIterators.push_back(mergeBlock->begin());
976
977 // Update each of the predecessor terminators with the new arguments.
978 SmallVector<SmallVector<Value, 8>, 2> newArguments(
979 1 + blocksToMerge.size(),
980 SmallVector<Value, 8>(operandsToMerge.size()));
981 unsigned curOpIndex = 0;
982 unsigned numOldArguments = leaderBlock->getNumArguments();
983 for (const auto &it : llvm::enumerate(operandsToMerge)) {
984 unsigned nextOpOffset = it.value().first - curOpIndex;
985 curOpIndex = it.value().first;
986
987 // Process the operand for each of the block iterators.
988 for (unsigned i = 0, e = blockIterators.size(); i != e; ++i) {
989 Block::iterator &blockIter = blockIterators[i];
990 std::advance(blockIter, nextOpOffset);
991 auto &operand = blockIter->getOpOperand(it.value().second);
992 newArguments[i][it.index()] = operand.get();
993
994 // Update the operand and insert an argument if this is the leader.
995 if (i == 0) {
996 Value operandVal = operand.get();
997 operand.set(leaderBlock->addArgument(operandVal.getType(),
998 operandVal.getLoc()));
999 }
1000 }
1001 }
1002
1003 // Prune redundant arguments and update the leader block argument list
1004 newArguments = pruneRedundantArguments(newArguments, rewriter,
1005 numOldArguments, leaderBlock);
1006
1007 // Update the predecessors for each of the blocks.
1008 auto updatePredecessors = [&](Block *block, unsigned clusterIndex) {
1009 for (auto predIt = block->pred_begin(), predE = block->pred_end();
1010 predIt != predE; ++predIt) {
1011 auto branch = cast<BranchOpInterface>((*predIt)->getTerminator());
1012 unsigned succIndex = predIt.getSuccessorIndex();
1013 branch.getSuccessorOperands(succIndex).append(
1014 newArguments[clusterIndex]);
1015 }
1016 };
1017 updatePredecessors(leaderBlock, /*clusterIndex=*/0);
1018 for (unsigned i = 0, e = blocksToMerge.size(); i != e; ++i)
1019 updatePredecessors(blocksToMerge[i], /*clusterIndex=*/i + 1);
1020 }
1021
1022 // Replace all uses of the merged blocks with the leader and erase them.
1023 for (Block *block : blocksToMerge) {
1024 block->replaceAllUsesWith(leaderBlock);
1025 rewriter.eraseBlock(block);
1026 }
1027 return success();
1028}
1029
1030/// Identify identical blocks within the given region and merge them, inserting
1031/// new block arguments as necessary. Returns success if any blocks were merged,
1032/// failure otherwise.
1033static LogicalResult mergeIdenticalBlocks(RewriterBase &rewriter,
1034 Region &region) {
1035 if (region.empty() || region.hasOneBlock())
1036 return failure();
1037
1038 // Identify sets of blocks, other than the entry block, that branch to the
1039 // same successors. We will use these groups to create clusters of equivalent
1040 // blocks.
1042 for (Block &block : llvm::drop_begin(region, 1))
1043 matchingSuccessors[block.getSuccessors()].push_back(&block);
1044
1045 bool mergedAnyBlocks = false;
1046 for (ArrayRef<Block *> blocks : llvm::make_second_range(matchingSuccessors)) {
1047 if (blocks.size() == 1)
1048 continue;
1049
1051 for (Block *block : blocks) {
1052 BlockEquivalenceData data(block);
1053
1054 // Don't allow merging if this block has any regions.
1055 // TODO: Add support for regions if necessary.
1056 bool hasNonEmptyRegion = llvm::any_of(*block, [](Operation &op) {
1057 return llvm::any_of(op.getRegions(),
1058 [](Region &region) { return !region.empty(); });
1059 });
1060 if (hasNonEmptyRegion)
1061 continue;
1062
1063 // Don't allow merging if this block's arguments are used outside of the
1064 // original block.
1065 bool argHasExternalUsers = llvm::any_of(
1066 block->getArguments(), [block](mlir::BlockArgument &arg) {
1067 return arg.isUsedOutsideOfBlock(block);
1068 });
1069 if (argHasExternalUsers)
1070 continue;
1071
1072 // Try to add this block to an existing cluster.
1073 bool addedToCluster = false;
1074 for (auto &cluster : clusters)
1075 if ((addedToCluster = succeeded(cluster.addToCluster(data))))
1076 break;
1077 if (!addedToCluster)
1078 clusters.emplace_back(std::move(data));
1079 }
1080 for (auto &cluster : clusters)
1081 mergedAnyBlocks |= succeeded(cluster.merge(rewriter));
1082 }
1083
1084 return success(mergedAnyBlocks);
1085}
1086
1087/// Identify identical blocks within the given regions and merge them, inserting
1088/// new block arguments as necessary.
1089static LogicalResult mergeIdenticalBlocks(RewriterBase &rewriter,
1090 MutableArrayRef<Region> regions) {
1091 llvm::SmallSetVector<Region *, 1> worklist;
1092 for (auto &region : regions)
1093 worklist.insert(&region);
1094 bool anyChanged = false;
1095 while (!worklist.empty()) {
1096 Region *region = worklist.pop_back_val();
1097 if (succeeded(mergeIdenticalBlocks(rewriter, *region))) {
1098 worklist.insert(region);
1099 anyChanged = true;
1100 }
1101
1102 // Add any nested regions to the worklist.
1103 for (Block &block : *region)
1104 for (auto &op : block)
1105 for (auto &nestedRegion : op.getRegions())
1106 worklist.insert(&nestedRegion);
1107 }
1108
1109 return success(anyChanged);
1110}
1111
1112/// If a block's argument is always the same across different invocations, then
1113/// drop the argument and use the value directly inside the block
1114static LogicalResult dropRedundantArguments(RewriterBase &rewriter,
1115 Block &block) {
1116 SmallVector<size_t> argsToErase;
1117
1118 // Go through the arguments of the block.
1119 for (auto [argIdx, blockOperand] : llvm::enumerate(block.getArguments())) {
1120 bool sameArg = true;
1121 Value commonValue;
1122
1123 // Go through the block predecessor and flag if they pass to the block
1124 // different values for the same argument.
1125 for (Block::pred_iterator predIt = block.pred_begin(),
1126 predE = block.pred_end();
1127 predIt != predE; ++predIt) {
1128 auto branch = dyn_cast<BranchOpInterface>((*predIt)->getTerminator());
1129 if (!branch) {
1130 sameArg = false;
1131 break;
1132 }
1133 unsigned succIndex = predIt.getSuccessorIndex();
1134 SuccessorOperands succOperands = branch.getSuccessorOperands(succIndex);
1135
1136 // Produced operands are generated by the terminator operation itself
1137 // (e.g., results of an async call) and cannot be forwarded or dropped.
1138 if (succOperands.isOperandProduced(argIdx)) {
1139 sameArg = false;
1140 break;
1141 }
1142
1143 // Get the forwarded operand value using operator[] which correctly
1144 // adjusts for the produced operand offset.
1145 Value operandValue = succOperands[argIdx];
1146 if (!commonValue) {
1147 commonValue = operandValue;
1148 continue;
1149 }
1150 if (operandValue != commonValue) {
1151 sameArg = false;
1152 break;
1153 }
1154 }
1155
1156 // If they are passing the same value, drop the argument.
1157 if (commonValue && sameArg) {
1158 argsToErase.push_back(argIdx);
1159
1160 // Remove the argument from the block.
1161 rewriter.replaceAllUsesWith(blockOperand, commonValue);
1162 }
1163 }
1164
1165 // Remove the arguments.
1166 for (size_t argIdx : llvm::reverse(argsToErase)) {
1167 block.eraseArgument(argIdx);
1168
1169 // Remove the argument from the branch ops.
1170 for (auto predIt = block.pred_begin(), predE = block.pred_end();
1171 predIt != predE; ++predIt) {
1172 auto branch = cast<BranchOpInterface>((*predIt)->getTerminator());
1173 unsigned succIndex = predIt.getSuccessorIndex();
1174 SuccessorOperands succOperands = branch.getSuccessorOperands(succIndex);
1175 succOperands.erase(argIdx);
1176 }
1177 }
1178 return success(!argsToErase.empty());
1179}
1180
1181/// This optimization drops redundant argument to blocks. I.e., if a given
1182/// argument to a block receives the same value from each of the block
1183/// predecessors, we can remove the argument from the block and use directly the
1184/// original value. This is a simple example:
1185///
1186/// %cond = llvm.call @rand() : () -> i1
1187/// %val0 = llvm.mlir.constant(1 : i64) : i64
1188/// %val1 = llvm.mlir.constant(2 : i64) : i64
1189/// %val2 = llvm.mlir.constant(3 : i64) : i64
1190/// llvm.cond_br %cond, ^bb1(%val0 : i64, %val1 : i64), ^bb2(%val0 : i64, %val2
1191/// : i64)
1192///
1193/// ^bb1(%arg0 : i64, %arg1 : i64):
1194/// llvm.call @foo(%arg0, %arg1)
1195///
1196/// The previous IR can be rewritten as:
1197/// %cond = llvm.call @rand() : () -> i1
1198/// %val0 = llvm.mlir.constant(1 : i64) : i64
1199/// %val1 = llvm.mlir.constant(2 : i64) : i64
1200/// %val2 = llvm.mlir.constant(3 : i64) : i64
1201/// llvm.cond_br %cond, ^bb1(%val1 : i64), ^bb2(%val2 : i64)
1202///
1203/// ^bb1(%arg0 : i64):
1204/// llvm.call @foo(%val0, %arg0)
1205///
1206static LogicalResult dropRedundantArguments(RewriterBase &rewriter,
1207 MutableArrayRef<Region> regions) {
1208 llvm::SmallSetVector<Region *, 1> worklist;
1209 for (Region &region : regions)
1210 worklist.insert(&region);
1211 bool anyChanged = false;
1212 while (!worklist.empty()) {
1213 Region *region = worklist.pop_back_val();
1214
1215 // Add any nested regions to the worklist.
1216 for (Block &block : *region) {
1217 anyChanged =
1218 succeeded(dropRedundantArguments(rewriter, block)) || anyChanged;
1219
1220 for (Operation &op : block)
1221 for (Region &nestedRegion : op.getRegions())
1222 worklist.insert(&nestedRegion);
1223 }
1224 }
1225 return success(anyChanged);
1226}
1227
1228//===----------------------------------------------------------------------===//
1229// Region Simplification
1230//===----------------------------------------------------------------------===//
1231
1232/// Run a set of structural simplifications over the given regions. This
1233/// includes transformations like unreachable block elimination, dead argument
1234/// elimination, as well as some other DCE. This function returns success if any
1235/// of the regions were simplified, failure otherwise.
1236LogicalResult mlir::simplifyRegions(RewriterBase &rewriter,
1238 bool mergeBlocks) {
1239 bool eliminatedBlocks = succeeded(eraseUnreachableBlocks(rewriter, regions));
1240 bool eliminatedOpsOrArgs = succeeded(runRegionDCE(rewriter, regions));
1241 bool mergedIdenticalBlocks = false;
1242 bool droppedRedundantArguments = false;
1243 if (mergeBlocks) {
1244 mergedIdenticalBlocks = succeeded(mergeIdenticalBlocks(rewriter, regions));
1245 droppedRedundantArguments =
1246 succeeded(dropRedundantArguments(rewriter, regions));
1247 }
1248 return success(eliminatedBlocks || eliminatedOpsOrArgs ||
1249 mergedIdenticalBlocks || droppedRedundantArguments);
1250}
1251
1252//===---------------------------------------------------------------------===//
1253// Move operation dependencies
1254//===---------------------------------------------------------------------===//
1255
1256/// Check if moving operations in the slice before `insertionPoint` would break
1257/// dominance due to block argument operands. Returns true if all block args
1258/// dominate the insertion point (no issue), false otherwise. If `failingOp` is
1259/// provided, it will be set to the first problematic op.
1260///
1261/// For operands defined by ops: either the defining op is in the slice (so
1262/// dominance preserved), or it already dominates insertionPoint (otherwise it
1263/// would be in the slice). So we only need to check block argument operands,
1264/// both as direct operands and as values captured inside regions.
1266 const llvm::SetVector<Operation *> &slice, Operation *insertionPoint,
1267 DominanceInfo &dominance, Operation **failingOp = nullptr) {
1268 Block *insertionBlock = insertionPoint->getBlock();
1269
1270 // Returns true if the block arg dominates, false otherwise. Sets failingOp
1271 // on failure.
1272 auto argDominates = [&](BlockArgument arg, Operation *op) {
1273 Block *argBlock = arg.getOwner();
1274 bool dominates = argBlock == insertionBlock ||
1275 dominance.dominates(argBlock, insertionBlock);
1276 if (!dominates && failingOp)
1277 *failingOp = op;
1278 return dominates;
1279 };
1280
1281 for (Operation *op : slice) {
1282 // Check direct operands.
1283 for (Value operand : op->getOperands()) {
1284 auto arg = dyn_cast<BlockArgument>(operand);
1285 if (!arg)
1286 continue;
1287 if (!argDominates(arg, op))
1288 return false;
1289 }
1290
1291 // Check block arguments captured inside regions. Process one region at a
1292 // time to enable early exit without collecting values from all regions.
1293 for (Region &region : op->getRegions()) {
1294 SetVector<Value> capturedValues;
1295 getUsedValuesDefinedAbove(region, region, capturedValues);
1296 for (Value val : capturedValues) {
1297 auto arg = dyn_cast<BlockArgument>(val);
1298 if (!arg)
1299 continue;
1300 if (!argDominates(arg, op))
1301 return false;
1302 }
1303 }
1304 }
1305 return true;
1306}
1307
1308/// Check if any region between an operation and an ancestor block is
1309/// isolated from above. If so, moving the operation out would break
1310/// the isolation semantics.
1311static bool hasIsolatedRegionBetween(Operation *op, Block *ancestorBlock) {
1312 Region *ancestorRegion = ancestorBlock->getParent();
1313
1314 // Walk up from the op's region to find if there's an isolated region
1315 // between the op and the ancestor.
1316 Region *region = op->getParentRegion();
1317 while (region && region != ancestorRegion) {
1318 Operation *parentOp = region->getParentOp();
1319 if (!parentOp)
1320 break;
1321
1322 if (parentOp->hasTrait<OpTrait::IsIsolatedFromAbove>())
1323 return true;
1324
1325 region = parentOp->getParentRegion();
1326 }
1327 return false;
1328}
1329
1331 Operation *op,
1332 Operation *insertionPoint,
1333 DominanceInfo &dominance) {
1334 Block *insertionBlock = insertionPoint->getBlock();
1335
1336 // If `insertionPoint` does not dominate `op`, do nothing.
1337 if (!dominance.properlyDominates(insertionPoint, op)) {
1338 return rewriter.notifyMatchFailure(op,
1339 "insertion point does not dominate op");
1340 }
1341
1342 // Verify we're not crossing an isolated region.
1343 if (hasIsolatedRegionBetween(op, insertionBlock)) {
1344 return rewriter.notifyMatchFailure(
1345 op, "cannot move operation across isolated-from-above region");
1346 }
1347
1348 // Find the backward slice of operation for each `Value` the operation
1349 // depends on. Prune the slice to only include operations not already
1350 // dominated by the `insertionPoint`.
1352 options.inclusive = false;
1353 options.omitUsesFromAbove = false;
1354 // Block arguments cannot be moved; dominance check handles this case.
1355 options.omitBlockArguments = true;
1356 bool dependsOnSideEffectingOp = false;
1357 options.filter = [&](Operation *sliceBoundaryOp) {
1358 // Skip the root op - we're moving its dependencies, not the op itself.
1359 // The root op is filtered out by options.inclusive = false anyway.
1360 if (sliceBoundaryOp == op)
1361 return true;
1362 bool dominated =
1363 dominance.properlyDominates(sliceBoundaryOp, insertionPoint);
1364 // Op is already before insertion point, no need to include in slice.
1365 if (dominated)
1366 return false;
1367 // Op needs to move but is side-effecting - stop traversal early.
1368 if (!isPure(sliceBoundaryOp)) {
1369 dependsOnSideEffectingOp = true;
1370 return false;
1371 }
1372 return true;
1373 };
1375 LogicalResult result = getBackwardSlice(op, &slice, options);
1376 assert(result.succeeded() && "expected a backward slice");
1377 (void)result;
1378
1379 // Check if any operation in the slice is side-effecting.
1380 if (dependsOnSideEffectingOp) {
1381 return rewriter.notifyMatchFailure(
1382 op, "cannot move operation with side-effecting dependencies");
1383 }
1384
1385 // If the slice contains `insertionPoint` cannot move the dependencies.
1386 if (slice.contains(insertionPoint)) {
1387 return rewriter.notifyMatchFailure(
1388 op,
1389 "cannot move dependencies before operation in backward slice of op");
1390 }
1391
1392 // Verify no operation in the slice uses a block argument that wouldn't
1393 // dominate at the new location.
1394 Operation *badOp = nullptr;
1395 if (!blockArgsDominateInsertionPoint(slice, insertionPoint, dominance,
1396 &badOp)) {
1397 return rewriter.notifyMatchFailure(
1398 badOp, "moving op would break dominance for block argument operand");
1399 }
1400
1401 // We should move the slice in topological order, but `getBackwardSlice`
1402 // already does that. So no need to sort again.
1403 for (Operation *op : slice) {
1404 rewriter.moveOpBefore(op, insertionPoint);
1405 }
1406 return success();
1407}
1408
1410 Operation *op,
1411 Operation *insertionPoint) {
1412 DominanceInfo dominance(op);
1413 return moveOperationDependencies(rewriter, op, insertionPoint, dominance);
1414}
1415
1417 ValueRange values,
1418 Operation *insertionPoint,
1419 DominanceInfo &dominance) {
1420 // Remove the values that already dominate the insertion point.
1421 SmallVector<Value> prunedValues;
1422 for (auto value : values) {
1423 if (dominance.properlyDominates(value, insertionPoint))
1424 continue;
1425 // Block arguments are not supported.
1426 if (isa<BlockArgument>(value)) {
1427 return rewriter.notifyMatchFailure(
1428 insertionPoint,
1429 "unsupported case of moving block argument before insertion point");
1430 }
1431
1432 Block *insertionBlock = insertionPoint->getBlock();
1433 Operation *definingOp = value.getDefiningOp();
1434 Block *definingBlock = definingOp->getBlock();
1435
1436 // Verify we're not crossing an isolated region.
1437 if (hasIsolatedRegionBetween(definingOp, insertionBlock)) {
1438 return rewriter.notifyMatchFailure(
1439 insertionPoint,
1440 "cannot move value definition across isolated-from-above region");
1441 }
1442
1443 // Verify the insertion point's block dominates the defining block,
1444 // otherwise we're trying to move "backwards" in the CFG which doesn't
1445 // make sense.
1446 if (!dominance.dominates(insertionBlock, definingBlock)) {
1447 return rewriter.notifyMatchFailure(
1448 insertionPoint,
1449 "insertion point block does not dominate the value's defining "
1450 "block");
1451 }
1452 prunedValues.push_back(value);
1453 }
1454
1455 // Find the backward slice of operation for each `Value` the operation
1456 // depends on. Prune the slice to only include operations not already
1457 // dominated by the `insertionPoint`
1459 options.inclusive = true;
1460 options.omitUsesFromAbove = false;
1461 // Block arguments cannot be moved, so we stop the slice computation there.
1462 // If an op uses a block argument that wouldn't dominate at the new location,
1463 // the dominance check will catch it.
1464 options.omitBlockArguments = true;
1465 bool dependsOnSideEffectingOp = false;
1466 options.filter = [&](Operation *sliceBoundaryOp) {
1467 bool dominated =
1468 dominance.properlyDominates(sliceBoundaryOp, insertionPoint);
1469 // Op is already before insertion point, no need to include in slice.
1470 if (dominated)
1471 return false;
1472 // Op needs to move but is side-effecting - stop traversal early.
1473 if (!isPure(sliceBoundaryOp)) {
1474 dependsOnSideEffectingOp = true;
1475 return false;
1476 }
1477 return true;
1478 };
1480 for (auto value : prunedValues) {
1481 LogicalResult result = getBackwardSlice(value, &slice, options);
1482 assert(result.succeeded() && "expected a backward slice");
1483 (void)result;
1484 }
1485
1486 // Check if any operation in the slice is side-effecting.
1487 if (dependsOnSideEffectingOp) {
1488 return rewriter.notifyMatchFailure(
1489 insertionPoint, "cannot move value definitions with side-effecting "
1490 "operations in the slice");
1491 }
1492
1493 // If the slice contains `insertionPoint` cannot move the dependencies.
1494 if (slice.contains(insertionPoint)) {
1495 return rewriter.notifyMatchFailure(
1496 insertionPoint,
1497 "cannot move dependencies before operation in backward slice of op");
1498 }
1499
1500 // Sort operations topologically. This is needed because we call
1501 // getBackwardSlice multiple times (once per value), and the combined slice
1502 // may not be in topological order when independent subgraphs interleave.
1503 mlir::topologicalSort(slice);
1504
1505 // Verify no operation in the slice uses a block argument that wouldn't
1506 // dominate at the new location.
1507 Operation *badOp = nullptr;
1508 if (!blockArgsDominateInsertionPoint(slice, insertionPoint, dominance,
1509 &badOp)) {
1510 return rewriter.notifyMatchFailure(
1511 badOp, "moving op would break dominance for block argument operand");
1512 }
1513
1514 for (Operation *op : slice)
1515 rewriter.moveOpBefore(op, insertionPoint);
1516 return success();
1517}
1518
1520 ValueRange values,
1521 Operation *insertionPoint) {
1522 DominanceInfo dominance(insertionPoint);
1523 return moveValueDefinitions(rewriter, values, insertionPoint, dominance);
1524}
return success()
static size_t hash(const T &value)
Local helper to compute std::hash for a value.
Definition IRCore.cpp:56
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be the output argument nBegin is set to its * replacement(set to `begin` if no invalidation happens). Since outgoing *copies could have been inserted at `end`
static llvm::ManagedStatic< PassManagerOptions > options
static LogicalResult mergeIdenticalBlocks(RewriterBase &rewriter, Region &region)
Identify identical blocks within the given region and merge them, inserting new block arguments as ne...
static void propagateLiveness(Region &region, LiveMap &liveMap)
static SmallVector< SmallVector< Value, 8 >, 2 > pruneRedundantArguments(const SmallVector< SmallVector< Value, 8 >, 2 > &newArguments, RewriterBase &rewriter, unsigned numOldArguments, Block *block)
Prunes the redundant list of new arguments.
static void processValue(Value value, LiveMap &liveMap)
static bool blockArgsDominateInsertionPoint(const llvm::SetVector< Operation * > &slice, Operation *insertionPoint, DominanceInfo &dominance, Operation **failingOp=nullptr)
Check if moving operations in the slice before insertionPoint would break dominance due to block argu...
static bool ableToUpdatePredOperands(Block *block, ArrayRef< Type > types)
Returns true if the predecessor terminators of the given block can have their operands updated by app...
static void eraseTerminatorSuccessorOperands(Operation *terminator, LiveMap &liveMap)
static LogicalResult dropRedundantArguments(RewriterBase &rewriter, Block &block)
If a block's argument is always the same across different invocations, then drop the argument and use...
static bool hasIsolatedRegionBetween(Operation *op, Block *ancestorBlock)
Check if any region between an operation and an ancestor block is isolated from above.
static void propagateTerminatorLiveness(Operation *op, LiveMap &liveMap)
static bool isUseSpeciallyKnownDead(OpOperand &use, LiveMap &liveMap)
static LogicalResult deleteDeadness(RewriterBase &rewriter, MutableArrayRef< Region > regions, LiveMap &liveMap)
This class represents an argument of a Block.
Definition Value.h:306
unsigned getArgNumber() const
Returns the number of this argument.
Definition Value.h:318
Block * getOwner() const
Returns the block that owns this argument.
Definition Value.h:315
Block represents an ordered list of Operations.
Definition Block.h:33
OpListType::iterator iterator
Definition Block.h:164
ValueTypeRange< BlockArgListType > getArgumentTypes()
Return a range containing the types of the arguments for this block.
Definition Block.cpp:154
BlockArgument getArgument(unsigned i)
Definition Block.h:153
unsigned getNumArguments()
Definition Block.h:152
iterator_range< pred_iterator > getPredecessors()
Definition Block.h:264
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition Block.cpp:27
pred_iterator pred_begin()
Definition Block.h:260
SuccessorRange getSuccessors()
Definition Block.h:294
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
Definition Block.cpp:158
BlockArgListType getArguments()
Definition Block.h:111
PredecessorIterator pred_iterator
Definition Block.h:259
iterator end()
Definition Block.h:168
iterator begin()
Definition Block.h:167
void eraseArgument(unsigned index)
Erase the argument at 'index' and remove it from the argument list.
Definition Block.cpp:198
pred_iterator pred_end()
Definition Block.h:263
A class for computing basic dominance information.
Definition Dominance.h:143
bool properlyDominates(Operation *a, Operation *b, bool enclosingOpOk=true) const
Return true if operation A properly dominates operation B, i.e.
bool dominates(Operation *a, Operation *b) const
Return true if operation A dominates operation B, i.e.
Definition Dominance.h:161
This is a utility class for mapping one set of IR entities to another.
Definition IRMapping.h:26
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
Definition IRMapping.h:30
void replaceAllUsesWith(ValueT &&newValue)
Replace all uses of 'this' value with the new value, updating anything in the IR that uses 'this' to ...
IRValueT get() const
Return the current value being used by this operand.
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:351
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes={}, ArrayRef< Location > locs={})
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Definition Builders.cpp:439
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
Definition Builders.cpp:571
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition Builders.h:434
This class represents an operand of an operation.
Definition Value.h:254
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
Definition Value.cpp:226
Set of flags used to control the behavior of the various IR print methods (e.g.
This is a value defined by a result of an operation.
Definition Value.h:454
This class provides the API for ops that are known to be isolated from above.
This class provides the API for ops that are known to be terminators.
A wrapper class that allows for printing an operation with a set of flags, useful to act as a "stream...
Definition Operation.h:1142
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:774
unsigned getNumSuccessors()
Definition Operation.h:731
Block * getBlock()
Returns the operation block that contains this operation.
Definition Operation.h:230
MutableArrayRef< OpOperand > getOpOperands()
Definition Operation.h:408
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition Operation.h:702
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:403
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
Definition Operation.h:822
Block * getSuccessor(unsigned index)
Definition Operation.h:733
SuccessorRange getSuccessors()
Definition Operation.h:728
result_range getResults()
Definition Operation.h:440
Region * getParentRegion()
Returns the region to which the instruction belongs.
Definition Operation.h:247
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
Block & front()
Definition Region.h:65
Region * getParentRegion()
Return the region containing this region or nullptr if the region is attached to a top-level operatio...
Definition Region.cpp:45
bool isAncestor(Region *other)
Return true if this region is ancestor of the other region.
Definition Region.h:249
iterator_range< OpIterator > getOps()
Definition Region.h:185
bool empty()
Definition Region.h:60
iterator begin()
Definition Region.h:55
Operation * getParentOp()
Return the parent operation this region is attached to.
Definition Region.h:213
BlockListType & getBlocks()
Definition Region.h:45
bool hasOneBlock()
Return true if this region has exactly one block.
Definition Region.h:68
RetT walk(FnT &&callback)
Walk all nested operations, blocks or regions (including this region), depending on the type of callb...
Definition Region.h:312
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
void replaceOpUsesWithIf(Operation *from, ValueRange to, function_ref< bool(OpOperand &)> functor, bool *allUsesReplaced=nullptr)
virtual void eraseBlock(Block *block)
This method erases all operations in a block.
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
virtual void replaceUsesWithIf(Value from, Value to, function_ref< bool(OpOperand &)> functor, bool *allUsesReplaced=nullptr)
Find uses of from and replace them with to if the functor returns true.
void moveOpBefore(Operation *op, Operation *existingOp)
Unlink this operation from its current block and insert it right before existingOp which may be in th...
void mergeBlocks(Block *source, Block *dest, ValueRange argValues={})
Inline the operations of block 'source' into the end of block 'dest'.
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
virtual void replaceAllUsesWith(Value from, Value to)
Find uses of from and replace them with to.
This class models how operands are forwarded to block arguments in control flow.
void erase(unsigned subStart, unsigned subLen=1)
Erase operands forwarded to the successor.
bool isOperandProduced(unsigned index) const
Returns true if the successor operand denoted by index is produced by the operation.
unsigned getProducedOperandCount() const
Returns the amount of operands that are produced internally by the operation.
unsigned size() const
Returns the amount of operands passed to the successor.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
Definition Value.h:188
Block * getParentBlock()
Return the Block in which this Value is defined.
Definition Value.cpp:46
Location getLoc() const
Return the location of this value.
Definition Value.cpp:24
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
Operation * getOwner() const
Return the owner of this operand.
Definition UseDefLists.h:38
Include the generated interface declarations.
void replaceAllUsesInRegionWith(Value orig, Value replacement, Region &region)
Replace all uses of orig within the given region with replacement.
LogicalResult getBackwardSlice(Operation *op, SetVector< Operation * > *backwardSlice, const BackwardSliceOptions &options={})
Fills backwardSlice with the computed backward slice (i.e.
bool computeTopologicalSorting(MutableArrayRef< Operation * > ops, function_ref< bool(Value, Operation *)> isOperandReady=nullptr)
Compute a topological ordering of the given ops.
LogicalResult eraseUnreachableBlocks(RewriterBase &rewriter, MutableArrayRef< Region > regions, bool recurse=true)
Erase the unreachable blocks within the provided regions.
LogicalResult moveOperationDependencies(RewriterBase &rewriter, Operation *op, Operation *insertionPoint, DominanceInfo &dominance)
Move the operation dependencies (producers) of op before insertionPoint, so that op itself can subseq...
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
Definition LLVM.h:122
bool isPure(Operation *op)
Returns true if the given operation is pure, i.e., is speculatable that does not touch memory.
bool wouldOpBeTriviallyDead(Operation *op)
Return true if the given operation would be dead if unused, and has no side effects on memory that wo...
llvm::SetVector< T, Vector, Set, N > SetVector
Definition LLVM.h:125
SmallVector< Value > makeRegionIsolatedFromAbove(RewriterBase &rewriter, Region &region, llvm::function_ref< bool(Operation *)> cloneOperationIntoRegion=[](Operation *) { return false;})
Make a region isolated from above.
bool eliminateTriviallyDeadOps(RewriterBase &rewriter, Region &region, bool includeNestedRegions=true)
Remove trivially dead operations from region.
bool isOpTriviallyDead(Operation *op)
Return true if the given operation is unused, and has no side effects on memory that prevent erasing.
void getUsedValuesDefinedAbove(Region &region, Region &limit, SetVector< Value > &values)
Fill values with a list of values defined at the ancestors of the limit region and used within region...
LogicalResult runRegionDCE(RewriterBase &rewriter, MutableArrayRef< Region > regions)
This function returns success if any operations or arguments were deleted, failure otherwise.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:120
LogicalResult simplifyRegions(RewriterBase &rewriter, MutableArrayRef< Region > regions, bool mergeBlocks=true)
Run a set of structural simplifications over the given regions.
LogicalResult moveValueDefinitions(RewriterBase &rewriter, ValueRange values, Operation *insertionPoint, DominanceInfo &dominance)
Move definitions of values (and their transitive dependencies) before insertionPoint.
void visitUsedValuesDefinedAbove(Region &region, Region &limit, function_ref< void(OpOperand *)> callback)
Calls callback for each use of a value within region or its descendants that was defined at the ances...
llvm::function_ref< Fn > function_ref
Definition LLVM.h:147
SetVector< Operation * > topologicalSort(const SetVector< Operation * > &toSort)
Sorts all operations in toSort topologically while also considering region semantics.
static llvm::hash_code ignoreHashValue(Value)
Helper that can be used with computeHash above to ignore operation operands/result mapping.
static bool isEquivalentTo(Operation *lhs, Operation *rhs, function_ref< LogicalResult(Value, Value)> checkEquivalent, function_ref< void(Value, Value)> markEquivalent=nullptr, Flags flags=Flags::None, function_ref< LogicalResult(ValueRange, ValueRange)> checkCommutativeEquivalent=nullptr)
Compare two operations (including their regions) and return if they are equivalent.
static LogicalResult ignoreValueEquivalence(Value lhs, Value rhs)
Helper that can be used with isEquivalentTo above to consider ops equivalent even if their operands a...
static llvm::hash_code computeHash(Operation *op, function_ref< llvm::hash_code(Value)> hashOperands=[](Value v) { return hash_value(v);}, function_ref< llvm::hash_code(Value)> hashResults=[](Value v) { return hash_value(v);}, Flags flags=Flags::None)
Compute a hash for the given operation.
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.