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();
92 void 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)";
113 const auto *predecessors = getOrCreateFor<PredecessorState>(
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: "
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";
193 void AbstractDenseForwardDataFlowAnalysis::visitBlock(
Block *block) {
194 LDBG() <<
"visitBlock (forward): " << block;
197 if (!getOrCreateFor<Executable>(point, point)->isLive()) {
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";
214 const auto *callsites = getOrCreateFor<PredecessorState>(
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;
261 if (!getOrCreateFor<Executable>(
262 point, getLatticeAnchor<CFGEdge>(predecessor, block))
264 LDBG() <<
" Skipping non-executable edge from " << predecessor;
268 LDBG() <<
" Joining state from predecessor " << predecessor;
278 LDBG() <<
"visitRegionBranchOperation (forward): "
280 LDBG() <<
" point: " << *point;
281 LDBG() <<
" after state: " << *after;
284 const auto *predecessors = getOrCreateFor<PredecessorState>(point, point);
285 assert(predecessors->allPredecessorsKnown() &&
286 "unexpected unresolved region successors");
288 LDBG() <<
" Processing " << predecessors->getKnownPredecessors().size()
289 <<
" known predecessors";
290 for (
Operation *op : predecessors->getKnownPredecessors()) {
291 LDBG() <<
" Processing predecessor: "
296 LDBG() <<
" Predecessor is the branch itself, getting state before "
302 <<
" Predecessor is terminator, getting state after terminator";
305 LDBG() <<
" before state: " << *before;
320 std::optional<unsigned> regionFrom =
321 op == branch ? std::optional<unsigned>()
322 : op->getBlock()->getParent()->getRegionNumber();
323 LDBG() <<
" regionFrom: "
324 << (regionFrom ? std::to_string(*regionFrom) :
"parent");
328 LDBG() <<
" Point is block start, regionTo: " << regionTo;
329 LDBG() <<
" Calling visitRegionBranchControlFlowTransfer with "
330 "regionFrom/regionTo";
335 "expected to be visiting the branch itself");
336 LDBG() <<
" Point is not block start, checking if predecessor is "
337 "region or op itself";
340 if (op->getParentOp() == branch || op == branch) {
341 LDBG() <<
" Predecessor is region or op itself, calling "
342 "visitRegionBranchControlFlowTransfer";
344 branch, regionFrom, std::nullopt, *before, after);
347 <<
" Predecessor is not region or op itself, performing join";
348 join(after, *before);
360 LDBG() <<
"initializeEquivalentLatticeAnchor (backward): "
363 if (isa<RegionBranchOpInterface, CallOpInterface>(op)) {
364 LDBG() <<
" Skipping "
366 <<
" (region branch or call)";
369 LDBG() <<
" Building equivalent lattice anchor for "
377 LDBG() <<
"initialize (backward): "
381 LDBG() <<
" Failed to process top-level operation";
386 LDBG() <<
" Processing region with " << region.getBlocks().size()
388 for (
Block &block : region) {
389 LDBG() <<
" Processing block with " << block.
getOperations().size()
392 for (
Operation &op : llvm::reverse(block)) {
393 LDBG() <<
" Initializing operation (backward): "
396 LDBG() <<
" Failed to initialize operation";
402 LDBG() <<
" Backward initialization completed successfully";
408 LDBG() <<
"visit (backward): " << *point;
410 LDBG() <<
" Processing operation: "
414 LDBG() <<
" Visiting block: " << point->
getBlock();
419 void AbstractDenseBackwardDataFlowAnalysis::visitCallOperation(
422 LDBG() <<
"visitCallOperation (backward): "
424 LDBG() <<
" after state: " << after;
425 LDBG() <<
" before state: " << *before;
430 LDBG() <<
" Non-interprocedural analysis, handling as external callee";
436 Operation *callee = call.resolveCallableInTable(&symbolTable);
438 LDBG() <<
" Resolved callee: "
441 LDBG() <<
" Resolved callee: null";
444 auto callable = dyn_cast_or_null<CallableOpInterface>(callee);
448 if (callable && (!callable.getCallableRegion() ||
449 callable.getCallableRegion()->empty())) {
450 LDBG() <<
" Callee has no region or empty region, handling as external "
457 LDBG() <<
" No callable found, setting to exit state";
461 Region *region = callable.getCallableRegion();
462 LDBG() <<
" Processing callable with region";
481 LDBG() <<
" Lattice at callee entry: " << latticeAtCalleeEntry;
484 latticeAtCalleeEntry, latticeBeforeCall);
489 LDBG() <<
"processOperation (backward): "
496 LDBG() <<
" Block not executable, skipping operation";
506 LDBG() <<
" before state: " << *before;
507 LDBG() <<
" after state: " << *after;
510 if (
auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
511 LDBG() <<
" Processing as region branch operation";
516 if (
auto call = dyn_cast<CallOpInterface>(op)) {
517 LDBG() <<
" Processing as call operation";
518 visitCallOperation(call, *after, before);
523 LDBG() <<
" Invoking operation transfer function";
527 void AbstractDenseBackwardDataFlowAnalysis::visitBlock(
Block *block) {
528 LDBG() <<
"visitBlock (backward): " << block;
533 LDBG() <<
" Block not executable, skipping";
538 LDBG() <<
" Block lattice state: " << *before;
542 auto isExitBlock = [](
Block *b) {
550 return isa_and_nonnull<RegionBranchTerminatorOpInterface>(
553 if (isExitBlock(block)) {
554 LDBG() <<
" Processing exit block";
558 auto callable = dyn_cast<CallableOpInterface>(block->
getParentOp());
559 if (callable && callable.getCallableRegion() == block->
getParent()) {
560 LDBG() <<
" Exit block of callable region";
561 const auto *callsites = getOrCreateFor<PredecessorState>(
565 if (!callsites->allPredecessorsKnown() ||
567 LDBG() <<
" Not all callsites known or non-interprocedural, setting "
572 LDBG() <<
" Processing " << callsites->getKnownPredecessors().size()
573 <<
" known callsites";
574 for (
Operation *callsite : callsites->getKnownPredecessors()) {
575 LDBG() <<
" Processing callsite: "
579 LDBG() <<
" Lattice after callsite: " << *after;
589 if (
auto branch = dyn_cast<RegionBranchOpInterface>(block->
getParentOp())) {
590 LDBG() <<
" Exit block of region branch operation";
591 visitRegionBranchOperation(point, branch, block->
getParent(), before);
597 LDBG() <<
" Cannot reason about successors, setting to exit state";
602 LDBG() <<
" Meeting state from " << block->
getSuccessors().size()
605 if (!getOrCreateFor<Executable>(point,
606 getLatticeAnchor<CFGEdge>(block, successor))
608 LDBG() <<
" Skipping non-executable edge to " << successor;
612 LDBG() <<
" Meeting state from successor " << successor;
619 void AbstractDenseBackwardDataFlowAnalysis::visitRegionBranchOperation(
622 LDBG() <<
"visitRegionBranchOperation (backward): "
624 LDBG() <<
" branchPoint: " << (branchPoint.
isParent() ?
"parent" :
"region");
625 LDBG() <<
" before state: " << *before;
631 branch.getSuccessorRegions(branchPoint, successors);
632 LDBG() <<
" Processing " << successors.size() <<
" successor regions";
635 if (successor.isParent() || successor.getSuccessor()->empty()) {
636 LDBG() <<
" Successor is parent or empty region";
639 Region *successorRegion = successor.getSuccessor();
640 assert(!successorRegion->
empty() &&
"unexpected empty successor region");
641 Block *successorBlock = &successorRegion->
front();
642 LDBG() <<
" Successor region with "
643 << successorRegion->
getBlocks().size() <<
" blocks";
645 if (!getOrCreateFor<Executable>(point,
648 LDBG() <<
" Successor block not executable, skipping";
654 LDBG() <<
" After state: " << *after;
Block represents an ordered list of Operations.
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
pred_iterator pred_begin()
SuccessorRange getSuccessors()
Operation * getTerminator()
Get the terminator operation of this block.
OpListType & getOperations()
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 * getProgramPointAfter(Operation *op)
ProgramPoint * getProgramPointBefore(Operation *op)
Get a uniqued program point instance.
const DataFlowConfig & getSolverConfig() const
Return the configuration of the solver used for this analysis.
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.
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),...
Block * getBlock()
Returns the operation block that contains this operation.
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Implement a predecessor iterator for blocks.
This class represents a point being branched from in the methods of the RegionBranchOpInterface.
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 represents a successor of a region.
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 const AbstractDenseLattice * getLatticeFor(ProgramPoint *dependent, LatticeAnchor anchor)=0
Get the dense lattice on the given lattice anchor and add dependent as its dependency.
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.
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.
void meet(AbstractDenseLattice *lhs, const AbstractDenseLattice &rhs)
Meet a lattice with another lattice and propagate an update if it changed.
virtual LogicalResult processOperation(Operation *op)
Visit an operation.
virtual AbstractDenseLattice * getLattice(LatticeAnchor anchor)=0
Get the dense lattice before the execution of the lattice anchor.
virtual void visitRegionBranchControlFlowTransfer(RegionBranchOpInterface branch, RegionBranchPoint regionFrom, RegionBranchPoint regionTo, const AbstractDenseLattice &after, AbstractDenseLattice *before)
Propagate the dense lattice backwards along the control flow edge from regionFrom to regionTo regions...
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.
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 LogicalResult processOperation(Operation *op)
Visit an operation.
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.
virtual AbstractDenseLattice * getLattice(LatticeAnchor anchor)=0
Get the dense lattice on the given lattice anchor.
LogicalResult visit(ProgramPoint *point) override
Visit a program point that modifies the state of the program.
virtual const AbstractDenseLattice * getLatticeFor(ProgramPoint *dependent, LatticeAnchor anchor)=0
Get the dense lattice on the given lattice anchor and add dependent as its dependency.
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
Operation * getPrevOp() const
Get the previous operation of this program point.
Operation * getNextOp() const
Get the next operation of this program point.
Block * getBlock() const
Get the block contains this program point.