#include "mlir/Transforms/RegionUtils.h"
#include "mlir/Analysis/TopologicalSortUtils.h"
#include "mlir/IR/Block.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/IRMapping.h"
#include "mlir/IR/Operation.h"
#include "mlir/IR/PatternMatch.h"
#include "mlir/IR/RegionGraphTraits.h"
#include "mlir/IR/Value.h"
#include "mlir/Interfaces/ControlFlowInterfaces.h"
#include "mlir/Interfaces/SideEffectInterfaces.h"
#include "mlir/Support/LogicalResult.h"
#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallSet.h"
#include <deque>
#include <iterator>
using namespace mlir;
void mlir::replaceAllUsesInRegionWith(Value orig, Value replacement,
Region ®ion) {
for (auto &use : llvm::make_early_inc_range(orig.getUses())) {
if (region.isAncestor(use.getOwner()->getParentRegion()))
use.set(replacement);
}
}
void mlir::visitUsedValuesDefinedAbove(
Region ®ion, Region &limit, function_ref<void(OpOperand *)> callback) {
assert(limit.isAncestor(®ion) &&
"expected isolation limit to be an ancestor of the given region");
SmallPtrSet<Region *, 4> properAncestors;
for (auto *reg = limit.getParentRegion(); reg != nullptr;
reg = reg->getParentRegion()) {
properAncestors.insert(reg);
}
region.walk([callback, &properAncestors](Operation *op) {
for (OpOperand &operand : op->getOpOperands())
if (properAncestors.count(operand.get().getParentRegion()))
callback(&operand);
});
}
void mlir::visitUsedValuesDefinedAbove(
MutableArrayRef<Region> regions, function_ref<void(OpOperand *)> callback) {
for (Region ®ion : regions)
visitUsedValuesDefinedAbove(region, region, callback);
}
void mlir::getUsedValuesDefinedAbove(Region ®ion, Region &limit,
SetVector<Value> &values) {
visitUsedValuesDefinedAbove(region, limit, [&](OpOperand *operand) {
values.insert(operand->get());
});
}
void mlir::getUsedValuesDefinedAbove(MutableArrayRef<Region> regions,
SetVector<Value> &values) {
for (Region ®ion : regions)
getUsedValuesDefinedAbove(region, region, values);
}
SmallVector<Value> mlir::makeRegionIsolatedFromAbove(
RewriterBase &rewriter, Region ®ion,
llvm::function_ref<bool(Operation *)> cloneOperationIntoRegion) {
llvm::SetVector<Value> initialCapturedValues;
mlir::getUsedValuesDefinedAbove(region, initialCapturedValues);
std::deque<Value> worklist(initialCapturedValues.begin(),
initialCapturedValues.end());
llvm::DenseSet<Value> visited;
llvm::DenseSet<Operation *> visitedOps;
llvm::SetVector<Value> finalCapturedValues;
SmallVector<Operation *> clonedOperations;
while (!worklist.empty()) {
Value currValue = worklist.front();
worklist.pop_front();
if (visited.count(currValue))
continue;
visited.insert(currValue);
Operation *definingOp = currValue.getDefiningOp();
if (!definingOp || visitedOps.count(definingOp)) {
finalCapturedValues.insert(currValue);
continue;
}
visitedOps.insert(definingOp);
if (!cloneOperationIntoRegion(definingOp)) {
finalCapturedValues.insert(currValue);
continue;
}
for (Value operand : definingOp->getOperands()) {
if (visited.count(operand))
continue;
worklist.push_back(operand);
}
clonedOperations.push_back(definingOp);
}
mlir::computeTopologicalSorting(clonedOperations);
OpBuilder::InsertionGuard g(rewriter);
Block *entryBlock = ®ion.front();
SmallVector<Type> newArgTypes =
llvm::to_vector(entryBlock->getArgumentTypes());
SmallVector<Location> newArgLocs = llvm::to_vector(llvm::map_range(
entryBlock->getArguments(), [](BlockArgument b) { return b.getLoc(); }));
for (auto value : finalCapturedValues) {
newArgTypes.push_back(value.getType());
newArgLocs.push_back(value.getLoc());
}
Block *newEntryBlock =
rewriter.createBlock(®ion, region.begin(), newArgTypes, newArgLocs);
auto newEntryBlockArgs = newEntryBlock->getArguments();
IRMapping map;
auto replaceIfFn = [&](OpOperand &use) {
return use.getOwner()->getBlock()->getParent() == ®ion;
};
for (auto [arg, capturedVal] :
llvm::zip(newEntryBlockArgs.take_back(finalCapturedValues.size()),
finalCapturedValues)) {
map.map(capturedVal, arg);
rewriter.replaceUsesWithIf(capturedVal, arg, replaceIfFn);
}
rewriter.setInsertionPointToStart(newEntryBlock);
for (auto *clonedOp : clonedOperations) {
Operation *newOp = rewriter.clone(*clonedOp, map);
rewriter.replaceOpUsesWithIf(clonedOp, newOp->getResults(), replaceIfFn);
}
rewriter.mergeBlocks(
entryBlock, newEntryBlock,
newEntryBlock->getArguments().take_front(entryBlock->getNumArguments()));
return llvm::to_vector(finalCapturedValues);
}
LogicalResult mlir::eraseUnreachableBlocks(RewriterBase &rewriter,
MutableArrayRef<Region> regions) {
llvm::df_iterator_default_set<Block *, 16> reachable;
bool erasedDeadBlocks = false;
SmallVector<Region *, 1> worklist;
worklist.reserve(regions.size());
for (Region ®ion : regions)
worklist.push_back(®ion);
while (!worklist.empty()) {
Region *region = worklist.pop_back_val();
if (region->empty())
continue;
if (std::next(region->begin()) == region->end()) {
for (Operation &op : region->front())
for (Region ®ion : op.getRegions())
worklist.push_back(®ion);
continue;
}
reachable.clear();
for (Block *block : depth_first_ext(®ion->front(), reachable))
(void)block ;
for (Block &block : llvm::make_early_inc_range(*region)) {
if (!reachable.count(&block)) {
block.dropAllDefinedValueUses();
rewriter.eraseBlock(&block);
erasedDeadBlocks = true;
continue;
}
for (Operation &op : block)
for (Region ®ion : op.getRegions())
worklist.push_back(®ion);
}
}
return success(erasedDeadBlocks);
}
namespace {
class LiveMap {
public:
bool wasProvenLive(Value value) {
if (OpResult result = dyn_cast<OpResult>(value))
return wasProvenLive(result.getOwner());
return wasProvenLive(cast<BlockArgument>(value));
}
bool wasProvenLive(BlockArgument arg) { return liveValues.count(arg); }
void setProvedLive(Value value) {
if (OpResult result = dyn_cast<OpResult>(value))
return setProvedLive(result.getOwner());
setProvedLive(cast<BlockArgument>(value));
}
void setProvedLive(BlockArgument arg) {
changed |= liveValues.insert(arg).second;
}
bool wasProvenLive(Operation *op) { return liveOps.count(op); }
void setProvedLive(Operation *op) { changed |= liveOps.insert(op).second; }
void resetChanged() { changed = false; }
bool hasChanged() { return changed; }
private:
bool changed = false;
DenseSet<Value> liveValues;
DenseSet<Operation *> liveOps;
};
}
static bool isUseSpeciallyKnownDead(OpOperand &use, LiveMap &liveMap) {
Operation *owner = use.getOwner();
unsigned operandIndex = use.getOperandNumber();
if (owner->hasTrait<OpTrait::IsTerminator>()) {
if (BranchOpInterface branchInterface = dyn_cast<BranchOpInterface>(owner))
if (auto arg = branchInterface.getSuccessorBlockArgument(operandIndex))
return !liveMap.wasProvenLive(*arg);
return false;
}
return false;
}
static void processValue(Value value, LiveMap &liveMap) {
bool provedLive = llvm::any_of(value.getUses(), [&](OpOperand &use) {
if (isUseSpeciallyKnownDead(use, liveMap))
return false;
return liveMap.wasProvenLive(use.getOwner());
});
if (provedLive)
liveMap.setProvedLive(value);
}
static void propagateLiveness(Region ®ion, LiveMap &liveMap);
static void propagateTerminatorLiveness(Operation *op, LiveMap &liveMap) {
liveMap.setProvedLive(op);
BranchOpInterface branchInterface = dyn_cast<BranchOpInterface>(op);
if (!branchInterface) {
for (Block *successor : op->getSuccessors())
for (BlockArgument arg : successor->getArguments())
liveMap.setProvedLive(arg);
return;
}
for (unsigned i = 0, e = op->getNumSuccessors(); i != e; ++i) {
SuccessorOperands successorOperands =
branchInterface.getSuccessorOperands(i);
for (unsigned opI = 0, opE = successorOperands.getProducedOperandCount();
opI != opE; ++opI)
liveMap.setProvedLive(op->getSuccessor(i)->getArgument(opI));
}
}
static void propagateLiveness(Operation *op, LiveMap &liveMap) {
for (Region ®ion : op->getRegions())
propagateLiveness(region, liveMap);
if (op->hasTrait<OpTrait::IsTerminator>())
return propagateTerminatorLiveness(op, liveMap);
if (liveMap.wasProvenLive(op))
return;
if (!wouldOpBeTriviallyDead(op))
return liveMap.setProvedLive(op);
for (Value value : op->getResults())
processValue(value, liveMap);
}
static void propagateLiveness(Region ®ion, LiveMap &liveMap) {
if (region.empty())
return;
for (Block *block : llvm::post_order(®ion.front())) {
for (Operation &op : llvm::reverse(block->getOperations()))
propagateLiveness(&op, liveMap);
if (block->isEntryBlock())
continue;
for (Value value : block->getArguments()) {
if (!liveMap.wasProvenLive(value))
processValue(value, liveMap);
}
}
}
static void eraseTerminatorSuccessorOperands(Operation *terminator,
LiveMap &liveMap) {
BranchOpInterface branchOp = dyn_cast<BranchOpInterface>(terminator);
if (!branchOp)
return;
for (unsigned succI = 0, succE = terminator->getNumSuccessors();
succI < succE; succI++) {
unsigned succ = succE - succI - 1;
SuccessorOperands succOperands = branchOp.getSuccessorOperands(succ);
Block *successor = terminator->getSuccessor(succ);
for (unsigned argI = 0, argE = succOperands.size(); argI < argE; ++argI) {
unsigned arg = argE - argI - 1;
if (!liveMap.wasProvenLive(successor->getArgument(arg)))
succOperands.erase(arg);
}
}
}
static LogicalResult deleteDeadness(RewriterBase &rewriter,
MutableArrayRef<Region> regions,
LiveMap &liveMap) {
bool erasedAnything = false;
for (Region ®ion : regions) {
if (region.empty())
continue;
bool hasSingleBlock = llvm::hasSingleElement(region);
for (Block *block : llvm::post_order(®ion.front())) {
if (!hasSingleBlock)
eraseTerminatorSuccessorOperands(block->getTerminator(), liveMap);
for (Operation &childOp :
llvm::make_early_inc_range(llvm::reverse(block->getOperations()))) {
if (!liveMap.wasProvenLive(&childOp)) {
erasedAnything = true;
childOp.dropAllUses();
rewriter.eraseOp(&childOp);
} else {
erasedAnything |= succeeded(
deleteDeadness(rewriter, childOp.getRegions(), liveMap));
}
}
}
for (Block &block : llvm::drop_begin(region.getBlocks(), 1)) {
block.eraseArguments(
[&](BlockArgument arg) { return !liveMap.wasProvenLive(arg); });
}
}
return success(erasedAnything);
}
LogicalResult mlir::runRegionDCE(RewriterBase &rewriter,
MutableArrayRef<Region> regions) {
LiveMap liveMap;
do {
liveMap.resetChanged();
for (Region ®ion : regions)
propagateLiveness(region, liveMap);
} while (liveMap.hasChanged());
return deleteDeadness(rewriter, regions, liveMap);
}
namespace {
struct BlockEquivalenceData {
BlockEquivalenceData(Block *block);
unsigned getOrderOf(Value value) const;
Block *block;
llvm::hash_code hash;
DenseMap<Operation *, unsigned> opOrderIndex;
};
}
BlockEquivalenceData::BlockEquivalenceData(Block *block)
: block(block), hash(0) {
unsigned orderIt = block->getNumArguments();
for (Operation &op : *block) {
if (unsigned numResults = op.getNumResults()) {
opOrderIndex.try_emplace(&op, orderIt);
orderIt += numResults;
}
auto opHash = OperationEquivalence::computeHash(
&op, OperationEquivalence::ignoreHashValue,
OperationEquivalence::ignoreHashValue,
OperationEquivalence::IgnoreLocations);
hash = llvm::hash_combine(hash, opHash);
}
}
unsigned BlockEquivalenceData::getOrderOf(Value value) const {
assert(value.getParentBlock() == block && "expected value of this block");
if (BlockArgument arg = dyn_cast<BlockArgument>(value))
return arg.getArgNumber();
OpResult result = cast<OpResult>(value);
auto opOrderIt = opOrderIndex.find(result.getDefiningOp());
assert(opOrderIt != opOrderIndex.end() && "expected op to have an order");
return opOrderIt->second + result.getResultNumber();
}
namespace {
class BlockMergeCluster {
public:
BlockMergeCluster(BlockEquivalenceData &&leaderData)
: leaderData(std::move(leaderData)) {}
LogicalResult addToCluster(BlockEquivalenceData &blockData);
LogicalResult merge(RewriterBase &rewriter);
private:
BlockEquivalenceData leaderData;
llvm::SmallSetVector<Block *, 1> blocksToMerge;
std::set<std::pair<int, int>> operandsToMerge;
};
}
LogicalResult BlockMergeCluster::addToCluster(BlockEquivalenceData &blockData) {
if (leaderData.hash != blockData.hash)
return failure();
Block *leaderBlock = leaderData.block, *mergeBlock = blockData.block;
if (leaderBlock->getArgumentTypes() != mergeBlock->getArgumentTypes())
return failure();
SmallVector<std::pair<int, int>, 8> mismatchedOperands;
auto lhsIt = leaderBlock->begin(), lhsE = leaderBlock->end();
auto rhsIt = blockData.block->begin(), rhsE = blockData.block->end();
for (int opI = 0; lhsIt != lhsE && rhsIt != rhsE; ++lhsIt, ++rhsIt, ++opI) {
if (!OperationEquivalence::isEquivalentTo(
&*lhsIt, &*rhsIt, OperationEquivalence::ignoreValueEquivalence,
nullptr,
OperationEquivalence::Flags::IgnoreLocations))
return failure();
auto lhsOperands = lhsIt->getOperands(), rhsOperands = rhsIt->getOperands();
for (int operand : llvm::seq<int>(0, lhsIt->getNumOperands())) {
Value lhsOperand = lhsOperands[operand];
Value rhsOperand = rhsOperands[operand];
if (lhsOperand == rhsOperand)
continue;
if (lhsOperand.getType() != rhsOperand.getType())
return failure();
bool lhsIsInBlock = lhsOperand.getParentBlock() == leaderBlock;
bool rhsIsInBlock = rhsOperand.getParentBlock() == mergeBlock;
if (lhsIsInBlock != rhsIsInBlock)
return failure();
if (!lhsIsInBlock) {
auto isValidSuccessorArg = [](Block *block, Value operand) {
if (operand.getDefiningOp() !=
operand.getParentBlock()->getTerminator())
return true;
return !llvm::is_contained(block->getPredecessors(),
operand.getParentBlock());
};
if (!isValidSuccessorArg(leaderBlock, lhsOperand) ||
!isValidSuccessorArg(mergeBlock, rhsOperand))
return failure();
mismatchedOperands.emplace_back(opI, operand);
continue;
}
if (leaderData.getOrderOf(lhsOperand) != blockData.getOrderOf(rhsOperand))
return failure();
}
if (rhsIt->isUsedOutsideOfBlock(mergeBlock) ||
lhsIt->isUsedOutsideOfBlock(leaderBlock)) {
return failure();
}
}
if (lhsIt != lhsE || rhsIt != rhsE)
return failure();
operandsToMerge.insert(mismatchedOperands.begin(), mismatchedOperands.end());
blocksToMerge.insert(blockData.block);
return success();
}
static bool ableToUpdatePredOperands(Block *block) {
for (auto it = block->pred_begin(), e = block->pred_end(); it != e; ++it) {
if (!isa<BranchOpInterface>((*it)->getTerminator()))
return false;
}
return true;
}
static SmallVector<SmallVector<Value, 8>, 2> pruneRedundantArguments(
const SmallVector<SmallVector<Value, 8>, 2> &newArguments,
RewriterBase &rewriter, Block *block) {
SmallVector<SmallVector<Value, 8>, 2> newArgumentsPruned(
newArguments.size(), SmallVector<Value, 8>());
if (newArguments.empty())
return newArguments;
unsigned numLists = newArguments.size();
unsigned numArgs = newArguments[0].size();
llvm::DenseMap<unsigned, unsigned> idxToReplacement;
DenseMap<Value, unsigned> firstValueToIdx;
for (unsigned j = 0; j < numArgs; ++j) {
Value newArg = newArguments[0][j];
if (!firstValueToIdx.contains(newArg))
firstValueToIdx[newArg] = j;
}
for (unsigned j = 0; j < numArgs; ++j) {
bool shouldReplaceJ = false;
unsigned replacement = 0;
unsigned k = firstValueToIdx[newArguments[0][j]];
if (k != j) {
shouldReplaceJ = true;
replacement = k;
for (unsigned i = 1; i < numLists; ++i)
shouldReplaceJ =
shouldReplaceJ && (newArguments[i][k] == newArguments[i][j]);
}
if (shouldReplaceJ)
idxToReplacement[j] = replacement;
}
for (unsigned i = 0; i < numLists; ++i)
for (unsigned j = 0; j < numArgs; ++j)
if (!idxToReplacement.contains(j))
newArgumentsPruned[i].push_back(newArguments[i][j]);
SmallVector<unsigned> toErase;
for (auto [idx, arg] : llvm::enumerate(block->getArguments())) {
if (idxToReplacement.contains(idx)) {
Value oldArg = block->getArgument(idx);
Value newArg = block->getArgument(idxToReplacement[idx]);
rewriter.replaceAllUsesWith(oldArg, newArg);
toErase.push_back(idx);
}
}
for (unsigned idxToErase : llvm::reverse(toErase))
block->eraseArgument(idxToErase);
return newArgumentsPruned;
}
LogicalResult BlockMergeCluster::merge(RewriterBase &rewriter) {
if (blocksToMerge.empty())
return failure();
Block *leaderBlock = leaderData.block;
if (!operandsToMerge.empty()) {
if (!ableToUpdatePredOperands(leaderBlock) ||
!llvm::all_of(blocksToMerge, ableToUpdatePredOperands))
return failure();
SmallVector<Block::iterator, 2> blockIterators;
blockIterators.reserve(blocksToMerge.size() + 1);
blockIterators.push_back(leaderBlock->begin());
for (Block *mergeBlock : blocksToMerge)
blockIterators.push_back(mergeBlock->begin());
SmallVector<SmallVector<Value, 8>, 2> newArguments(
1 + blocksToMerge.size(),
SmallVector<Value, 8>(operandsToMerge.size()));
unsigned curOpIndex = 0;
for (const auto &it : llvm::enumerate(operandsToMerge)) {
unsigned nextOpOffset = it.value().first - curOpIndex;
curOpIndex = it.value().first;
for (unsigned i = 0, e = blockIterators.size(); i != e; ++i) {
Block::iterator &blockIter = blockIterators[i];
std::advance(blockIter, nextOpOffset);
auto &operand = blockIter->getOpOperand(it.value().second);
newArguments[i][it.index()] = operand.get();
if (i == 0) {
Value operandVal = operand.get();
operand.set(leaderBlock->addArgument(operandVal.getType(),
operandVal.getLoc()));
}
}
}
newArguments = pruneRedundantArguments(newArguments, rewriter, leaderBlock);
auto updatePredecessors = [&](Block *block, unsigned clusterIndex) {
for (auto predIt = block->pred_begin(), predE = block->pred_end();
predIt != predE; ++predIt) {
auto branch = cast<BranchOpInterface>((*predIt)->getTerminator());
unsigned succIndex = predIt.getSuccessorIndex();
branch.getSuccessorOperands(succIndex).append(
newArguments[clusterIndex]);
}
};
updatePredecessors(leaderBlock, 0);
for (unsigned i = 0, e = blocksToMerge.size(); i != e; ++i)
updatePredecessors(blocksToMerge[i], i + 1);
}
for (Block *block : blocksToMerge) {
block->replaceAllUsesWith(leaderBlock);
rewriter.eraseBlock(block);
}
return success();
}
static LogicalResult mergeIdenticalBlocks(RewriterBase &rewriter,
Region ®ion) {
if (region.empty() || llvm::hasSingleElement(region))
return failure();
DenseMap<SuccessorRange, SmallVector<Block *, 1>> matchingSuccessors;
for (Block &block : llvm::drop_begin(region, 1))
matchingSuccessors[block.getSuccessors()].push_back(&block);
bool mergedAnyBlocks = false;
for (ArrayRef<Block *> blocks : llvm::make_second_range(matchingSuccessors)) {
if (blocks.size() == 1)
continue;
SmallVector<BlockMergeCluster, 1> clusters;
for (Block *block : blocks) {
BlockEquivalenceData data(block);
bool hasNonEmptyRegion = llvm::any_of(*block, [](Operation &op) {
return llvm::any_of(op.getRegions(),
[](Region ®ion) { return !region.empty(); });
});
if (hasNonEmptyRegion)
continue;
bool addedToCluster = false;
for (auto &cluster : clusters)
if ((addedToCluster = succeeded(cluster.addToCluster(data))))
break;
if (!addedToCluster)
clusters.emplace_back(std::move(data));
}
for (auto &cluster : clusters)
mergedAnyBlocks |= succeeded(cluster.merge(rewriter));
}
return success(mergedAnyBlocks);
}
static LogicalResult mergeIdenticalBlocks(RewriterBase &rewriter,
MutableArrayRef<Region> regions) {
llvm::SmallSetVector<Region *, 1> worklist;
for (auto ®ion : regions)
worklist.insert(®ion);
bool anyChanged = false;
while (!worklist.empty()) {
Region *region = worklist.pop_back_val();
if (succeeded(mergeIdenticalBlocks(rewriter, *region))) {
worklist.insert(region);
anyChanged = true;
}
for (Block &block : *region)
for (auto &op : block)
for (auto &nestedRegion : op.getRegions())
worklist.insert(&nestedRegion);
}
return success(anyChanged);
}
static LogicalResult dropRedundantArguments(RewriterBase &rewriter,
Block &block) {
SmallVector<size_t> argsToErase;
for (auto [argIdx, blockOperand] : llvm::enumerate(block.getArguments())) {
bool sameArg = true;
Value commonValue;
for (auto predIt = block.pred_begin(), predE = block.pred_end();
predIt != predE; ++predIt) {
auto branch = dyn_cast<BranchOpInterface>((*predIt)->getTerminator());
if (!branch) {
sameArg = false;
break;
}
unsigned succIndex = predIt.getSuccessorIndex();
SuccessorOperands succOperands = branch.getSuccessorOperands(succIndex);
auto branchOperands = succOperands.getForwardedOperands();
if (!commonValue) {
commonValue = branchOperands[argIdx];
} else {
if (branchOperands[argIdx] != commonValue) {
sameArg = false;
break;
}
}
}
if (commonValue && sameArg) {
argsToErase.push_back(argIdx);
rewriter.replaceAllUsesWith(blockOperand, commonValue);
}
}
for (auto argIdx : llvm::reverse(argsToErase)) {
block.eraseArgument(argIdx);
for (auto predIt = block.pred_begin(), predE = block.pred_end();
predIt != predE; ++predIt) {
auto branch = cast<BranchOpInterface>((*predIt)->getTerminator());
unsigned succIndex = predIt.getSuccessorIndex();
SuccessorOperands succOperands = branch.getSuccessorOperands(succIndex);
succOperands.erase(argIdx);
}
}
return success(!argsToErase.empty());
}
static LogicalResult dropRedundantArguments(RewriterBase &rewriter,
MutableArrayRef<Region> regions) {
llvm::SmallSetVector<Region *, 1> worklist;
for (Region ®ion : regions)
worklist.insert(®ion);
bool anyChanged = false;
while (!worklist.empty()) {
Region *region = worklist.pop_back_val();
for (Block &block : *region) {
anyChanged = succeeded(dropRedundantArguments(rewriter, block));
for (Operation &op : block)
for (Region &nestedRegion : op.getRegions())
worklist.insert(&nestedRegion);
}
}
return success(anyChanged);
}
LogicalResult mlir::simplifyRegions(RewriterBase &rewriter,
MutableArrayRef<Region> regions,
bool mergeBlocks) {
bool eliminatedBlocks = succeeded(eraseUnreachableBlocks(rewriter, regions));
bool eliminatedOpsOrArgs = succeeded(runRegionDCE(rewriter, regions));
bool mergedIdenticalBlocks = false;
bool droppedRedundantArguments = false;
if (mergeBlocks) {
mergedIdenticalBlocks = succeeded(mergeIdenticalBlocks(rewriter, regions));
droppedRedundantArguments =
succeeded(dropRedundantArguments(rewriter, regions));
}
return success(eliminatedBlocks || eliminatedOpsOrArgs ||
mergedIdenticalBlocks || droppedRedundantArguments);
}