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