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"
11c095afcbSMogball #include "mlir/Interfaces/CallInterfaces.h"
12c095afcbSMogball #include "mlir/Interfaces/ControlFlowInterfaces.h"
13c095afcbSMogball
14c095afcbSMogball using namespace mlir;
15c095afcbSMogball using namespace mlir::dataflow;
16c095afcbSMogball
17c095afcbSMogball //===----------------------------------------------------------------------===//
18c095afcbSMogball // Executable
19c095afcbSMogball //===----------------------------------------------------------------------===//
20c095afcbSMogball
setToLive()21c095afcbSMogball ChangeResult Executable::setToLive() {
22c095afcbSMogball if (live)
23c095afcbSMogball return ChangeResult::NoChange;
24c095afcbSMogball live = true;
25c095afcbSMogball return ChangeResult::Change;
26c095afcbSMogball }
27c095afcbSMogball
print(raw_ostream & os) const28c095afcbSMogball void Executable::print(raw_ostream &os) const {
29c095afcbSMogball os << (live ? "live" : "dead");
30c095afcbSMogball }
31c095afcbSMogball
onUpdate(DataFlowSolver * solver) const32c095afcbSMogball void Executable::onUpdate(DataFlowSolver *solver) const {
33c095afcbSMogball if (auto *block = point.dyn_cast<Block *>()) {
34c095afcbSMogball // Re-invoke the analyses on the block itself.
35c095afcbSMogball for (DataFlowAnalysis *analysis : subscribers)
36c095afcbSMogball solver->enqueue({block, analysis});
37c095afcbSMogball // Re-invoke the analyses on all operations in the block.
38c095afcbSMogball for (DataFlowAnalysis *analysis : subscribers)
39c095afcbSMogball for (Operation &op : *block)
40c095afcbSMogball solver->enqueue({&op, analysis});
41c095afcbSMogball } else if (auto *programPoint = point.dyn_cast<GenericProgramPoint *>()) {
42c095afcbSMogball // Re-invoke the analysis on the successor block.
43c095afcbSMogball if (auto *edge = dyn_cast<CFGEdge>(programPoint)) {
44c095afcbSMogball for (DataFlowAnalysis *analysis : subscribers)
45c095afcbSMogball solver->enqueue({edge->getTo(), analysis});
46c095afcbSMogball }
47c095afcbSMogball }
48c095afcbSMogball }
49c095afcbSMogball
50c095afcbSMogball //===----------------------------------------------------------------------===//
51c095afcbSMogball // PredecessorState
52c095afcbSMogball //===----------------------------------------------------------------------===//
53c095afcbSMogball
print(raw_ostream & os) const54c095afcbSMogball void PredecessorState::print(raw_ostream &os) const {
55c095afcbSMogball if (allPredecessorsKnown())
56c095afcbSMogball os << "(all) ";
57c095afcbSMogball os << "predecessors:\n";
58c095afcbSMogball for (Operation *op : getKnownPredecessors())
59c095afcbSMogball os << " " << *op << "\n";
60c095afcbSMogball }
61c095afcbSMogball
join(Operation * predecessor)629432fbfeSMogball ChangeResult PredecessorState::join(Operation *predecessor) {
639432fbfeSMogball return knownPredecessors.insert(predecessor) ? ChangeResult::Change
649432fbfeSMogball : ChangeResult::NoChange;
659432fbfeSMogball }
669432fbfeSMogball
join(Operation * predecessor,ValueRange inputs)679432fbfeSMogball ChangeResult PredecessorState::join(Operation *predecessor, ValueRange inputs) {
689432fbfeSMogball ChangeResult result = join(predecessor);
699432fbfeSMogball if (!inputs.empty()) {
709432fbfeSMogball ValueRange &curInputs = successorInputs[predecessor];
719432fbfeSMogball if (curInputs != inputs) {
729432fbfeSMogball curInputs = inputs;
739432fbfeSMogball result |= ChangeResult::Change;
749432fbfeSMogball }
759432fbfeSMogball }
769432fbfeSMogball return result;
779432fbfeSMogball }
789432fbfeSMogball
79c095afcbSMogball //===----------------------------------------------------------------------===//
80c095afcbSMogball // CFGEdge
81c095afcbSMogball //===----------------------------------------------------------------------===//
82c095afcbSMogball
getLoc() const83c095afcbSMogball Location CFGEdge::getLoc() const {
84c095afcbSMogball return FusedLoc::get(
85c095afcbSMogball getFrom()->getParent()->getContext(),
86c095afcbSMogball {getFrom()->getParent()->getLoc(), getTo()->getParent()->getLoc()});
87c095afcbSMogball }
88c095afcbSMogball
print(raw_ostream & os) const89c095afcbSMogball void CFGEdge::print(raw_ostream &os) const {
90c095afcbSMogball getFrom()->print(os);
91c095afcbSMogball os << "\n -> \n";
92c095afcbSMogball getTo()->print(os);
93c095afcbSMogball }
94c095afcbSMogball
95c095afcbSMogball //===----------------------------------------------------------------------===//
96c095afcbSMogball // DeadCodeAnalysis
97c095afcbSMogball //===----------------------------------------------------------------------===//
98c095afcbSMogball
DeadCodeAnalysis(DataFlowSolver & solver)99c095afcbSMogball DeadCodeAnalysis::DeadCodeAnalysis(DataFlowSolver &solver)
100c095afcbSMogball : DataFlowAnalysis(solver) {
101c095afcbSMogball registerPointKind<CFGEdge>();
102c095afcbSMogball }
103c095afcbSMogball
initialize(Operation * top)104c095afcbSMogball LogicalResult DeadCodeAnalysis::initialize(Operation *top) {
105c095afcbSMogball // Mark the top-level blocks as executable.
106c095afcbSMogball for (Region ®ion : top->getRegions()) {
107c095afcbSMogball if (region.empty())
108c095afcbSMogball continue;
109c095afcbSMogball auto *state = getOrCreate<Executable>(®ion.front());
110c095afcbSMogball propagateIfChanged(state, state->setToLive());
111c095afcbSMogball }
112c095afcbSMogball
113c095afcbSMogball // Mark as overdefined the predecessors of symbol callables with potentially
114c095afcbSMogball // unknown predecessors.
115c095afcbSMogball initializeSymbolCallables(top);
116c095afcbSMogball
117c095afcbSMogball return initializeRecursively(top);
118c095afcbSMogball }
119c095afcbSMogball
initializeSymbolCallables(Operation * top)120c095afcbSMogball void DeadCodeAnalysis::initializeSymbolCallables(Operation *top) {
121c095afcbSMogball auto walkFn = [&](Operation *symTable, bool allUsesVisible) {
122c095afcbSMogball Region &symbolTableRegion = symTable->getRegion(0);
123c095afcbSMogball Block *symbolTableBlock = &symbolTableRegion.front();
124c095afcbSMogball
125c095afcbSMogball bool foundSymbolCallable = false;
126c095afcbSMogball for (auto callable : symbolTableBlock->getOps<CallableOpInterface>()) {
127c095afcbSMogball Region *callableRegion = callable.getCallableRegion();
128c095afcbSMogball if (!callableRegion)
129c095afcbSMogball continue;
130c095afcbSMogball auto symbol = dyn_cast<SymbolOpInterface>(callable.getOperation());
131c095afcbSMogball if (!symbol)
132c095afcbSMogball continue;
133c095afcbSMogball
134c095afcbSMogball // Public symbol callables or those for which we can't see all uses have
135c095afcbSMogball // potentially unknown callsites.
136c095afcbSMogball if (symbol.isPublic() || (!allUsesVisible && symbol.isNested())) {
137c095afcbSMogball auto *state = getOrCreate<PredecessorState>(callable);
138c095afcbSMogball propagateIfChanged(state, state->setHasUnknownPredecessors());
139c095afcbSMogball }
140c095afcbSMogball foundSymbolCallable = true;
141c095afcbSMogball }
142c095afcbSMogball
143c095afcbSMogball // Exit early if no eligible symbol callables were found in the table.
144c095afcbSMogball if (!foundSymbolCallable)
145c095afcbSMogball return;
146c095afcbSMogball
147c095afcbSMogball // Walk the symbol table to check for non-call uses of symbols.
148c095afcbSMogball Optional<SymbolTable::UseRange> uses =
149c095afcbSMogball SymbolTable::getSymbolUses(&symbolTableRegion);
150c095afcbSMogball if (!uses) {
151c095afcbSMogball // If we couldn't gather the symbol uses, conservatively assume that
152c095afcbSMogball // we can't track information for any nested symbols.
153c095afcbSMogball return top->walk([&](CallableOpInterface callable) {
154c095afcbSMogball auto *state = getOrCreate<PredecessorState>(callable);
155c095afcbSMogball propagateIfChanged(state, state->setHasUnknownPredecessors());
156c095afcbSMogball });
157c095afcbSMogball }
158c095afcbSMogball
159c095afcbSMogball for (const SymbolTable::SymbolUse &use : *uses) {
160c095afcbSMogball if (isa<CallOpInterface>(use.getUser()))
161c095afcbSMogball continue;
162c095afcbSMogball // If a callable symbol has a non-call use, then we can't be guaranteed to
163c095afcbSMogball // know all callsites.
164c095afcbSMogball Operation *symbol = symbolTable.lookupSymbolIn(top, use.getSymbolRef());
165c095afcbSMogball auto *state = getOrCreate<PredecessorState>(symbol);
166c095afcbSMogball propagateIfChanged(state, state->setHasUnknownPredecessors());
167c095afcbSMogball }
168c095afcbSMogball };
169c095afcbSMogball SymbolTable::walkSymbolTables(top, /*allSymUsesVisible=*/!top->getBlock(),
170c095afcbSMogball walkFn);
171c095afcbSMogball }
172c095afcbSMogball
173*ab701975SMogball /// Returns true if the operation is a returning terminator in region
174*ab701975SMogball /// control-flow or the terminator of a callable region.
isRegionOrCallableReturn(Operation * op)175*ab701975SMogball static bool isRegionOrCallableReturn(Operation *op) {
176*ab701975SMogball return !op->getNumSuccessors() &&
177*ab701975SMogball isa<RegionBranchOpInterface, CallableOpInterface>(op->getParentOp()) &&
178*ab701975SMogball op->getBlock()->getTerminator() == op;
179*ab701975SMogball }
180*ab701975SMogball
initializeRecursively(Operation * op)181c095afcbSMogball LogicalResult DeadCodeAnalysis::initializeRecursively(Operation *op) {
182c095afcbSMogball // Initialize the analysis by visiting every op with control-flow semantics.
183c095afcbSMogball if (op->getNumRegions() || op->getNumSuccessors() ||
184*ab701975SMogball isRegionOrCallableReturn(op) || isa<CallOpInterface>(op)) {
185c095afcbSMogball // When the liveness of the parent block changes, make sure to re-invoke the
186c095afcbSMogball // analysis on the op.
187c095afcbSMogball if (op->getBlock())
188c095afcbSMogball getOrCreate<Executable>(op->getBlock())->blockContentSubscribe(this);
189c095afcbSMogball // Visit the op.
190c095afcbSMogball if (failed(visit(op)))
191c095afcbSMogball return failure();
192c095afcbSMogball }
193c095afcbSMogball // Recurse on nested operations.
194c095afcbSMogball for (Region ®ion : op->getRegions())
195c095afcbSMogball for (Operation &op : region.getOps())
196c095afcbSMogball if (failed(initializeRecursively(&op)))
197c095afcbSMogball return failure();
198c095afcbSMogball return success();
199c095afcbSMogball }
200c095afcbSMogball
markEdgeLive(Block * from,Block * to)201c095afcbSMogball void DeadCodeAnalysis::markEdgeLive(Block *from, Block *to) {
202c095afcbSMogball auto *state = getOrCreate<Executable>(to);
203c095afcbSMogball propagateIfChanged(state, state->setToLive());
204c095afcbSMogball auto *edgeState = getOrCreate<Executable>(getProgramPoint<CFGEdge>(from, to));
205c095afcbSMogball propagateIfChanged(edgeState, edgeState->setToLive());
206c095afcbSMogball }
207c095afcbSMogball
markEntryBlocksLive(Operation * op)208c095afcbSMogball void DeadCodeAnalysis::markEntryBlocksLive(Operation *op) {
209c095afcbSMogball for (Region ®ion : op->getRegions()) {
210c095afcbSMogball if (region.empty())
211c095afcbSMogball continue;
212c095afcbSMogball auto *state = getOrCreate<Executable>(®ion.front());
213c095afcbSMogball propagateIfChanged(state, state->setToLive());
214c095afcbSMogball }
215c095afcbSMogball }
216c095afcbSMogball
visit(ProgramPoint point)217c095afcbSMogball LogicalResult DeadCodeAnalysis::visit(ProgramPoint point) {
218c095afcbSMogball if (point.is<Block *>())
219c095afcbSMogball return success();
220c095afcbSMogball auto *op = point.dyn_cast<Operation *>();
221c095afcbSMogball if (!op)
222c095afcbSMogball return emitError(point.getLoc(), "unknown program point kind");
223c095afcbSMogball
224c095afcbSMogball // If the parent block is not executable, there is nothing to do.
225c095afcbSMogball if (!getOrCreate<Executable>(op->getBlock())->isLive())
226c095afcbSMogball return success();
227c095afcbSMogball
228c095afcbSMogball // We have a live call op. Add this as a live predecessor of the callee.
229c095afcbSMogball if (auto call = dyn_cast<CallOpInterface>(op))
230c095afcbSMogball visitCallOperation(call);
231c095afcbSMogball
232c095afcbSMogball // Visit the regions.
233c095afcbSMogball if (op->getNumRegions()) {
234c095afcbSMogball // Check if we can reason about the region control-flow.
235c095afcbSMogball if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
236c095afcbSMogball visitRegionBranchOperation(branch);
237c095afcbSMogball
238c095afcbSMogball // Check if this is a callable operation.
239c095afcbSMogball } else if (auto callable = dyn_cast<CallableOpInterface>(op)) {
240c095afcbSMogball const auto *callsites = getOrCreateFor<PredecessorState>(op, callable);
241c095afcbSMogball
242c095afcbSMogball // If the callsites could not be resolved or are known to be non-empty,
243c095afcbSMogball // mark the callable as executable.
244c095afcbSMogball if (!callsites->allPredecessorsKnown() ||
245c095afcbSMogball !callsites->getKnownPredecessors().empty())
246c095afcbSMogball markEntryBlocksLive(callable);
247c095afcbSMogball
248c095afcbSMogball // Otherwise, conservatively mark all entry blocks as executable.
249c095afcbSMogball } else {
250c095afcbSMogball markEntryBlocksLive(op);
251c095afcbSMogball }
252c095afcbSMogball }
253c095afcbSMogball
254*ab701975SMogball if (isRegionOrCallableReturn(op)) {
255c095afcbSMogball if (auto branch = dyn_cast<RegionBranchOpInterface>(op->getParentOp())) {
256c095afcbSMogball // Visit the exiting terminator of a region.
257c095afcbSMogball visitRegionTerminator(op, branch);
258c095afcbSMogball } else if (auto callable =
259c095afcbSMogball dyn_cast<CallableOpInterface>(op->getParentOp())) {
260c095afcbSMogball // Visit the exiting terminator of a callable.
261c095afcbSMogball visitCallableTerminator(op, callable);
262c095afcbSMogball }
263c095afcbSMogball }
264c095afcbSMogball // Visit the successors.
265c095afcbSMogball if (op->getNumSuccessors()) {
266c095afcbSMogball // Check if we can reason about the control-flow.
267c095afcbSMogball if (auto branch = dyn_cast<BranchOpInterface>(op)) {
268c095afcbSMogball visitBranchOperation(branch);
269c095afcbSMogball
270c095afcbSMogball // Otherwise, conservatively mark all successors as exectuable.
271c095afcbSMogball } else {
272c095afcbSMogball for (Block *successor : op->getSuccessors())
273c095afcbSMogball markEdgeLive(op->getBlock(), successor);
274c095afcbSMogball }
275c095afcbSMogball }
276c095afcbSMogball
277c095afcbSMogball return success();
278c095afcbSMogball }
279c095afcbSMogball
visitCallOperation(CallOpInterface call)280c095afcbSMogball void DeadCodeAnalysis::visitCallOperation(CallOpInterface call) {
281c095afcbSMogball Operation *callableOp = nullptr;
282c095afcbSMogball if (Value callableValue = call.getCallableForCallee().dyn_cast<Value>())
283c095afcbSMogball callableOp = callableValue.getDefiningOp();
284c095afcbSMogball else
285c095afcbSMogball callableOp = call.resolveCallable(&symbolTable);
286c095afcbSMogball
287c095afcbSMogball // A call to a externally-defined callable has unknown predecessors.
288c095afcbSMogball const auto isExternalCallable = [](Operation *op) {
289c095afcbSMogball if (auto callable = dyn_cast<CallableOpInterface>(op))
290c095afcbSMogball return !callable.getCallableRegion();
291c095afcbSMogball return false;
292c095afcbSMogball };
293c095afcbSMogball
294c095afcbSMogball // TODO: Add support for non-symbol callables when necessary. If the
295c095afcbSMogball // callable has non-call uses we would mark as having reached pessimistic
296c095afcbSMogball // fixpoint, otherwise allow for propagating the return values out.
297c095afcbSMogball if (isa_and_nonnull<SymbolOpInterface>(callableOp) &&
298c095afcbSMogball !isExternalCallable(callableOp)) {
299c095afcbSMogball // Add the live callsite.
300c095afcbSMogball auto *callsites = getOrCreate<PredecessorState>(callableOp);
301c095afcbSMogball propagateIfChanged(callsites, callsites->join(call));
302c095afcbSMogball } else {
303c095afcbSMogball // Mark this call op's predecessors as overdefined.
304c095afcbSMogball auto *predecessors = getOrCreate<PredecessorState>(call);
305c095afcbSMogball propagateIfChanged(predecessors, predecessors->setHasUnknownPredecessors());
306c095afcbSMogball }
307c095afcbSMogball }
308c095afcbSMogball
309c095afcbSMogball /// Get the constant values of the operands of an operation. If any of the
310c095afcbSMogball /// constant value lattices are uninitialized, return none to indicate the
311c095afcbSMogball /// analysis should bail out.
getOperandValuesImpl(Operation * op,function_ref<const Lattice<ConstantValue> * (Value)> getLattice)312c095afcbSMogball static Optional<SmallVector<Attribute>> getOperandValuesImpl(
313c095afcbSMogball Operation *op,
314c095afcbSMogball function_ref<const Lattice<ConstantValue> *(Value)> getLattice) {
315c095afcbSMogball SmallVector<Attribute> operands;
316c095afcbSMogball operands.reserve(op->getNumOperands());
317c095afcbSMogball for (Value operand : op->getOperands()) {
318c095afcbSMogball const Lattice<ConstantValue> *cv = getLattice(operand);
319c095afcbSMogball // If any of the operands' values are uninitialized, bail out.
320c095afcbSMogball if (cv->isUninitialized())
321c095afcbSMogball return {};
322c095afcbSMogball operands.push_back(cv->getValue().getConstantValue());
323c095afcbSMogball }
324c095afcbSMogball return operands;
325c095afcbSMogball }
326c095afcbSMogball
327c095afcbSMogball Optional<SmallVector<Attribute>>
getOperandValues(Operation * op)328c095afcbSMogball DeadCodeAnalysis::getOperandValues(Operation *op) {
329c095afcbSMogball return getOperandValuesImpl(op, [&](Value value) {
330c095afcbSMogball auto *lattice = getOrCreate<Lattice<ConstantValue>>(value);
331c095afcbSMogball lattice->useDefSubscribe(this);
332c095afcbSMogball return lattice;
333c095afcbSMogball });
334c095afcbSMogball }
335c095afcbSMogball
visitBranchOperation(BranchOpInterface branch)336c095afcbSMogball void DeadCodeAnalysis::visitBranchOperation(BranchOpInterface branch) {
337c095afcbSMogball // Try to deduce a single successor for the branch.
338c095afcbSMogball Optional<SmallVector<Attribute>> operands = getOperandValues(branch);
339c095afcbSMogball if (!operands)
340c095afcbSMogball return;
341c095afcbSMogball
342c095afcbSMogball if (Block *successor = branch.getSuccessorForOperands(*operands)) {
343c095afcbSMogball markEdgeLive(branch->getBlock(), successor);
344c095afcbSMogball } else {
345c095afcbSMogball // Otherwise, mark all successors as executable and outgoing edges.
346c095afcbSMogball for (Block *successor : branch->getSuccessors())
347c095afcbSMogball markEdgeLive(branch->getBlock(), successor);
348c095afcbSMogball }
349c095afcbSMogball }
350c095afcbSMogball
visitRegionBranchOperation(RegionBranchOpInterface branch)351c095afcbSMogball void DeadCodeAnalysis::visitRegionBranchOperation(
352c095afcbSMogball RegionBranchOpInterface branch) {
353c095afcbSMogball // Try to deduce which regions are executable.
354c095afcbSMogball Optional<SmallVector<Attribute>> operands = getOperandValues(branch);
355c095afcbSMogball if (!operands)
356c095afcbSMogball return;
357c095afcbSMogball
358c095afcbSMogball SmallVector<RegionSuccessor> successors;
359c095afcbSMogball branch.getSuccessorRegions(/*index=*/{}, *operands, successors);
360c095afcbSMogball for (const RegionSuccessor &successor : successors) {
3619432fbfeSMogball // The successor can be either an entry block or the parent operation.
3629432fbfeSMogball ProgramPoint point = successor.getSuccessor()
3639432fbfeSMogball ? &successor.getSuccessor()->front()
3649432fbfeSMogball : ProgramPoint(branch);
365c095afcbSMogball // Mark the entry block as executable.
3669432fbfeSMogball auto *state = getOrCreate<Executable>(point);
367c095afcbSMogball propagateIfChanged(state, state->setToLive());
368c095afcbSMogball // Add the parent op as a predecessor.
3699432fbfeSMogball auto *predecessors = getOrCreate<PredecessorState>(point);
3709432fbfeSMogball propagateIfChanged(
3719432fbfeSMogball predecessors,
3729432fbfeSMogball predecessors->join(branch, successor.getSuccessorInputs()));
373c095afcbSMogball }
374c095afcbSMogball }
375c095afcbSMogball
visitRegionTerminator(Operation * op,RegionBranchOpInterface branch)376c095afcbSMogball void DeadCodeAnalysis::visitRegionTerminator(Operation *op,
377c095afcbSMogball RegionBranchOpInterface branch) {
378c095afcbSMogball Optional<SmallVector<Attribute>> operands = getOperandValues(op);
379c095afcbSMogball if (!operands)
380c095afcbSMogball return;
381c095afcbSMogball
382c095afcbSMogball SmallVector<RegionSuccessor> successors;
383c095afcbSMogball branch.getSuccessorRegions(op->getParentRegion()->getRegionNumber(),
384c095afcbSMogball *operands, successors);
385c095afcbSMogball
386c095afcbSMogball // Mark successor region entry blocks as executable and add this op to the
387c095afcbSMogball // list of predecessors.
388c095afcbSMogball for (const RegionSuccessor &successor : successors) {
389c095afcbSMogball PredecessorState *predecessors;
390c095afcbSMogball if (Region *region = successor.getSuccessor()) {
391c095afcbSMogball auto *state = getOrCreate<Executable>(®ion->front());
392c095afcbSMogball propagateIfChanged(state, state->setToLive());
393c095afcbSMogball predecessors = getOrCreate<PredecessorState>(®ion->front());
394c095afcbSMogball } else {
395c095afcbSMogball // Add this terminator as a predecessor to the parent op.
396c095afcbSMogball predecessors = getOrCreate<PredecessorState>(branch);
397c095afcbSMogball }
3989432fbfeSMogball propagateIfChanged(predecessors,
3999432fbfeSMogball predecessors->join(op, successor.getSuccessorInputs()));
400c095afcbSMogball }
401c095afcbSMogball }
402c095afcbSMogball
visitCallableTerminator(Operation * op,CallableOpInterface callable)403c095afcbSMogball void DeadCodeAnalysis::visitCallableTerminator(Operation *op,
404c095afcbSMogball CallableOpInterface callable) {
405c095afcbSMogball // If there are no exiting values, we have nothing to do.
406c095afcbSMogball if (op->getNumOperands() == 0)
407c095afcbSMogball return;
408c095afcbSMogball
409c095afcbSMogball // Add as predecessors to all callsites this return op.
410c095afcbSMogball auto *callsites = getOrCreateFor<PredecessorState>(op, callable);
411c095afcbSMogball bool canResolve = op->hasTrait<OpTrait::ReturnLike>();
412c095afcbSMogball for (Operation *predecessor : callsites->getKnownPredecessors()) {
413c095afcbSMogball assert(isa<CallOpInterface>(predecessor));
414c095afcbSMogball auto *predecessors = getOrCreate<PredecessorState>(predecessor);
415c095afcbSMogball if (canResolve) {
416c095afcbSMogball propagateIfChanged(predecessors, predecessors->join(op));
417c095afcbSMogball } else {
418c095afcbSMogball // If the terminator is not a return-like, then conservatively assume we
419c095afcbSMogball // can't resolve the predecessor.
420c095afcbSMogball propagateIfChanged(predecessors,
421c095afcbSMogball predecessors->setHasUnknownPredecessors());
422c095afcbSMogball }
423c095afcbSMogball }
424c095afcbSMogball }
425