xref: /llvm-project/mlir/lib/Analysis/DataFlow/DeadCodeAnalysis.cpp (revision 13317502da8ee3885854f67700140586c0edafee)
1c095afcbSMogball //===- DeadCodeAnalysis.cpp - Dead code analysis --------------------------===//
2c095afcbSMogball //
3c095afcbSMogball // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4c095afcbSMogball // See https://llvm.org/LICENSE.txt for license information.
5c095afcbSMogball // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6c095afcbSMogball //
7c095afcbSMogball //===----------------------------------------------------------------------===//
8c095afcbSMogball 
9c095afcbSMogball #include "mlir/Analysis/DataFlow/DeadCodeAnalysis.h"
10c095afcbSMogball #include "mlir/Analysis/DataFlow/ConstantPropagationAnalysis.h"
11e48038a8SMehdi Amini #include "mlir/Analysis/DataFlow/SparseAnalysis.h"
126a666737SZhixun Tan #include "mlir/Analysis/DataFlowFramework.h"
13e48038a8SMehdi Amini #include "mlir/IR/Attributes.h"
14e48038a8SMehdi Amini #include "mlir/IR/Block.h"
15e48038a8SMehdi Amini #include "mlir/IR/Diagnostics.h"
16e48038a8SMehdi Amini #include "mlir/IR/Location.h"
17e48038a8SMehdi Amini #include "mlir/IR/Operation.h"
18e48038a8SMehdi Amini #include "mlir/IR/SymbolTable.h"
19e48038a8SMehdi Amini #include "mlir/IR/Value.h"
20e48038a8SMehdi Amini #include "mlir/IR/ValueRange.h"
21c095afcbSMogball #include "mlir/Interfaces/CallInterfaces.h"
22c095afcbSMogball #include "mlir/Interfaces/ControlFlowInterfaces.h"
23e48038a8SMehdi Amini #include "mlir/Support/LLVM.h"
24e48038a8SMehdi Amini #include "llvm/Support/Casting.h"
25e48038a8SMehdi Amini #include <cassert>
26a1fe1f5fSKazu Hirata #include <optional>
27c095afcbSMogball 
28c095afcbSMogball using namespace mlir;
29c095afcbSMogball using namespace mlir::dataflow;
30c095afcbSMogball 
31c095afcbSMogball //===----------------------------------------------------------------------===//
32c095afcbSMogball // Executable
33c095afcbSMogball //===----------------------------------------------------------------------===//
34c095afcbSMogball 
35c095afcbSMogball ChangeResult Executable::setToLive() {
36c095afcbSMogball   if (live)
37c095afcbSMogball     return ChangeResult::NoChange;
38c095afcbSMogball   live = true;
39c095afcbSMogball   return ChangeResult::Change;
40c095afcbSMogball }
41c095afcbSMogball 
42c095afcbSMogball void Executable::print(raw_ostream &os) const {
43c095afcbSMogball   os << (live ? "live" : "dead");
44c095afcbSMogball }
45c095afcbSMogball 
46c095afcbSMogball void Executable::onUpdate(DataFlowSolver *solver) const {
476a666737SZhixun Tan   AnalysisState::onUpdate(solver);
486a666737SZhixun Tan 
494b3f251bSdonald chen   if (ProgramPoint *pp = llvm::dyn_cast_if_present<ProgramPoint *>(anchor)) {
504b3f251bSdonald chen     if (pp->isBlockStart()) {
51c095afcbSMogball       // Re-invoke the analyses on the block itself.
52c095afcbSMogball       for (DataFlowAnalysis *analysis : subscribers)
534b3f251bSdonald chen         solver->enqueue({pp, analysis});
54c095afcbSMogball       // Re-invoke the analyses on all operations in the block.
55c095afcbSMogball       for (DataFlowAnalysis *analysis : subscribers)
564b3f251bSdonald chen         for (Operation &op : *pp->getBlock())
574b3f251bSdonald chen           solver->enqueue({solver->getProgramPointAfter(&op), analysis});
58b6603e1bSdonald chen     }
59b6603e1bSdonald chen   } else if (auto *latticeAnchor =
60b6603e1bSdonald chen                  llvm::dyn_cast_if_present<GenericLatticeAnchor *>(anchor)) {
61c095afcbSMogball     // Re-invoke the analysis on the successor block.
62b6603e1bSdonald chen     if (auto *edge = dyn_cast<CFGEdge>(latticeAnchor)) {
63c095afcbSMogball       for (DataFlowAnalysis *analysis : subscribers)
644b3f251bSdonald chen         solver->enqueue(
654b3f251bSdonald chen             {solver->getProgramPointBefore(edge->getTo()), analysis});
66c095afcbSMogball     }
67c095afcbSMogball   }
68c095afcbSMogball }
69c095afcbSMogball 
70c095afcbSMogball //===----------------------------------------------------------------------===//
71c095afcbSMogball // PredecessorState
72c095afcbSMogball //===----------------------------------------------------------------------===//
73c095afcbSMogball 
74c095afcbSMogball void PredecessorState::print(raw_ostream &os) const {
75c095afcbSMogball   if (allPredecessorsKnown())
76c095afcbSMogball     os << "(all) ";
77c095afcbSMogball   os << "predecessors:\n";
78c095afcbSMogball   for (Operation *op : getKnownPredecessors())
79c095afcbSMogball     os << "  " << *op << "\n";
80c095afcbSMogball }
81c095afcbSMogball 
829432fbfeSMogball ChangeResult PredecessorState::join(Operation *predecessor) {
839432fbfeSMogball   return knownPredecessors.insert(predecessor) ? ChangeResult::Change
849432fbfeSMogball                                                : ChangeResult::NoChange;
859432fbfeSMogball }
869432fbfeSMogball 
879432fbfeSMogball ChangeResult PredecessorState::join(Operation *predecessor, ValueRange inputs) {
889432fbfeSMogball   ChangeResult result = join(predecessor);
899432fbfeSMogball   if (!inputs.empty()) {
909432fbfeSMogball     ValueRange &curInputs = successorInputs[predecessor];
919432fbfeSMogball     if (curInputs != inputs) {
929432fbfeSMogball       curInputs = inputs;
939432fbfeSMogball       result |= ChangeResult::Change;
949432fbfeSMogball     }
959432fbfeSMogball   }
969432fbfeSMogball   return result;
979432fbfeSMogball }
989432fbfeSMogball 
99c095afcbSMogball //===----------------------------------------------------------------------===//
100c095afcbSMogball // CFGEdge
101c095afcbSMogball //===----------------------------------------------------------------------===//
102c095afcbSMogball 
103c095afcbSMogball Location CFGEdge::getLoc() const {
104c095afcbSMogball   return FusedLoc::get(
105c095afcbSMogball       getFrom()->getParent()->getContext(),
106c095afcbSMogball       {getFrom()->getParent()->getLoc(), getTo()->getParent()->getLoc()});
107c095afcbSMogball }
108c095afcbSMogball 
109c095afcbSMogball void CFGEdge::print(raw_ostream &os) const {
110c095afcbSMogball   getFrom()->print(os);
111c095afcbSMogball   os << "\n -> \n";
112c095afcbSMogball   getTo()->print(os);
113c095afcbSMogball }
114c095afcbSMogball 
115c095afcbSMogball //===----------------------------------------------------------------------===//
116c095afcbSMogball // DeadCodeAnalysis
117c095afcbSMogball //===----------------------------------------------------------------------===//
118c095afcbSMogball 
119c095afcbSMogball DeadCodeAnalysis::DeadCodeAnalysis(DataFlowSolver &solver)
120c095afcbSMogball     : DataFlowAnalysis(solver) {
121b6603e1bSdonald chen   registerAnchorKind<CFGEdge>();
122c095afcbSMogball }
123c095afcbSMogball 
124c095afcbSMogball LogicalResult DeadCodeAnalysis::initialize(Operation *top) {
125c095afcbSMogball   // Mark the top-level blocks as executable.
126c095afcbSMogball   for (Region &region : top->getRegions()) {
127c095afcbSMogball     if (region.empty())
128c095afcbSMogball       continue;
1294b3f251bSdonald chen     auto *state =
1304b3f251bSdonald chen         getOrCreate<Executable>(getProgramPointBefore(&region.front()));
131c095afcbSMogball     propagateIfChanged(state, state->setToLive());
132c095afcbSMogball   }
133c095afcbSMogball 
134c095afcbSMogball   // Mark as overdefined the predecessors of symbol callables with potentially
135c095afcbSMogball   // unknown predecessors.
136c095afcbSMogball   initializeSymbolCallables(top);
137c095afcbSMogball 
138c095afcbSMogball   return initializeRecursively(top);
139c095afcbSMogball }
140c095afcbSMogball 
141c095afcbSMogball void DeadCodeAnalysis::initializeSymbolCallables(Operation *top) {
14262fe67f9SJeff Niu   analysisScope = top;
143c095afcbSMogball   auto walkFn = [&](Operation *symTable, bool allUsesVisible) {
144c095afcbSMogball     Region &symbolTableRegion = symTable->getRegion(0);
145c095afcbSMogball     Block *symbolTableBlock = &symbolTableRegion.front();
146c095afcbSMogball 
147c095afcbSMogball     bool foundSymbolCallable = false;
148c095afcbSMogball     for (auto callable : symbolTableBlock->getOps<CallableOpInterface>()) {
149c095afcbSMogball       Region *callableRegion = callable.getCallableRegion();
150c095afcbSMogball       if (!callableRegion)
151c095afcbSMogball         continue;
152c095afcbSMogball       auto symbol = dyn_cast<SymbolOpInterface>(callable.getOperation());
153c095afcbSMogball       if (!symbol)
154c095afcbSMogball         continue;
155c095afcbSMogball 
156c095afcbSMogball       // Public symbol callables or those for which we can't see all uses have
157c095afcbSMogball       // potentially unknown callsites.
158c095afcbSMogball       if (symbol.isPublic() || (!allUsesVisible && symbol.isNested())) {
1594b3f251bSdonald chen         auto *state =
1604b3f251bSdonald chen             getOrCreate<PredecessorState>(getProgramPointAfter(callable));
161c095afcbSMogball         propagateIfChanged(state, state->setHasUnknownPredecessors());
162c095afcbSMogball       }
163c095afcbSMogball       foundSymbolCallable = true;
164c095afcbSMogball     }
165c095afcbSMogball 
166c095afcbSMogball     // Exit early if no eligible symbol callables were found in the table.
167c095afcbSMogball     if (!foundSymbolCallable)
168c095afcbSMogball       return;
169c095afcbSMogball 
170c095afcbSMogball     // Walk the symbol table to check for non-call uses of symbols.
171e8bcc37fSRamkumar Ramachandra     std::optional<SymbolTable::UseRange> uses =
172c095afcbSMogball         SymbolTable::getSymbolUses(&symbolTableRegion);
173c095afcbSMogball     if (!uses) {
174c095afcbSMogball       // If we couldn't gather the symbol uses, conservatively assume that
175c095afcbSMogball       // we can't track information for any nested symbols.
176c095afcbSMogball       return top->walk([&](CallableOpInterface callable) {
1774b3f251bSdonald chen         auto *state =
1784b3f251bSdonald chen             getOrCreate<PredecessorState>(getProgramPointAfter(callable));
179c095afcbSMogball         propagateIfChanged(state, state->setHasUnknownPredecessors());
180c095afcbSMogball       });
181c095afcbSMogball     }
182c095afcbSMogball 
183c095afcbSMogball     for (const SymbolTable::SymbolUse &use : *uses) {
184c095afcbSMogball       if (isa<CallOpInterface>(use.getUser()))
185c095afcbSMogball         continue;
186c095afcbSMogball       // If a callable symbol has a non-call use, then we can't be guaranteed to
187c095afcbSMogball       // know all callsites.
188c095afcbSMogball       Operation *symbol = symbolTable.lookupSymbolIn(top, use.getSymbolRef());
189*13317502SShlomi Regev       if (!symbol)
190*13317502SShlomi Regev         continue;
1914b3f251bSdonald chen       auto *state = getOrCreate<PredecessorState>(getProgramPointAfter(symbol));
192c095afcbSMogball       propagateIfChanged(state, state->setHasUnknownPredecessors());
193c095afcbSMogball     }
194c095afcbSMogball   };
195c095afcbSMogball   SymbolTable::walkSymbolTables(top, /*allSymUsesVisible=*/!top->getBlock(),
196c095afcbSMogball                                 walkFn);
197c095afcbSMogball }
198c095afcbSMogball 
199ab701975SMogball /// Returns true if the operation is a returning terminator in region
200ab701975SMogball /// control-flow or the terminator of a callable region.
201ab701975SMogball static bool isRegionOrCallableReturn(Operation *op) {
2024b3f251bSdonald chen   return op->getBlock() != nullptr && !op->getNumSuccessors() &&
203ab701975SMogball          isa<RegionBranchOpInterface, CallableOpInterface>(op->getParentOp()) &&
204ab701975SMogball          op->getBlock()->getTerminator() == op;
205ab701975SMogball }
206ab701975SMogball 
207c095afcbSMogball LogicalResult DeadCodeAnalysis::initializeRecursively(Operation *op) {
208c095afcbSMogball   // Initialize the analysis by visiting every op with control-flow semantics.
209c095afcbSMogball   if (op->getNumRegions() || op->getNumSuccessors() ||
210ab701975SMogball       isRegionOrCallableReturn(op) || isa<CallOpInterface>(op)) {
211c095afcbSMogball     // When the liveness of the parent block changes, make sure to re-invoke the
212c095afcbSMogball     // analysis on the op.
213c095afcbSMogball     if (op->getBlock())
2144b3f251bSdonald chen       getOrCreate<Executable>(getProgramPointBefore(op->getBlock()))
2154b3f251bSdonald chen           ->blockContentSubscribe(this);
216c095afcbSMogball     // Visit the op.
2174b3f251bSdonald chen     if (failed(visit(getProgramPointAfter(op))))
218c095afcbSMogball       return failure();
219c095afcbSMogball   }
220c095afcbSMogball   // Recurse on nested operations.
221c095afcbSMogball   for (Region &region : op->getRegions())
222c095afcbSMogball     for (Operation &op : region.getOps())
223c095afcbSMogball       if (failed(initializeRecursively(&op)))
224c095afcbSMogball         return failure();
225c095afcbSMogball   return success();
226c095afcbSMogball }
227c095afcbSMogball 
228c095afcbSMogball void DeadCodeAnalysis::markEdgeLive(Block *from, Block *to) {
2294b3f251bSdonald chen   auto *state = getOrCreate<Executable>(getProgramPointBefore(to));
230c095afcbSMogball   propagateIfChanged(state, state->setToLive());
231b6603e1bSdonald chen   auto *edgeState =
232b6603e1bSdonald chen       getOrCreate<Executable>(getLatticeAnchor<CFGEdge>(from, to));
233c095afcbSMogball   propagateIfChanged(edgeState, edgeState->setToLive());
234c095afcbSMogball }
235c095afcbSMogball 
236c095afcbSMogball void DeadCodeAnalysis::markEntryBlocksLive(Operation *op) {
237c095afcbSMogball   for (Region &region : op->getRegions()) {
238c095afcbSMogball     if (region.empty())
239c095afcbSMogball       continue;
2404b3f251bSdonald chen     auto *state =
2414b3f251bSdonald chen         getOrCreate<Executable>(getProgramPointBefore(&region.front()));
242c095afcbSMogball     propagateIfChanged(state, state->setToLive());
243c095afcbSMogball   }
244c095afcbSMogball }
245c095afcbSMogball 
2464b3f251bSdonald chen LogicalResult DeadCodeAnalysis::visit(ProgramPoint *point) {
2474b3f251bSdonald chen   if (point->isBlockStart())
248c095afcbSMogball     return success();
2494b3f251bSdonald chen   Operation *op = point->getPrevOp();
250c095afcbSMogball 
251c095afcbSMogball   // If the parent block is not executable, there is nothing to do.
2524b3f251bSdonald chen   if (op->getBlock() != nullptr &&
2534b3f251bSdonald chen       !getOrCreate<Executable>(getProgramPointBefore(op->getBlock()))->isLive())
254c095afcbSMogball     return success();
255c095afcbSMogball 
256c095afcbSMogball   // We have a live call op. Add this as a live predecessor of the callee.
257c095afcbSMogball   if (auto call = dyn_cast<CallOpInterface>(op))
258c095afcbSMogball     visitCallOperation(call);
259c095afcbSMogball 
260c095afcbSMogball   // Visit the regions.
261c095afcbSMogball   if (op->getNumRegions()) {
262c095afcbSMogball     // Check if we can reason about the region control-flow.
263c095afcbSMogball     if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
264c095afcbSMogball       visitRegionBranchOperation(branch);
265c095afcbSMogball 
266c095afcbSMogball       // Check if this is a callable operation.
267c095afcbSMogball     } else if (auto callable = dyn_cast<CallableOpInterface>(op)) {
2684b3f251bSdonald chen       const auto *callsites = getOrCreateFor<PredecessorState>(
2694b3f251bSdonald chen           getProgramPointAfter(op), getProgramPointAfter(callable));
270c095afcbSMogball 
271c095afcbSMogball       // If the callsites could not be resolved or are known to be non-empty,
272c095afcbSMogball       // mark the callable as executable.
273c095afcbSMogball       if (!callsites->allPredecessorsKnown() ||
274c095afcbSMogball           !callsites->getKnownPredecessors().empty())
275c095afcbSMogball         markEntryBlocksLive(callable);
276c095afcbSMogball 
277c095afcbSMogball       // Otherwise, conservatively mark all entry blocks as executable.
278c095afcbSMogball     } else {
279c095afcbSMogball       markEntryBlocksLive(op);
280c095afcbSMogball     }
281c095afcbSMogball   }
282c095afcbSMogball 
283ab701975SMogball   if (isRegionOrCallableReturn(op)) {
284c095afcbSMogball     if (auto branch = dyn_cast<RegionBranchOpInterface>(op->getParentOp())) {
285c095afcbSMogball       // Visit the exiting terminator of a region.
2866833a380SJustin Fargnoli       visitRegionTerminator(op, branch);
287c095afcbSMogball     } else if (auto callable =
288c095afcbSMogball                    dyn_cast<CallableOpInterface>(op->getParentOp())) {
289c095afcbSMogball       // Visit the exiting terminator of a callable.
290c095afcbSMogball       visitCallableTerminator(op, callable);
291c095afcbSMogball     }
292c095afcbSMogball   }
293c095afcbSMogball   // Visit the successors.
294c095afcbSMogball   if (op->getNumSuccessors()) {
295c095afcbSMogball     // Check if we can reason about the control-flow.
296c095afcbSMogball     if (auto branch = dyn_cast<BranchOpInterface>(op)) {
297c095afcbSMogball       visitBranchOperation(branch);
298c095afcbSMogball 
299c095afcbSMogball       // Otherwise, conservatively mark all successors as exectuable.
300c095afcbSMogball     } else {
301c095afcbSMogball       for (Block *successor : op->getSuccessors())
302c095afcbSMogball         markEdgeLive(op->getBlock(), successor);
303c095afcbSMogball     }
304c095afcbSMogball   }
305c095afcbSMogball 
306c095afcbSMogball   return success();
307c095afcbSMogball }
308c095afcbSMogball 
309c095afcbSMogball void DeadCodeAnalysis::visitCallOperation(CallOpInterface call) {
310d1cad229SHenrich Lauko   Operation *callableOp = call.resolveCallableInTable(&symbolTable);
311c095afcbSMogball 
312c095afcbSMogball   // A call to a externally-defined callable has unknown predecessors.
31362fe67f9SJeff Niu   const auto isExternalCallable = [this](Operation *op) {
31462fe67f9SJeff Niu     // A callable outside the analysis scope is an external callable.
31562fe67f9SJeff Niu     if (!analysisScope->isAncestor(op))
31662fe67f9SJeff Niu       return true;
31762fe67f9SJeff Niu     // Otherwise, check if the callable region is defined.
318c095afcbSMogball     if (auto callable = dyn_cast<CallableOpInterface>(op))
319c095afcbSMogball       return !callable.getCallableRegion();
320c095afcbSMogball     return false;
321c095afcbSMogball   };
322c095afcbSMogball 
323c095afcbSMogball   // TODO: Add support for non-symbol callables when necessary. If the
324c095afcbSMogball   // callable has non-call uses we would mark as having reached pessimistic
325c095afcbSMogball   // fixpoint, otherwise allow for propagating the return values out.
326c095afcbSMogball   if (isa_and_nonnull<SymbolOpInterface>(callableOp) &&
327c095afcbSMogball       !isExternalCallable(callableOp)) {
328c095afcbSMogball     // Add the live callsite.
3294b3f251bSdonald chen     auto *callsites =
3304b3f251bSdonald chen         getOrCreate<PredecessorState>(getProgramPointAfter(callableOp));
331c095afcbSMogball     propagateIfChanged(callsites, callsites->join(call));
332c095afcbSMogball   } else {
333c095afcbSMogball     // Mark this call op's predecessors as overdefined.
3344b3f251bSdonald chen     auto *predecessors =
3354b3f251bSdonald chen         getOrCreate<PredecessorState>(getProgramPointAfter(call));
336c095afcbSMogball     propagateIfChanged(predecessors, predecessors->setHasUnknownPredecessors());
337c095afcbSMogball   }
338c095afcbSMogball }
339c095afcbSMogball 
340c095afcbSMogball /// Get the constant values of the operands of an operation. If any of the
341f09b0e35SKazu Hirata /// constant value lattices are uninitialized, return std::nullopt to indicate
342f09b0e35SKazu Hirata /// the analysis should bail out.
3430a81ace0SKazu Hirata static std::optional<SmallVector<Attribute>> getOperandValuesImpl(
344c095afcbSMogball     Operation *op,
345c095afcbSMogball     function_ref<const Lattice<ConstantValue> *(Value)> getLattice) {
346c095afcbSMogball   SmallVector<Attribute> operands;
347c095afcbSMogball   operands.reserve(op->getNumOperands());
348c095afcbSMogball   for (Value operand : op->getOperands()) {
349c095afcbSMogball     const Lattice<ConstantValue> *cv = getLattice(operand);
350c095afcbSMogball     // If any of the operands' values are uninitialized, bail out.
35147bf3e38SZhixun Tan     if (cv->getValue().isUninitialized())
352c095afcbSMogball       return {};
353c095afcbSMogball     operands.push_back(cv->getValue().getConstantValue());
354c095afcbSMogball   }
355c095afcbSMogball   return operands;
356c095afcbSMogball }
357c095afcbSMogball 
3580a81ace0SKazu Hirata std::optional<SmallVector<Attribute>>
359c095afcbSMogball DeadCodeAnalysis::getOperandValues(Operation *op) {
360c095afcbSMogball   return getOperandValuesImpl(op, [&](Value value) {
361c095afcbSMogball     auto *lattice = getOrCreate<Lattice<ConstantValue>>(value);
362c095afcbSMogball     lattice->useDefSubscribe(this);
363c095afcbSMogball     return lattice;
364c095afcbSMogball   });
365c095afcbSMogball }
366c095afcbSMogball 
367c095afcbSMogball void DeadCodeAnalysis::visitBranchOperation(BranchOpInterface branch) {
368c095afcbSMogball   // Try to deduce a single successor for the branch.
3690a81ace0SKazu Hirata   std::optional<SmallVector<Attribute>> operands = getOperandValues(branch);
370c095afcbSMogball   if (!operands)
371c095afcbSMogball     return;
372c095afcbSMogball 
373c095afcbSMogball   if (Block *successor = branch.getSuccessorForOperands(*operands)) {
374c095afcbSMogball     markEdgeLive(branch->getBlock(), successor);
375c095afcbSMogball   } else {
376c095afcbSMogball     // Otherwise, mark all successors as executable and outgoing edges.
377c095afcbSMogball     for (Block *successor : branch->getSuccessors())
378c095afcbSMogball       markEdgeLive(branch->getBlock(), successor);
379c095afcbSMogball   }
380c095afcbSMogball }
381c095afcbSMogball 
382c095afcbSMogball void DeadCodeAnalysis::visitRegionBranchOperation(
383c095afcbSMogball     RegionBranchOpInterface branch) {
384c095afcbSMogball   // Try to deduce which regions are executable.
3850a81ace0SKazu Hirata   std::optional<SmallVector<Attribute>> operands = getOperandValues(branch);
386c095afcbSMogball   if (!operands)
387c095afcbSMogball     return;
388c095afcbSMogball 
389c095afcbSMogball   SmallVector<RegionSuccessor> successors;
390138df298SMarkus Böck   branch.getEntrySuccessorRegions(*operands, successors);
391c095afcbSMogball   for (const RegionSuccessor &successor : successors) {
3929432fbfeSMogball     // The successor can be either an entry block or the parent operation.
3934b3f251bSdonald chen     ProgramPoint *point =
3944b3f251bSdonald chen         successor.getSuccessor()
3954b3f251bSdonald chen             ? getProgramPointBefore(&successor.getSuccessor()->front())
3964b3f251bSdonald chen             : getProgramPointAfter(branch);
397c095afcbSMogball     // Mark the entry block as executable.
3989432fbfeSMogball     auto *state = getOrCreate<Executable>(point);
399c095afcbSMogball     propagateIfChanged(state, state->setToLive());
400c095afcbSMogball     // Add the parent op as a predecessor.
4019432fbfeSMogball     auto *predecessors = getOrCreate<PredecessorState>(point);
4029432fbfeSMogball     propagateIfChanged(
4039432fbfeSMogball         predecessors,
4049432fbfeSMogball         predecessors->join(branch, successor.getSuccessorInputs()));
405c095afcbSMogball   }
406c095afcbSMogball }
407c095afcbSMogball 
4086833a380SJustin Fargnoli void DeadCodeAnalysis::visitRegionTerminator(Operation *op,
4096833a380SJustin Fargnoli                                              RegionBranchOpInterface branch) {
4100a81ace0SKazu Hirata   std::optional<SmallVector<Attribute>> operands = getOperandValues(op);
411c095afcbSMogball   if (!operands)
412c095afcbSMogball     return;
413c095afcbSMogball 
414c095afcbSMogball   SmallVector<RegionSuccessor> successors;
4156833a380SJustin Fargnoli   if (auto terminator = dyn_cast<RegionBranchTerminatorOpInterface>(op))
4166833a380SJustin Fargnoli     terminator.getSuccessorRegions(*operands, successors);
4176833a380SJustin Fargnoli   else
4186833a380SJustin Fargnoli     branch.getSuccessorRegions(op->getParentRegion(), successors);
419c095afcbSMogball 
420c095afcbSMogball   // Mark successor region entry blocks as executable and add this op to the
421c095afcbSMogball   // list of predecessors.
422c095afcbSMogball   for (const RegionSuccessor &successor : successors) {
423c095afcbSMogball     PredecessorState *predecessors;
424c095afcbSMogball     if (Region *region = successor.getSuccessor()) {
4254b3f251bSdonald chen       auto *state =
4264b3f251bSdonald chen           getOrCreate<Executable>(getProgramPointBefore(&region->front()));
427c095afcbSMogball       propagateIfChanged(state, state->setToLive());
4284b3f251bSdonald chen       predecessors = getOrCreate<PredecessorState>(
4294b3f251bSdonald chen           getProgramPointBefore(&region->front()));
430c095afcbSMogball     } else {
431c095afcbSMogball       // Add this terminator as a predecessor to the parent op.
4324b3f251bSdonald chen       predecessors =
4334b3f251bSdonald chen           getOrCreate<PredecessorState>(getProgramPointAfter(branch));
434c095afcbSMogball     }
4359432fbfeSMogball     propagateIfChanged(predecessors,
4369432fbfeSMogball                        predecessors->join(op, successor.getSuccessorInputs()));
437c095afcbSMogball   }
438c095afcbSMogball }
439c095afcbSMogball 
440c095afcbSMogball void DeadCodeAnalysis::visitCallableTerminator(Operation *op,
441c095afcbSMogball                                                CallableOpInterface callable) {
442c095afcbSMogball   // Add as predecessors to all callsites this return op.
4434b3f251bSdonald chen   auto *callsites = getOrCreateFor<PredecessorState>(
4444b3f251bSdonald chen       getProgramPointAfter(op), getProgramPointAfter(callable));
445c095afcbSMogball   bool canResolve = op->hasTrait<OpTrait::ReturnLike>();
446c095afcbSMogball   for (Operation *predecessor : callsites->getKnownPredecessors()) {
447c095afcbSMogball     assert(isa<CallOpInterface>(predecessor));
4484b3f251bSdonald chen     auto *predecessors =
4494b3f251bSdonald chen         getOrCreate<PredecessorState>(getProgramPointAfter(predecessor));
450c095afcbSMogball     if (canResolve) {
451c095afcbSMogball       propagateIfChanged(predecessors, predecessors->join(op));
452c095afcbSMogball     } else {
453c095afcbSMogball       // If the terminator is not a return-like, then conservatively assume we
454c095afcbSMogball       // can't resolve the predecessor.
455c095afcbSMogball       propagateIfChanged(predecessors,
456c095afcbSMogball                          predecessors->setHasUnknownPredecessors());
457c095afcbSMogball     }
458c095afcbSMogball   }
459c095afcbSMogball }
460