19#include "llvm/ADT/STLExtras.h"
20#include "llvm/Support/DebugLog.h"
27#define DEBUG_TYPE "dense-analysis"
35 LDBG() <<
"initializeEquivalentLatticeAnchor: "
38 if (isa<RegionBranchOpInterface, CallOpInterface>(op)) {
39 LDBG() <<
" Skipping "
41 <<
" (region branch or call)";
44 LDBG() <<
" Building equivalent lattice anchor for "
51 LDBG() <<
"initialize (forward): "
55 LDBG() <<
" Failed to process top-level operation";
60 LDBG() <<
" Processing region with " << region.getBlocks().size()
62 for (
Block &block : region) {
63 LDBG() <<
" Processing block with " << block.getOperations().size()
67 LDBG() <<
" Initializing operation: "
70 LDBG() <<
" Failed to initialize operation";
76 LDBG() <<
" Forward initialization completed successfully";
81 LDBG() <<
"visit (forward): " << *point;
83 LDBG() <<
" Processing operation: "
87 LDBG() <<
" Visiting block: " << point->
getBlock();
92void AbstractDenseForwardDataFlowAnalysis::visitCallOperation(
95 LDBG() <<
"visitCallOperation (forward): "
97 LDBG() <<
" before state: " << before;
98 LDBG() <<
" after state: " << *after;
102 auto isExternalCallable = [&]() {
104 dyn_cast_if_present<CallableOpInterface>(call.resolveCallable());
105 return callable && !callable.getCallableRegion();
108 LDBG() <<
" Handling as external callee (non-interprocedural or external)";
117 if (!predecessors->allPredecessorsKnown()) {
118 LDBG() <<
" Not all predecessors known, setting to entry state";
122 LDBG() <<
" Processing " << predecessors->getKnownPredecessors().size()
123 <<
" known predecessors";
124 for (Operation *predecessor : predecessors->getKnownPredecessors()) {
125 LDBG() <<
" Processing predecessor: "
126 << OpWithFlags(predecessor, OpPrintingFlags().skipRegions());
140 AbstractDenseLattice *latticeAfterCall = after;
141 const AbstractDenseLattice *latticeAtCalleeReturn =
144 LDBG() <<
" Lattice at callee return: " << *latticeAtCalleeReturn;
146 *latticeAtCalleeReturn, latticeAfterCall);
152 LDBG() <<
"processOperation (forward): "
159 LDBG() <<
" Block not executable, skipping operation";
169 LDBG() <<
" before state: " << *before;
170 LDBG() <<
" after state: " << *after;
174 if (
auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
175 LDBG() <<
" Processing as region branch operation";
182 if (
auto call = dyn_cast<CallOpInterface>(op)) {
183 LDBG() <<
" Processing as call operation";
184 visitCallOperation(call, *before, after);
189 LDBG() <<
" Invoking operation transfer function";
193void AbstractDenseForwardDataFlowAnalysis::visitBlock(
Block *block) {
194 LDBG() <<
"visitBlock (forward): " << block;
198 LDBG() <<
" Block not executable, skipping";
204 LDBG() <<
" Block lattice state: " << *after;
209 LDBG() <<
" Processing entry block";
211 auto callable = dyn_cast<CallableOpInterface>(block->
getParentOp());
212 if (callable && callable.getCallableRegion() == block->
getParent()) {
213 LDBG() <<
" Entry block of callable region";
219 if (!callsites->allPredecessorsKnown() ||
221 LDBG() <<
" Not all callsites known or non-interprocedural, setting "
225 LDBG() <<
" Processing " << callsites->getKnownPredecessors().size()
226 <<
" known callsites";
227 for (
Operation *callsite : callsites->getKnownPredecessors()) {
228 LDBG() <<
" Processing callsite: "
233 LDBG() <<
" Lattice before callsite: " << *before;
243 if (
auto branch = dyn_cast<RegionBranchOpInterface>(block->
getParentOp())) {
244 LDBG() <<
" Entry block of region branch operation";
249 LDBG() <<
" Cannot reason about data-flow, setting to entry state";
254 LDBG() <<
" Joining state from "
260 Block *predecessor = *it;
264 LDBG() <<
" Skipping non-executable edge from " << predecessor;
268 LDBG() <<
" Joining state from predecessor " << predecessor;
279 LDBG() <<
"visitRegionBranchOperation (forward): "
281 LDBG() <<
" point: " << *point;
282 LDBG() <<
" after state: " << *after;
286 assert(predecessors->allPredecessorsKnown() &&
287 "unexpected unresolved region successors");
289 LDBG() <<
" Processing " << predecessors->getKnownPredecessors().size()
290 <<
" known predecessors";
291 for (
Operation *op : predecessors->getKnownPredecessors()) {
292 LDBG() <<
" Processing predecessor: "
297 LDBG() <<
" Predecessor is the branch itself, getting state before "
303 <<
" Predecessor is terminator, getting state after terminator";
306 LDBG() <<
" before state: " << *before;
321 std::optional<unsigned> regionFrom =
322 op == branch ? std::optional<unsigned>()
323 : op->getBlock()->getParent()->getRegionNumber();
324 LDBG() <<
" regionFrom: "
325 << (regionFrom ? std::to_string(*regionFrom) :
"parent");
329 LDBG() <<
" Point is block start, regionTo: " << regionTo;
330 LDBG() <<
" Calling visitRegionBranchControlFlowTransfer with "
331 "regionFrom/regionTo";
336 "expected to be visiting the branch itself");
337 LDBG() <<
" Point is not block start, checking if predecessor is "
338 "region or op itself";
341 if (op->getParentOp() == branch || op == branch) {
342 LDBG() <<
" Predecessor is region or op itself, calling "
343 "visitRegionBranchControlFlowTransfer";
345 branch, regionFrom, std::nullopt, *before, after);
348 <<
" Predecessor is not region or op itself, performing join";
349 join(after, *before);
361 LDBG() <<
"initializeEquivalentLatticeAnchor (backward): "
364 if (isa<RegionBranchOpInterface, CallOpInterface>(op)) {
365 LDBG() <<
" Skipping "
367 <<
" (region branch or call)";
370 LDBG() <<
" Building equivalent lattice anchor for "
378 LDBG() <<
"initialize (backward): "
382 LDBG() <<
" Failed to process top-level operation";
387 LDBG() <<
" Processing region with " << region.getBlocks().size()
389 for (
Block &block : region) {
390 LDBG() <<
" Processing block with " << block.
getOperations().size()
393 for (
Operation &op : llvm::reverse(block)) {
394 LDBG() <<
" Initializing operation (backward): "
397 LDBG() <<
" Failed to initialize operation";
403 LDBG() <<
" Backward initialization completed successfully";
409 LDBG() <<
"visit (backward): " << *point;
411 LDBG() <<
" Processing operation: "
415 LDBG() <<
" Visiting block: " << point->
getBlock();
420void AbstractDenseBackwardDataFlowAnalysis::visitCallOperation(
423 LDBG() <<
"visitCallOperation (backward): "
425 LDBG() <<
" after state: " << after;
426 LDBG() <<
" before state: " << *before;
431 LDBG() <<
" Non-interprocedural analysis, handling as external callee";
437 Operation *callee = call.resolveCallableInTable(&symbolTable);
439 LDBG() <<
" Resolved callee: "
442 LDBG() <<
" Resolved callee: null";
445 auto callable = dyn_cast_or_null<CallableOpInterface>(callee);
449 if (callable && (!callable.getCallableRegion() ||
450 callable.getCallableRegion()->empty())) {
451 LDBG() <<
" Callee has no region or empty region, handling as external "
458 LDBG() <<
" No callable found, setting to exit state";
462 Region *region = callable.getCallableRegion();
463 LDBG() <<
" Processing callable with region";
480 const AbstractDenseLattice &latticeAtCalleeEntry =
482 LDBG() <<
" Lattice at callee entry: " << latticeAtCalleeEntry;
483 AbstractDenseLattice *latticeBeforeCall = before;
485 latticeAtCalleeEntry, latticeBeforeCall);
490 LDBG() <<
"processOperation (backward): "
497 LDBG() <<
" Block not executable, skipping operation";
507 LDBG() <<
" before state: " << *before;
508 LDBG() <<
" after state: " << *after;
511 if (
auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
512 LDBG() <<
" Processing as region branch operation";
517 if (
auto call = dyn_cast<CallOpInterface>(op)) {
518 LDBG() <<
" Processing as call operation";
519 visitCallOperation(call, *after, before);
524 LDBG() <<
" Invoking operation transfer function";
528void AbstractDenseBackwardDataFlowAnalysis::visitBlock(
Block *block) {
529 LDBG() <<
"visitBlock (backward): " << block;
534 LDBG() <<
" Block not executable, skipping";
539 LDBG() <<
" Block lattice state: " << *before;
543 auto isExitBlock = [](
Block *
b) {
551 return isa_and_nonnull<RegionBranchTerminatorOpInterface>(
554 if (isExitBlock(block)) {
555 LDBG() <<
" Processing exit block";
559 auto callable = dyn_cast<CallableOpInterface>(block->
getParentOp());
560 if (callable && callable.getCallableRegion() == block->
getParent()) {
561 LDBG() <<
" Exit block of callable region";
566 if (!callsites->allPredecessorsKnown() ||
568 LDBG() <<
" Not all callsites known or non-interprocedural, setting "
573 LDBG() <<
" Processing " << callsites->getKnownPredecessors().size()
574 <<
" known callsites";
575 for (Operation *callsite : callsites->getKnownPredecessors()) {
576 LDBG() <<
" Processing callsite: "
577 << OpWithFlags(callsite, OpPrintingFlags().skipRegions());
578 const AbstractDenseLattice *after =
580 LDBG() <<
" Lattice after callsite: " << *after;
590 if (
auto branch = dyn_cast<RegionBranchOpInterface>(block->
getParentOp())) {
591 LDBG() <<
" Exit block of region branch operation";
593 cast<RegionBranchTerminatorOpInterface>(block->
getTerminator());
594 visitRegionBranchOperation(point, branch, terminator, before);
600 LDBG() <<
" Cannot reason about successors, setting to exit state";
605 LDBG() <<
" Meeting state from " << block->
getSuccessors().size()
611 LDBG() <<
" Skipping non-executable edge to " << successor;
615 LDBG() <<
" Meeting state from successor " << successor;
624void AbstractDenseBackwardDataFlowAnalysis::visitRegionBranchOperation(
625 ProgramPoint *point, RegionBranchOpInterface branch,
627 LDBG() <<
"visitRegionBranchOperation (backward): "
628 << OpWithFlags(branch.getOperation(), OpPrintingFlags().skipRegions());
629 LDBG() <<
" branchPoint: " << (branchPoint.
isParent() ?
"parent" :
"region");
630 LDBG() <<
" before state: " << *before;
635 SmallVector<RegionSuccessor> successors;
636 branch.getSuccessorRegions(branchPoint, successors);
637 LDBG() <<
" Processing " << successors.size() <<
" successor regions";
638 for (
const RegionSuccessor &successor : successors) {
639 const AbstractDenseLattice *after;
640 if (successor.isParent() || successor.getSuccessor()->empty()) {
641 LDBG() <<
" Successor is parent or empty region";
644 Region *successorRegion = successor.getSuccessor();
645 assert(!successorRegion->
empty() &&
"unexpected empty successor region");
646 Block *successorBlock = &successorRegion->
front();
647 LDBG() <<
" Successor region with "
648 << successorRegion->
getBlocks().size() <<
" blocks";
653 LDBG() <<
" Successor block not executable, skipping";
659 LDBG() <<
" After state: " << *after;
Block represents an ordered list of Operations.
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
OpListType & getOperations()
pred_iterator pred_begin()
SuccessorRange getSuccessors()
Operation * getTerminator()
Get the terminator operation of this block.
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.
ProgramPoint * getProgramPointBefore(Operation *op)
Get a uniqued program point instance.
const DataFlowConfig & getSolverConfig() const
Return the configuration of the solver used for this analysis.
ProgramPoint * getProgramPointAfter(Operation *op)
AnchorT * getLatticeAnchor(Args &&...args)
Get or create a custom lattice anchor.
const StateT * getOrCreateFor(ProgramPoint *dependent, AnchorT anchor)
Get a read-only analysis state for the given point and create a dependency on dependent.
Set of flags used to control the behavior of the various IR print methods (e.g.
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.
Block * getBlock()
Returns the operation block that contains this operation.
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
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),...
bool isParent() const
Returns true if branching from the parent op.
static constexpr RegionBranchPoint parent()
Returns an instance of RegionBranchPoint representing the parent operation.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
unsigned getRegionNumber()
Return the number of this region in the parent operation.
BlockListType & getBlocks()
virtual void setToExitState(AbstractDenseLattice *lattice)=0
Set the dense lattice before at the control flow exit point and propagate the update if it changed.
LogicalResult initialize(Operation *top) override
Initialize the analysis by visiting every program point whose execution may modify the program state;...
virtual void visitCallControlFlowTransfer(CallOpInterface call, CallControlFlowAction action, const AbstractDenseLattice &after, AbstractDenseLattice *before)
Propagate the dense lattice backwards along the call control flow edge, which can be either entering ...
virtual void initializeEquivalentLatticeAnchor(Operation *top) override
Initialize lattice anchor equivalence class from the provided top-level operation.
virtual void buildOperationEquivalentLatticeAnchor(Operation *op)
Visit an operation.
virtual void visitBlockTransfer(Block *block, ProgramPoint *point, Block *successor, const AbstractDenseLattice &after, AbstractDenseLattice *before)
Visit a block and propagate the dense lattice backward along the control flow edge from successor to ...
virtual AbstractDenseLattice * getLattice(LatticeAnchor anchor)=0
Get the dense lattice before the execution of the lattice anchor.
LogicalResult visit(ProgramPoint *point) override
Visit a program point that modifies the state of the program.
virtual LogicalResult visitOperationImpl(Operation *op, const AbstractDenseLattice &after, AbstractDenseLattice *before)=0
Propagate the dense lattice after the execution of an operation to the lattice before its execution.
virtual LogicalResult processOperation(Operation *op)
Visit an operation.
virtual void visitRegionBranchControlFlowTransfer(RegionBranchOpInterface branch, RegionBranchPoint regionFrom, RegionSuccessor regionTo, const AbstractDenseLattice &after, AbstractDenseLattice *before)
Propagate the dense lattice backwards along the control flow edge from regionFrom to regionTo regions...
virtual const AbstractDenseLattice * getLatticeFor(ProgramPoint *dependent, LatticeAnchor anchor)=0
Get the dense lattice on the given lattice anchor and add dependent as its dependency.
virtual LogicalResult visitOperationImpl(Operation *op, const AbstractDenseLattice &before, AbstractDenseLattice *after)=0
Propagate the dense lattice before the execution of an operation to the lattice after its execution.
virtual const AbstractDenseLattice * getLatticeFor(ProgramPoint *dependent, LatticeAnchor anchor)=0
Get the dense lattice on the given lattice anchor and add dependent as its dependency.
void visitRegionBranchOperation(ProgramPoint *point, RegionBranchOpInterface branch, AbstractDenseLattice *after)
Visit a program point within a region branch operation with predecessors in it.
virtual void initializeEquivalentLatticeAnchor(Operation *top) override
Initialize lattice anchor equivalence class from the provided top-level operation.
void join(AbstractDenseLattice *lhs, const AbstractDenseLattice &rhs)
Join a lattice with another and propagate an update if it changed.
virtual void setToEntryState(AbstractDenseLattice *lattice)=0
Set the dense lattice at control flow entry point and propagate an update if it changed.
LogicalResult initialize(Operation *top) override
Initialize the analysis by visiting every program point whose execution may modify the program state;...
virtual void visitCallControlFlowTransfer(CallOpInterface call, CallControlFlowAction action, const AbstractDenseLattice &before, AbstractDenseLattice *after)
Propagate the dense lattice forward along the call control flow edge, which can be either entering or...
virtual AbstractDenseLattice * getLattice(LatticeAnchor anchor)=0
Get the dense lattice on the given lattice anchor.
virtual LogicalResult processOperation(Operation *op)
Visit an operation.
virtual void visitBlockTransfer(Block *block, ProgramPoint *point, Block *predecessor, const AbstractDenseLattice &before, AbstractDenseLattice *after)
Visit a block and propagate the dense lattice forward along the control flow edge from predecessor to...
virtual void visitRegionBranchControlFlowTransfer(RegionBranchOpInterface branch, std::optional< unsigned > regionFrom, std::optional< unsigned > regionTo, const AbstractDenseLattice &before, AbstractDenseLattice *after)
Propagate the dense lattice forward along the control flow edge from regionFrom to regionTo regions o...
virtual void buildOperationEquivalentLatticeAnchor(Operation *op)
Visit an operation.
LogicalResult visit(ProgramPoint *point) override
Visit a program point that modifies the state of the program.
This class represents a dense lattice.
Include the generated interface declarations.
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 * getNextOp() const
Get the next operation of this program point.
Operation * getPrevOp() const
Get the previous operation of this program point.