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(
477 bool isDead = !liveMap.wasProvenLive(arg);
478 erasedAnything |= isDead;
483 return success(erasedAnything);
507 liveMap.resetChanged();
509 for (
Region ®ion : regions)
511 }
while (liveMap.hasChanged());
517 bool includeNestedRegions) {
518 LDBG() <<
"Starting eliminateTriviallyDeadOps with "
520 <<
" blocks, includeNestedRegions=" << includeNestedRegions;
522 LDBG(2) <<
" -> parent operation: "
525 bool changed =
false;
526 unsigned erasedOps = 0;
527 unsigned seededOps = 0;
528 unsigned enqueuedDefs = 0;
537 for (
Block &block : llvm::reverse(region)) {
538 LDBG(2) <<
"Scanning block " << &block <<
" with "
539 << block.getOperations().size() <<
" operations";
541 llvm::make_early_inc_range(llvm::reverse(block.getOperations()))) {
542 LDBG(3) <<
"Visiting operation: "
545 LDBG() <<
"Erasing trivially dead operation: "
552 if (includeNestedRegions) {
553 unsigned regionIdx = 0;
554 for (
Region &nested : op.getRegions()) {
555 LDBG(2) <<
"Recursing into nested region #" << regionIdx
556 <<
" of operation " << op.getName();
559 LDBG(2) <<
"Finished nested region #" << regionIdx <<
" of operation "
560 << op.getName() <<
", changed=" << nestedChanged;
561 changed |= nestedChanged;
581 LDBG(2) <<
"Stage 2: Seeding trivially dead operation worklist";
584 LDBG(2) <<
"Seeded worklist with operation: "
586 worklist.push_back(&op);
591 LDBG(2) <<
"Initial worklist size: " << worklist.size();
593 while (!worklist.empty()) {
595 LDBG(2) <<
"Popped operation from worklist: "
606 LDBG(3) <<
"Processing operands of operation erased: "
609 Operation *defOp = opOperand.get().getDefiningOp();
611 LDBG(4) <<
"Skipping operand #" << opOperand.getOperandNumber()
612 <<
": value has no defining operation";
616 LDBG(4) <<
"Skipping operand #" << opOperand.getOperandNumber()
617 <<
": defining operation is outside the current region";
620 LDBG(4) <<
"Dropping operand #" << opOperand.getOperandNumber()
621 <<
" from defining operation: "
625 LDBG(2) <<
"Enqueued newly trivially dead defining operation: "
627 worklist.push_back(defOp);
630 LDBG(4) <<
"Defining operation is still not trivially dead: "
635 LDBG() <<
"Erasing trivially dead worklist operation: "
640 LDBG() <<
"Finished eliminateTriviallyDeadOps, erased " << erasedOps
641 <<
" operations, seeded " << seededOps <<
" operations, enqueued "
642 << enqueuedDefs <<
" defining operations, changed=" << changed;
671struct BlockEquivalenceData {
672 BlockEquivalenceData(
Block *block);
676 unsigned getOrderOf(
Value value)
const;
681 llvm::hash_code
hash;
689BlockEquivalenceData::BlockEquivalenceData(
Block *block)
690 : block(block),
hash(0) {
693 if (
unsigned numResults = op.getNumResults()) {
694 opOrderIndex.try_emplace(&op, orderIt);
695 orderIt += numResults;
701 hash = llvm::hash_combine(
hash, opHash);
705unsigned BlockEquivalenceData::getOrderOf(
Value value)
const {
706 assert(value.
getParentBlock() == block &&
"expected value of this block");
709 if (BlockArgument arg = dyn_cast<BlockArgument>(value))
713 OpResult
result = cast<OpResult>(value);
714 auto opOrderIt = opOrderIndex.find(
result.getDefiningOp());
715 assert(opOrderIt != opOrderIndex.end() &&
"expected op to have an order");
716 return opOrderIt->second +
result.getResultNumber();
725class BlockMergeCluster {
727 BlockMergeCluster(BlockEquivalenceData &&leaderData)
728 : leaderData(std::move(leaderData)) {}
732 LogicalResult addToCluster(BlockEquivalenceData &blockData);
735 LogicalResult merge(RewriterBase &rewriter);
739 BlockEquivalenceData leaderData;
742 llvm::SmallSetVector<Block *, 1> blocksToMerge;
746 std::set<std::pair<int, int>> operandsToMerge;
750LogicalResult BlockMergeCluster::addToCluster(BlockEquivalenceData &blockData) {
751 if (leaderData.hash != blockData.hash)
753 Block *leaderBlock = leaderData.block, *mergeBlock = blockData.block;
758 SmallVector<std::pair<int, int>, 8> mismatchedOperands;
759 auto lhsIt = leaderBlock->
begin(), lhsE = leaderBlock->
end();
760 auto rhsIt = blockData.block->
begin(), rhsE = blockData.block->
end();
761 for (
int opI = 0; lhsIt != lhsE && rhsIt != rhsE; ++lhsIt, ++rhsIt, ++opI) {
766 OperationEquivalence::Flags::IgnoreLocations))
771 auto lhsOperands = lhsIt->getOperands(), rhsOperands = rhsIt->getOperands();
772 for (
int operand : llvm::seq<int>(0, lhsIt->getNumOperands())) {
773 Value lhsOperand = lhsOperands[operand];
774 Value rhsOperand = rhsOperands[operand];
775 if (lhsOperand == rhsOperand)
784 if (lhsIsInBlock != rhsIsInBlock)
794 auto isValidSuccessorArg = [](
Block *block, Value operand) {
795 if (operand.getDefiningOp() !=
796 operand.getParentBlock()->getTerminator())
799 operand.getParentBlock());
802 if (!isValidSuccessorArg(leaderBlock, lhsOperand) ||
803 !isValidSuccessorArg(mergeBlock, rhsOperand))
806 mismatchedOperands.emplace_back(opI, operand);
812 if (leaderData.getOrderOf(lhsOperand) != blockData.getOrderOf(rhsOperand))
822 if (rhsIt->isUsedOutsideOfBlock(mergeBlock) ||
823 lhsIt->isUsedOutsideOfBlock(leaderBlock)) {
828 if (lhsIt != lhsE || rhsIt != rhsE)
832 operandsToMerge.insert(mismatchedOperands.begin(), mismatchedOperands.end());
833 blocksToMerge.insert(blockData.block);
843 auto branch = dyn_cast<BranchOpInterface>((*it)->getTerminator());
846 for (
Type type : types)
847 if (!branch.areTypesCompatible(type, type))
866 if (newArguments.empty())
870 unsigned numLists = newArguments.size();
871 unsigned numArgs = newArguments[0].size();
881 for (
unsigned j = 0;
j < numArgs; ++
j) {
882 Value newArg = newArguments[0][
j];
883 firstValueToIdx.try_emplace(newArg,
j);
887 for (
unsigned j = 0;
j < numArgs; ++
j) {
899 unsigned k = firstValueToIdx[newArguments[0][
j]];
903 bool shouldReplaceJ =
true;
908 for (
unsigned i = 1; i < numLists; ++i)
910 shouldReplaceJ && (newArguments[i][k] == newArguments[i][
j]);
917 for (
unsigned i = 0; i < numLists; ++i)
918 for (
unsigned j = 0;
j < numArgs; ++
j)
919 if (!idxToReplacement.contains(
j))
920 newArgumentsPruned[i].push_back(newArguments[i][
j]);
924 for (
auto [idx, arg] : llvm::enumerate(block->
getArguments())) {
925 if (idxToReplacement.contains(idx)) {
928 block->
getArgument(numOldArguments + idxToReplacement[idx]);
930 toErase.push_back(numOldArguments + idx);
935 for (
unsigned idxToErase : llvm::reverse(toErase))
937 return newArgumentsPruned;
940LogicalResult BlockMergeCluster::merge(RewriterBase &rewriter) {
942 if (blocksToMerge.empty())
945 Block *leaderBlock = leaderData.block;
946 if (!operandsToMerge.empty()) {
955 SmallVector<Type> operandTypes;
956 operandTypes.reserve(operandsToMerge.size());
958 unsigned curOpIndex = 0;
960 for (
const auto &it : operandsToMerge) {
961 std::advance(opIt, it.first - curOpIndex);
962 curOpIndex = it.first;
963 operandTypes.push_back(opIt->getOperand(it.second).getType());
967 !llvm::all_of(blocksToMerge, [&](
Block *block) {
974 SmallVector<Block::iterator, 2> blockIterators;
975 blockIterators.reserve(blocksToMerge.size() + 1);
976 blockIterators.push_back(leaderBlock->
begin());
977 for (
Block *mergeBlock : blocksToMerge)
978 blockIterators.push_back(mergeBlock->begin());
981 SmallVector<SmallVector<Value, 8>, 2> newArguments(
982 1 + blocksToMerge.size(),
983 SmallVector<Value, 8>(operandsToMerge.size()));
984 unsigned curOpIndex = 0;
986 for (
const auto &it : llvm::enumerate(operandsToMerge)) {
987 unsigned nextOpOffset = it.value().first - curOpIndex;
988 curOpIndex = it.value().first;
991 for (
unsigned i = 0, e = blockIterators.size(); i != e; ++i) {
993 std::advance(blockIter, nextOpOffset);
994 auto &operand = blockIter->getOpOperand(it.value().second);
995 newArguments[i][it.index()] = operand.get();
999 Value operandVal = operand.get();
1008 numOldArguments, leaderBlock);
1011 auto updatePredecessors = [&](
Block *block,
unsigned clusterIndex) {
1013 predIt != predE; ++predIt) {
1014 auto branch = cast<BranchOpInterface>((*predIt)->getTerminator());
1015 unsigned succIndex = predIt.getSuccessorIndex();
1016 branch.getSuccessorOperands(succIndex).append(
1017 newArguments[clusterIndex]);
1020 updatePredecessors(leaderBlock, 0);
1021 for (
unsigned i = 0, e = blocksToMerge.size(); i != e; ++i)
1022 updatePredecessors(blocksToMerge[i], i + 1);
1026 for (
Block *block : blocksToMerge) {
1045 for (
Block &block : llvm::drop_begin(region, 1))
1046 matchingSuccessors[block.
getSuccessors()].push_back(&block);
1048 bool mergedAnyBlocks =
false;
1050 if (blocks.size() == 1)
1054 for (
Block *block : blocks) {
1055 BlockEquivalenceData data(block);
1059 bool hasNonEmptyRegion = llvm::any_of(*block, [](
Operation &op) {
1061 [](
Region ®ion) { return !region.empty(); });
1063 if (hasNonEmptyRegion)
1068 bool argHasExternalUsers = llvm::any_of(
1070 return arg.isUsedOutsideOfBlock(block);
1072 if (argHasExternalUsers)
1076 bool addedToCluster =
false;
1077 for (
auto &cluster : clusters)
1078 if ((addedToCluster = succeeded(cluster.addToCluster(data))))
1080 if (!addedToCluster)
1081 clusters.emplace_back(std::move(data));
1083 for (
auto &cluster : clusters)
1084 mergedAnyBlocks |= succeeded(cluster.merge(rewriter));
1087 return success(mergedAnyBlocks);
1094 llvm::SmallSetVector<Region *, 1> worklist;
1095 for (
auto ®ion : regions)
1096 worklist.insert(®ion);
1097 bool anyChanged =
false;
1098 while (!worklist.empty()) {
1099 Region *region = worklist.pop_back_val();
1101 worklist.insert(region);
1106 for (
Block &block : *region)
1107 for (
auto &op : block)
1108 for (
auto &nestedRegion : op.getRegions())
1109 worklist.insert(&nestedRegion);
1122 for (
auto [argIdx, blockOperand] : llvm::enumerate(block.
getArguments())) {
1123 bool sameArg =
true;
1130 predIt != predE; ++predIt) {
1131 auto branch = dyn_cast<BranchOpInterface>((*predIt)->getTerminator());
1136 unsigned succIndex = predIt.getSuccessorIndex();
1148 Value operandValue = succOperands[argIdx];
1150 commonValue = operandValue;
1153 if (operandValue != commonValue) {
1160 if (commonValue && sameArg) {
1161 argsToErase.push_back(argIdx);
1169 for (
size_t argIdx : llvm::reverse(argsToErase)) {
1174 predIt != predE; ++predIt) {
1175 auto branch = cast<BranchOpInterface>((*predIt)->getTerminator());
1176 unsigned succIndex = predIt.getSuccessorIndex();
1178 succOperands.
erase(argIdx);
1181 return success(!argsToErase.empty());
1211 llvm::SmallSetVector<Region *, 1> worklist;
1212 for (
Region ®ion : regions)
1213 worklist.insert(®ion);
1214 bool anyChanged =
false;
1215 while (!worklist.empty()) {
1216 Region *region = worklist.pop_back_val();
1219 for (
Block &block : *region) {
1224 for (
Region &nestedRegion : op.getRegions())
1225 worklist.insert(&nestedRegion);
1243 bool eliminatedOpsOrArgs = succeeded(
runRegionDCE(rewriter, regions));
1244 bool mergedIdenticalBlocks =
false;
1245 bool droppedRedundantArguments =
false;
1248 droppedRedundantArguments =
1251 return success(eliminatedBlocks || eliminatedOpsOrArgs ||
1252 mergedIdenticalBlocks || droppedRedundantArguments);
1277 bool dominates = argBlock == insertionBlock ||
1278 dominance.
dominates(argBlock, insertionBlock);
1279 if (!dominates && failingOp)
1286 for (
auto arg : llvm::make_isa_range<BlockArgument>(op->getOperands())) {
1287 if (!argDominates(arg, op))
1293 for (
Region ®ion : op->getRegions()) {
1296 for (
auto arg : llvm::make_isa_range<BlockArgument>(capturedValues)) {
1297 if (!argDominates(arg, op))
1314 while (region && region != ancestorRegion) {
1336 "insertion point does not dominate op");
1342 op,
"cannot move operation across isolated-from-above region");
1350 options.omitUsesFromAbove =
false;
1352 options.omitBlockArguments =
true;
1353 bool dependsOnSideEffectingOp =
false;
1357 if (sliceBoundaryOp == op)
1365 if (!
isPure(sliceBoundaryOp)) {
1366 dependsOnSideEffectingOp =
true;
1373 assert(
result.succeeded() &&
"expected a backward slice");
1377 if (dependsOnSideEffectingOp) {
1379 op,
"cannot move operation with side-effecting dependencies");
1383 if (
slice.contains(insertionPoint)) {
1386 "cannot move dependencies before operation in backward slice of op");
1395 badOp,
"moving op would break dominance for block argument operand");
1419 for (
auto value : values) {
1423 if (isa<BlockArgument>(value)) {
1426 "unsupported case of moving block argument before insertion point");
1437 "cannot move value definition across isolated-from-above region");
1443 if (!dominance.
dominates(insertionBlock, definingBlock)) {
1446 "insertion point block does not dominate the value's defining "
1449 prunedValues.push_back(value);
1457 options.omitUsesFromAbove =
false;
1461 options.omitBlockArguments =
true;
1462 bool dependsOnSideEffectingOp =
false;
1470 if (!
isPure(sliceBoundaryOp)) {
1471 dependsOnSideEffectingOp =
true;
1477 for (
auto value : prunedValues) {
1479 assert(
result.succeeded() &&
"expected a backward slice");
1484 if (dependsOnSideEffectingOp) {
1486 insertionPoint,
"cannot move value definitions with side-effecting "
1487 "operations in the slice");
1491 if (
slice.contains(insertionPoint)) {
1494 "cannot move dependencies before operation in backward slice of op");
1508 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)
static SmallVector< Value > slice(OpBuilder &b, Location loc, Value value, VectorType nativeType)
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.