MLIR 22.0.0git
DeadCodeAnalysis.cpp
Go to the documentation of this file.
1//===- DeadCodeAnalysis.cpp - 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
13#include "mlir/IR/Attributes.h"
14#include "mlir/IR/Block.h"
15#include "mlir/IR/Diagnostics.h"
16#include "mlir/IR/Location.h"
17#include "mlir/IR/Operation.h"
19#include "mlir/IR/SymbolTable.h"
20#include "mlir/IR/Value.h"
21#include "mlir/IR/ValueRange.h"
24#include "mlir/Support/LLVM.h"
25#include "llvm/ADT/ScopeExit.h"
26#include "llvm/Support/Casting.h"
27#include "llvm/Support/Debug.h"
28#include "llvm/Support/DebugLog.h"
29#include <cassert>
30#include <optional>
31
32#define DEBUG_TYPE "dead-code-analysis"
33
34using namespace mlir;
35using namespace mlir::dataflow;
36
37//===----------------------------------------------------------------------===//
38// Executable
39//===----------------------------------------------------------------------===//
40
42 if (live)
44 live = true;
46}
47
49 os << (live ? "live" : "dead");
50}
51
54
55 if (ProgramPoint *pp = llvm::dyn_cast_if_present<ProgramPoint *>(anchor)) {
56 if (pp->isBlockStart()) {
57 // Re-invoke the analyses on the block itself.
58 for (DataFlowAnalysis *analysis : subscribers)
59 solver->enqueue({pp, analysis});
60 // Re-invoke the analyses on all operations in the block.
61 for (DataFlowAnalysis *analysis : subscribers)
62 for (Operation &op : *pp->getBlock())
63 solver->enqueue({solver->getProgramPointAfter(&op), analysis});
64 }
65 } else if (auto *latticeAnchor =
66 llvm::dyn_cast_if_present<GenericLatticeAnchor *>(anchor)) {
67 // Re-invoke the analysis on the successor block.
68 if (auto *edge = dyn_cast<CFGEdge>(latticeAnchor)) {
69 for (DataFlowAnalysis *analysis : subscribers)
70 solver->enqueue(
71 {solver->getProgramPointBefore(edge->getTo()), analysis});
72 }
73 }
74}
75
76//===----------------------------------------------------------------------===//
77// PredecessorState
78//===----------------------------------------------------------------------===//
79
82 os << "(all) ";
83 os << "predecessors:";
84 if (getKnownPredecessors().empty())
85 os << " (none)";
86 else
87 os << "\n";
88 llvm::interleave(
90 [&](Operation *op) {
91 os << " " << OpWithFlags(op, OpPrintingFlags().skipRegions());
92 },
93 "\n");
94}
95
97 return knownPredecessors.insert(predecessor) ? ChangeResult::Change
99}
100
102 ChangeResult result = join(predecessor);
103 if (!inputs.empty()) {
104 ValueRange &curInputs = successorInputs[predecessor];
105 if (curInputs != inputs) {
106 curInputs = inputs;
108 }
109 }
110 return result;
111}
112
113//===----------------------------------------------------------------------===//
114// CFGEdge
115//===----------------------------------------------------------------------===//
116
118 return FusedLoc::get(
119 getFrom()->getParent()->getContext(),
120 {getFrom()->getParent()->getLoc(), getTo()->getParent()->getLoc()});
121}
122
124 getFrom()->print(os);
125 os << "\n -> \n";
126 getTo()->print(os);
127}
128
129//===----------------------------------------------------------------------===//
130// DeadCodeAnalysis
131//===----------------------------------------------------------------------===//
132
137
139 LDBG() << "Initializing DeadCodeAnalysis for top-level op: "
140 << OpWithFlags(top, OpPrintingFlags().skipRegions());
141 // Mark the top-level blocks as executable.
142 for (Region &region : top->getRegions()) {
143 if (region.empty())
144 continue;
145 auto *state =
147 propagateIfChanged(state, state->setToLive());
148 LDBG() << "Marked entry block live for region in op: "
149 << OpWithFlags(top, OpPrintingFlags().skipRegions());
150 }
151
152 // Mark as overdefined the predecessors of symbol callables with potentially
153 // unknown predecessors.
154 initializeSymbolCallables(top);
155
156 return initializeRecursively(top);
157}
158
159void DeadCodeAnalysis::initializeSymbolCallables(Operation *top) {
160 LDBG() << "[init] Entering initializeSymbolCallables for top-level op: "
161 << OpWithFlags(top, OpPrintingFlags().skipRegions());
162 analysisScope = top;
163 hasSymbolTable = top->hasTrait<OpTrait::SymbolTable>();
164 auto walkFn = [&](Operation *symTable, bool allUsesVisible) {
165 LDBG() << "[init] Processing symbol table op: "
166 << OpWithFlags(symTable, OpPrintingFlags().skipRegions());
167 Region &symbolTableRegion = symTable->getRegion(0);
168 Block *symbolTableBlock = &symbolTableRegion.front();
169
170 bool foundSymbolCallable = false;
171 for (auto callable : symbolTableBlock->getOps<CallableOpInterface>()) {
172 LDBG() << "[init] Found CallableOpInterface: "
173 << OpWithFlags(callable.getOperation(),
174 OpPrintingFlags().skipRegions());
175 Region *callableRegion = callable.getCallableRegion();
176 if (!callableRegion)
177 continue;
178 auto symbol = dyn_cast<SymbolOpInterface>(callable.getOperation());
179 if (!symbol)
180 continue;
181
182 // Public symbol callables or those for which we can't see all uses have
183 // potentially unknown callsites.
184 if (symbol.isPublic() || (!allUsesVisible && symbol.isNested())) {
185 auto *state =
187 propagateIfChanged(state, state->setHasUnknownPredecessors());
188 LDBG() << "[init] Marked callable as having unknown predecessors: "
189 << OpWithFlags(callable.getOperation(),
190 OpPrintingFlags().skipRegions());
191 }
192 foundSymbolCallable = true;
193 }
194
195 // Exit early if no eligible symbol callables were found in the table.
196 if (!foundSymbolCallable)
197 return;
198
199 // Walk the symbol table to check for non-call uses of symbols.
200 std::optional<SymbolTable::UseRange> uses =
201 SymbolTable::getSymbolUses(&symbolTableRegion);
202 if (!uses) {
203 // If we couldn't gather the symbol uses, conservatively assume that
204 // we can't track information for any nested symbols.
205 LDBG() << "[init] Could not gather symbol uses, conservatively marking "
206 "all nested callables as having unknown predecessors";
207 return top->walk([&](CallableOpInterface callable) {
208 auto *state =
210 propagateIfChanged(state, state->setHasUnknownPredecessors());
211 LDBG() << "[init] Marked nested callable as "
212 "having unknown predecessors: "
213 << OpWithFlags(callable.getOperation(),
214 OpPrintingFlags().skipRegions());
215 });
216 }
217
218 for (const SymbolTable::SymbolUse &use : *uses) {
219 if (isa<CallOpInterface>(use.getUser()))
220 continue;
221 // If a callable symbol has a non-call use, then we can't be guaranteed to
222 // know all callsites.
223 Operation *symbol = symbolTable.lookupSymbolIn(top, use.getSymbolRef());
224 if (!symbol)
225 continue;
227 propagateIfChanged(state, state->setHasUnknownPredecessors());
228 LDBG() << "[init] Found non-call use for symbol, "
229 "marked as having unknown predecessors: "
230 << OpWithFlags(symbol, OpPrintingFlags().skipRegions());
231 }
232 };
233 SymbolTable::walkSymbolTables(top, /*allSymUsesVisible=*/!top->getBlock(),
234 walkFn);
235 LDBG() << "[init] Finished initializeSymbolCallables for top-level op: "
236 << OpWithFlags(top, OpPrintingFlags().skipRegions());
237}
238
239/// Returns true if the operation is a returning terminator in region
240/// control-flow or the terminator of a callable region.
242 return op->getBlock() != nullptr && !op->getNumSuccessors() &&
243 isa<RegionBranchOpInterface, CallableOpInterface>(op->getParentOp()) &&
244 op->getBlock()->getTerminator() == op;
245}
246
247LogicalResult DeadCodeAnalysis::initializeRecursively(Operation *op) {
248 LDBG() << "[init] Entering initializeRecursively for op: "
249 << OpWithFlags(op, OpPrintingFlags().skipRegions());
250 // Initialize the analysis by visiting every op with control-flow semantics.
251 if (op->getNumRegions() || op->getNumSuccessors() ||
252 isRegionOrCallableReturn(op) || isa<CallOpInterface>(op)) {
253 LDBG() << "[init] Visiting op with control-flow semantics: "
254 << OpWithFlags(op, OpPrintingFlags().skipRegions());
255 // When the liveness of the parent block changes, make sure to
256 // re-invoke the analysis on the op.
257 if (op->getBlock())
259 ->blockContentSubscribe(this);
260 // Visit the op.
262 return failure();
263 }
264 // Recurse on nested operations.
265 if (op->getNumRegions()) {
266 // If we haven't seen a symbol table yet, check if the current operation
267 // has one. If so, update the flag to allow for resolving callables in
268 // nested regions.
269 bool savedHasSymbolTable = hasSymbolTable;
270 auto restoreHasSymbolTable =
271 llvm::make_scope_exit([&]() { hasSymbolTable = savedHasSymbolTable; });
272 if (!hasSymbolTable && op->hasTrait<OpTrait::SymbolTable>())
273 hasSymbolTable = true;
274
275 for (Region &region : op->getRegions()) {
276 LDBG() << "[init] Recursing into region of op: "
277 << OpWithFlags(op, OpPrintingFlags().skipRegions());
278 for (Operation &nestedOp : region.getOps()) {
279 LDBG() << "[init] Recursing into nested op: "
280 << OpWithFlags(&nestedOp, OpPrintingFlags().skipRegions());
281 if (failed(initializeRecursively(&nestedOp)))
282 return failure();
283 }
284 }
285 }
286 LDBG() << "[init] Finished initializeRecursively for op: "
287 << OpWithFlags(op, OpPrintingFlags().skipRegions());
288 return success();
289}
290
291void DeadCodeAnalysis::markEdgeLive(Block *from, Block *to) {
292 LDBG() << "Marking edge live from block " << from << " to block " << to;
294 propagateIfChanged(state, state->setToLive());
295 auto *edgeState =
297 propagateIfChanged(edgeState, edgeState->setToLive());
298}
299
300void DeadCodeAnalysis::markEntryBlocksLive(Operation *op) {
301 LDBG() << "Marking entry blocks live for op: "
302 << OpWithFlags(op, OpPrintingFlags().skipRegions());
303 for (Region &region : op->getRegions()) {
304 if (region.empty())
305 continue;
306 auto *state =
308 propagateIfChanged(state, state->setToLive());
309 LDBG() << "Marked entry block live for region in op: "
310 << OpWithFlags(op, OpPrintingFlags().skipRegions());
311 }
312}
313
315 LDBG() << "Visiting program point: " << *point;
316 if (point->isBlockStart())
317 return success();
318 Operation *op = point->getPrevOp();
319 LDBG() << "Visiting operation: "
320 << OpWithFlags(op, OpPrintingFlags().skipRegions());
321
322 // If the parent block is not executable, there is nothing to do.
323 if (op->getBlock() != nullptr &&
325 ->isLive()) {
326 LDBG() << "Parent block not live, skipping op: "
327 << OpWithFlags(op, OpPrintingFlags().skipRegions());
328 return success();
329 }
330
331 // We have a live call op. Add this as a live predecessor of the callee.
332 if (auto call = dyn_cast<CallOpInterface>(op)) {
333 LDBG() << "Visiting call operation: "
334 << OpWithFlags(op, OpPrintingFlags().skipRegions());
335 visitCallOperation(call);
336 }
337
338 // Visit the regions.
339 if (op->getNumRegions()) {
340 // Check if we can reason about the region control-flow.
341 if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
342 LDBG() << "Visiting region branch operation: "
343 << OpWithFlags(op, OpPrintingFlags().skipRegions());
344 visitRegionBranchOperation(branch);
345
346 // Check if this is a callable operation.
347 } else if (auto callable = dyn_cast<CallableOpInterface>(op)) {
348 LDBG() << "Visiting callable operation: "
349 << OpWithFlags(op, OpPrintingFlags().skipRegions());
350 const auto *callsites = getOrCreateFor<PredecessorState>(
352
353 // If the callsites could not be resolved or are known to be non-empty,
354 // mark the callable as executable.
355 if (!callsites->allPredecessorsKnown() ||
356 !callsites->getKnownPredecessors().empty())
357 markEntryBlocksLive(callable);
358
359 // Otherwise, conservatively mark all entry blocks as executable.
360 } else {
361 LDBG() << "Marking all entry blocks live for op: "
362 << OpWithFlags(op, OpPrintingFlags().skipRegions());
363 markEntryBlocksLive(op);
364 }
365 }
366
367 if (isRegionOrCallableReturn(op)) {
368 if (auto branch = dyn_cast<RegionBranchOpInterface>(op->getParentOp())) {
369 LDBG() << "Visiting region terminator: "
370 << OpWithFlags(op, OpPrintingFlags().skipRegions());
371 // Visit the exiting terminator of a region.
372 visitRegionTerminator(op, branch);
373 } else if (auto callable =
374 dyn_cast<CallableOpInterface>(op->getParentOp())) {
375 LDBG() << "Visiting callable terminator: "
376 << OpWithFlags(op, OpPrintingFlags().skipRegions());
377 // Visit the exiting terminator of a callable.
378 visitCallableTerminator(op, callable);
379 }
380 }
381 // Visit the successors.
382 if (op->getNumSuccessors()) {
383 // Check if we can reason about the control-flow.
384 if (auto branch = dyn_cast<BranchOpInterface>(op)) {
385 LDBG() << "Visiting branch operation: "
386 << OpWithFlags(op, OpPrintingFlags().skipRegions());
387 visitBranchOperation(branch);
388
389 // Otherwise, conservatively mark all successors as exectuable.
390 } else {
391 LDBG() << "Marking all successors live for op: "
392 << OpWithFlags(op, OpPrintingFlags().skipRegions());
393 for (Block *successor : op->getSuccessors())
394 markEdgeLive(op->getBlock(), successor);
395 }
396 }
397
398 return success();
399}
400
401void DeadCodeAnalysis::visitCallOperation(CallOpInterface call) {
402 LDBG() << "visitCallOperation: "
403 << OpWithFlags(call.getOperation(), OpPrintingFlags().skipRegions());
404
405 Operation *callableOp = nullptr;
406 if (hasSymbolTable)
407 callableOp = call.resolveCallableInTable(&symbolTable);
408 else
409 LDBG()
410 << "No symbol table present in analysis scope, can't resolve callable";
411
412 // A call to a externally-defined callable has unknown predecessors.
413 const auto isExternalCallable = [this](Operation *op) {
414 // A callable outside the analysis scope is an external callable.
415 if (!analysisScope->isAncestor(op))
416 return true;
417 // Otherwise, check if the callable region is defined.
418 if (auto callable = dyn_cast<CallableOpInterface>(op))
419 return !callable.getCallableRegion();
420 return false;
421 };
422
423 // TODO: Add support for non-symbol callables when necessary. If the
424 // callable has non-call uses we would mark as having reached pessimistic
425 // fixpoint, otherwise allow for propagating the return values out.
426 if (isa_and_nonnull<SymbolOpInterface>(callableOp) &&
427 !isExternalCallable(callableOp)) {
428 // Add the live callsite.
429 auto *callsites =
431 propagateIfChanged(callsites, callsites->join(call));
432 LDBG() << "Added callsite as predecessor for callable: "
433 << OpWithFlags(callableOp, OpPrintingFlags().skipRegions());
434 } else {
435 // Mark this call op's predecessors as overdefined.
436 auto *predecessors =
438 propagateIfChanged(predecessors, predecessors->setHasUnknownPredecessors());
439 LDBG() << "Marked call op's predecessors as unknown for: "
440 << OpWithFlags(call.getOperation(), OpPrintingFlags().skipRegions());
441 }
442}
443
444/// Get the constant values of the operands of an operation. If any of the
445/// constant value lattices are uninitialized, return std::nullopt to indicate
446/// the analysis should bail out.
447std::optional<SmallVector<Attribute>>
448DeadCodeAnalysis::getOperandValues(Operation *op) {
449 SmallVector<Attribute> operands;
450 operands.reserve(op->getNumOperands());
451 for (Value operand : op->getOperands()) {
452 Lattice<ConstantValue> *cv = getOrCreate<Lattice<ConstantValue>>(operand);
453 cv->useDefSubscribe(this);
454 // If any of the operands' values are uninitialized, bail out.
455 if (cv->getValue().isUninitialized())
456 return std::nullopt;
457 operands.push_back(cv->getValue().getConstantValue());
458 }
459 return operands;
460}
461
462void DeadCodeAnalysis::visitBranchOperation(BranchOpInterface branch) {
463 LDBG() << "visitBranchOperation: "
464 << OpWithFlags(branch.getOperation(), OpPrintingFlags().skipRegions());
465 // Try to deduce a single successor for the branch.
466 std::optional<SmallVector<Attribute>> operands = getOperandValues(branch);
467 if (!operands)
468 return;
469
470 if (Block *successor = branch.getSuccessorForOperands(*operands)) {
471 markEdgeLive(branch->getBlock(), successor);
472 LDBG() << "Branch has single successor: " << successor;
473 } else {
474 // Otherwise, mark all successors as executable and outgoing edges.
475 for (Block *successor : branch->getSuccessors())
476 markEdgeLive(branch->getBlock(), successor);
477 LDBG() << "Branch has multiple/all successors live";
478 }
479}
480
481void DeadCodeAnalysis::visitRegionBranchOperation(
482 RegionBranchOpInterface branch) {
483 LDBG() << "visitRegionBranchOperation: "
484 << OpWithFlags(branch.getOperation(), OpPrintingFlags().skipRegions());
485 // Try to deduce which regions are executable.
486 std::optional<SmallVector<Attribute>> operands = getOperandValues(branch);
487 if (!operands)
488 return;
489
490 SmallVector<RegionSuccessor> successors;
491 branch.getEntrySuccessorRegions(*operands, successors);
492
493 visitRegionBranchEdges(branch, branch.getOperation(), successors);
494}
495
496void DeadCodeAnalysis::visitRegionTerminator(Operation *op,
497 RegionBranchOpInterface branch) {
498 LDBG() << "visitRegionTerminator: " << *op;
499 std::optional<SmallVector<Attribute>> operands = getOperandValues(op);
500 if (!operands)
501 return;
502
503 SmallVector<RegionSuccessor> successors;
504 auto terminator = dyn_cast<RegionBranchTerminatorOpInterface>(op);
505 if (!terminator)
506 return;
507 terminator.getSuccessorRegions(*operands, successors);
508 visitRegionBranchEdges(branch, op, successors);
509}
510
511void DeadCodeAnalysis::visitRegionBranchEdges(
512 RegionBranchOpInterface regionBranchOp, Operation *predecessorOp,
513 const SmallVector<RegionSuccessor> &successors) {
514 for (const RegionSuccessor &successor : successors) {
515 // The successor can be either an entry block or the parent operation.
516 ProgramPoint *point =
517 successor.getSuccessor()
518 ? getProgramPointBefore(&successor.getSuccessor()->front())
519 : getProgramPointAfter(regionBranchOp);
520
521 // Mark the entry block as executable.
522 auto *state = getOrCreate<Executable>(point);
523 propagateIfChanged(state, state->setToLive());
524 LDBG() << "Marked region successor live: " << *point;
525
526 // Add the parent op as a predecessor.
527 auto *predecessors = getOrCreate<PredecessorState>(point);
529 predecessors,
530 predecessors->join(predecessorOp, successor.getSuccessorInputs()));
531 LDBG() << "Added region branch as predecessor for successor: " << *point;
532 }
533}
534
535void DeadCodeAnalysis::visitCallableTerminator(Operation *op,
536 CallableOpInterface callable) {
537 LDBG() << "visitCallableTerminator: " << *op;
538 // Add as predecessors to all callsites this return op.
539 auto *callsites = getOrCreateFor<PredecessorState>(
541 bool canResolve = op->hasTrait<OpTrait::ReturnLike>();
542 for (Operation *predecessor : callsites->getKnownPredecessors()) {
543 assert(isa<CallOpInterface>(predecessor));
544 auto *predecessors =
546 if (canResolve) {
547 propagateIfChanged(predecessors, predecessors->join(op));
548 LDBG() << "Added callable terminator as predecessor for callsite: "
549 << OpWithFlags(predecessor, OpPrintingFlags().skipRegions());
550 } else {
551 // If the terminator is not a return-like, then conservatively assume we
552 // can't resolve the predecessor.
553 propagateIfChanged(predecessors,
554 predecessors->setHasUnknownPredecessors());
555 LDBG() << "Could not resolve callable terminator for callsite: "
556 << OpWithFlags(predecessor, OpPrintingFlags().skipRegions());
557 }
558 }
559}
return success()
static bool isRegionOrCallableReturn(Operation *op)
Returns true if the operation is a returning terminator in region control-flow or the terminator of a...
b getContext())
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.
friend class DataFlowSolver
Allow the framework to access the dependents.
Block represents an ordered list of Operations.
Definition Block.h:33
iterator_range< op_iterator< OpT > > getOps()
Return an iterator range over the operations within this block that are of 'OpT'.
Definition Block.h:193
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition Block.cpp:27
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:244
void print(raw_ostream &os)
Base class for all data-flow analyses.
ProgramPoint * getProgramPointBefore(Operation *op)
Get a uniqued program point instance.
void propagateIfChanged(AnalysisState *state, ChangeResult changed)
Propagate an update to a state if it changed.
StateT * getOrCreate(AnchorT anchor)
Get the analysis state associated with the lattice anchor.
ProgramPoint * getProgramPointAfter(Operation *op)
DataFlowAnalysis(DataFlowSolver &solver)
Create an analysis with a reference to the parent solver.
AnchorT * getLatticeAnchor(Args &&...args)
Get or create a custom lattice anchor.
void registerAnchorKind()
Register a custom lattice anchor class.
friend class DataFlowSolver
Allow the data-flow solver to access the internals of this class.
const StateT * getOrCreateFor(ProgramPoint *dependent, AnchorT anchor)
Get a read-only analysis state for the given point and create a dependency on dependent.
void enqueue(WorkItem item)
Push a work item onto the worklist.
ProgramPoint * getProgramPointBefore(Operation *op)
Get a uniqued program point instance.
ProgramPoint * getProgramPointAfter(Operation *op)
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
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...
Definition Operation.h:1111
Operation is the basic unit of execution within MLIR.
Definition Operation.h:88
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
Definition Operation.h:686
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:749
unsigned getNumSuccessors()
Definition Operation.h:706
Block * getBlock()
Returns the operation block that contains this operation.
Definition Operation.h:213
unsigned getNumRegions()
Returns the number of regions held by this operation.
Definition Operation.h:674
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:234
unsigned getNumOperands()
Definition Operation.h:346
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition Operation.h:677
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:378
bool isAncestor(Operation *other)
Return true if this operation is an ancestor of the other operation.
Definition Operation.h:263
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),...
Definition Operation.h:797
SuccessorRange getSuccessors()
Definition Operation.h:703
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
Block & front()
Definition Region.h:65
Location getLoc()
Return a location for this region.
Definition Region.cpp:31
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.
Definition ValueRange.h:387
void useDefSubscribe(DataFlowAnalysis *analysis)
Subscribe an analysis to updates of the lattice.
Location getLoc() const override
Get a fused location of both blocks.
Block * getTo() const
Get the target block.
Block * getFrom() const
Get the block from which the edge originates.
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.
ValueT & getValue()
Return the value held by this lattice.
ArrayRef< Operation * > getKnownPredecessors() const
Get the known 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.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:561
Include the generated interface declarations.
ChangeResult
A result type used to indicate if a change happened.
Program point represents a specific location in the execution of a program.
Operation * getPrevOp() const
Get the previous operation of this program point.