21#include "llvm/ADT/STLExtras.h"
22#include "llvm/Support/DebugLog.h"
29#define DEBUG_TYPE "dataflow"
61 for (
Value argument : region.front().getArguments())
65 return initializeRecursively(top);
69AbstractSparseForwardDataFlowAnalysis::initializeRecursively(
Operation *op) {
70 LDBG() <<
"Initializing recursively for operation: "
75 if (failed(visitOperation(op))) {
76 LDBG() <<
"Failed to visit operation: "
82 LDBG() <<
"Processing region with " << region.getBlocks().size()
84 for (
Block &block : region) {
85 LDBG() <<
"Processing block with " << block.getNumArguments()
88 ->blockContentSubscribe(
this);
91 LDBG() <<
"Recursively initializing nested operation: "
93 if (failed(initializeRecursively(&op))) {
94 LDBG() <<
"Failed to initialize nested operation: "
102 LDBG() <<
"Successfully completed recursive initialization for operation: "
103 << OpWithFlags(op, OpPrintingFlags().skipRegions());
110 return visitOperation(point->
getPrevOp());
116AbstractSparseForwardDataFlowAnalysis::visitOperation(
Operation *op) {
131 resultLattices.push_back(resultLattice);
135 if (
auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
148 operandLattices.push_back(operandLattice);
151 if (
auto call = dyn_cast<CallOpInterface>(op))
158void AbstractSparseForwardDataFlowAnalysis::visitBlock(
Block *block) {
168 SmallVector<AbstractSparseLattice *> argLattices;
172 argLattices.push_back(argLattice);
179 auto callable = dyn_cast<CallableOpInterface>(block->
getParentOp());
180 if (callable && callable.getCallableRegion() == block->
getParent())
184 if (
auto branch = dyn_cast<RegionBranchOpInterface>(block->
getParentOp())) {
199 Block *predecessor = *it;
203 auto *edgeExecutable =
205 edgeExecutable->blockContentSubscribe(
this);
206 if (!edgeExecutable->isLive())
212 SuccessorOperands operands =
213 branch.getSuccessorOperands(it.getSuccessorIndex());
214 for (
auto [idx, lattice] : llvm::enumerate(argLattices)) {
215 if (Value operand = operands[idx]) {
231 CallOpInterface call,
236 auto isExternalCallable = [&]() {
238 dyn_cast_if_present<CallableOpInterface>(call.resolveCallable());
239 return callable && !callable.getCallableRegion();
252 if (!predecessors->allPredecessorsKnown()) {
260 ResultRange forwardedResults = call.getForwardedResults();
261 unsigned firstForwarded = forwardedResults.empty()
262 ? resultLattices.size()
263 : forwardedResults[0].getResultNumber();
266 resultLattices.drop_front(firstForwarded + forwardedResults.size()));
268 resultLattices.slice(firstForwarded, forwardedResults.size());
270 for (
Operation *predecessor : predecessors->getKnownPredecessors())
271 for (
auto &&[operand, resLattice] :
272 llvm::zip_equal(predecessor->getOperands(), forwardedResultLattices))
279 CallableOpInterface callable,
281 Block *entryBlock = &callable.getCallableRegion()->
front();
286 if (!callsites->allPredecessorsKnown() ||
290 for (
Operation *callsite : callsites->getKnownPredecessors()) {
291 auto call = cast<CallOpInterface>(callsite);
292 for (
auto it : llvm::zip(call.getArgOperands(), argLattices))
293 join(std::get<1>(it),
299void AbstractSparseForwardDataFlowAnalysis::visitRegionSuccessors(
303 assert(predecessors->allPredecessorsKnown() &&
304 "unexpected unresolved region successors");
306 for (
Operation *op : predecessors->getKnownPredecessors()) {
308 std::optional<OperandRange> operands;
312 operands = branch.getEntrySuccessorOperands(successor);
314 }
else if (
auto regionTerminator =
315 dyn_cast<RegionBranchTerminatorOpInterface>(op)) {
316 operands = regionTerminator.getSuccessorOperands(successor);
324 ValueRange inputs = predecessors->getSuccessorInputs(op);
325 assert(inputs.size() == operands->size() &&
326 "expected the same number of successor inputs as operands");
329 unsigned firstIndex = 0;
330 if (inputs.size() != lattices.size()) {
333 firstIndex = cast<OpResult>(inputs.front()).getResultNumber();
334 SmallVector<Value> nonSuccessorInputs =
335 branch.getNonSuccessorInputs(successor);
336 SmallVector<AbstractSparseLattice *> nonSuccessorInputLattices =
337 llvm::map_to_vector(nonSuccessorInputs, valueToLattices);
340 nonSuccessorInputLattices);
343 firstIndex = cast<BlockArgument>(inputs.front()).getArgNumber();
345 SmallVector<Value> nonSuccessorInputs =
346 branch.getNonSuccessorInputs(RegionSuccessor(region));
347 SmallVector<AbstractSparseLattice *> nonSuccessorInputLattices =
348 llvm::map_to_vector(nonSuccessorInputs, valueToLattices);
351 nonSuccessorInputLattices);
355 for (
auto [lattice, operand] :
356 llvm::zip(lattices.drop_front(firstIndex), *operands))
392 return initializeRecursively(top);
396AbstractSparseBackwardDataFlowAnalysis::initializeRecursively(
Operation *op) {
397 if (failed(visitOperation(op)))
401 for (
Block &block : region) {
403 ->blockContentSubscribe(
this);
407 for (
auto it = block.
rbegin(); it != block.
rend(); it++)
408 if (failed(initializeRecursively(&*it)))
423 return visitOperation(point->
getPrevOp());
429 resultLattices.reserve(values.size());
432 resultLattices.push_back(resultLattice);
434 return resultLattices;
438AbstractSparseBackwardDataFlowAnalysis::getLatticeElementsFor(
441 resultLattices.reserve(values.size());
444 getLatticeElementFor(point,
result);
445 resultLattices.push_back(resultLattice);
447 return resultLattices;
455AbstractSparseBackwardDataFlowAnalysis::visitOperation(Operation *op) {
456 LDBG() <<
"Visiting operation: "
457 << OpWithFlags(op, OpPrintingFlags().skipRegions()) <<
" with "
465 LDBG() <<
"Operation is in dead block, bailing out";
469 LDBG() <<
"Creating lattice elements for " << op->
getNumOperands()
471 SmallVector<AbstractSparseLattice *> operandLattices =
473 SmallVector<const AbstractSparseLattice *> resultLattices =
478 if (
auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
479 LDBG() <<
"Processing RegionBranchOpInterface operation";
480 visitRegionSuccessors(branch, operandLattices);
484 if (
auto branch = dyn_cast<BranchOpInterface>(op)) {
485 LDBG() <<
"Processing BranchOpInterface operation with "
495 for (
auto [index, block] : llvm::enumerate(op->
getSuccessors())) {
496 SuccessorOperands successorOperands = branch.getSuccessorOperands(index);
498 if (!forwarded.empty()) {
499 MutableArrayRef<OpOperand> operands = op->
getOpOperands().slice(
501 for (OpOperand &operand : operands) {
502 unaccounted.reset(operand.getOperandNumber());
503 if (std::optional<BlockArgument> blockArg =
505 successorOperands, operand.getOperandNumber(), block)) {
514 for (
int index : unaccounted.set_bits()) {
523 if (
auto call = dyn_cast<CallOpInterface>(op)) {
524 LDBG() <<
"Processing CallOpInterface operation";
525 Operation *callableOp = call.resolveCallableInTable(&symbolTable);
526 if (
auto callable = dyn_cast_or_null<CallableOpInterface>(callableOp)) {
534 OperandRange argOperands = call.getArgOperands();
535 MutableArrayRef<OpOperand> argOpOperands =
537 Region *region = callable.getCallableRegion();
538 if (!region || region->
empty() ||
547 for (
auto [blockArg, argOpOperand] :
551 unaccounted.reset(argOpOperand.getOperandNumber());
556 for (
int index : unaccounted.set_bits()) {
574 if (
auto terminator = dyn_cast<RegionBranchTerminatorOpInterface>(op)) {
575 LDBG() <<
"Processing RegionBranchTerminatorOpInterface operation";
576 if (
auto branch = dyn_cast<RegionBranchOpInterface>(op->
getParentOp())) {
577 visitRegionSuccessorsFromTerminator(terminator, branch);
582 if (op->
hasTrait<OpTrait::ReturnLike>()) {
583 LDBG() <<
"Processing ReturnLike operation";
586 if (
auto callable = dyn_cast<CallableOpInterface>(op->
getParentOp())) {
587 LDBG() <<
"Callable parent found, visiting callable operation";
592 LDBG() <<
"Using default visitOperationImpl for operation: "
593 << OpWithFlags(op, OpPrintingFlags().skipRegions());
598 Operation *op, CallableOpInterface callable,
607 cast<CallOpInterface>(call).getForwardedResults();
610 for (
auto [op,
result] : llvm::zip(operandLattices, callResultLattices))
622void AbstractSparseBackwardDataFlowAnalysis::visitRegionSuccessors(
623 RegionBranchOpInterface branch,
627 BitVector unaccounted(branch->getNumOperands(),
true);
630 for (
const auto &[operand, inputs] : mapping) {
631 for (
Value input : inputs) {
634 unaccounted.reset(operand->getOperandNumber());
640 branch.getEntrySuccessorRegions(operands, successors);
644 auto valueToArgument = [](
Value value) {
645 return cast<BlockArgument>(value);
647 SmallVector<BlockArgument> noControlFlowArguments = llvm::map_to_vector(
648 branch.getNonSuccessorInputs(successor), valueToArgument);
654 for (
int index : unaccounted.set_bits()) {
659void AbstractSparseBackwardDataFlowAnalysis::
660 visitRegionSuccessorsFromTerminator(
661 RegionBranchTerminatorOpInterface terminator,
662 RegionBranchOpInterface branch) {
663 assert(terminator->getParentOp() == branch.getOperation() &&
664 "expected `branch` to be the parent op of `terminator`");
668 BitVector unaccounted(terminator->getNumOperands(),
true);
671 branch.getSuccessorOperandInputMapping(mapping,
672 RegionBranchPoint(terminator));
673 for (
const auto &[operand, inputs] : mapping) {
674 for (Value input : inputs) {
677 unaccounted.reset(operand->getOperandNumber());
683 for (
int index : unaccounted.set_bits()) {
689AbstractSparseBackwardDataFlowAnalysis::getLatticeElementFor(
690 ProgramPoint *point, Value value) {
static MutableArrayRef< OpOperand > operandsToOpOperands(OperandRange &operands)
virtual void onUpdate(DataFlowSolver *solver) const
This function is called by the solver when the analysis state is updated to enqueue more work items.
LatticeAnchor anchor
The lattice anchor to which the state belongs.
friend class DataFlowSolver
Allow the framework to access the dependents.
Block represents an ordered list of Operations.
unsigned getNumArguments()
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
pred_iterator pred_begin()
Operation * getTerminator()
Get the terminator operation of this block.
BlockArgListType getArguments()
PredecessorIterator pred_iterator
bool isEntryBlock()
Return if this block is the entry block in the parent region.
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
reverse_iterator rbegin()
Base class for all data-flow analyses.
void addDependency(AnalysisState *state, ProgramPoint *point)
Create a dependency between the given analysis state and lattice anchor on this analysis.
ProgramPoint * getProgramPointBefore(Operation *op)
Get a uniqued program point instance.
void propagateIfChanged(AnalysisState *state, ChangeResult changed)
Propagate an update to a state if it changed.
const DataFlowConfig & getSolverConfig() const
Return the configuration of the solver used for this analysis.
StateT * getOrCreate(AnchorT anchor)
Get the analysis state associated with the lattice anchor.
ProgramPoint * getProgramPointAfter(Operation *op)
DataFlowAnalysis(DataFlowSolver &solver)
Create an analysis with a reference to the parent solver.
AnchorT * getLatticeAnchor(Args &&...args)
Get or create a custom lattice anchor.
void registerAnchorKind()
Register a custom lattice anchor class.
friend class DataFlowSolver
Allow the data-flow solver to access the internals of this class.
const StateT * getOrCreateFor(ProgramPoint *dependent, AnchorT anchor)
Get a read-only analysis state for the given point and create a dependency on dependent.
void enqueue(WorkItem item)
Push a work item onto the worklist, if it is not already there.
ProgramPoint * getProgramPointAfter(Operation *op)
Set of flags used to control the behavior of the various IR print methods (e.g.
A wrapper class that allows for printing an operation with a set of flags, useful to act as a "stream...
This class implements the operand iterators for the Operation class.
unsigned getBeginOperandIndex() const
Return the operand index of the first element of this range.
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.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
MutableArrayRef< OpOperand > getOpOperands()
unsigned getNumOperands()
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
operand_range getOperands()
Returns an iterator on the underlying Value's.
SuccessorRange getSuccessors()
result_range getResults()
OpOperand & getOpOperand(unsigned idx)
unsigned getNumResults()
Return the number of results held by this operation.
static constexpr RegionBranchPoint parent()
Returns an instance of RegionBranchPoint representing the parent operation.
This class represents a successor of a region.
bool isOperation() const
Return true if the successor is an operation.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
This class implements the result iterators for the Operation class.
OperandRange getForwardedOperands() const
Get the range of operands that are simply forwarded to the successor.
This class represents a collection of SymbolTables.
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...
virtual void setToExitState(AbstractSparseLattice *lattice)=0
Set the given lattice element(s) at control flow exit point(s) and propagate the update if it chaned.
SmallVector< AbstractSparseLattice * > getLatticeElements(ValueRange values)
Get the lattice elements for a range of values.
AbstractSparseBackwardDataFlowAnalysis(DataFlowSolver &solver, SymbolTableCollection &symbolTable)
virtual AbstractSparseLattice * getLatticeElement(Value value)=0
Get the lattice element for a value.
virtual void visitBranchOperand(OpOperand &operand)=0
virtual void visitCallOperand(OpOperand &operand)=0
virtual void visitNonControlFlowArguments(RegionSuccessor &successor, ArrayRef< BlockArgument > arguments)=0
LogicalResult visit(ProgramPoint *point) override
Visit a program point.
void meet(AbstractSparseLattice *lhs, const AbstractSparseLattice &rhs)
Join the lattice element and propagate and update if it changed.
virtual LogicalResult visitCallableOperation(Operation *op, CallableOpInterface callable, ArrayRef< AbstractSparseLattice * > operandLattices)
Visits a callable operation.
virtual void visitExternalCallImpl(CallOpInterface call, ArrayRef< AbstractSparseLattice * > operandLattices, ArrayRef< const AbstractSparseLattice * > resultLattices)=0
The transfer function for calls to external functions.
LogicalResult initialize(Operation *top) override
Initialize the analysis by visiting the operation and everything nested under it.
void setAllToExitStates(ArrayRef< AbstractSparseLattice * > lattices)
Set the given lattice element(s) at control flow exit point(s) and propagate the update if it chaned.
virtual LogicalResult visitOperationImpl(Operation *op, ArrayRef< AbstractSparseLattice * > operandLattices, ArrayRef< const AbstractSparseLattice * > resultLattices)=0
The operation transfer function.
LogicalResult visit(ProgramPoint *point) override
Visit a program point.
LogicalResult initialize(Operation *top) override
Initialize the analysis by visiting every owner of an SSA value: all operations and blocks.
virtual void visitExternalCallImpl(CallOpInterface call, ArrayRef< const AbstractSparseLattice * > argumentLattices, ArrayRef< AbstractSparseLattice * > resultLattices)=0
The transfer function for calls to external functions.
AbstractSparseForwardDataFlowAnalysis(DataFlowSolver &solver)
void setAllToEntryStates(ArrayRef< AbstractSparseLattice * > lattices)
virtual void setToEntryState(AbstractSparseLattice *lattice)=0
Set the given lattice element(s) at control flow entry point(s).
const AbstractSparseLattice * getLatticeElementFor(ProgramPoint *point, Value value)
Get a read-only lattice element for a value and add it as a dependency to a program point.
virtual LogicalResult visitCallOperation(CallOpInterface call, ArrayRef< const AbstractSparseLattice * > operandLattices, ArrayRef< AbstractSparseLattice * > resultLattices)
Visits a call operation.
virtual void visitCallableOperation(CallableOpInterface callable, ArrayRef< AbstractSparseLattice * > argLattices)
Visits a callable operation.
virtual void visitNonControlFlowArgumentsImpl(ProgramPoint *point, Operation *op, const RegionSuccessor &successor, ValueRange nonSuccessorInputs, ArrayRef< AbstractSparseLattice * > nonSuccessorInputLattices)=0
Compute lattice values for successor values not supplied by control-flow forwarding at point.
virtual AbstractSparseLattice * getLatticeElement(Value value)=0
Get the lattice element of a value.
virtual LogicalResult visitOperationImpl(Operation *op, ArrayRef< const AbstractSparseLattice * > operandLattices, ArrayRef< AbstractSparseLattice * > resultLattices)=0
The operation transfer function.
void join(AbstractSparseLattice *lhs, const AbstractSparseLattice &rhs)
Join the lattice element and propagate and update if it changed.
This class represents an abstract lattice.
void onUpdate(DataFlowSolver *solver) const override
When the lattice gets updated, propagate an update to users of the value using its use-def chain to s...
void useDefSubscribe(DataFlowAnalysis *analysis)
Subscribe an analysis to updates of the lattice.
This analysis state represents a set of live control-flow "predecessors" of a program point (either a...
ArrayRef< Operation * > getKnownPredecessors() const
Get the known predecessors.
bool allPredecessorsKnown() const
Returns true if all predecessors are known.
std::optional< BlockArgument > getBranchSuccessorArgument(const SuccessorOperands &operands, unsigned operandIndex, Block *successor)
Return the BlockArgument corresponding to operand operandIndex in some successor if operandIndex is w...
Include the generated interface declarations.
DenseMap< OpOperand *, SmallVector< Value > > RegionBranchSuccessorMapping
A mapping from successor operands to successor inputs.
Program point represents a specific location in the execution of a program.
bool isBlockStart() const
Block * getBlock() const
Get the block contains this program point.
Operation * getPrevOp() const
Get the previous operation of this program point.