MLIR 23.0.0git
DataFlowFramework.cpp
Go to the documentation of this file.
1//===- DataFlowFramework.cpp - A generic framework for data-flow analysis -===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "mlir/IR/Location.h"
11#include "mlir/IR/Operation.h"
12#include "mlir/IR/SymbolTable.h"
13#include "mlir/IR/Value.h"
14#include "llvm/ADT/ScopeExit.h"
15#include "llvm/ADT/iterator.h"
16#include "llvm/Config/abi-breaking.h"
17#include "llvm/Support/Casting.h"
18#include "llvm/Support/DebugLog.h"
19#include "llvm/Support/raw_ostream.h"
20
21#define DEBUG_TYPE "dataflow"
22#if LLVM_ENABLE_ABI_BREAKING_CHECKS
23#define DATAFLOW_DEBUG(X) X
24#else
25#define DATAFLOW_DEBUG(X)
26#endif // LLVM_ENABLE_ABI_BREAKING_CHECKS
27
28using namespace mlir;
29
30//===----------------------------------------------------------------------===//
31// GenericLatticeAnchor
32//===----------------------------------------------------------------------===//
33
35
36//===----------------------------------------------------------------------===//
37// AnalysisState
38//===----------------------------------------------------------------------===//
39
41
43 DataFlowAnalysis *analysis) {
44 auto inserted = dependents.insert({dependent, analysis});
47 if (inserted) {
48 LDBG() << "Creating dependency between " << debugName << " of " << anchor
49 << "\nand " << debugName << " on " << *dependent;
50 }
51 });
52}
53
54void AnalysisState::dump() const { print(llvm::errs()); }
55
56//===----------------------------------------------------------------------===//
57// ProgramPoint
58//===----------------------------------------------------------------------===//
59
61 if (isNull()) {
62 os << "<NULL POINT>";
63 return;
64 }
65 if (!isBlockStart()) {
66 os << "<after operation>:"
67 << OpWithFlags(getPrevOp(), OpPrintingFlags().skipRegions());
68 return;
69 }
70 if (!isBlockEnd()) {
71 os << "<before operation>:"
72 << OpWithFlags(getNextOp(), OpPrintingFlags().skipRegions());
73 return;
74 }
75 os << "<beginning of empty block>";
76}
77
78//===----------------------------------------------------------------------===//
79// LatticeAnchor
80//===----------------------------------------------------------------------===//
81
83 if (isNull()) {
84 os << "<NULL POINT>";
85 return;
86 }
87 if (auto *latticeAnchor = llvm::dyn_cast<GenericLatticeAnchor *>(*this))
88 return latticeAnchor->print(os);
89 if (auto value = llvm::dyn_cast<Value>(*this)) {
90 return value.print(os, OpPrintingFlags().skipRegions());
91 }
92
93 return llvm::cast<ProgramPoint *>(*this)->print(os);
94}
95
97 if (auto *latticeAnchor = llvm::dyn_cast<GenericLatticeAnchor *>(*this))
98 return latticeAnchor->getLoc();
99 if (auto value = llvm::dyn_cast<Value>(*this))
100 return value.getLoc();
101
102 ProgramPoint *pp = llvm::cast<ProgramPoint *>(*this);
103 if (!pp->isBlockStart())
104 return pp->getPrevOp()->getLoc();
105 return pp->getBlock()->getParent()->getLoc();
106}
107
108//===----------------------------------------------------------------------===//
109// DataFlowSolver
110//===----------------------------------------------------------------------===//
111
113 Operation *top,
114 llvm::function_ref<bool(DataFlowAnalysis &)> analysisFilter) {
115 // Enable enqueue to the worklist.
116 isRunning = true;
117 llvm::scope_exit guard([&]() { isRunning = false; });
118
119 bool isInterprocedural = config.isInterprocedural();
120 llvm::scope_exit restoreInterprocedural(
121 [&]() { config.setInterprocedural(isInterprocedural); });
122 if (isInterprocedural && !top->hasTrait<OpTrait::SymbolTable>())
123 config.setInterprocedural(false);
124
125 auto shouldInitialize = [&](DataFlowAnalysis &analysis) {
126 return !analysisFilter || analysisFilter(analysis);
127 };
128
129 // Initialize equivalent lattice anchors.
130 for (DataFlowAnalysis &analysis : llvm::make_pointee_range(childAnalyses)) {
131 if (!shouldInitialize(analysis))
132 continue;
133 analysis.initializeEquivalentLatticeAnchor(top);
134 }
135
136 // Initialize the analyses.
137 for (DataFlowAnalysis &analysis : llvm::make_pointee_range(childAnalyses)) {
138 if (!shouldInitialize(analysis))
139 continue;
140 DATAFLOW_DEBUG(LDBG() << "Priming analysis: " << analysis.debugName);
141 if (failed(analysis.initialize(top)))
142 return failure();
143 }
144
145 // Run the analysis until fixpoint.
146 // Iterate until all states are in some initialized state and the worklist
147 // is exhausted.
148 while (!worklist.empty()) {
149 auto [point, analysis] = worklist.front();
150 worklist.pop();
151
152 DATAFLOW_DEBUG(LDBG() << "Invoking '" << analysis->debugName
153 << "' on: " << *point);
154 if (failed(analysis->visit(point)))
155 return failure();
156 }
157
158 return success();
159}
160
162 ChangeResult changed) {
163 assert(isRunning &&
164 "DataFlowSolver is not running, should not use propagateIfChanged");
165 if (changed == ChangeResult::Change) {
166 DATAFLOW_DEBUG(LDBG() << "Propagating update to " << state->debugName
167 << " of " << state->anchor << "\n"
168 << "Value: " << *state);
169 state->onUpdate(this);
170 }
171}
172
173//===----------------------------------------------------------------------===//
174// DataFlowAnalysis
175//===----------------------------------------------------------------------===//
176
178
180
182 ProgramPoint *point) {
183 state->addDependency(point, this);
184}
185
187 ChangeResult changed) {
188 solver.propagateIfChanged(state, changed);
189}
return success()
#define DATAFLOW_DEBUG(X)
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be inserted(the insertion happens right before the *insertion point). Since `begin` can itself be invalidated due to the memref *rewriting done from this method
Base class for generic analysis states.
LLVM_DUMP_METHOD void dump() const
void addDependency(ProgramPoint *point, DataFlowAnalysis *analysis)
Add a dependency to this analysis state on a lattice anchor and an analysis.
virtual void print(raw_ostream &os) const =0
Print the contents of the analysis state.
virtual void onUpdate(DataFlowSolver *solver) const
This function is called by the solver when the analysis state is updated to enqueue more work items.
virtual ~AnalysisState()
LatticeAnchor anchor
The lattice anchor to which the state belongs.
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition Block.cpp:27
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.
void propagateIfChanged(AnalysisState *state, ChangeResult changed)
Propagate an update to a state if it changed.
DataFlowAnalysis(DataFlowSolver &solver)
Create an analysis with a reference to the parent solver.
friend class DataFlowSolver
Allow the data-flow solver to access the internals of this class.
LogicalResult initializeAndRun(Operation *top, llvm::function_ref< bool(DataFlowAnalysis &)> analysisFilter=nullptr)
Initialize analyses starting from the provided top-level operation and run the analysis until fixpoin...
friend class DataFlowAnalysis
Allow the base child analysis class to access the internals of the solver.
void propagateIfChanged(AnalysisState *state, ChangeResult changed)
Propagate an update to an analysis state if it changed by pushing dependent work items to the back of...
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
Set of flags used to control the behavior of the various IR print methods (e.g.
A trait used to provide symbol table functionalities to a region operation.
A wrapper class that allows for printing an operation with a set of flags, useful to act as a "stream...
Definition Operation.h:1143
Operation is the basic unit of execution within MLIR.
Definition Operation.h:88
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:775
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:241
Location getLoc()
Return a location for this region.
Definition Region.cpp:31
Include the generated interface declarations.
ChangeResult
A result type used to indicate if a change happened.
Location getLoc() const
Get the source location of the lattice anchor.
void print(raw_ostream &os) const
Print the lattice anchor.
Program point represents a specific location in the execution of a program.
bool isNull() const
Returns true if this program point is set.
Block * getBlock() const
Get the block contains this program point.
Operation * getNextOp() const
Get the next operation of this program point.
void print(raw_ostream &os) const
Print the program point.
Operation * getPrevOp() const
Get the previous operation of this program point.