Simplify the design of `RegionSuccessor`. There is no need to store the
`Operation *` pointer when branching out of the region branch op (to the
parent). There is no API to even access the `Operation *` pointer.
Add a new helper function `RegionSuccessor::parent` to construct a
region successor that points to the parent. This aligns the
`RegionSuccessor` design and API with `RegionBranchPoint`:
* Both classes now have a `parent()` helper function.
`ClassName::parent()` can be used in documentation to precisely describe
the source/target of a region branch.
* Both classes now use `nullptr` internally to represent "parent".
This API change also protects against incorrect API usage: users can no
longer pass an incorrect parent op. If a region successor is not a
region of the region branch op, it *must* branch out of region branch op
itself ("parent"). However, the previous API allowed passing other
operations. There was one such API violation in a [test
case](https://github.com/llvm/llvm-project/pull/174945/files#diff-d5717e4a8d7344b2ff77762b8fa480bcfec0eeee97a86195c787d791a6217e13L71).
Also clean up the documentation to use the correct terminology (such as
"successor operands", "successor inputs") consistently.
Note: This PR effectively rolls back some changes from #161575. That PR
introduced `llvm::PointerUnion<Region *, Operation *>
successor{nullptr};`. It is unclear from the commit message why that
change was made.
Note for LLVM integration: You may have to slightly modify
`getSuccessorRegion` implementations: Replace
`RegionSuccessor(getOperation(), getOperation()->getResults())` with
`RegionSuccessor::parent(getResults())`.
102 lines
4.0 KiB
C++
102 lines
4.0 KiB
C++
#include "mlir/Analysis/SliceWalk.h"
|
|
#include "mlir/Interfaces/ControlFlowInterfaces.h"
|
|
|
|
using namespace mlir;
|
|
|
|
WalkContinuation mlir::walkSlice(ValueRange rootValues,
|
|
WalkCallback walkCallback) {
|
|
// Search the backward slice starting from the root values.
|
|
SmallVector<Value> workList = rootValues;
|
|
llvm::SmallDenseSet<Value, 16> seenValues;
|
|
while (!workList.empty()) {
|
|
// Search the backward slice of the current value.
|
|
Value current = workList.pop_back_val();
|
|
|
|
// Skip the current value if it has already been seen.
|
|
if (!seenValues.insert(current).second)
|
|
continue;
|
|
|
|
// Call the walk callback with the current value.
|
|
WalkContinuation continuation = walkCallback(current);
|
|
if (continuation.wasInterrupted())
|
|
return continuation;
|
|
if (continuation.wasSkipped())
|
|
continue;
|
|
|
|
assert(continuation.wasAdvancedTo());
|
|
// Add the next values to the work list if the walk should continue.
|
|
workList.append(continuation.getNextValues().begin(),
|
|
continuation.getNextValues().end());
|
|
}
|
|
|
|
return WalkContinuation::skip();
|
|
}
|
|
|
|
/// Returns the predecessor branch operands that match `blockArg`, or nullopt if
|
|
/// some of the predecessor terminators do not implement the BranchOpInterface.
|
|
static std::optional<SmallVector<Value>>
|
|
getBlockPredecessorOperands(BlockArgument blockArg) {
|
|
Block *block = blockArg.getOwner();
|
|
|
|
// Search the predecessor operands for all predecessor terminators.
|
|
SmallVector<Value> predecessorOperands;
|
|
for (auto it = block->pred_begin(); it != block->pred_end(); ++it) {
|
|
Block *predecessor = *it;
|
|
auto branchOp = dyn_cast<BranchOpInterface>(predecessor->getTerminator());
|
|
if (!branchOp)
|
|
return std::nullopt;
|
|
SuccessorOperands successorOperands =
|
|
branchOp.getSuccessorOperands(it.getSuccessorIndex());
|
|
// Store the predecessor operand if the block argument matches an operand
|
|
// and is not produced by the terminator.
|
|
if (Value operand = successorOperands[blockArg.getArgNumber()])
|
|
predecessorOperands.push_back(operand);
|
|
}
|
|
|
|
return predecessorOperands;
|
|
}
|
|
|
|
std::optional<SmallVector<Value>>
|
|
mlir::getControlFlowPredecessors(Value value) {
|
|
if (OpResult opResult = dyn_cast<OpResult>(value)) {
|
|
if (auto selectOp = opResult.getDefiningOp<SelectLikeOpInterface>())
|
|
return SmallVector<Value>(
|
|
{selectOp.getTrueValue(), selectOp.getFalseValue()});
|
|
auto regionOp = opResult.getDefiningOp<RegionBranchOpInterface>();
|
|
// If the interface is not implemented, there are no control flow
|
|
// predecessors to work with.
|
|
if (!regionOp)
|
|
return std::nullopt;
|
|
// Add the control flow predecessor operands to the work list.
|
|
RegionSuccessor region = RegionSuccessor::parent(regionOp->getResults());
|
|
SmallVector<Value> predecessorOperands;
|
|
// TODO (#175168): This assumes that there are no non-successor-inputs
|
|
// in front of the op result.
|
|
regionOp.getPredecessorValues(region, opResult.getResultNumber(),
|
|
predecessorOperands);
|
|
return predecessorOperands;
|
|
}
|
|
|
|
auto blockArg = cast<BlockArgument>(value);
|
|
Block *block = blockArg.getOwner();
|
|
// Search the region predecessor operands for structured control flow.
|
|
if (block->isEntryBlock()) {
|
|
if (auto regionBranchOp =
|
|
dyn_cast<RegionBranchOpInterface>(block->getParentOp())) {
|
|
RegionSuccessor region(blockArg.getParentRegion());
|
|
SmallVector<Value> predecessorOperands;
|
|
// TODO (#175168): This assumes that there are no non-successor-inputs
|
|
// in front of the block argument.
|
|
regionBranchOp.getPredecessorValues(region, blockArg.getArgNumber(),
|
|
predecessorOperands);
|
|
return predecessorOperands;
|
|
}
|
|
// If the interface is not implemented, there are no control flow
|
|
// predecessors to work with.
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Search the block predecessor operands for unstructured control flow.
|
|
return getBlockPredecessorOperands(blockArg);
|
|
}
|