22 #include "llvm/ADT/DepthFirstIterator.h"
23 #include "llvm/ADT/PostOrderIterator.h"
24 #include "llvm/ADT/STLExtras.h"
25 #include "llvm/ADT/SmallSet.h"
34 for (
auto &use : llvm::make_early_inc_range(orig.
getUses())) {
35 if (region.
isAncestor(use.getOwner()->getParentRegion()))
43 "expected isolation limit to be an ancestor of the given region");
50 properAncestors.insert(reg);
56 if (properAncestors.count(operand.get().getParentRegion()))
63 for (
Region ®ion : regions)
70 values.insert(operand->
get());
76 for (
Region ®ion : regions)
92 std::deque<Value> worklist(initialCapturedValues.begin(),
93 initialCapturedValues.end());
99 while (!worklist.empty()) {
100 Value currValue = worklist.front();
101 worklist.pop_front();
102 if (visited.count(currValue))
104 visited.insert(currValue);
107 if (!definingOp || visitedOps.count(definingOp)) {
108 finalCapturedValues.insert(currValue);
111 visitedOps.insert(definingOp);
113 if (!cloneOperationIntoRegion(definingOp)) {
116 finalCapturedValues.insert(currValue);
123 if (visited.count(operand))
125 worklist.push_back(operand);
127 clonedOperations.push_back(definingOp);
144 for (
auto value : finalCapturedValues) {
145 newArgTypes.push_back(value.getType());
146 newArgLocs.push_back(value.getLoc());
150 Block *newEntryBlock =
157 return use.getOwner()->getBlock()->getParent() == ®ion;
159 for (
auto [arg, capturedVal] :
160 llvm::zip(newEntryBlockArgs.take_back(finalCapturedValues.size()),
161 finalCapturedValues)) {
162 map.
map(capturedVal, arg);
166 for (
auto *clonedOp : clonedOperations) {
171 entryBlock, newEntryBlock,
173 return llvm::to_vector(finalCapturedValues);
186 llvm::df_iterator_default_set<Block *, 16> reachable;
188 bool erasedDeadBlocks =
false;
191 worklist.reserve(regions.size());
192 for (
Region ®ion : regions)
193 worklist.push_back(®ion);
194 while (!worklist.empty()) {
195 Region *region = worklist.pop_back_val();
200 if (std::next(region->
begin()) == region->
end()) {
202 for (
Region ®ion : op.getRegions())
203 worklist.push_back(®ion);
209 for (
Block *block : depth_first_ext(®ion->
front(), reachable))
214 for (
Block &block : llvm::make_early_inc_range(*region)) {
215 if (!reachable.count(&block)) {
216 block.dropAllDefinedValueUses();
218 erasedDeadBlocks =
true;
224 for (
Region ®ion : op.getRegions())
225 worklist.push_back(®ion);
229 return success(erasedDeadBlocks);
248 bool wasProvenLive(
Value value) {
251 if (
OpResult result = dyn_cast<OpResult>(value))
252 return wasProvenLive(result.getOwner());
253 return wasProvenLive(cast<BlockArgument>(value));
255 bool wasProvenLive(
BlockArgument arg) {
return liveValues.count(arg); }
256 void setProvedLive(
Value value) {
259 if (
OpResult result = dyn_cast<OpResult>(value))
260 return setProvedLive(result.getOwner());
261 setProvedLive(cast<BlockArgument>(value));
264 changed |= liveValues.insert(arg).second;
268 bool wasProvenLive(
Operation *op) {
return liveOps.count(op); }
269 void setProvedLive(
Operation *op) { changed |= liveOps.insert(op).second; }
272 void resetChanged() { changed =
false; }
273 bool hasChanged() {
return changed; }
276 bool changed =
false;
297 if (BranchOpInterface branchInterface = dyn_cast<BranchOpInterface>(owner))
298 if (
auto arg = branchInterface.getSuccessorBlockArgument(operandIndex))
299 return !liveMap.wasProvenLive(*arg);
307 if (isUseSpeciallyKnownDead(use, liveMap))
309 return liveMap.wasProvenLive(use.getOwner());
312 liveMap.setProvedLive(value);
319 liveMap.setProvedLive(op);
322 BranchOpInterface branchInterface = dyn_cast<BranchOpInterface>(op);
323 if (!branchInterface) {
326 liveMap.setProvedLive(arg);
334 branchInterface.getSuccessorOperands(i);
351 if (liveMap.wasProvenLive(op))
356 return liveMap.setProvedLive(op);
367 for (
Block *block : llvm::post_order(®ion.
front())) {
370 for (
Operation &op : llvm::reverse(block->getOperations()))
377 if (block->isEntryBlock())
380 for (
Value value : block->getArguments()) {
381 if (!liveMap.wasProvenLive(value))
389 BranchOpInterface branchOp = dyn_cast<BranchOpInterface>(terminator);
394 succI < succE; succI++) {
399 unsigned succ = succE - succI - 1;
403 for (
unsigned argI = 0, argE = succOperands.
size(); argI < argE; ++argI) {
406 unsigned arg = argE - argI - 1;
407 if (!liveMap.wasProvenLive(successor->
getArgument(arg)))
408 succOperands.
erase(arg);
416 bool erasedAnything =
false;
417 for (
Region ®ion : regions) {
420 bool hasSingleBlock = llvm::hasSingleElement(region);
427 for (
Block *block : llvm::post_order(®ion.
front())) {
431 llvm::make_early_inc_range(llvm::reverse(block->getOperations()))) {
432 if (!liveMap.wasProvenLive(&childOp)) {
433 erasedAnything =
true;
434 childOp.dropAllUses();
437 erasedAnything |= succeeded(
446 block.eraseArguments(
447 [&](
BlockArgument arg) {
return !liveMap.wasProvenLive(arg); });
450 return success(erasedAnything);
474 liveMap.resetChanged();
476 for (
Region ®ion : regions)
478 }
while (liveMap.hasChanged());
507 struct BlockEquivalenceData {
508 BlockEquivalenceData(
Block *block);
512 unsigned getOrderOf(
Value value)
const;
517 llvm::hash_code hash;
525 BlockEquivalenceData::BlockEquivalenceData(
Block *block)
526 : block(block), hash(0) {
529 if (
unsigned numResults = op.getNumResults()) {
530 opOrderIndex.try_emplace(&op, orderIt);
531 orderIt += numResults;
537 hash = llvm::hash_combine(hash, opHash);
541 unsigned BlockEquivalenceData::getOrderOf(
Value value)
const {
542 assert(value.
getParentBlock() == block &&
"expected value of this block");
549 OpResult result = cast<OpResult>(value);
551 assert(opOrderIt != opOrderIndex.end() &&
"expected op to have an order");
560 class BlockMergeCluster {
562 BlockMergeCluster(BlockEquivalenceData &&leaderData)
563 : leaderData(std::move(leaderData)) {}
567 LogicalResult addToCluster(BlockEquivalenceData &blockData);
574 BlockEquivalenceData leaderData;
577 llvm::SmallSetVector<Block *, 1> blocksToMerge;
581 std::set<std::pair<int, int>> operandsToMerge;
585 LogicalResult BlockMergeCluster::addToCluster(BlockEquivalenceData &blockData) {
586 if (leaderData.hash != blockData.hash)
588 Block *leaderBlock = leaderData.block, *mergeBlock = blockData.block;
594 auto lhsIt = leaderBlock->
begin(), lhsE = leaderBlock->
end();
595 auto rhsIt = blockData.block->begin(), rhsE = blockData.block->end();
596 for (
int opI = 0; lhsIt != lhsE && rhsIt != rhsE; ++lhsIt, ++rhsIt, ++opI) {
598 if (!OperationEquivalence::isEquivalentTo(
599 &*lhsIt, &*rhsIt, OperationEquivalence::ignoreValueEquivalence,
601 OperationEquivalence::Flags::IgnoreLocations))
606 auto lhsOperands = lhsIt->getOperands(), rhsOperands = rhsIt->getOperands();
607 for (
int operand : llvm::seq<int>(0, lhsIt->getNumOperands())) {
608 Value lhsOperand = lhsOperands[operand];
609 Value rhsOperand = rhsOperands[operand];
610 if (lhsOperand == rhsOperand)
619 if (lhsIsInBlock != rhsIsInBlock)
629 auto isValidSuccessorArg = [](
Block *block,
Value operand) {
630 if (operand.getDefiningOp() !=
631 operand.getParentBlock()->getTerminator())
634 operand.getParentBlock());
637 if (!isValidSuccessorArg(leaderBlock, lhsOperand) ||
638 !isValidSuccessorArg(mergeBlock, rhsOperand))
641 mismatchedOperands.emplace_back(opI, operand);
647 if (leaderData.getOrderOf(lhsOperand) != blockData.getOrderOf(rhsOperand))
657 if (rhsIt->isUsedOutsideOfBlock(mergeBlock) ||
658 lhsIt->isUsedOutsideOfBlock(leaderBlock)) {
663 if (lhsIt != lhsE || rhsIt != rhsE)
667 operandsToMerge.insert(mismatchedOperands.begin(), mismatchedOperands.end());
668 blocksToMerge.insert(blockData.block);
676 if (!isa<BranchOpInterface>((*it)->getTerminator()))
695 if (newArguments.empty())
699 unsigned numLists = newArguments.size();
700 unsigned numArgs = newArguments[0].size();
710 for (
unsigned j = 0;
j < numArgs; ++
j) {
711 Value newArg = newArguments[0][
j];
712 firstValueToIdx.try_emplace(newArg,
j);
716 for (
unsigned j = 0;
j < numArgs; ++
j) {
728 unsigned k = firstValueToIdx[newArguments[0][
j]];
732 bool shouldReplaceJ =
true;
733 unsigned replacement = k;
737 for (
unsigned i = 1; i < numLists; ++i)
739 shouldReplaceJ && (newArguments[i][k] == newArguments[i][
j]);
742 idxToReplacement[
j] = replacement;
746 for (
unsigned i = 0; i < numLists; ++i)
747 for (
unsigned j = 0;
j < numArgs; ++
j)
748 if (!idxToReplacement.contains(
j))
749 newArgumentsPruned[i].push_back(newArguments[i][
j]);
754 if (idxToReplacement.contains(idx)) {
757 block->
getArgument(numOldArguments + idxToReplacement[idx]);
759 toErase.push_back(numOldArguments + idx);
764 for (
unsigned idxToErase : llvm::reverse(toErase))
766 return newArgumentsPruned;
769 LogicalResult BlockMergeCluster::merge(
RewriterBase &rewriter) {
771 if (blocksToMerge.empty())
774 Block *leaderBlock = leaderData.block;
775 if (!operandsToMerge.empty()) {
788 blockIterators.reserve(blocksToMerge.size() + 1);
789 blockIterators.push_back(leaderBlock->
begin());
790 for (
Block *mergeBlock : blocksToMerge)
791 blockIterators.push_back(mergeBlock->begin());
795 1 + blocksToMerge.size(),
797 unsigned curOpIndex = 0;
800 unsigned nextOpOffset = it.value().first - curOpIndex;
801 curOpIndex = it.value().first;
804 for (
unsigned i = 0, e = blockIterators.size(); i != e; ++i) {
806 std::advance(blockIter, nextOpOffset);
807 auto &operand = blockIter->getOpOperand(it.value().second);
808 newArguments[i][it.index()] = operand.get();
812 Value operandVal = operand.get();
821 numOldArguments, leaderBlock);
824 auto updatePredecessors = [&](
Block *block,
unsigned clusterIndex) {
826 predIt != predE; ++predIt) {
827 auto branch = cast<BranchOpInterface>((*predIt)->getTerminator());
828 unsigned succIndex = predIt.getSuccessorIndex();
829 branch.getSuccessorOperands(succIndex).append(
830 newArguments[clusterIndex]);
833 updatePredecessors(leaderBlock, 0);
834 for (
unsigned i = 0, e = blocksToMerge.size(); i != e; ++i)
835 updatePredecessors(blocksToMerge[i], i + 1);
839 for (
Block *block : blocksToMerge) {
851 if (region.
empty() || llvm::hasSingleElement(region))
858 for (
Block &block : llvm::drop_begin(region, 1))
861 bool mergedAnyBlocks =
false;
863 if (blocks.size() == 1)
867 for (
Block *block : blocks) {
868 BlockEquivalenceData data(block);
872 bool hasNonEmptyRegion = llvm::any_of(*block, [](
Operation &op) {
874 [](
Region ®ion) { return !region.empty(); });
876 if (hasNonEmptyRegion)
881 bool argHasExternalUsers = llvm::any_of(
883 return arg.isUsedOutsideOfBlock(block);
885 if (argHasExternalUsers)
889 bool addedToCluster =
false;
890 for (
auto &cluster : clusters)
891 if ((addedToCluster = succeeded(cluster.addToCluster(data))))
894 clusters.emplace_back(std::move(data));
896 for (
auto &cluster : clusters)
897 mergedAnyBlocks |= succeeded(cluster.merge(rewriter));
900 return success(mergedAnyBlocks);
907 llvm::SmallSetVector<Region *, 1> worklist;
908 for (
auto ®ion : regions)
909 worklist.insert(®ion);
910 bool anyChanged =
false;
911 while (!worklist.empty()) {
912 Region *region = worklist.pop_back_val();
914 worklist.insert(region);
919 for (
Block &block : *region)
920 for (
auto &op : block)
921 for (
auto &nestedRegion : op.getRegions())
922 worklist.insert(&nestedRegion);
925 return success(anyChanged);
943 predIt != predE; ++predIt) {
944 auto branch = dyn_cast<BranchOpInterface>((*predIt)->getTerminator());
949 unsigned succIndex = predIt.getSuccessorIndex();
953 commonValue = branchOperands[argIdx];
956 if (branchOperands[argIdx] != commonValue) {
963 if (commonValue && sameArg) {
964 argsToErase.push_back(argIdx);
972 for (
size_t argIdx : llvm::reverse(argsToErase)) {
977 predIt != predE; ++predIt) {
978 auto branch = cast<BranchOpInterface>((*predIt)->getTerminator());
979 unsigned succIndex = predIt.getSuccessorIndex();
981 succOperands.
erase(argIdx);
984 return success(!argsToErase.empty());
1014 llvm::SmallSetVector<Region *, 1> worklist;
1015 for (
Region ®ion : regions)
1016 worklist.insert(®ion);
1017 bool anyChanged =
false;
1018 while (!worklist.empty()) {
1019 Region *region = worklist.pop_back_val();
1022 for (
Block &block : *region) {
1027 for (
Region &nestedRegion : op.getRegions())
1028 worklist.insert(&nestedRegion);
1031 return success(anyChanged);
1046 bool eliminatedOpsOrArgs = succeeded(
runRegionDCE(rewriter, regions));
1047 bool mergedIdenticalBlocks =
false;
1048 bool droppedRedundantArguments =
false;
1051 droppedRedundantArguments =
1054 return success(eliminatedBlocks || eliminatedOpsOrArgs ||
1055 mergedIdenticalBlocks || droppedRedundantArguments);
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 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 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 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 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 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()
pred_iterator pred_begin()
SuccessorRange getSuccessors()
iterator_range< pred_iterator > getPredecessors()
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
BlockArgListType getArguments()
void eraseArgument(unsigned index)
Erase the argument at 'index' and remove it from the argument list.
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.
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.
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes=std::nullopt, ArrayRef< Location > locs=std::nullopt)
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
This class represents an operand of an operation.
unsigned getOperandNumber()
Return which operand this is in the OpOperand list of the Operation.
This is a value defined by a result of an operation.
unsigned getResultNumber() const
Returns the number of this result.
This class provides the API for ops that are known to be terminators.
Operation is the basic unit of execution within MLIR.
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Block * getSuccessor(unsigned index)
unsigned getNumSuccessors()
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
MutableArrayRef< OpOperand > getOpOperands()
operand_range getOperands()
Returns an iterator on the underlying Value's.
SuccessorRange getSuccessors()
result_range getResults()
Implement a predecessor iterator for blocks.
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.
BlockListType & getBlocks()
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.
void replaceAllUsesWith(Value from, Value to)
Find uses of from and replace them with to.
void mergeBlocks(Block *source, Block *dest, ValueRange argValues=std::nullopt)
Inline the operations of block 'source' into the end of block 'dest'.
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
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.
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.
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.
OperandRange getForwardedOperands() const
Get the range of operands that are simply forwarded to the successor.
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.
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Include the generated interface declarations.
void replaceAllUsesInRegionWith(Value orig, Value replacement, Region ®ion)
Replace all uses of orig within the given region with replacement.
bool computeTopologicalSorting(MutableArrayRef< Operation * > ops, function_ref< bool(Value, Operation *)> isOperandReady=nullptr)
Compute a topological ordering of the given ops.
bool wouldOpBeTriviallyDead(Operation *op)
Return true if the given operation would be dead if unused, and has no side effects on memory that wo...
LogicalResult eraseUnreachableBlocks(RewriterBase &rewriter, MutableArrayRef< Region > regions)
Erase the unreachable blocks within the provided regions.
SmallVector< Value > makeRegionIsolatedFromAbove(RewriterBase &rewriter, Region ®ion, llvm::function_ref< bool(Operation *)> cloneOperationIntoRegion=[](Operation *) { return false;})
Make a region isolated from above.
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.
LogicalResult simplifyRegions(RewriterBase &rewriter, MutableArrayRef< Region > regions, bool mergeBlocks=true)
Run a set of structural simplifications over the given regions.
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...
static llvm::hash_code ignoreHashValue(Value)
Helper that can be used with computeHash above to ignore operation operands/result mapping.
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.