24 #include "llvm/Support/Casting.h"
43 os << (live ?
"live" :
"dead");
50 if (pp->isBlockStart()) {
53 solver->
enqueue({pp, analysis});
59 }
else if (
auto *latticeAnchor =
60 llvm::dyn_cast_if_present<GenericLatticeAnchor *>(
anchor)) {
62 if (
auto *edge = dyn_cast<CFGEdge>(latticeAnchor)) {
77 os <<
"predecessors:\n";
79 os <<
" " << *op <<
"\n";
89 if (!inputs.empty()) {
90 ValueRange &curInputs = successorInputs[predecessor];
91 if (curInputs != inputs) {
121 registerAnchorKind<CFGEdge>();
136 initializeSymbolCallables(top);
138 return initializeRecursively(top);
141 void DeadCodeAnalysis::initializeSymbolCallables(
Operation *top) {
143 auto walkFn = [&](
Operation *symTable,
bool allUsesVisible) {
145 Block *symbolTableBlock = &symbolTableRegion.
front();
147 bool foundSymbolCallable =
false;
148 for (
auto callable : symbolTableBlock->
getOps<CallableOpInterface>()) {
149 Region *callableRegion = callable.getCallableRegion();
152 auto symbol = dyn_cast<SymbolOpInterface>(callable.getOperation());
158 if (symbol.isPublic() || (!allUsesVisible && symbol.isNested())) {
163 foundSymbolCallable =
true;
167 if (!foundSymbolCallable)
171 std::optional<SymbolTable::UseRange> uses =
176 return top->
walk([&](CallableOpInterface callable) {
184 if (isa<CallOpInterface>(use.getUser()))
203 isa<RegionBranchOpInterface, CallableOpInterface>(op->
getParentOp()) &&
207 LogicalResult DeadCodeAnalysis::initializeRecursively(
Operation *op) {
215 ->blockContentSubscribe(
this);
223 if (failed(initializeRecursively(&op)))
228 void DeadCodeAnalysis::markEdgeLive(
Block *from,
Block *to) {
232 getOrCreate<Executable>(getLatticeAnchor<CFGEdge>(from, to));
236 void DeadCodeAnalysis::markEntryBlocksLive(
Operation *op) {
257 if (
auto call = dyn_cast<CallOpInterface>(op))
258 visitCallOperation(call);
263 if (
auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
264 visitRegionBranchOperation(branch);
267 }
else if (
auto callable = dyn_cast<CallableOpInterface>(op)) {
268 const auto *callsites = getOrCreateFor<PredecessorState>(
273 if (!callsites->allPredecessorsKnown() ||
274 !callsites->getKnownPredecessors().empty())
275 markEntryBlocksLive(callable);
279 markEntryBlocksLive(op);
284 if (
auto branch = dyn_cast<RegionBranchOpInterface>(op->
getParentOp())) {
286 visitRegionTerminator(op, branch);
287 }
else if (
auto callable =
288 dyn_cast<CallableOpInterface>(op->
getParentOp())) {
290 visitCallableTerminator(op, callable);
296 if (
auto branch = dyn_cast<BranchOpInterface>(op)) {
297 visitBranchOperation(branch);
302 markEdgeLive(op->
getBlock(), successor);
309 void DeadCodeAnalysis::visitCallOperation(CallOpInterface call) {
310 Operation *callableOp = call.resolveCallableInTable(&symbolTable);
313 const auto isExternalCallable = [
this](
Operation *op) {
318 if (
auto callable = dyn_cast<CallableOpInterface>(op))
319 return !callable.getCallableRegion();
326 if (isa_and_nonnull<SymbolOpInterface>(callableOp) &&
327 !isExternalCallable(callableOp)) {
351 if (cv->
getValue().isUninitialized())
353 operands.push_back(cv->
getValue().getConstantValue());
358 std::optional<SmallVector<Attribute>>
359 DeadCodeAnalysis::getOperandValues(
Operation *op) {
361 auto *lattice = getOrCreate<Lattice<ConstantValue>>(value);
362 lattice->useDefSubscribe(
this);
367 void DeadCodeAnalysis::visitBranchOperation(BranchOpInterface branch) {
369 std::optional<SmallVector<Attribute>> operands = getOperandValues(branch);
373 if (
Block *successor = branch.getSuccessorForOperands(*operands)) {
374 markEdgeLive(branch->getBlock(), successor);
378 markEdgeLive(branch->getBlock(), successor);
382 void DeadCodeAnalysis::visitRegionBranchOperation(
383 RegionBranchOpInterface branch) {
385 std::optional<SmallVector<Attribute>> operands = getOperandValues(branch);
390 branch.getEntrySuccessorRegions(*operands, successors);
394 successor.getSuccessor()
398 auto *state = getOrCreate<Executable>(point);
401 auto *predecessors = getOrCreate<PredecessorState>(point);
404 predecessors->join(branch, successor.getSuccessorInputs()));
408 void DeadCodeAnalysis::visitRegionTerminator(
Operation *op,
409 RegionBranchOpInterface branch) {
410 std::optional<SmallVector<Attribute>> operands = getOperandValues(op);
415 if (
auto terminator = dyn_cast<RegionBranchTerminatorOpInterface>(op))
416 terminator.getSuccessorRegions(*operands, successors);
424 if (
Region *region = successor.getSuccessor()) {
428 predecessors = getOrCreate<PredecessorState>(
436 predecessors->
join(op, successor.getSuccessorInputs()));
440 void DeadCodeAnalysis::visitCallableTerminator(
Operation *op,
441 CallableOpInterface callable) {
443 auto *callsites = getOrCreateFor<PredecessorState>(
446 for (
Operation *predecessor : callsites->getKnownPredecessors()) {
447 assert(isa<CallOpInterface>(predecessor));
static std::optional< SmallVector< Attribute > > getOperandValuesImpl(Operation *op, function_ref< const Lattice< ConstantValue > *(Value)> getLattice)
Get the constant values of the operands of an operation.
static bool isRegionOrCallableReturn(Operation *op)
Returns true if the operation is a returning terminator in region control-flow or the terminator of a...
static MLIRContext * getContext(OpFoldResult val)
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.
Block represents an ordered list of Operations.
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
SuccessorRange getSuccessors()
Operation * getTerminator()
Get the terminator operation of this block.
void print(raw_ostream &os)
iterator_range< op_iterator< OpT > > getOps()
Return an iterator range over the operations within this block that are of 'OpT'.
Base class for all data-flow analyses.
void propagateIfChanged(AnalysisState *state, ChangeResult changed)
Propagate an update to a state if it changed.
ProgramPoint * getProgramPointAfter(Operation *op)
ProgramPoint * getProgramPointBefore(Operation *op)
Get a uniqued program point instance.
The general data-flow analysis solver.
void enqueue(WorkItem item)
Push a work item onto the worklist.
ProgramPoint * getProgramPointAfter(Operation *op)
ProgramPoint * getProgramPointBefore(Operation *op)
Get a uniqued program point instance.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
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()
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),...
unsigned getNumRegions()
Returns the number of regions held by this operation.
unsigned getNumOperands()
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Block * getBlock()
Returns the operation block that contains this operation.
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
operand_range getOperands()
Returns an iterator on the underlying Value's.
bool isAncestor(Operation *other)
Return true if this operation is an ancestor of the other operation.
SuccessorRange getSuccessors()
Region * getParentRegion()
Returns the region to which the instruction belongs.
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.
Location getLoc()
Return a location for this region.
Operation * lookupSymbolIn(Operation *symbolTableOp, StringAttr symbol)
Look up a symbol with the specified name within the specified symbol table operation,...
This class represents a specific symbol use.
static void walkSymbolTables(Operation *op, bool allSymUsesVisible, function_ref< void(Operation *, bool)> callback)
Walks all symbol table operations nested within, and including, op.
static std::optional< UseRange > getSymbolUses(Operation *from)
Get an iterator range for all of the uses, for any symbol, that are nested within the given operation...
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...
Block * getTo() const
Get the target block.
Block * getFrom() const
Get the block from which the edge originates.
Location getLoc() const override
Get a fused location of both blocks.
void print(raw_ostream &os) const override
Print the blocks between the control-flow edge.
DeadCodeAnalysis(DataFlowSolver &solver)
LogicalResult visit(ProgramPoint *point) override
Visit an operation with control-flow semantics and deduce which of its successors are live.
LogicalResult initialize(Operation *top) override
Initialize the analysis by visiting every operation with potential control-flow semantics.
void onUpdate(DataFlowSolver *solver) const override
When the state of the lattice anchor is changed to live, re-invoke subscribed analyses on the operati...
ChangeResult setToLive()
Set the state of the lattice anchor to live.
void print(raw_ostream &os) const override
Print the liveness.
This class represents a lattice holding a specific value of type ValueT.
ValueT & getValue()
Return the value held by this lattice.
This analysis state represents a set of live control-flow "predecessors" of a program point (either a...
ChangeResult setHasUnknownPredecessors()
Indicate that there are potentially unknown predecessors.
bool allPredecessorsKnown() const
Returns true if all predecessors are known.
void print(raw_ostream &os) const override
Print the known predecessors.
ChangeResult join(Operation *predecessor)
Add a known predecessor.
ArrayRef< Operation * > getKnownPredecessors() const
Get the known predecessors.
Include the generated interface declarations.
ChangeResult
A result type used to indicate if a change happened.
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
This trait indicates that a terminator operation is "return-like".
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.