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 unsigned succIndex = it.getSuccessorIndex();
844 for (
Type type : types)
845 if (!branch.mayForwardTypeToSuccessor(succIndex, type))
864 if (newArguments.empty())
868 unsigned numLists = newArguments.size();
869 unsigned numArgs = newArguments[0].size();
879 for (
unsigned j = 0;
j < numArgs; ++
j) {
880 Value newArg = newArguments[0][
j];
881 firstValueToIdx.try_emplace(newArg,
j);
885 for (
unsigned j = 0;
j < numArgs; ++
j) {
897 unsigned k = firstValueToIdx[newArguments[0][
j]];
901 bool shouldReplaceJ =
true;
906 for (
unsigned i = 1; i < numLists; ++i)
908 shouldReplaceJ && (newArguments[i][k] == newArguments[i][
j]);
915 for (
unsigned i = 0; i < numLists; ++i)
916 for (
unsigned j = 0;
j < numArgs; ++
j)
917 if (!idxToReplacement.contains(
j))
918 newArgumentsPruned[i].push_back(newArguments[i][
j]);
922 for (
auto [idx, arg] : llvm::enumerate(block->
getArguments())) {
923 if (idxToReplacement.contains(idx)) {
926 block->
getArgument(numOldArguments + idxToReplacement[idx]);
928 toErase.push_back(numOldArguments + idx);
933 for (
unsigned idxToErase : llvm::reverse(toErase))
935 return newArgumentsPruned;
938LogicalResult BlockMergeCluster::merge(RewriterBase &rewriter) {
940 if (blocksToMerge.empty())
943 Block *leaderBlock = leaderData.block;
944 if (!operandsToMerge.empty()) {
953 SmallVector<Type> operandTypes;
954 operandTypes.reserve(operandsToMerge.size());
956 unsigned curOpIndex = 0;
958 for (
const auto &it : operandsToMerge) {
959 std::advance(opIt, it.first - curOpIndex);
960 curOpIndex = it.first;
961 operandTypes.push_back(opIt->getOperand(it.second).getType());
965 !llvm::all_of(blocksToMerge, [&](
Block *block) {
972 SmallVector<Block::iterator, 2> blockIterators;
973 blockIterators.reserve(blocksToMerge.size() + 1);
974 blockIterators.push_back(leaderBlock->
begin());
975 for (
Block *mergeBlock : blocksToMerge)
976 blockIterators.push_back(mergeBlock->begin());
979 SmallVector<SmallVector<Value, 8>, 2> newArguments(
980 1 + blocksToMerge.size(),
981 SmallVector<Value, 8>(operandsToMerge.size()));
982 unsigned curOpIndex = 0;
984 for (
const auto &it : llvm::enumerate(operandsToMerge)) {
985 unsigned nextOpOffset = it.value().first - curOpIndex;
986 curOpIndex = it.value().first;
989 for (
unsigned i = 0, e = blockIterators.size(); i != e; ++i) {
991 std::advance(blockIter, nextOpOffset);
992 auto &operand = blockIter->getOpOperand(it.value().second);
993 newArguments[i][it.index()] = operand.get();
997 Value operandVal = operand.get();
1006 numOldArguments, leaderBlock);
1009 auto updatePredecessors = [&](
Block *block,
unsigned clusterIndex) {
1011 predIt != predE; ++predIt) {
1012 auto branch = cast<BranchOpInterface>((*predIt)->getTerminator());
1013 unsigned succIndex = predIt.getSuccessorIndex();
1014 branch.getSuccessorOperands(succIndex).append(
1015 newArguments[clusterIndex]);
1018 updatePredecessors(leaderBlock, 0);
1019 for (
unsigned i = 0, e = blocksToMerge.size(); i != e; ++i)
1020 updatePredecessors(blocksToMerge[i], i + 1);
1024 for (
Block *block : blocksToMerge) {
1043 for (
Block &block : llvm::drop_begin(region, 1))
1044 matchingSuccessors[block.
getSuccessors()].push_back(&block);
1046 bool mergedAnyBlocks =
false;
1048 if (blocks.size() == 1)
1052 for (
Block *block : blocks) {
1053 BlockEquivalenceData data(block);
1057 bool hasNonEmptyRegion = llvm::any_of(*block, [](
Operation &op) {
1059 [](
Region ®ion) { return !region.empty(); });
1061 if (hasNonEmptyRegion)
1066 bool argHasExternalUsers = llvm::any_of(
1068 return arg.isUsedOutsideOfBlock(block);
1070 if (argHasExternalUsers)
1074 bool addedToCluster =
false;
1075 for (
auto &cluster : clusters)
1076 if ((addedToCluster = succeeded(cluster.addToCluster(data))))
1078 if (!addedToCluster)
1079 clusters.emplace_back(std::move(data));
1081 for (
auto &cluster : clusters)
1082 mergedAnyBlocks |= succeeded(cluster.merge(rewriter));
1085 return success(mergedAnyBlocks);
1092 llvm::SmallSetVector<Region *, 1> worklist;
1093 for (
auto ®ion : regions)
1094 worklist.insert(®ion);
1095 bool anyChanged =
false;
1096 while (!worklist.empty()) {
1097 Region *region = worklist.pop_back_val();
1099 worklist.insert(region);
1104 for (
Block &block : *region)
1105 for (
auto &op : block)
1106 for (
auto &nestedRegion : op.getRegions())
1107 worklist.insert(&nestedRegion);
1120 for (
auto [argIdx, blockOperand] : llvm::enumerate(block.
getArguments())) {
1121 bool sameArg =
true;
1128 predIt != predE; ++predIt) {
1129 auto branch = dyn_cast<BranchOpInterface>((*predIt)->getTerminator());
1134 unsigned succIndex = predIt.getSuccessorIndex();
1146 Value operandValue = succOperands[argIdx];
1148 commonValue = operandValue;
1151 if (operandValue != commonValue) {
1158 if (commonValue && sameArg) {
1159 argsToErase.push_back(argIdx);
1167 for (
size_t argIdx : llvm::reverse(argsToErase)) {
1172 predIt != predE; ++predIt) {
1173 auto branch = cast<BranchOpInterface>((*predIt)->getTerminator());
1174 unsigned succIndex = predIt.getSuccessorIndex();
1176 succOperands.
erase(argIdx);
1179 return success(!argsToErase.empty());
1209 llvm::SmallSetVector<Region *, 1> worklist;
1210 for (
Region ®ion : regions)
1211 worklist.insert(®ion);
1212 bool anyChanged =
false;
1213 while (!worklist.empty()) {
1214 Region *region = worklist.pop_back_val();
1217 for (
Block &block : *region) {
1222 for (
Region &nestedRegion : op.getRegions())
1223 worklist.insert(&nestedRegion);
1241 bool eliminatedOpsOrArgs = succeeded(
runRegionDCE(rewriter, regions));
1242 bool mergedIdenticalBlocks =
false;
1243 bool droppedRedundantArguments =
false;
1246 droppedRedundantArguments =
1249 return success(eliminatedBlocks || eliminatedOpsOrArgs ||
1250 mergedIdenticalBlocks || droppedRedundantArguments);
1275 bool dominates = argBlock == insertionBlock ||
1276 dominance.
dominates(argBlock, insertionBlock);
1277 if (!dominates && failingOp)
1284 for (
Value operand : op->getOperands()) {
1285 auto arg = dyn_cast<BlockArgument>(operand);
1288 if (!argDominates(arg, op))
1294 for (
Region ®ion : op->getRegions()) {
1297 for (
Value val : capturedValues) {
1298 auto arg = dyn_cast<BlockArgument>(val);
1301 if (!argDominates(arg, op))
1318 while (region && region != ancestorRegion) {
1340 "insertion point does not dominate op");
1346 op,
"cannot move operation across isolated-from-above region");
1354 options.omitUsesFromAbove =
false;
1356 options.omitBlockArguments =
true;
1357 bool dependsOnSideEffectingOp =
false;
1361 if (sliceBoundaryOp == op)
1369 if (!
isPure(sliceBoundaryOp)) {
1370 dependsOnSideEffectingOp =
true;
1377 assert(
result.succeeded() &&
"expected a backward slice");
1381 if (dependsOnSideEffectingOp) {
1383 op,
"cannot move operation with side-effecting dependencies");
1387 if (slice.contains(insertionPoint)) {
1390 "cannot move dependencies before operation in backward slice of op");
1399 badOp,
"moving op would break dominance for block argument operand");
1423 for (
auto value : values) {
1427 if (isa<BlockArgument>(value)) {
1430 "unsupported case of moving block argument before insertion point");
1441 "cannot move value definition across isolated-from-above region");
1447 if (!dominance.
dominates(insertionBlock, definingBlock)) {
1450 "insertion point block does not dominate the value's defining "
1453 prunedValues.push_back(value);
1461 options.omitUsesFromAbove =
false;
1465 options.omitBlockArguments =
true;
1466 bool dependsOnSideEffectingOp =
false;
1474 if (!
isPure(sliceBoundaryOp)) {
1475 dependsOnSideEffectingOp =
true;
1481 for (
auto value : prunedValues) {
1483 assert(
result.succeeded() &&
"expected a backward slice");
1488 if (dependsOnSideEffectingOp) {
1490 insertionPoint,
"cannot move value definitions with side-effecting "
1491 "operations in the slice");
1495 if (slice.contains(insertionPoint)) {
1498 "cannot move dependencies before operation in backward slice of op");
1512 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.