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"
34#define DEBUG_TYPE "region-utils"
38 for (
auto &use : llvm::make_early_inc_range(orig.
getUses())) {
39 if (region.
isAncestor(use.getOwner()->getParentRegion()))
47 "expected isolation limit to be an ancestor of the given region");
54 properAncestors.insert(reg);
60 if (properAncestors.count(operand.get().getParentRegion()))
67 for (
Region ®ion : regions)
74 values.insert(operand->
get());
80 for (
Region ®ion : regions)
96 std::deque<Value> worklist(initialCapturedValues.begin(),
97 initialCapturedValues.end());
103 while (!worklist.empty()) {
104 Value currValue = worklist.front();
105 worklist.pop_front();
106 if (visited.count(currValue))
108 visited.insert(currValue);
111 if (!definingOp || visitedOps.count(definingOp)) {
112 finalCapturedValues.insert(currValue);
115 visitedOps.insert(definingOp);
117 if (!cloneOperationIntoRegion(definingOp)) {
120 finalCapturedValues.insert(currValue);
127 if (visited.count(operand))
129 worklist.push_back(operand);
131 clonedOperations.push_back(definingOp);
148 for (
auto value : finalCapturedValues) {
149 newArgTypes.push_back(value.getType());
150 newArgLocs.push_back(value.getLoc());
154 Block *newEntryBlock =
161 return region.
isAncestor(use.getOwner()->getParentRegion());
164 for (
auto [arg, capturedVal] :
165 llvm::zip(newEntryBlockArgs.take_back(finalCapturedValues.size()),
166 finalCapturedValues)) {
167 map.
map(capturedVal, arg);
171 for (
auto *clonedOp : clonedOperations) {
176 entryBlock, newEntryBlock,
178 return llvm::to_vector(finalCapturedValues);
191 LDBG() <<
"Starting eraseUnreachableBlocks with " << regions.size()
195 llvm::df_iterator_default_set<Block *, 16> reachable;
197 int erasedDeadBlocks = 0;
200 worklist.reserve(regions.size());
201 for (
Region ®ion : regions)
202 worklist.push_back(®ion);
204 LDBG(2) <<
"Initial worklist size: " << worklist.size();
206 while (!worklist.empty()) {
207 Region *region = worklist.pop_back_val();
208 if (region->
empty()) {
209 LDBG(2) <<
"Skipping empty region";
213 LDBG(2) <<
"Processing region with " << region->
getBlocks().size()
216 LDBG(2) <<
" -> for operation: "
224 for (
Region ®ion : op.getRegions())
225 worklist.push_back(®ion);
231 for (
Block *block : depth_first_ext(®ion->
front(), reachable))
234 LDBG(2) <<
"Found " << reachable.size() <<
" reachable blocks out of "
235 << region->
getBlocks().size() <<
" total blocks";
239 for (
Block &block : llvm::make_early_inc_range(*region)) {
240 if (!reachable.count(&block)) {
241 LDBG() <<
"Erasing unreachable block: " << █
242 block.dropAllDefinedValueUses();
251 for (
Region ®ion : op.getRegions())
252 worklist.push_back(®ion);
256 LDBG() <<
"Finished eraseUnreachableBlocks, erased " << erasedDeadBlocks
259 return success(erasedDeadBlocks > 0);
278 bool wasProvenLive(
Value value) {
282 return wasProvenLive(
result.getOwner());
283 return wasProvenLive(cast<BlockArgument>(value));
285 bool wasProvenLive(BlockArgument arg) {
return liveValues.count(arg); }
286 void setProvedLive(Value value) {
289 if (OpResult
result = dyn_cast<OpResult>(value))
290 return setProvedLive(
result.getOwner());
291 setProvedLive(cast<BlockArgument>(value));
293 void setProvedLive(BlockArgument arg) {
294 changed |= liveValues.insert(arg).second;
298 bool wasProvenLive(Operation *op) {
return liveOps.count(op); }
299 void setProvedLive(Operation *op) { changed |= liveOps.insert(op).second; }
302 void resetChanged() { changed =
false; }
303 bool hasChanged() {
return changed; }
306 bool changed =
false;
327 if (BranchOpInterface branchInterface = dyn_cast<BranchOpInterface>(owner))
328 if (
auto arg = branchInterface.getSuccessorBlockArgument(operandIndex))
329 return !liveMap.wasProvenLive(*arg);
337 if (isUseSpeciallyKnownDead(use, liveMap))
339 return liveMap.wasProvenLive(use.getOwner());
342 liveMap.setProvedLive(value);
349 liveMap.setProvedLive(op);
352 BranchOpInterface branchInterface = dyn_cast<BranchOpInterface>(op);
353 if (!branchInterface) {
356 liveMap.setProvedLive(arg);
364 branchInterface.getSuccessorOperands(i);
381 if (liveMap.wasProvenLive(op))
386 return liveMap.setProvedLive(op);
397 for (
Block *block : llvm::post_order(®ion.
front())) {
400 for (
Operation &op : llvm::reverse(block->getOperations()))
407 if (block->isEntryBlock())
410 for (
Value value : block->getArguments()) {
411 if (!liveMap.wasProvenLive(value))
419 BranchOpInterface branchOp = dyn_cast<BranchOpInterface>(terminator);
424 succI < succE; succI++) {
429 unsigned succ = succE - succI - 1;
433 for (
unsigned argI = 0, argE = succOperands.
size(); argI < argE; ++argI) {
436 unsigned arg = argE - argI - 1;
437 if (!liveMap.wasProvenLive(successor->
getArgument(arg)))
438 succOperands.
erase(arg);
446 bool erasedAnything =
false;
447 for (
Region ®ion : regions) {
457 for (
Block *block : llvm::post_order(®ion.
front())) {
461 llvm::make_early_inc_range(llvm::reverse(block->getOperations()))) {
462 if (!liveMap.wasProvenLive(&childOp)) {
463 erasedAnything =
true;
464 childOp.dropAllUses();
467 erasedAnything |= succeeded(
476 block.eraseArguments(
477 [&](
BlockArgument arg) {
return !liveMap.wasProvenLive(arg); });
480 return success(erasedAnything);
504 liveMap.resetChanged();
506 for (
Region ®ion : regions)
508 }
while (liveMap.hasChanged());
514 bool includeNestedRegions) {
515 LDBG() <<
"Starting eliminateTriviallyDeadOps with "
517 <<
" blocks, includeNestedRegions=" << includeNestedRegions;
519 LDBG(2) <<
" -> parent operation: "
522 bool changed =
false;
523 unsigned erasedOps = 0;
524 unsigned seededOps = 0;
525 unsigned enqueuedDefs = 0;
534 for (
Block &block : llvm::reverse(region)) {
535 LDBG(2) <<
"Scanning block " << &block <<
" with "
536 << block.getOperations().size() <<
" operations";
538 llvm::make_early_inc_range(llvm::reverse(block.getOperations()))) {
539 LDBG(3) <<
"Visiting operation: "
542 LDBG() <<
"Erasing trivially dead operation: "
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();
556 LDBG(2) <<
"Finished nested region #" << regionIdx <<
" of operation "
557 << op.getName() <<
", changed=" << nestedChanged;
558 changed |= nestedChanged;
578 LDBG(2) <<
"Stage 2: Seeding trivially dead operation worklist";
581 LDBG(2) <<
"Seeded worklist with operation: "
583 worklist.push_back(&op);
588 LDBG(2) <<
"Initial worklist size: " << worklist.size();
590 while (!worklist.empty()) {
592 LDBG(2) <<
"Popped operation from worklist: "
603 LDBG(3) <<
"Processing operands of operation erased: "
606 Operation *defOp = opOperand.get().getDefiningOp();
608 LDBG(4) <<
"Skipping operand #" << opOperand.getOperandNumber()
609 <<
": value has no defining operation";
613 LDBG(4) <<
"Skipping operand #" << opOperand.getOperandNumber()
614 <<
": defining operation is outside the current region";
617 LDBG(4) <<
"Dropping operand #" << opOperand.getOperandNumber()
618 <<
" from defining operation: "
622 LDBG(2) <<
"Enqueued newly trivially dead defining operation: "
624 worklist.push_back(defOp);
627 LDBG(4) <<
"Defining operation is still not trivially dead: "
632 LDBG() <<
"Erasing trivially dead worklist operation: "
637 LDBG() <<
"Finished eliminateTriviallyDeadOps, erased " << erasedOps
638 <<
" operations, seeded " << seededOps <<
" operations, enqueued "
639 << enqueuedDefs <<
" defining operations, changed=" << changed;
668struct BlockEquivalenceData {
669 BlockEquivalenceData(
Block *block);
673 unsigned getOrderOf(
Value value)
const;
678 llvm::hash_code
hash;
686BlockEquivalenceData::BlockEquivalenceData(
Block *block)
687 : block(block),
hash(0) {
690 if (
unsigned numResults = op.getNumResults()) {
691 opOrderIndex.try_emplace(&op, orderIt);
692 orderIt += numResults;
698 hash = llvm::hash_combine(
hash, opHash);
702unsigned BlockEquivalenceData::getOrderOf(
Value value)
const {
703 assert(value.
getParentBlock() == block &&
"expected value of this block");
706 if (BlockArgument arg = dyn_cast<BlockArgument>(value))
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();
722class BlockMergeCluster {
724 BlockMergeCluster(BlockEquivalenceData &&leaderData)
725 : leaderData(std::move(leaderData)) {}
729 LogicalResult addToCluster(BlockEquivalenceData &blockData);
732 LogicalResult merge(RewriterBase &rewriter);
736 BlockEquivalenceData leaderData;
739 llvm::SmallSetVector<Block *, 1> blocksToMerge;
743 std::set<std::pair<int, int>> operandsToMerge;
747LogicalResult BlockMergeCluster::addToCluster(BlockEquivalenceData &blockData) {
748 if (leaderData.hash != blockData.hash)
750 Block *leaderBlock = leaderData.block, *mergeBlock = blockData.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) {
763 OperationEquivalence::Flags::IgnoreLocations))
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)
781 if (lhsIsInBlock != rhsIsInBlock)
791 auto isValidSuccessorArg = [](
Block *block, Value operand) {
792 if (operand.getDefiningOp() !=
793 operand.getParentBlock()->getTerminator())
796 operand.getParentBlock());
799 if (!isValidSuccessorArg(leaderBlock, lhsOperand) ||
800 !isValidSuccessorArg(mergeBlock, rhsOperand))
803 mismatchedOperands.emplace_back(opI, operand);
809 if (leaderData.getOrderOf(lhsOperand) != blockData.getOrderOf(rhsOperand))
819 if (rhsIt->isUsedOutsideOfBlock(mergeBlock) ||
820 lhsIt->isUsedOutsideOfBlock(leaderBlock)) {
825 if (lhsIt != lhsE || rhsIt != rhsE)
829 operandsToMerge.insert(mismatchedOperands.begin(), mismatchedOperands.end());
830 blocksToMerge.insert(blockData.block);
840 auto branch = dyn_cast<BranchOpInterface>((*it)->getTerminator());
843 for (
Type type : types)
844 if (!branch.areTypesCompatible(type, type))
863 if (newArguments.empty())
867 unsigned numLists = newArguments.size();
868 unsigned numArgs = newArguments[0].size();
878 for (
unsigned j = 0;
j < numArgs; ++
j) {
879 Value newArg = newArguments[0][
j];
880 firstValueToIdx.try_emplace(newArg,
j);
884 for (
unsigned j = 0;
j < numArgs; ++
j) {
896 unsigned k = firstValueToIdx[newArguments[0][
j]];
900 bool shouldReplaceJ =
true;
905 for (
unsigned i = 1; i < numLists; ++i)
907 shouldReplaceJ && (newArguments[i][k] == newArguments[i][
j]);
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]);
921 for (
auto [idx, arg] : llvm::enumerate(block->
getArguments())) {
922 if (idxToReplacement.contains(idx)) {
925 block->
getArgument(numOldArguments + idxToReplacement[idx]);
927 toErase.push_back(numOldArguments + idx);
932 for (
unsigned idxToErase : llvm::reverse(toErase))
934 return newArgumentsPruned;
937LogicalResult BlockMergeCluster::merge(RewriterBase &rewriter) {
939 if (blocksToMerge.empty())
942 Block *leaderBlock = leaderData.block;
943 if (!operandsToMerge.empty()) {
952 SmallVector<Type> operandTypes;
953 operandTypes.reserve(operandsToMerge.size());
955 unsigned curOpIndex = 0;
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());
964 !llvm::all_of(blocksToMerge, [&](
Block *block) {
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());
978 SmallVector<SmallVector<Value, 8>, 2> newArguments(
979 1 + blocksToMerge.size(),
980 SmallVector<Value, 8>(operandsToMerge.size()));
981 unsigned curOpIndex = 0;
983 for (
const auto &it : llvm::enumerate(operandsToMerge)) {
984 unsigned nextOpOffset = it.value().first - curOpIndex;
985 curOpIndex = it.value().first;
988 for (
unsigned i = 0, e = blockIterators.size(); i != e; ++i) {
990 std::advance(blockIter, nextOpOffset);
991 auto &operand = blockIter->getOpOperand(it.value().second);
992 newArguments[i][it.index()] = operand.get();
996 Value operandVal = operand.get();
1005 numOldArguments, leaderBlock);
1008 auto updatePredecessors = [&](
Block *block,
unsigned clusterIndex) {
1010 predIt != predE; ++predIt) {
1011 auto branch = cast<BranchOpInterface>((*predIt)->getTerminator());
1012 unsigned succIndex = predIt.getSuccessorIndex();
1013 branch.getSuccessorOperands(succIndex).append(
1014 newArguments[clusterIndex]);
1017 updatePredecessors(leaderBlock, 0);
1018 for (
unsigned i = 0, e = blocksToMerge.size(); i != e; ++i)
1019 updatePredecessors(blocksToMerge[i], i + 1);
1023 for (
Block *block : blocksToMerge) {
1042 for (
Block &block : llvm::drop_begin(region, 1))
1043 matchingSuccessors[block.
getSuccessors()].push_back(&block);
1045 bool mergedAnyBlocks =
false;
1047 if (blocks.size() == 1)
1051 for (
Block *block : blocks) {
1052 BlockEquivalenceData data(block);
1056 bool hasNonEmptyRegion = llvm::any_of(*block, [](
Operation &op) {
1058 [](
Region ®ion) { return !region.empty(); });
1060 if (hasNonEmptyRegion)
1065 bool argHasExternalUsers = llvm::any_of(
1067 return arg.isUsedOutsideOfBlock(block);
1069 if (argHasExternalUsers)
1073 bool addedToCluster =
false;
1074 for (
auto &cluster : clusters)
1075 if ((addedToCluster = succeeded(cluster.addToCluster(data))))
1077 if (!addedToCluster)
1078 clusters.emplace_back(std::move(data));
1080 for (
auto &cluster : clusters)
1081 mergedAnyBlocks |= succeeded(cluster.merge(rewriter));
1084 return success(mergedAnyBlocks);
1091 llvm::SmallSetVector<Region *, 1> worklist;
1092 for (
auto ®ion : regions)
1093 worklist.insert(®ion);
1094 bool anyChanged =
false;
1095 while (!worklist.empty()) {
1096 Region *region = worklist.pop_back_val();
1098 worklist.insert(region);
1103 for (
Block &block : *region)
1104 for (
auto &op : block)
1105 for (
auto &nestedRegion : op.getRegions())
1106 worklist.insert(&nestedRegion);
1119 for (
auto [argIdx, blockOperand] : llvm::enumerate(block.
getArguments())) {
1120 bool sameArg =
true;
1127 predIt != predE; ++predIt) {
1128 auto branch = dyn_cast<BranchOpInterface>((*predIt)->getTerminator());
1133 unsigned succIndex = predIt.getSuccessorIndex();
1145 Value operandValue = succOperands[argIdx];
1147 commonValue = operandValue;
1150 if (operandValue != commonValue) {
1157 if (commonValue && sameArg) {
1158 argsToErase.push_back(argIdx);
1166 for (
size_t argIdx : llvm::reverse(argsToErase)) {
1171 predIt != predE; ++predIt) {
1172 auto branch = cast<BranchOpInterface>((*predIt)->getTerminator());
1173 unsigned succIndex = predIt.getSuccessorIndex();
1175 succOperands.
erase(argIdx);
1178 return success(!argsToErase.empty());
1208 llvm::SmallSetVector<Region *, 1> worklist;
1209 for (
Region ®ion : regions)
1210 worklist.insert(®ion);
1211 bool anyChanged =
false;
1212 while (!worklist.empty()) {
1213 Region *region = worklist.pop_back_val();
1216 for (
Block &block : *region) {
1221 for (
Region &nestedRegion : op.getRegions())
1222 worklist.insert(&nestedRegion);
1240 bool eliminatedOpsOrArgs = succeeded(
runRegionDCE(rewriter, regions));
1241 bool mergedIdenticalBlocks =
false;
1242 bool droppedRedundantArguments =
false;
1245 droppedRedundantArguments =
1248 return success(eliminatedBlocks || eliminatedOpsOrArgs ||
1249 mergedIdenticalBlocks || droppedRedundantArguments);
1274 bool dominates = argBlock == insertionBlock ||
1275 dominance.
dominates(argBlock, insertionBlock);
1276 if (!dominates && failingOp)
1283 for (
Value operand : op->getOperands()) {
1284 auto arg = dyn_cast<BlockArgument>(operand);
1287 if (!argDominates(arg, op))
1293 for (
Region ®ion : op->getRegions()) {
1296 for (
Value val : capturedValues) {
1297 auto arg = dyn_cast<BlockArgument>(val);
1300 if (!argDominates(arg, op))
1317 while (region && region != ancestorRegion) {
1339 "insertion point does not dominate op");
1345 op,
"cannot move operation across isolated-from-above region");
1353 options.omitUsesFromAbove =
false;
1355 options.omitBlockArguments =
true;
1356 bool dependsOnSideEffectingOp =
false;
1360 if (sliceBoundaryOp == op)
1368 if (!
isPure(sliceBoundaryOp)) {
1369 dependsOnSideEffectingOp =
true;
1376 assert(
result.succeeded() &&
"expected a backward slice");
1380 if (dependsOnSideEffectingOp) {
1382 op,
"cannot move operation with side-effecting dependencies");
1386 if (slice.contains(insertionPoint)) {
1389 "cannot move dependencies before operation in backward slice of op");
1398 badOp,
"moving op would break dominance for block argument operand");
1422 for (
auto value : values) {
1426 if (isa<BlockArgument>(value)) {
1429 "unsupported case of moving block argument before insertion point");
1440 "cannot move value definition across isolated-from-above region");
1446 if (!dominance.
dominates(insertionBlock, definingBlock)) {
1449 "insertion point block does not dominate the value's defining "
1452 prunedValues.push_back(value);
1460 options.omitUsesFromAbove =
false;
1464 options.omitBlockArguments =
true;
1465 bool dependsOnSideEffectingOp =
false;
1473 if (!
isPure(sliceBoundaryOp)) {
1474 dependsOnSideEffectingOp =
true;
1480 for (
auto value : prunedValues) {
1482 assert(
result.succeeded() &&
"expected a backward slice");
1487 if (dependsOnSideEffectingOp) {
1489 insertionPoint,
"cannot move value definitions with side-effecting "
1490 "operations in the slice");
1494 if (slice.contains(insertionPoint)) {
1497 "cannot move dependencies before operation in backward slice of op");
1511 badOp,
"moving op would break dominance for block argument operand");
static size_t hash(const T &value)
Local helper to compute std::hash for a value.
*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 ®ion)
Identify identical blocks within the given region and merge them, inserting new block arguments as ne...
static void propagateLiveness(Region ®ion, 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.
unsigned getArgNumber() const
Returns the number of this argument.
Block * getOwner() const
Returns the block that owns this argument.
Block represents an ordered list of Operations.
OpListType::iterator iterator
ValueTypeRange< BlockArgListType > getArgumentTypes()
Return a range containing the types of the arguments for this block.
BlockArgument getArgument(unsigned i)
unsigned getNumArguments()
iterator_range< pred_iterator > getPredecessors()
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
pred_iterator pred_begin()
SuccessorRange getSuccessors()
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
BlockArgListType getArguments()
PredecessorIterator pred_iterator
void eraseArgument(unsigned index)
Erase the argument at 'index' and remove it from the argument list.
A class for computing basic dominance information.
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.
This is a utility class for mapping one set of IR entities to another.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
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.
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.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
This class represents an operand of an operation.
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
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.
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...
Operation is the basic unit of execution within MLIR.
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
unsigned getNumSuccessors()
Block * getBlock()
Returns the operation block that contains this operation.
MutableArrayRef< OpOperand > getOpOperands()
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
operand_range getOperands()
Returns an iterator on the underlying Value's.
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),...
Block * getSuccessor(unsigned index)
SuccessorRange getSuccessors()
result_range getResults()
Region * getParentRegion()
Returns the region to which the instruction belongs.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Region * getParentRegion()
Return the region containing this region or nullptr if the region is attached to a top-level operatio...
bool isAncestor(Region *other)
Return true if this region is ancestor of the other region.
iterator_range< OpIterator > getOps()
Operation * getParentOp()
Return the parent operation this region is attached to.
BlockListType & getBlocks()
bool hasOneBlock()
Return true if this region has exactly one block.
RetT walk(FnT &&callback)
Walk all nested operations, blocks or regions (including this region), depending on the type of callb...
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...
This class provides an abstraction over the different types of ranges over Values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
Block * getParentBlock()
Return the Block in which this Value is defined.
Location getLoc() const
Return the location of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Operation * getOwner() const
Return the owner of this operand.
Include the generated interface declarations.
void replaceAllUsesInRegionWith(Value orig, Value replacement, Region ®ion)
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
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
SmallVector< Value > makeRegionIsolatedFromAbove(RewriterBase &rewriter, Region ®ion, llvm::function_ref< bool(Operation *)> cloneOperationIntoRegion=[](Operation *) { return false;})
Make a region isolated from above.
bool eliminateTriviallyDeadOps(RewriterBase &rewriter, Region ®ion, 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 ®ion, 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
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 ®ion, 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
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.