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 
34 using namespace mlir;
35 using namespace mlir::dataflow;
36 
37 //===----------------------------------------------------------------------===//
38 // Executable
39 //===----------------------------------------------------------------------===//
40 
42  if (live)
44  live = true;
45  return ChangeResult::Change;
46 }
47 
48 void Executable::print(raw_ostream &os) const {
49  os << (live ? "live" : "dead");
50 }
51 
52 void Executable::onUpdate(DataFlowSolver *solver) const {
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 
80 void PredecessorState::print(raw_ostream &os) const {
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;
107  result |= ChangeResult::Change;
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 
123 void CFGEdge::print(raw_ostream &os) const {
124  getFrom()->print(os);
125  os << "\n -> \n";
126  getTo()->print(os);
127 }
128 
129 //===----------------------------------------------------------------------===//
130 // DeadCodeAnalysis
131 //===----------------------------------------------------------------------===//
132 
134  : DataFlowAnalysis(solver) {
135  registerAnchorKind<CFGEdge>();
136 }
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 =
146  getOrCreate<Executable>(getProgramPointBefore(&region.front()));
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 
159 void 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 =
186  getOrCreate<PredecessorState>(getProgramPointAfter(callable));
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 =
209  getOrCreate<PredecessorState>(getProgramPointAfter(callable));
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;
226  auto *state = getOrCreate<PredecessorState>(getProgramPointAfter(symbol));
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 
247 LogicalResult 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())
258  getOrCreate<Executable>(getProgramPointBefore(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 
291 void DeadCodeAnalysis::markEdgeLive(Block *from, Block *to) {
292  LDBG() << "Marking edge live from block " << from << " to block " << to;
293  auto *state = getOrCreate<Executable>(getProgramPointBefore(to));
294  propagateIfChanged(state, state->setToLive());
295  auto *edgeState =
296  getOrCreate<Executable>(getLatticeAnchor<CFGEdge>(from, to));
297  propagateIfChanged(edgeState, edgeState->setToLive());
298 }
299 
300 void 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 =
307  getOrCreate<Executable>(getProgramPointBefore(&region.front()));
308  propagateIfChanged(state, state->setToLive());
309  LDBG() << "Marked entry block live for region in op: "
310  << OpWithFlags(op, OpPrintingFlags().skipRegions());
311  }
312 }
313 
314 LogicalResult DeadCodeAnalysis::visit(ProgramPoint *point) {
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 &&
324  !getOrCreate<Executable>(getProgramPointBefore(op->getBlock()))
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 
401 void 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 =
430  getOrCreate<PredecessorState>(getProgramPointAfter(callableOp));
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 =
437  getOrCreate<PredecessorState>(getProgramPointAfter(call));
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.
447 std::optional<SmallVector<Attribute>>
448 DeadCodeAnalysis::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 
462 void 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 
481 void 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 
496 void 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  if (auto terminator = dyn_cast<RegionBranchTerminatorOpInterface>(op))
505  terminator.getSuccessorRegions(*operands, successors);
506  else
507  branch.getSuccessorRegions(op->getParentRegion(), successors);
508 
509  visitRegionBranchEdges(branch, op, successors);
510 }
511 
512 void DeadCodeAnalysis::visitRegionBranchEdges(
513  RegionBranchOpInterface regionBranchOp, Operation *predecessorOp,
514  const SmallVector<RegionSuccessor> &successors) {
515  for (const RegionSuccessor &successor : successors) {
516  // The successor can be either an entry block or the parent operation.
517  ProgramPoint *point =
518  successor.getSuccessor()
519  ? getProgramPointBefore(&successor.getSuccessor()->front())
520  : getProgramPointAfter(regionBranchOp);
521 
522  // Mark the entry block as executable.
523  auto *state = getOrCreate<Executable>(point);
524  propagateIfChanged(state, state->setToLive());
525  LDBG() << "Marked region successor live: " << point;
526 
527  // Add the parent op as a predecessor.
528  auto *predecessors = getOrCreate<PredecessorState>(point);
530  predecessors,
531  predecessors->join(predecessorOp, successor.getSuccessorInputs()));
532  LDBG() << "Added region branch as predecessor for successor: " << point;
533  }
534 }
535 
536 void DeadCodeAnalysis::visitCallableTerminator(Operation *op,
537  CallableOpInterface callable) {
538  LDBG() << "visitCallableTerminator: " << *op;
539  // Add as predecessors to all callsites this return op.
540  auto *callsites = getOrCreateFor<PredecessorState>(
542  bool canResolve = op->hasTrait<OpTrait::ReturnLike>();
543  for (Operation *predecessor : callsites->getKnownPredecessors()) {
544  assert(isa<CallOpInterface>(predecessor));
545  auto *predecessors =
546  getOrCreate<PredecessorState>(getProgramPointAfter(predecessor));
547  if (canResolve) {
548  propagateIfChanged(predecessors, predecessors->join(op));
549  LDBG() << "Added callable terminator as predecessor for callsite: "
550  << OpWithFlags(predecessor, OpPrintingFlags().skipRegions());
551  } else {
552  // If the terminator is not a return-like, then conservatively assume we
553  // can't resolve the predecessor.
554  propagateIfChanged(predecessors,
555  predecessors->setHasUnknownPredecessors());
556  LDBG() << "Could not resolve callable terminator for callsite: "
557  << OpWithFlags(predecessor, OpPrintingFlags().skipRegions());
558  }
559  }
560 }
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.
Definition: Block.h:33
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition: Block.cpp:27
SuccessorRange getSuccessors()
Definition: Block.h:270
Operation * getTerminator()
Get the terminator operation of this block.
Definition: Block.cpp:244
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'.
Definition: Block.h:193
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...
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.
Definition: SymbolTable.h:452
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
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
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
unsigned getNumRegions()
Returns the number of regions held by this operation.
Definition: Operation.h:674
unsigned getNumOperands()
Definition: Operation.h:346
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition: Operation.h:234
Block * getBlock()
Returns the operation block that contains this operation.
Definition: Operation.h:213
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
Definition: Operation.h:686
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
SuccessorRange getSuccessors()
Definition: Operation.h:703
Region * getParentRegion()
Returns the region to which the instruction belongs.
Definition: Operation.h:230
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.
Definition: Region.h:26
Location getLoc()
Return a location for this region.
Definition: Region.cpp:31
Block & front()
Definition: Region.h:65
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.
Definition: SymbolTable.h:183
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
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:96
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.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition: Remarks.h:491
detail::InFlightRemark analysis(Location loc, RemarkOpts opts)
Report an optimization analysis remark.
Definition: Remarks.h:497
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.