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);
838 if (!isa<BranchOpInterface>((*it)->getTerminator()))
857 if (newArguments.empty())
861 unsigned numLists = newArguments.size();
862 unsigned numArgs = newArguments[0].size();
872 for (
unsigned j = 0;
j < numArgs; ++
j) {
873 Value newArg = newArguments[0][
j];
874 firstValueToIdx.try_emplace(newArg,
j);
878 for (
unsigned j = 0;
j < numArgs; ++
j) {
890 unsigned k = firstValueToIdx[newArguments[0][
j]];
894 bool shouldReplaceJ =
true;
899 for (
unsigned i = 1; i < numLists; ++i)
901 shouldReplaceJ && (newArguments[i][k] == newArguments[i][
j]);
908 for (
unsigned i = 0; i < numLists; ++i)
909 for (
unsigned j = 0;
j < numArgs; ++
j)
910 if (!idxToReplacement.contains(
j))
911 newArgumentsPruned[i].push_back(newArguments[i][
j]);
915 for (
auto [idx, arg] : llvm::enumerate(block->
getArguments())) {
916 if (idxToReplacement.contains(idx)) {
919 block->
getArgument(numOldArguments + idxToReplacement[idx]);
921 toErase.push_back(numOldArguments + idx);
926 for (
unsigned idxToErase : llvm::reverse(toErase))
928 return newArgumentsPruned;
931LogicalResult BlockMergeCluster::merge(RewriterBase &rewriter) {
933 if (blocksToMerge.empty())
936 Block *leaderBlock = leaderData.block;
937 if (!operandsToMerge.empty()) {
949 SmallVector<Block::iterator, 2> blockIterators;
950 blockIterators.reserve(blocksToMerge.size() + 1);
951 blockIterators.push_back(leaderBlock->
begin());
952 for (
Block *mergeBlock : blocksToMerge)
953 blockIterators.push_back(mergeBlock->begin());
956 SmallVector<SmallVector<Value, 8>, 2> newArguments(
957 1 + blocksToMerge.size(),
958 SmallVector<Value, 8>(operandsToMerge.size()));
959 unsigned curOpIndex = 0;
961 for (
const auto &it : llvm::enumerate(operandsToMerge)) {
962 unsigned nextOpOffset = it.value().first - curOpIndex;
963 curOpIndex = it.value().first;
966 for (
unsigned i = 0, e = blockIterators.size(); i != e; ++i) {
968 std::advance(blockIter, nextOpOffset);
969 auto &operand = blockIter->getOpOperand(it.value().second);
970 newArguments[i][it.index()] = operand.get();
974 Value operandVal = operand.get();
983 numOldArguments, leaderBlock);
986 auto updatePredecessors = [&](
Block *block,
unsigned clusterIndex) {
988 predIt != predE; ++predIt) {
989 auto branch = cast<BranchOpInterface>((*predIt)->getTerminator());
990 unsigned succIndex = predIt.getSuccessorIndex();
991 branch.getSuccessorOperands(succIndex).append(
992 newArguments[clusterIndex]);
995 updatePredecessors(leaderBlock, 0);
996 for (
unsigned i = 0, e = blocksToMerge.size(); i != e; ++i)
997 updatePredecessors(blocksToMerge[i], i + 1);
1001 for (
Block *block : blocksToMerge) {
1020 for (
Block &block : llvm::drop_begin(region, 1))
1021 matchingSuccessors[block.
getSuccessors()].push_back(&block);
1023 bool mergedAnyBlocks =
false;
1025 if (blocks.size() == 1)
1029 for (
Block *block : blocks) {
1030 BlockEquivalenceData data(block);
1034 bool hasNonEmptyRegion = llvm::any_of(*block, [](
Operation &op) {
1036 [](
Region ®ion) { return !region.empty(); });
1038 if (hasNonEmptyRegion)
1043 bool argHasExternalUsers = llvm::any_of(
1045 return arg.isUsedOutsideOfBlock(block);
1047 if (argHasExternalUsers)
1051 bool addedToCluster =
false;
1052 for (
auto &cluster : clusters)
1053 if ((addedToCluster = succeeded(cluster.addToCluster(data))))
1055 if (!addedToCluster)
1056 clusters.emplace_back(std::move(data));
1058 for (
auto &cluster : clusters)
1059 mergedAnyBlocks |= succeeded(cluster.merge(rewriter));
1062 return success(mergedAnyBlocks);
1069 llvm::SmallSetVector<Region *, 1> worklist;
1070 for (
auto ®ion : regions)
1071 worklist.insert(®ion);
1072 bool anyChanged =
false;
1073 while (!worklist.empty()) {
1074 Region *region = worklist.pop_back_val();
1076 worklist.insert(region);
1081 for (
Block &block : *region)
1082 for (
auto &op : block)
1083 for (
auto &nestedRegion : op.getRegions())
1084 worklist.insert(&nestedRegion);
1097 for (
auto [argIdx, blockOperand] : llvm::enumerate(block.
getArguments())) {
1098 bool sameArg =
true;
1105 predIt != predE; ++predIt) {
1106 auto branch = dyn_cast<BranchOpInterface>((*predIt)->getTerminator());
1111 unsigned succIndex = predIt.getSuccessorIndex();
1123 Value operandValue = succOperands[argIdx];
1125 commonValue = operandValue;
1128 if (operandValue != commonValue) {
1135 if (commonValue && sameArg) {
1136 argsToErase.push_back(argIdx);
1144 for (
size_t argIdx : llvm::reverse(argsToErase)) {
1149 predIt != predE; ++predIt) {
1150 auto branch = cast<BranchOpInterface>((*predIt)->getTerminator());
1151 unsigned succIndex = predIt.getSuccessorIndex();
1153 succOperands.
erase(argIdx);
1156 return success(!argsToErase.empty());
1186 llvm::SmallSetVector<Region *, 1> worklist;
1187 for (
Region ®ion : regions)
1188 worklist.insert(®ion);
1189 bool anyChanged =
false;
1190 while (!worklist.empty()) {
1191 Region *region = worklist.pop_back_val();
1194 for (
Block &block : *region) {
1199 for (
Region &nestedRegion : op.getRegions())
1200 worklist.insert(&nestedRegion);
1218 bool eliminatedOpsOrArgs = succeeded(
runRegionDCE(rewriter, regions));
1219 bool mergedIdenticalBlocks =
false;
1220 bool droppedRedundantArguments =
false;
1223 droppedRedundantArguments =
1226 return success(eliminatedBlocks || eliminatedOpsOrArgs ||
1227 mergedIdenticalBlocks || droppedRedundantArguments);
1252 bool dominates = argBlock == insertionBlock ||
1253 dominance.
dominates(argBlock, insertionBlock);
1254 if (!dominates && failingOp)
1261 for (
Value operand : op->getOperands()) {
1262 auto arg = dyn_cast<BlockArgument>(operand);
1265 if (!argDominates(arg, op))
1271 for (
Region ®ion : op->getRegions()) {
1274 for (
Value val : capturedValues) {
1275 auto arg = dyn_cast<BlockArgument>(val);
1278 if (!argDominates(arg, op))
1295 while (region && region != ancestorRegion) {
1317 "insertion point does not dominate op");
1323 op,
"cannot move operation across isolated-from-above region");
1331 options.omitUsesFromAbove =
false;
1333 options.omitBlockArguments =
true;
1334 bool dependsOnSideEffectingOp =
false;
1338 if (sliceBoundaryOp == op)
1346 if (!
isPure(sliceBoundaryOp)) {
1347 dependsOnSideEffectingOp =
true;
1354 assert(
result.succeeded() &&
"expected a backward slice");
1358 if (dependsOnSideEffectingOp) {
1360 op,
"cannot move operation with side-effecting dependencies");
1364 if (slice.contains(insertionPoint)) {
1367 "cannot move dependencies before operation in backward slice of op");
1376 badOp,
"moving op would break dominance for block argument operand");
1400 for (
auto value : values) {
1404 if (isa<BlockArgument>(value)) {
1407 "unsupported case of moving block argument before insertion point");
1418 "cannot move value definition across isolated-from-above region");
1424 if (!dominance.
dominates(insertionBlock, definingBlock)) {
1427 "insertion point block does not dominate the value's defining "
1430 prunedValues.push_back(value);
1438 options.omitUsesFromAbove =
false;
1442 options.omitBlockArguments =
true;
1443 bool dependsOnSideEffectingOp =
false;
1451 if (!
isPure(sliceBoundaryOp)) {
1452 dependsOnSideEffectingOp =
true;
1458 for (
auto value : prunedValues) {
1460 assert(
result.succeeded() &&
"expected a backward slice");
1465 if (dependsOnSideEffectingOp) {
1467 insertionPoint,
"cannot move value definitions with side-effecting "
1468 "operations in the slice");
1472 if (slice.contains(insertionPoint)) {
1475 "cannot move dependencies before operation in backward slice of op");
1489 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 ableToUpdatePredOperands(Block *block)
Returns true if the predecessor terminators of the given block can not have their operands updated.
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 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.
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.