25 #include "llvm/ADT/ScopeExit.h" 
   26 #include "llvm/Support/Casting.h" 
   27 #include "llvm/Support/Debug.h" 
   28 #include "llvm/Support/DebugLog.h" 
   32 #define DEBUG_TYPE "dead-code-analysis" 
   49   os << (live ? 
"live" : 
"dead");
 
   56     if (pp->isBlockStart()) {
 
   65   } 
else if (
auto *latticeAnchor =
 
   66                  llvm::dyn_cast_if_present<GenericLatticeAnchor *>(
anchor)) {
 
   68     if (
auto *edge = dyn_cast<CFGEdge>(latticeAnchor)) {
 
   83   os << 
"predecessors:";
 
  103   if (!inputs.empty()) {
 
  104     ValueRange &curInputs = successorInputs[predecessor];
 
  105     if (curInputs != inputs) {
 
  135   registerAnchorKind<CFGEdge>();
 
  139   LDBG() << 
"Initializing DeadCodeAnalysis for top-level op: " 
  148     LDBG() << 
"Marked entry block live for region in op: " 
  154   initializeSymbolCallables(top);
 
  156   return initializeRecursively(top);
 
  159 void DeadCodeAnalysis::initializeSymbolCallables(
Operation *top) {
 
  160   LDBG() << 
"[init] Entering initializeSymbolCallables for top-level op: " 
  164   auto walkFn = [&](
Operation *symTable, 
bool allUsesVisible) {
 
  165     LDBG() << 
"[init] Processing symbol table op: " 
  168     Block *symbolTableBlock = &symbolTableRegion.
front();
 
  170     bool foundSymbolCallable = 
false;
 
  171     for (
auto callable : symbolTableBlock->
getOps<CallableOpInterface>()) {
 
  172       LDBG() << 
"[init] Found CallableOpInterface: " 
  175       Region *callableRegion = callable.getCallableRegion();
 
  178       auto symbol = dyn_cast<SymbolOpInterface>(callable.getOperation());
 
  184       if (symbol.isPublic() || (!allUsesVisible && symbol.isNested())) {
 
  188         LDBG() << 
"[init] Marked callable as having unknown predecessors: " 
  192       foundSymbolCallable = 
true;
 
  196     if (!foundSymbolCallable)
 
  200     std::optional<SymbolTable::UseRange> uses =
 
  205       LDBG() << 
"[init] Could not gather symbol uses, conservatively marking " 
  206                 "all nested callables as having unknown predecessors";
 
  207       return top->
walk([&](CallableOpInterface callable) {
 
  211         LDBG() << 
"[init] Marked nested callable as " 
  212                   "having unknown predecessors: " 
  219       if (isa<CallOpInterface>(use.getUser()))
 
  228       LDBG() << 
"[init] Found non-call use for symbol, " 
  229                 "marked as having unknown predecessors: " 
  235   LDBG() << 
"[init] Finished initializeSymbolCallables for top-level op: " 
  243          isa<RegionBranchOpInterface, CallableOpInterface>(op->
getParentOp()) &&
 
  247 LogicalResult DeadCodeAnalysis::initializeRecursively(
Operation *op) {
 
  248   LDBG() << 
"[init] Entering initializeRecursively for op: " 
  253     LDBG() << 
"[init] Visiting op with control-flow semantics: " 
  259           ->blockContentSubscribe(
this);
 
  269     bool savedHasSymbolTable = hasSymbolTable;
 
  270     auto restoreHasSymbolTable =
 
  271         llvm::make_scope_exit([&]() { hasSymbolTable = savedHasSymbolTable; });
 
  273       hasSymbolTable = 
true;
 
  276       LDBG() << 
"[init] Recursing into region of op: " 
  278       for (
Operation &nestedOp : region.getOps()) {
 
  279         LDBG() << 
"[init] Recursing into nested op: " 
  281         if (
failed(initializeRecursively(&nestedOp)))
 
  286   LDBG() << 
"[init] Finished initializeRecursively for op: " 
  291 void DeadCodeAnalysis::markEdgeLive(
Block *from, 
Block *to) {
 
  292   LDBG() << 
"Marking edge live from block " << from << 
" to block " << to;
 
  296       getOrCreate<Executable>(getLatticeAnchor<CFGEdge>(from, to));
 
  300 void DeadCodeAnalysis::markEntryBlocksLive(
Operation *op) {
 
  301   LDBG() << 
"Marking entry blocks live for op: " 
  309     LDBG() << 
"Marked entry block live for region in op: " 
  315   LDBG() << 
"Visiting program point: " << *point;
 
  319   LDBG() << 
"Visiting operation: " 
  326     LDBG() << 
"Parent block not live, skipping op: " 
  332   if (
auto call = dyn_cast<CallOpInterface>(op)) {
 
  333     LDBG() << 
"Visiting call operation: " 
  335     visitCallOperation(call);
 
  341     if (
auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
 
  342       LDBG() << 
"Visiting region branch operation: " 
  344       visitRegionBranchOperation(branch);
 
  347     } 
else if (
auto callable = dyn_cast<CallableOpInterface>(op)) {
 
  348       LDBG() << 
"Visiting callable operation: " 
  350       const auto *callsites = getOrCreateFor<PredecessorState>(
 
  355       if (!callsites->allPredecessorsKnown() ||
 
  356           !callsites->getKnownPredecessors().empty())
 
  357         markEntryBlocksLive(callable);
 
  361       LDBG() << 
"Marking all entry blocks live for op: " 
  363       markEntryBlocksLive(op);
 
  368     if (
auto branch = dyn_cast<RegionBranchOpInterface>(op->
getParentOp())) {
 
  369       LDBG() << 
"Visiting region terminator: " 
  372       visitRegionTerminator(op, branch);
 
  373     } 
else if (
auto callable =
 
  374                    dyn_cast<CallableOpInterface>(op->
getParentOp())) {
 
  375       LDBG() << 
"Visiting callable terminator: " 
  378       visitCallableTerminator(op, callable);
 
  384     if (
auto branch = dyn_cast<BranchOpInterface>(op)) {
 
  385       LDBG() << 
"Visiting branch operation: " 
  387       visitBranchOperation(branch);
 
  391       LDBG() << 
"Marking all successors live for op: " 
  394         markEdgeLive(op->
getBlock(), successor);
 
  401 void DeadCodeAnalysis::visitCallOperation(CallOpInterface call) {
 
  402   LDBG() << 
"visitCallOperation: " 
  407     callableOp = call.resolveCallableInTable(&symbolTable);
 
  410         << 
"No symbol table present in analysis scope, can't resolve callable";
 
  413   const auto isExternalCallable = [
this](
Operation *op) {
 
  418     if (
auto callable = dyn_cast<CallableOpInterface>(op))
 
  419       return !callable.getCallableRegion();
 
  426   if (isa_and_nonnull<SymbolOpInterface>(callableOp) &&
 
  427       !isExternalCallable(callableOp)) {
 
  432     LDBG() << 
"Added callsite as predecessor for callable: " 
  439     LDBG() << 
"Marked call op's predecessors as unknown for: " 
  447 std::optional<SmallVector<Attribute>>
 
  448 DeadCodeAnalysis::getOperandValues(
Operation *op) {
 
  455     if (cv->
getValue().isUninitialized())
 
  457     operands.push_back(cv->
getValue().getConstantValue());
 
  462 void DeadCodeAnalysis::visitBranchOperation(BranchOpInterface branch) {
 
  463   LDBG() << 
"visitBranchOperation: " 
  466   std::optional<SmallVector<Attribute>> operands = getOperandValues(branch);
 
  470   if (
Block *successor = branch.getSuccessorForOperands(*operands)) {
 
  471     markEdgeLive(branch->getBlock(), successor);
 
  472     LDBG() << 
"Branch has single successor: " << successor;
 
  476       markEdgeLive(branch->getBlock(), successor);
 
  477     LDBG() << 
"Branch has multiple/all successors live";
 
  481 void DeadCodeAnalysis::visitRegionBranchOperation(
 
  482     RegionBranchOpInterface branch) {
 
  483   LDBG() << 
"visitRegionBranchOperation: " 
  486   std::optional<SmallVector<Attribute>> operands = getOperandValues(branch);
 
  491   branch.getEntrySuccessorRegions(*operands, successors);
 
  493   visitRegionBranchEdges(branch, branch.getOperation(), successors);
 
  496 void DeadCodeAnalysis::visitRegionTerminator(
Operation *op,
 
  497                                              RegionBranchOpInterface branch) {
 
  498   LDBG() << 
"visitRegionTerminator: " << *op;
 
  499   std::optional<SmallVector<Attribute>> operands = getOperandValues(op);
 
  504   auto terminator = dyn_cast<RegionBranchTerminatorOpInterface>(op);
 
  507   terminator.getSuccessorRegions(*operands, successors);
 
  508   visitRegionBranchEdges(branch, op, successors);
 
  511 void DeadCodeAnalysis::visitRegionBranchEdges(
 
  512     RegionBranchOpInterface regionBranchOp, 
Operation *predecessorOp,
 
  517         successor.getSuccessor()
 
  522     auto *state = getOrCreate<Executable>(point);
 
  524     LDBG() << 
"Marked region successor live: " << *point;
 
  527     auto *predecessors = getOrCreate<PredecessorState>(point);
 
  530         predecessors->join(predecessorOp, successor.getSuccessorInputs()));
 
  531     LDBG() << 
"Added region branch as predecessor for successor: " << *point;
 
  535 void DeadCodeAnalysis::visitCallableTerminator(
Operation *op,
 
  536                                                CallableOpInterface callable) {
 
  537   LDBG() << 
"visitCallableTerminator: " << *op;
 
  539   auto *callsites = getOrCreateFor<PredecessorState>(
 
  542   for (
Operation *predecessor : callsites->getKnownPredecessors()) {
 
  543     assert(isa<CallOpInterface>(predecessor));
 
  548       LDBG() << 
"Added callable terminator as predecessor for callsite: " 
  554                          predecessors->setHasUnknownPredecessors());
 
  555       LDBG() << 
"Could not resolve callable terminator for callsite: " 
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...
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...
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()
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.
virtual 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...
void useDefSubscribe(DataFlowAnalysis *analysis)
Subscribe an analysis to updates of the lattice.
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.
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.