MLIR  20.0.0git
SparseAnalysis.cpp
Go to the documentation of this file.
1 //===- SparseAnalysis.cpp - Sparse data-flow 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 
12 #include "mlir/IR/Attributes.h"
13 #include "mlir/IR/Operation.h"
14 #include "mlir/IR/Region.h"
15 #include "mlir/IR/SymbolTable.h"
16 #include "mlir/IR/Value.h"
17 #include "mlir/IR/ValueRange.h"
20 #include "mlir/Support/LLVM.h"
21 #include "llvm/ADT/STLExtras.h"
22 #include "llvm/Support/Casting.h"
23 #include <cassert>
24 #include <optional>
25 
26 using namespace mlir;
27 using namespace mlir::dataflow;
28 
29 //===----------------------------------------------------------------------===//
30 // AbstractSparseLattice
31 //===----------------------------------------------------------------------===//
32 
35 
36  // Push all users of the value to the queue.
37  for (Operation *user : point.get<Value>().getUsers())
38  for (DataFlowAnalysis *analysis : useDefSubscribers)
39  solver->enqueue({user, analysis});
40 }
41 
42 //===----------------------------------------------------------------------===//
43 // AbstractSparseForwardDataFlowAnalysis
44 //===----------------------------------------------------------------------===//
45 
47  DataFlowSolver &solver)
48  : DataFlowAnalysis(solver) {
49  registerPointKind<CFGEdge>();
50 }
51 
52 LogicalResult
54  // Mark the entry block arguments as having reached their pessimistic
55  // fixpoints.
56  for (Region &region : top->getRegions()) {
57  if (region.empty())
58  continue;
59  for (Value argument : region.front().getArguments())
61  }
62 
63  return initializeRecursively(top);
64 }
65 
66 LogicalResult
67 AbstractSparseForwardDataFlowAnalysis::initializeRecursively(Operation *op) {
68  // Initialize the analysis by visiting every owner of an SSA value (all
69  // operations and blocks).
70  visitOperation(op);
71  for (Region &region : op->getRegions()) {
72  for (Block &block : region) {
73  getOrCreate<Executable>(&block)->blockContentSubscribe(this);
74  visitBlock(&block);
75  for (Operation &op : block)
76  if (failed(initializeRecursively(&op)))
77  return failure();
78  }
79  }
80 
81  return success();
82 }
83 
85  if (Operation *op = llvm::dyn_cast_if_present<Operation *>(point))
86  visitOperation(op);
87  else if (Block *block = llvm::dyn_cast_if_present<Block *>(point))
88  visitBlock(block);
89  else
90  return failure();
91  return success();
92 }
93 
94 void AbstractSparseForwardDataFlowAnalysis::visitOperation(Operation *op) {
95  // Exit early on operations with no results.
96  if (op->getNumResults() == 0)
97  return;
98 
99  // If the containing block is not executable, bail out.
100  if (!getOrCreate<Executable>(op->getBlock())->isLive())
101  return;
102 
103  // Get the result lattices.
105  resultLattices.reserve(op->getNumResults());
106  for (Value result : op->getResults()) {
107  AbstractSparseLattice *resultLattice = getLatticeElement(result);
108  resultLattices.push_back(resultLattice);
109  }
110 
111  // The results of a region branch operation are determined by control-flow.
112  if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
113  return visitRegionSuccessors({branch}, branch,
114  /*successor=*/RegionBranchPoint::parent(),
115  resultLattices);
116  }
117 
118  // Grab the lattice elements of the operands.
120  operandLattices.reserve(op->getNumOperands());
121  for (Value operand : op->getOperands()) {
122  AbstractSparseLattice *operandLattice = getLatticeElement(operand);
123  operandLattice->useDefSubscribe(this);
124  operandLattices.push_back(operandLattice);
125  }
126 
127  if (auto call = dyn_cast<CallOpInterface>(op)) {
128  // If the call operation is to an external function, attempt to infer the
129  // results from the call arguments.
130  auto callable =
131  dyn_cast_if_present<CallableOpInterface>(call.resolveCallable());
132  if (!getSolverConfig().isInterprocedural() ||
133  (callable && !callable.getCallableRegion())) {
134  return visitExternalCallImpl(call, operandLattices, resultLattices);
135  }
136 
137  // Otherwise, the results of a call operation are determined by the
138  // callgraph.
139  const auto *predecessors = getOrCreateFor<PredecessorState>(op, call);
140  // If not all return sites are known, then conservatively assume we can't
141  // reason about the data-flow.
142  if (!predecessors->allPredecessorsKnown())
143  return setAllToEntryStates(resultLattices);
144  for (Operation *predecessor : predecessors->getKnownPredecessors())
145  for (auto it : llvm::zip(predecessor->getOperands(), resultLattices))
146  join(std::get<1>(it), *getLatticeElementFor(op, std::get<0>(it)));
147  return;
148  }
149 
150  // Invoke the operation transfer function.
151  visitOperationImpl(op, operandLattices, resultLattices);
152 }
153 
154 void AbstractSparseForwardDataFlowAnalysis::visitBlock(Block *block) {
155  // Exit early on blocks with no arguments.
156  if (block->getNumArguments() == 0)
157  return;
158 
159  // If the block is not executable, bail out.
160  if (!getOrCreate<Executable>(block)->isLive())
161  return;
162 
163  // Get the argument lattices.
165  argLattices.reserve(block->getNumArguments());
166  for (BlockArgument argument : block->getArguments()) {
167  AbstractSparseLattice *argLattice = getLatticeElement(argument);
168  argLattices.push_back(argLattice);
169  }
170 
171  // The argument lattices of entry blocks are set by region control-flow or the
172  // callgraph.
173  if (block->isEntryBlock()) {
174  // Check if this block is the entry block of a callable region.
175  auto callable = dyn_cast<CallableOpInterface>(block->getParentOp());
176  if (callable && callable.getCallableRegion() == block->getParent()) {
177  const auto *callsites = getOrCreateFor<PredecessorState>(block, callable);
178  // If not all callsites are known, conservatively mark all lattices as
179  // having reached their pessimistic fixpoints.
180  if (!callsites->allPredecessorsKnown() ||
181  !getSolverConfig().isInterprocedural()) {
182  return setAllToEntryStates(argLattices);
183  }
184  for (Operation *callsite : callsites->getKnownPredecessors()) {
185  auto call = cast<CallOpInterface>(callsite);
186  for (auto it : llvm::zip(call.getArgOperands(), argLattices))
187  join(std::get<1>(it), *getLatticeElementFor(block, std::get<0>(it)));
188  }
189  return;
190  }
191 
192  // Check if the lattices can be determined from region control flow.
193  if (auto branch = dyn_cast<RegionBranchOpInterface>(block->getParentOp())) {
194  return visitRegionSuccessors(block, branch, block->getParent(),
195  argLattices);
196  }
197 
198  // Otherwise, we can't reason about the data-flow.
200  RegionSuccessor(block->getParent()),
201  argLattices, /*firstIndex=*/0);
202  }
203 
204  // Iterate over the predecessors of the non-entry block.
205  for (Block::pred_iterator it = block->pred_begin(), e = block->pred_end();
206  it != e; ++it) {
207  Block *predecessor = *it;
208 
209  // If the edge from the predecessor block to the current block is not live,
210  // bail out.
211  auto *edgeExecutable =
212  getOrCreate<Executable>(getProgramPoint<CFGEdge>(predecessor, block));
213  edgeExecutable->blockContentSubscribe(this);
214  if (!edgeExecutable->isLive())
215  continue;
216 
217  // Check if we can reason about the data-flow from the predecessor.
218  if (auto branch =
219  dyn_cast<BranchOpInterface>(predecessor->getTerminator())) {
220  SuccessorOperands operands =
221  branch.getSuccessorOperands(it.getSuccessorIndex());
222  for (auto [idx, lattice] : llvm::enumerate(argLattices)) {
223  if (Value operand = operands[idx]) {
224  join(lattice, *getLatticeElementFor(block, operand));
225  } else {
226  // Conservatively consider internally produced arguments as entry
227  // points.
228  setAllToEntryStates(lattice);
229  }
230  }
231  } else {
232  return setAllToEntryStates(argLattices);
233  }
234  }
235 }
236 
237 void AbstractSparseForwardDataFlowAnalysis::visitRegionSuccessors(
238  ProgramPoint point, RegionBranchOpInterface branch,
240  const auto *predecessors = getOrCreateFor<PredecessorState>(point, point);
241  assert(predecessors->allPredecessorsKnown() &&
242  "unexpected unresolved region successors");
243 
244  for (Operation *op : predecessors->getKnownPredecessors()) {
245  // Get the incoming successor operands.
246  std::optional<OperandRange> operands;
247 
248  // Check if the predecessor is the parent op.
249  if (op == branch) {
250  operands = branch.getEntrySuccessorOperands(successor);
251  // Otherwise, try to deduce the operands from a region return-like op.
252  } else if (auto regionTerminator =
253  dyn_cast<RegionBranchTerminatorOpInterface>(op)) {
254  operands = regionTerminator.getSuccessorOperands(successor);
255  }
256 
257  if (!operands) {
258  // We can't reason about the data-flow.
259  return setAllToEntryStates(lattices);
260  }
261 
262  ValueRange inputs = predecessors->getSuccessorInputs(op);
263  assert(inputs.size() == operands->size() &&
264  "expected the same number of successor inputs as operands");
265 
266  unsigned firstIndex = 0;
267  if (inputs.size() != lattices.size()) {
268  if (llvm::dyn_cast_if_present<Operation *>(point)) {
269  if (!inputs.empty())
270  firstIndex = cast<OpResult>(inputs.front()).getResultNumber();
272  branch,
274  branch->getResults().slice(firstIndex, inputs.size())),
275  lattices, firstIndex);
276  } else {
277  if (!inputs.empty())
278  firstIndex = cast<BlockArgument>(inputs.front()).getArgNumber();
279  Region *region = point.get<Block *>()->getParent();
281  branch,
282  RegionSuccessor(region, region->getArguments().slice(
283  firstIndex, inputs.size())),
284  lattices, firstIndex);
285  }
286  }
287 
288  for (auto it : llvm::zip(*operands, lattices.drop_front(firstIndex)))
289  join(std::get<1>(it), *getLatticeElementFor(point, std::get<0>(it)));
290  }
291 }
292 
293 const AbstractSparseLattice *
295  Value value) {
297  addDependency(state, point);
298  return state;
299 }
300 
303  for (AbstractSparseLattice *lattice : lattices)
304  setToEntryState(lattice);
305 }
306 
308  AbstractSparseLattice *lhs, const AbstractSparseLattice &rhs) {
309  propagateIfChanged(lhs, lhs->join(rhs));
310 }
311 
312 //===----------------------------------------------------------------------===//
313 // AbstractSparseBackwardDataFlowAnalysis
314 //===----------------------------------------------------------------------===//
315 
317  DataFlowSolver &solver, SymbolTableCollection &symbolTable)
318  : DataFlowAnalysis(solver), symbolTable(symbolTable) {
319  registerPointKind<CFGEdge>();
320 }
321 
322 LogicalResult
324  return initializeRecursively(top);
325 }
326 
327 LogicalResult
328 AbstractSparseBackwardDataFlowAnalysis::initializeRecursively(Operation *op) {
329  visitOperation(op);
330  for (Region &region : op->getRegions()) {
331  for (Block &block : region) {
332  getOrCreate<Executable>(&block)->blockContentSubscribe(this);
333  // Initialize ops in reverse order, so we can do as much initial
334  // propagation as possible without having to go through the
335  // solver queue.
336  for (auto it = block.rbegin(); it != block.rend(); it++)
337  if (failed(initializeRecursively(&*it)))
338  return failure();
339  }
340  }
341  return success();
342 }
343 
344 LogicalResult
346  if (Operation *op = llvm::dyn_cast_if_present<Operation *>(point))
347  visitOperation(op);
348  else if (llvm::dyn_cast_if_present<Block *>(point))
349  // For backward dataflow, we don't have to do any work for the blocks
350  // themselves. CFG edges between blocks are processed by the BranchOp
351  // logic in `visitOperation`, and entry blocks for functions are tied
352  // to the CallOp arguments by visitOperation.
353  return success();
354  else
355  return failure();
356  return success();
357 }
358 
362  resultLattices.reserve(values.size());
363  for (Value result : values) {
364  AbstractSparseLattice *resultLattice = getLatticeElement(result);
365  resultLattices.push_back(resultLattice);
366  }
367  return resultLattices;
368 }
369 
371 AbstractSparseBackwardDataFlowAnalysis::getLatticeElementsFor(
372  ProgramPoint point, ValueRange values) {
374  resultLattices.reserve(values.size());
375  for (Value result : values) {
376  const AbstractSparseLattice *resultLattice =
377  getLatticeElementFor(point, result);
378  resultLattices.push_back(resultLattice);
379  }
380  return resultLattices;
381 }
382 
384  return MutableArrayRef<OpOperand>(operands.getBase(), operands.size());
385 }
386 
387 void AbstractSparseBackwardDataFlowAnalysis::visitOperation(Operation *op) {
388  // If we're in a dead block, bail out.
389  if (!getOrCreate<Executable>(op->getBlock())->isLive())
390  return;
391 
392  SmallVector<AbstractSparseLattice *> operandLattices =
395  getLatticeElementsFor(op, op->getResults());
396 
397  // Block arguments of region branch operations flow back into the operands
398  // of the parent op
399  if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
400  visitRegionSuccessors(branch, operandLattices);
401  return;
402  }
403 
404  if (auto branch = dyn_cast<BranchOpInterface>(op)) {
405  // Block arguments of successor blocks flow back into our operands.
406 
407  // We remember all operands not forwarded to any block in a BitVector.
408  // We can't just cut out a range here, since the non-forwarded ops might
409  // be non-contiguous (if there's more than one successor).
410  BitVector unaccounted(op->getNumOperands(), true);
411 
412  for (auto [index, block] : llvm::enumerate(op->getSuccessors())) {
413  SuccessorOperands successorOperands = branch.getSuccessorOperands(index);
414  OperandRange forwarded = successorOperands.getForwardedOperands();
415  if (!forwarded.empty()) {
416  MutableArrayRef<OpOperand> operands = op->getOpOperands().slice(
417  forwarded.getBeginOperandIndex(), forwarded.size());
418  for (OpOperand &operand : operands) {
419  unaccounted.reset(operand.getOperandNumber());
420  if (std::optional<BlockArgument> blockArg =
422  successorOperands, operand.getOperandNumber(), block)) {
423  meet(getLatticeElement(operand.get()),
424  *getLatticeElementFor(op, *blockArg));
425  }
426  }
427  }
428  }
429  // Operands not forwarded to successor blocks are typically parameters
430  // of the branch operation itself (for example the boolean for if/else).
431  for (int index : unaccounted.set_bits()) {
432  OpOperand &operand = op->getOpOperand(index);
433  visitBranchOperand(operand);
434  }
435  return;
436  }
437 
438  // For function calls, connect the arguments of the entry blocks to the
439  // operands of the call op that are forwarded to these arguments.
440  if (auto call = dyn_cast<CallOpInterface>(op)) {
441  Operation *callableOp = call.resolveCallable(&symbolTable);
442  if (auto callable = dyn_cast_or_null<CallableOpInterface>(callableOp)) {
443  // Not all operands of a call op forward to arguments. Such operands are
444  // stored in `unaccounted`.
445  BitVector unaccounted(op->getNumOperands(), true);
446 
447  // If the call invokes an external function (or a function treated as
448  // external due to config), defer to the corresponding extension hook.
449  // By default, it just does `visitCallOperand` for all operands.
450  OperandRange argOperands = call.getArgOperands();
451  MutableArrayRef<OpOperand> argOpOperands =
452  operandsToOpOperands(argOperands);
453  Region *region = callable.getCallableRegion();
454  if (!region || region->empty() || !getSolverConfig().isInterprocedural())
455  return visitExternalCallImpl(call, operandLattices, resultLattices);
456 
457  // Otherwise, propagate information from the entry point of the function
458  // back to operands whenever possible.
459  Block &block = region->front();
460  for (auto [blockArg, argOpOperand] :
461  llvm::zip(block.getArguments(), argOpOperands)) {
462  meet(getLatticeElement(argOpOperand.get()),
463  *getLatticeElementFor(op, blockArg));
464  unaccounted.reset(argOpOperand.getOperandNumber());
465  }
466 
467  // Handle the operands of the call op that aren't forwarded to any
468  // arguments.
469  for (int index : unaccounted.set_bits()) {
470  OpOperand &opOperand = op->getOpOperand(index);
471  visitCallOperand(opOperand);
472  }
473  return;
474  }
475  }
476 
477  // When the region of an op implementing `RegionBranchOpInterface` has a
478  // terminator implementing `RegionBranchTerminatorOpInterface` or a
479  // return-like terminator, the region's successors' arguments flow back into
480  // the "successor operands" of this terminator.
481  //
482  // A successor operand with respect to an op implementing
483  // `RegionBranchOpInterface` is an operand that is forwarded to a region
484  // successor's input. There are two types of successor operands: the operands
485  // of this op itself and the operands of the terminators of the regions of
486  // this op.
487  if (auto terminator = dyn_cast<RegionBranchTerminatorOpInterface>(op)) {
488  if (auto branch = dyn_cast<RegionBranchOpInterface>(op->getParentOp())) {
489  visitRegionSuccessorsFromTerminator(terminator, branch);
490  return;
491  }
492  }
493 
494  if (op->hasTrait<OpTrait::ReturnLike>()) {
495  // Going backwards, the operands of the return are derived from the
496  // results of all CallOps calling this CallableOp.
497  if (auto callable = dyn_cast<CallableOpInterface>(op->getParentOp())) {
498  const PredecessorState *callsites =
499  getOrCreateFor<PredecessorState>(op, callable);
500  if (callsites->allPredecessorsKnown()) {
501  for (Operation *call : callsites->getKnownPredecessors()) {
502  SmallVector<const AbstractSparseLattice *> callResultLattices =
503  getLatticeElementsFor(op, call->getResults());
504  for (auto [op, result] :
505  llvm::zip(operandLattices, callResultLattices))
506  meet(op, *result);
507  }
508  } else {
509  // If we don't know all the callers, we can't know where the
510  // returned values go. Note that, in particular, this will trigger
511  // for the return ops of any public functions.
512  setAllToExitStates(operandLattices);
513  }
514  return;
515  }
516  }
517 
518  visitOperationImpl(op, operandLattices, resultLattices);
519 }
520 
521 void AbstractSparseBackwardDataFlowAnalysis::visitRegionSuccessors(
522  RegionBranchOpInterface branch,
523  ArrayRef<AbstractSparseLattice *> operandLattices) {
524  Operation *op = branch.getOperation();
525  SmallVector<RegionSuccessor> successors;
526  SmallVector<Attribute> operands(op->getNumOperands(), nullptr);
527  branch.getEntrySuccessorRegions(operands, successors);
528 
529  // All operands not forwarded to any successor. This set can be non-contiguous
530  // in the presence of multiple successors.
531  BitVector unaccounted(op->getNumOperands(), true);
532 
533  for (RegionSuccessor &successor : successors) {
534  OperandRange operands = branch.getEntrySuccessorOperands(successor);
535  MutableArrayRef<OpOperand> opoperands = operandsToOpOperands(operands);
536  ValueRange inputs = successor.getSuccessorInputs();
537  for (auto [operand, input] : llvm::zip(opoperands, inputs)) {
538  meet(getLatticeElement(operand.get()), *getLatticeElementFor(op, input));
539  unaccounted.reset(operand.getOperandNumber());
540  }
541  }
542  // All operands not forwarded to regions are typically parameters of the
543  // branch operation itself (for example the boolean for if/else).
544  for (int index : unaccounted.set_bits()) {
545  visitBranchOperand(op->getOpOperand(index));
546  }
547 }
548 
549 void AbstractSparseBackwardDataFlowAnalysis::
550  visitRegionSuccessorsFromTerminator(
551  RegionBranchTerminatorOpInterface terminator,
552  RegionBranchOpInterface branch) {
553  assert(isa<RegionBranchTerminatorOpInterface>(terminator) &&
554  "expected a `RegionBranchTerminatorOpInterface` op");
555  assert(terminator->getParentOp() == branch.getOperation() &&
556  "expected `branch` to be the parent op of `terminator`");
557 
558  SmallVector<Attribute> operandAttributes(terminator->getNumOperands(),
559  nullptr);
560  SmallVector<RegionSuccessor> successors;
561  terminator.getSuccessorRegions(operandAttributes, successors);
562  // All operands not forwarded to any successor. This set can be
563  // non-contiguous in the presence of multiple successors.
564  BitVector unaccounted(terminator->getNumOperands(), true);
565 
566  for (const RegionSuccessor &successor : successors) {
567  ValueRange inputs = successor.getSuccessorInputs();
568  OperandRange operands = terminator.getSuccessorOperands(successor);
569  MutableArrayRef<OpOperand> opOperands = operandsToOpOperands(operands);
570  for (auto [opOperand, input] : llvm::zip(opOperands, inputs)) {
571  meet(getLatticeElement(opOperand.get()),
572  *getLatticeElementFor(terminator, input));
573  unaccounted.reset(const_cast<OpOperand &>(opOperand).getOperandNumber());
574  }
575  }
576  // Visit operands of the branch op not forwarded to the next region.
577  // (Like e.g. the boolean of `scf.conditional`)
578  for (int index : unaccounted.set_bits()) {
579  visitBranchOperand(terminator->getOpOperand(index));
580  }
581 }
582 
583 const AbstractSparseLattice *
584 AbstractSparseBackwardDataFlowAnalysis::getLatticeElementFor(ProgramPoint point,
585  Value value) {
587  addDependency(state, point);
588  return state;
589 }
590 
593  for (AbstractSparseLattice *lattice : lattices)
594  setToExitState(lattice);
595 }
596 
598  AbstractSparseLattice *lhs, const AbstractSparseLattice &rhs) {
599  propagateIfChanged(lhs, lhs->meet(rhs));
600 }
static MutableArrayRef< OpOperand > operandsToOpOperands(OperandRange &operands)
virtual void onUpdate(DataFlowSolver *solver) const
This function is called by the solver when the analysis state is updated to enqueue more work items.
ProgramPoint point
The program point to which the state belongs.
This class represents an argument of a Block.
Definition: Value.h:319
Block represents an ordered list of Operations.
Definition: Block.h:31
unsigned getNumArguments()
Definition: Block.h:126
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition: Block.cpp:26
pred_iterator pred_begin()
Definition: Block.h:231
Operation * getTerminator()
Get the terminator operation of this block.
Definition: Block.cpp:243
reverse_iterator rend()
Definition: Block.h:144
BlockArgListType getArguments()
Definition: Block.h:85
bool isEntryBlock()
Return if this block is the entry block in the parent region.
Definition: Block.cpp:35
pred_iterator pred_end()
Definition: Block.h:234
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
Definition: Block.cpp:30
reverse_iterator rbegin()
Definition: Block.h:143
Base class for all data-flow analyses.
void propagateIfChanged(AnalysisState *state, ChangeResult changed)
Propagate an update to a state if it changed.
const DataFlowConfig & getSolverConfig() const
Return the configuration of the solver used for this analysis.
void addDependency(AnalysisState *state, ProgramPoint point)
Create a dependency between the given analysis state and program point on this analysis.
The general data-flow analysis solver.
void enqueue(WorkItem item)
Push a work item onto the worklist.
This class represents an operand of an operation.
Definition: Value.h:267
This class implements the operand iterators for the Operation class.
Definition: ValueRange.h:42
unsigned getBeginOperandIndex() const
Return the operand index of the first element of this range.
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:745
OpOperand & getOpOperand(unsigned idx)
Definition: Operation.h:383
unsigned getNumOperands()
Definition: Operation.h:341
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
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition: Operation.h:672
MutableArrayRef< OpOperand > getOpOperands()
Definition: Operation.h:378
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition: Operation.h:373
SuccessorRange getSuccessors()
Definition: Operation.h:699
result_range getResults()
Definition: Operation.h:410
unsigned getNumResults()
Return the number of results held by this operation.
Definition: Operation.h:399
Implement a predecessor iterator for blocks.
Definition: BlockSupport.h:51
This class represents a point being branched from in the methods of the RegionBranchOpInterface.
static constexpr RegionBranchPoint parent()
Returns an instance of RegionBranchPoint representing the parent operation.
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
BlockArgListType getArguments()
Definition: Region.h:81
bool empty()
Definition: Region.h:60
Block & front()
Definition: Region.h:65
This class models how operands are forwarded to block arguments in control flow.
OperandRange getForwardedOperands() const
Get the range of operands that are simply forwarded to the successor.
This class represents a collection of SymbolTables.
Definition: SymbolTable.h:283
This class provides an abstraction over the different types of ranges over Values.
Definition: ValueRange.h:381
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:96
user_range getUsers() const
Definition: Value.h:228
virtual void setToExitState(AbstractSparseLattice *lattice)=0
Set the given lattice element(s) at control flow exit point(s).
SmallVector< AbstractSparseLattice * > getLatticeElements(ValueRange values)
Get the lattice elements for a range of values.
AbstractSparseBackwardDataFlowAnalysis(DataFlowSolver &solver, SymbolTableCollection &symbolTable)
virtual void visitBranchOperand(OpOperand &operand)=0
virtual void visitOperationImpl(Operation *op, ArrayRef< AbstractSparseLattice * > operandLattices, ArrayRef< const AbstractSparseLattice * > resultLattices)=0
The operation transfer function.
virtual void visitCallOperand(OpOperand &operand)=0
LogicalResult visit(ProgramPoint point) override
Visit a program point.
void meet(AbstractSparseLattice *lhs, const AbstractSparseLattice &rhs)
Join the lattice element and propagate and update if it changed.
virtual void visitExternalCallImpl(CallOpInterface call, ArrayRef< AbstractSparseLattice * > operandLattices, ArrayRef< const AbstractSparseLattice * > resultLattices)=0
The transfer function for calls to external functions.
virtual AbstractSparseLattice * getLatticeElement(Value value)=0
Get the lattice element for a value.
LogicalResult initialize(Operation *top) override
Initialize the analysis by visiting the operation and everything nested under it.
void setAllToExitStates(ArrayRef< AbstractSparseLattice * > lattices)
Set the given lattice element(s) at control flow exit point(s).
LogicalResult visit(ProgramPoint point) override
Visit a program point.
LogicalResult initialize(Operation *top) override
Initialize the analysis by visiting every owner of an SSA value: all operations and blocks.
virtual void visitOperationImpl(Operation *op, ArrayRef< const AbstractSparseLattice * > operandLattices, ArrayRef< AbstractSparseLattice * > resultLattices)=0
The operation transfer function.
virtual AbstractSparseLattice * getLatticeElement(Value value)=0
Get the lattice element of a value.
virtual void visitExternalCallImpl(CallOpInterface call, ArrayRef< const AbstractSparseLattice * > argumentLattices, ArrayRef< AbstractSparseLattice * > resultLattices)=0
The transfer function for calls to external functions.
void setAllToEntryStates(ArrayRef< AbstractSparseLattice * > lattices)
virtual void setToEntryState(AbstractSparseLattice *lattice)=0
Set the given lattice element(s) at control flow entry point(s).
virtual void visitNonControlFlowArgumentsImpl(Operation *op, const RegionSuccessor &successor, ArrayRef< AbstractSparseLattice * > argLattices, unsigned firstIndex)=0
Given an operation with region control-flow, the lattices of the operands, and a region successor,...
const AbstractSparseLattice * getLatticeElementFor(ProgramPoint point, Value value)
Get a read-only lattice element for a value and add it as a dependency to a program point.
void join(AbstractSparseLattice *lhs, const AbstractSparseLattice &rhs)
Join the lattice element and propagate and update if it changed.
This class represents an abstract lattice.
void onUpdate(DataFlowSolver *solver) const override
When the lattice gets updated, propagate an update to users of the value using its use-def chain to s...
virtual ChangeResult join(const AbstractSparseLattice &rhs)
Join the information contained in 'rhs' into this lattice.
virtual ChangeResult meet(const AbstractSparseLattice &rhs)
Meet (intersect) the information in this lattice with 'rhs'.
void useDefSubscribe(DataFlowAnalysis *analysis)
Subscribe an analysis to updates of the lattice.
This analysis state represents a set of live control-flow "predecessors" of a program point (either a...
bool allPredecessorsKnown() const
Returns true if all predecessors are known.
ArrayRef< Operation * > getKnownPredecessors() const
Get the known predecessors.
std::optional< BlockArgument > getBranchSuccessorArgument(const SuccessorOperands &operands, unsigned operandIndex, Block *successor)
Return the BlockArgument corresponding to operand operandIndex in some successor if operandIndex is w...
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Definition: Matchers.h:285
Include the generated interface declarations.
This trait indicates that a terminator operation is "return-like".
Fundamental IR components are supported as first-class program points.