1 //===- DeadCodeAnalysis.h - Dead code 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 //
9 // This file implements dead code analysis using the data-flow analysis
10 // framework. This analysis uses the results of constant propagation to
11 // determine live blocks, control-flow edges, and control-flow predecessors.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #ifndef MLIR_ANALYSIS_DATAFLOW_DEADCODEANALYSIS_H
16 #define MLIR_ANALYSIS_DATAFLOW_DEADCODEANALYSIS_H
17 
18 #include "mlir/Analysis/DataFlowFramework.h"
19 #include "mlir/IR/SymbolTable.h"
20 #include "llvm/ADT/SmallPtrSet.h"
21 
22 namespace mlir {
23 
24 class CallOpInterface;
25 class CallableOpInterface;
26 class BranchOpInterface;
27 class RegionBranchOpInterface;
28 
29 namespace dataflow {
30 
31 //===----------------------------------------------------------------------===//
32 // Executable
33 //===----------------------------------------------------------------------===//
34 
35 /// This is a simple analysis state that represents whether the associated
36 /// program point (either a block or a control-flow edge) is live.
37 class Executable : public AnalysisState {
38 public:
39   using AnalysisState::AnalysisState;
40 
41   /// The state is initialized by default.
isUninitialized()42   bool isUninitialized() const override { return false; }
43 
44   /// The state is always initialized.
defaultInitialize()45   ChangeResult defaultInitialize() override { return ChangeResult::NoChange; }
46 
47   /// Set the state of the program point to live.
48   ChangeResult setToLive();
49 
50   /// Get whether the program point is live.
isLive()51   bool isLive() const { return live; }
52 
53   /// Print the liveness.
54   void print(raw_ostream &os) const override;
55 
56   /// When the state of the program point is changed to live, re-invoke
57   /// subscribed analyses on the operations in the block and on the block
58   /// itself.
59   void onUpdate(DataFlowSolver *solver) const override;
60 
61   /// Subscribe an analysis to changes to the liveness.
blockContentSubscribe(DataFlowAnalysis * analysis)62   void blockContentSubscribe(DataFlowAnalysis *analysis) {
63     subscribers.insert(analysis);
64   }
65 
66 private:
67   /// Whether the program point is live. Optimistically assume that the program
68   /// point is dead.
69   bool live = false;
70 
71   /// A set of analyses that should be updated when this state changes.
72   SetVector<DataFlowAnalysis *, SmallVector<DataFlowAnalysis *, 4>,
73             SmallPtrSet<DataFlowAnalysis *, 4>>
74       subscribers;
75 };
76 
77 //===----------------------------------------------------------------------===//
78 // PredecessorState
79 //===----------------------------------------------------------------------===//
80 
81 /// This analysis state represents a set of live control-flow "predecessors" of
82 /// a program point (either an operation or a block), which are the last
83 /// operations along all execution paths that pass through this point.
84 ///
85 /// For example, in dead-code analysis, an operation with region control-flow
86 /// can be the predecessor of a region's entry block or itself, the exiting
87 /// terminator of a region can be the predecessor of the parent operation or
88 /// another region's entry block, the callsite of a callable operation can be
89 /// the predecessor to its entry block, and the exiting terminator or a callable
90 /// operation can be the predecessor of the call operation.
91 ///
92 /// The state can optionally contain information about which values are
93 /// propagated from each predecessor to the successor point.
94 ///
95 /// The state can indicate that it is underdefined, meaning that not all live
96 /// control-flow predecessors can be known.
97 class PredecessorState : public AnalysisState {
98 public:
99   using AnalysisState::AnalysisState;
100 
101   /// The state is initialized by default.
isUninitialized()102   bool isUninitialized() const override { return false; }
103 
104   /// The state is always initialized.
defaultInitialize()105   ChangeResult defaultInitialize() override { return ChangeResult::NoChange; }
106 
107   /// Print the known predecessors.
108   void print(raw_ostream &os) const override;
109 
110   /// Returns true if all predecessors are known.
allPredecessorsKnown()111   bool allPredecessorsKnown() const { return allKnown; }
112 
113   /// Indicate that there are potentially unknown predecessors.
setHasUnknownPredecessors()114   ChangeResult setHasUnknownPredecessors() {
115     return std::exchange(allKnown, false) ? ChangeResult::Change
116                                           : ChangeResult::NoChange;
117   }
118 
119   /// Get the known predecessors.
getKnownPredecessors()120   ArrayRef<Operation *> getKnownPredecessors() const {
121     return knownPredecessors.getArrayRef();
122   }
123 
124   /// Get the successor inputs from a predecessor.
getSuccessorInputs(Operation * predecessor)125   ValueRange getSuccessorInputs(Operation *predecessor) const {
126     return successorInputs.lookup(predecessor);
127   }
128 
129   /// Add a known predecessor.
130   ChangeResult join(Operation *predecessor);
131 
132   /// Add a known predecessor with successor inputs.
133   ChangeResult join(Operation *predecessor, ValueRange inputs);
134 
135 private:
136   /// Whether all predecessors are known. Optimistically assume that we know
137   /// all predecessors.
138   bool allKnown = true;
139 
140   /// The known control-flow predecessors of this program point.
141   SetVector<Operation *, SmallVector<Operation *, 4>,
142             SmallPtrSet<Operation *, 4>>
143       knownPredecessors;
144 
145   /// The successor inputs when branching from a given predecessor.
146   DenseMap<Operation *, ValueRange> successorInputs;
147 };
148 
149 //===----------------------------------------------------------------------===//
150 // CFGEdge
151 //===----------------------------------------------------------------------===//
152 
153 /// This program point represents a control-flow edge between a block and one
154 /// of its successors.
155 class CFGEdge
156     : public GenericProgramPointBase<CFGEdge, std::pair<Block *, Block *>> {
157 public:
158   using Base::Base;
159 
160   /// Get the block from which the edge originates.
getFrom()161   Block *getFrom() const { return getValue().first; }
162   /// Get the target block.
getTo()163   Block *getTo() const { return getValue().second; }
164 
165   /// Print the blocks between the control-flow edge.
166   void print(raw_ostream &os) const override;
167   /// Get a fused location of both blocks.
168   Location getLoc() const override;
169 };
170 
171 //===----------------------------------------------------------------------===//
172 // DeadCodeAnalysis
173 //===----------------------------------------------------------------------===//
174 
175 /// Dead code analysis analyzes control-flow, as understood by
176 /// `RegionBranchOpInterface` and `BranchOpInterface`, and the callgraph, as
177 /// understood by `CallableOpInterface` and `CallOpInterface`.
178 ///
179 /// This analysis uses known constant values of operands to determine the
180 /// liveness of each block and each edge between a block and its predecessors.
181 /// For region control-flow, this analysis determines the predecessor operations
182 /// for region entry blocks and region control-flow operations. For the
183 /// callgraph, this analysis determines the callsites and live returns of every
184 /// function.
185 class DeadCodeAnalysis : public DataFlowAnalysis {
186 public:
187   explicit DeadCodeAnalysis(DataFlowSolver &solver);
188 
189   /// Initialize the analysis by visiting every operation with potential
190   /// control-flow semantics.
191   LogicalResult initialize(Operation *top) override;
192 
193   /// Visit an operation with control-flow semantics and deduce which of its
194   /// successors are live.
195   LogicalResult visit(ProgramPoint point) override;
196 
197 private:
198   /// Find and mark symbol callables with potentially unknown callsites as
199   /// having overdefined predecessors. `top` is the top-level operation that the
200   /// analysis is operating on.
201   void initializeSymbolCallables(Operation *top);
202 
203   /// Recursively Initialize the analysis on nested regions.
204   LogicalResult initializeRecursively(Operation *op);
205 
206   /// Visit the given call operation and compute any necessary lattice state.
207   void visitCallOperation(CallOpInterface call);
208 
209   /// Visit the given branch operation with successors and try to determine
210   /// which are live from the current block.
211   void visitBranchOperation(BranchOpInterface branch);
212 
213   /// Visit the given region branch operation, which defines regions, and
214   /// compute any necessary lattice state. This also resolves the lattice state
215   /// of both the operation results and any nested regions.
216   void visitRegionBranchOperation(RegionBranchOpInterface branch);
217 
218   /// Visit the given terminator operation that exits a region under an
219   /// operation with control-flow semantics. These are terminators with no CFG
220   /// successors.
221   void visitRegionTerminator(Operation *op, RegionBranchOpInterface branch);
222 
223   /// Visit the given terminator operation that exits a callable region. These
224   /// are terminators with no CFG successors.
225   void visitCallableTerminator(Operation *op, CallableOpInterface callable);
226 
227   /// Mark the edge between `from` and `to` as executable.
228   void markEdgeLive(Block *from, Block *to);
229 
230   /// Mark the entry blocks of the operation as executable.
231   void markEntryBlocksLive(Operation *op);
232 
233   /// Get the constant values of the operands of the operation. Returns none if
234   /// any of the operand lattices are uninitialized.
235   Optional<SmallVector<Attribute>> getOperandValues(Operation *op);
236 
237   /// A symbol table used for O(1) symbol lookups during simplification.
238   SymbolTableCollection symbolTable;
239 };
240 
241 } // end namespace dataflow
242 } // end namespace mlir
243 
244 #endif // MLIR_ANALYSIS_DATAFLOW_DEADCODEANALYSIS_H
245