13#include <llvm/Support/DebugLog.h>
23#define DEBUG_TYPE "liveness-analysis"
33 os << (
isLive ?
"live" :
"not live");
43 const auto *otherLiveness =
reinterpret_cast<const Liveness *
>(&other);
82 LDBG() <<
"[visitOperation] Enter: "
86 LDBG() <<
"[visitOperation] Operation has memory effects or is "
87 "return-like, marking operands live";
88 for (
auto *operand : operands) {
89 LDBG() <<
" [visitOperation] Marking operand live: " << operand <<
" ("
90 << operand->isLive <<
")";
96 bool foundLiveResult =
false;
98 if (r->isLive && !foundLiveResult) {
99 LDBG() <<
"[visitOperation] Found live result, "
100 "meeting all operands with result: "
104 for (
Liveness *operand : operands) {
105 LDBG() <<
" [visitOperation] Meeting operand: " << operand
106 <<
" with result: " << r;
109 foundLiveResult =
true;
111 LDBG() <<
"[visitOperation] Adding dependency for result: " << r
120 LDBG() <<
"Visiting branch operand: " << operand.
get()
125 assert((isa<RegionBranchOpInterface>(op) || isa<BranchOpInterface>(op) ||
126 isa<RegionBranchTerminatorOpInterface>(op)) &&
127 "expected the op to be `RegionBranchOpInterface`, "
128 "`BranchOpInterface` or `RegionBranchTerminatorOpInterface`");
138 bool mayLive =
false;
140 if (
auto regionBranchOp = dyn_cast<RegionBranchOpInterface>(op)) {
152 LDBG() <<
"[visitBranchOperand] Non-forwarded branch operand may be "
153 "live due to live result #"
154 << resultIndex <<
": "
164 for (
Block &block : region)
165 blocks.push_back(&block);
168 }
else if (isa<BranchOpInterface>(op)) {
175 LDBG() <<
"[visitBranchOperand] Non-forwarded branch operand may "
176 "be live due to branch op interface";
179 assert(isa<RegionBranchOpInterface>(parentOp) &&
180 "expected parent op to implement `RegionBranchOpInterface`");
192 LDBG() <<
"[visitBranchOperand] Non-forwarded branch "
193 "operand may be live due to parent live result: "
204 for (
Block &block : region)
205 blocks.push_back(&block);
209 for (
Block *block : blocks) {
215 LDBG() <<
"Non-forwarded branch operand may be "
216 "live due to memory effect in block: "
225 LDBG() <<
"Marking branch operand live: " << operand.
get();
237 LDBG() <<
"Visiting operation for non-forwarded branch operand: "
245 if (!isa<RegionBranchTerminatorOpInterface>(op))
251 LDBG() <<
"Visiting parent operation for non-forwarded branch operand: "
257 LDBG() <<
"Visiting call operand: " << operand.
get()
258 <<
" in op: " << *operand.
getOwner();
261 assert(isa<CallOpInterface>(operand.
getOwner()) &&
262 "expected the op to implement `CallOpInterface`");
271 LDBG() <<
"Marking call operand live: " << operand.
get();
278 LDBG() <<
"visitNonControlFlowArguments visit the region # "
283 llvm::map_to_vector(arguments, valuesToLattices);
285 llvm::map_to_vector(parentOp->
getResults(), valuesToLattices);
287 for (
Liveness *resultLattice : parentResultLattices) {
288 if (resultLattice->isLive) {
289 for (
Liveness *argumentLattice : argumentLattices) {
290 LDBG() <<
"make lattice: " << argumentLattice <<
" live";
300 LDBG() <<
"setToExitState for lattice: " << lattice;
302 LDBG() <<
"Lattice already live, nothing to do";
306 LDBG() <<
"Marking lattice live due to exit state";
316 LDBG() <<
"Constructing RunLivenessAnalysis for op: " << op->
getName();
321 LDBG() <<
"Initializing and running solver";
322 (
void)solver.initializeAndRun(op);
323 LDBG() <<
"RunLivenessAnalysis initialized for op: " << op->
getName()
324 <<
" check on unreachable code now:";
331 LDBG() <<
"Result: " <<
result.index() <<
" of "
333 <<
" has no liveness info (unreachable), mark dead";
337 for (
auto &block : region) {
338 for (
auto blockArg : llvm::enumerate(block.getArguments())) {
341 LDBG() <<
"Block argument: " << blockArg.index() <<
" of "
343 <<
" has no liveness info, mark dead";
344 solver.getOrCreateState<
Liveness>(blockArg.value());
352 return solver.lookupState<
Liveness>(val);
Block represents an ordered list of Operations.
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.
ProgramPoint * getProgramPointAfter(Operation *op)
IRValueT get() const
Return the current value being used by this operand.
This class represents an operand of an operation.
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...
Operation is the basic unit of execution within MLIR.
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
OperationName getName()
The name of an operation is the key identifier for it.
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),...
result_range getResults()
unsigned getNumResults()
Return the number of results held by this operation.
This class represents a successor of a region.
Region * getSuccessor() const
Return the given region successor.
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.
Operation * getParentOp()
Return the parent operation this region is attached to.
This class represents a collection of SymbolTables.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
void meet(AbstractSparseLattice *lhs, const AbstractSparseLattice &rhs)
Join the lattice element and propagate and update if it changed.
An analysis that, by going backwards along the dataflow graph, annotates each value with a boolean st...
void setToExitState(Liveness *lattice) override
Set the given lattice element(s) at control flow exit point(s).
void visitBranchOperand(OpOperand &operand) override
void visitCallOperand(OpOperand &operand) override
void visitNonControlFlowArguments(RegionSuccessor &successor, ArrayRef< BlockArgument > arguments) override
LogicalResult visitOperation(Operation *op, ArrayRef< Liveness * > operands, ArrayRef< const Liveness * > results) override
For every value, liveness analysis determines whether or not it is "live".
Liveness * getLatticeElement(Value value) override
Operation * getOwner() const
Return the owner of this operand.
void loadBaselineAnalyses(DataFlowSolver &solver)
Populates a DataFlowSolver with analyses that are required to ensure user-defined analyses are run pr...
Include the generated interface declarations.
ChangeResult
A result type used to indicate if a change happened.
bool isMemoryEffectFree(Operation *op)
Returns true if the given operation is free of memory effects.
This trait indicates that a terminator operation is "return-like".
This lattice represents, for a given value, whether or not it is "live".
void print(raw_ostream &os) const override
AbstractSparseLattice(Value value)
Lattices can only be created for values.
ChangeResult meet(const AbstractSparseLattice &other) override
Meet (intersect) the information in this lattice with 'rhs'.
RunLivenessAnalysis(Operation *op)
const Liveness * getLiveness(Value val)