MLIR 23.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/DebugLog.h"
23#include <cassert>
24#include <optional>
25
26using namespace mlir;
27using namespace mlir::dataflow;
28
29#define DEBUG_TYPE "dataflow"
30
31//===----------------------------------------------------------------------===//
32// AbstractSparseLattice
33//===----------------------------------------------------------------------===//
34
37
38 // Push all users of the value to the queue.
39 for (Operation *user : cast<Value>(anchor).getUsers())
40 for (DataFlowAnalysis *analysis : useDefSubscribers)
41 solver->enqueue({solver->getProgramPointAfter(user), analysis});
42}
43
44//===----------------------------------------------------------------------===//
45// AbstractSparseForwardDataFlowAnalysis
46//===----------------------------------------------------------------------===//
47
53
54LogicalResult
56 // Mark the entry block arguments as having reached their pessimistic
57 // fixpoints.
58 for (Region &region : top->getRegions()) {
59 if (region.empty())
60 continue;
61 for (Value argument : region.front().getArguments())
63 }
64
65 return initializeRecursively(top);
66}
67
68LogicalResult
69AbstractSparseForwardDataFlowAnalysis::initializeRecursively(Operation *op) {
70 LDBG() << "Initializing recursively for operation: "
71 << OpWithFlags(op, OpPrintingFlags().skipRegions());
72
73 // Initialize the analysis by visiting every owner of an SSA value (all
74 // operations and blocks).
75 if (failed(visitOperation(op))) {
76 LDBG() << "Failed to visit operation: "
77 << OpWithFlags(op, OpPrintingFlags().skipRegions());
78 return failure();
79 }
80
81 for (Region &region : op->getRegions()) {
82 LDBG() << "Processing region with " << region.getBlocks().size()
83 << " blocks";
84 for (Block &block : region) {
85 LDBG() << "Processing block with " << block.getNumArguments()
86 << " arguments";
88 ->blockContentSubscribe(this);
89 visitBlock(&block);
90 for (Operation &op : block) {
91 LDBG() << "Recursively initializing nested operation: "
92 << OpWithFlags(&op, OpPrintingFlags().skipRegions());
93 if (failed(initializeRecursively(&op))) {
94 LDBG() << "Failed to initialize nested operation: "
95 << OpWithFlags(&op, OpPrintingFlags().skipRegions());
96 return failure();
97 }
98 }
99 }
100 }
101
102 LDBG() << "Successfully completed recursive initialization for operation: "
103 << OpWithFlags(op, OpPrintingFlags().skipRegions());
104 return success();
105}
106
107LogicalResult
109 if (!point->isBlockStart())
110 return visitOperation(point->getPrevOp());
111 visitBlock(point->getBlock());
112 return success();
113}
114
115LogicalResult
116AbstractSparseForwardDataFlowAnalysis::visitOperation(Operation *op) {
117 // Exit early on operations with no results.
118 if (op->getNumResults() == 0)
119 return success();
120
121 // If the containing block is not executable, bail out.
122 if (op->getBlock() != nullptr &&
124 return success();
125
126 // Get the result lattices.
128 resultLattices.reserve(op->getNumResults());
129 for (Value result : op->getResults()) {
131 resultLattices.push_back(resultLattice);
132 }
133
134 // The results of a region branch operation are determined by control-flow.
135 if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
136 visitRegionSuccessors(getProgramPointAfter(branch), branch,
137 RegionSuccessor::parent(), resultLattices);
138 return success();
139 }
140
141 // Grab the lattice elements of the operands.
143 operandLattices.reserve(op->getNumOperands());
144 for (Value operand : op->getOperands()) {
145 AbstractSparseLattice *operandLattice = getLatticeElement(operand);
146 operandLattice->useDefSubscribe(this);
147 operandLattices.push_back(operandLattice);
148 }
149
150 if (auto call = dyn_cast<CallOpInterface>(op))
151 return visitCallOperation(call, operandLattices, resultLattices);
152
153 // Invoke the operation transfer function.
154 return visitOperationImpl(op, operandLattices, resultLattices);
155}
156
157void AbstractSparseForwardDataFlowAnalysis::visitBlock(Block *block) {
158 // Exit early on blocks with no arguments.
159 if (block->getNumArguments() == 0)
160 return;
161
162 // If the block is not executable, bail out.
163 if (!getOrCreate<Executable>(getProgramPointBefore(block))->isLive())
164 return;
165
166 // Get the argument lattices.
167 SmallVector<AbstractSparseLattice *> argLattices;
168 argLattices.reserve(block->getNumArguments());
169 for (BlockArgument argument : block->getArguments()) {
170 AbstractSparseLattice *argLattice = getLatticeElement(argument);
171 argLattices.push_back(argLattice);
172 }
173
174 // The argument lattices of entry blocks are set by region control-flow or the
175 // callgraph.
176 if (block->isEntryBlock()) {
177 // Check if this block is the entry block of a callable region.
178 auto callable = dyn_cast<CallableOpInterface>(block->getParentOp());
179 if (callable && callable.getCallableRegion() == block->getParent())
180 return visitCallableOperation(callable, argLattices);
181
182 // Check if the lattices can be determined from region control flow.
183 if (auto branch = dyn_cast<RegionBranchOpInterface>(block->getParentOp())) {
184 return visitRegionSuccessors(getProgramPointBefore(block), branch,
185 block->getParent(), argLattices);
186 }
187
188 // All block arguments are non-successor-inputs.
190 RegionSuccessor(block->getParent()),
191 block->getArguments(), argLattices);
192 }
193
194 // Iterate over the predecessors of the non-entry block.
195 for (Block::pred_iterator it = block->pred_begin(), e = block->pred_end();
196 it != e; ++it) {
197 Block *predecessor = *it;
198
199 // If the edge from the predecessor block to the current block is not live,
200 // bail out.
201 auto *edgeExecutable =
203 edgeExecutable->blockContentSubscribe(this);
204 if (!edgeExecutable->isLive())
205 continue;
206
207 // Check if we can reason about the data-flow from the predecessor.
208 if (auto branch =
209 dyn_cast<BranchOpInterface>(predecessor->getTerminator())) {
210 SuccessorOperands operands =
211 branch.getSuccessorOperands(it.getSuccessorIndex());
212 for (auto [idx, lattice] : llvm::enumerate(argLattices)) {
213 if (Value operand = operands[idx]) {
214 join(lattice,
216 } else {
217 // Conservatively consider internally produced arguments as entry
218 // points.
219 setAllToEntryStates(lattice);
220 }
221 }
222 } else {
223 return setAllToEntryStates(argLattices);
224 }
225 }
226}
227
229 CallOpInterface call,
231 ArrayRef<AbstractSparseLattice *> resultLattices) {
232 // If the call operation is to an external function, attempt to infer the
233 // results from the call arguments.
234 auto isExternalCallable = [&]() {
235 auto callable =
236 dyn_cast_if_present<CallableOpInterface>(call.resolveCallable());
237 return callable && !callable.getCallableRegion();
238 };
239 if (!getSolverConfig().isInterprocedural() || isExternalCallable()) {
240 visitExternalCallImpl(call, operandLattices, resultLattices);
241 return success();
242 }
243
244 // Otherwise, the results of a call operation are determined by the
245 // callgraph.
246 const auto *predecessors = getOrCreateFor<PredecessorState>(
248 // If not all return sites are known, then conservatively assume we can't
249 // reason about the data-flow.
250 if (!predecessors->allPredecessorsKnown()) {
251 setAllToEntryStates(resultLattices);
252 return success();
253 }
254 for (Operation *predecessor : predecessors->getKnownPredecessors())
255 for (auto &&[operand, resLattice] :
256 llvm::zip(predecessor->getOperands(), resultLattices))
257 join(resLattice,
259 return success();
260}
261
263 CallableOpInterface callable,
265 Block *entryBlock = &callable.getCallableRegion()->front();
266 const auto *callsites = getOrCreateFor<PredecessorState>(
267 getProgramPointBefore(entryBlock), getProgramPointAfter(callable));
268 // If not all callsites are known, conservatively mark all lattices as
269 // having reached their pessimistic fixpoints.
270 if (!callsites->allPredecessorsKnown() ||
271 !getSolverConfig().isInterprocedural()) {
272 return setAllToEntryStates(argLattices);
273 }
274 for (Operation *callsite : callsites->getKnownPredecessors()) {
275 auto call = cast<CallOpInterface>(callsite);
276 for (auto it : llvm::zip(call.getArgOperands(), argLattices))
277 join(std::get<1>(it),
279 std::get<0>(it)));
280 }
281}
282
283void AbstractSparseForwardDataFlowAnalysis::visitRegionSuccessors(
284 ProgramPoint *point, RegionBranchOpInterface branch,
286 const auto *predecessors = getOrCreateFor<PredecessorState>(point, point);
287 assert(predecessors->allPredecessorsKnown() &&
288 "unexpected unresolved region successors");
289
290 for (Operation *op : predecessors->getKnownPredecessors()) {
291 // Get the incoming successor operands.
292 std::optional<OperandRange> operands;
293
294 // Check if the predecessor is the parent op.
295 if (op == branch) {
296 operands = branch.getEntrySuccessorOperands(successor);
297 // Otherwise, try to deduce the operands from a region return-like op.
298 } else if (auto regionTerminator =
299 dyn_cast<RegionBranchTerminatorOpInterface>(op)) {
300 operands = regionTerminator.getSuccessorOperands(successor);
301 }
302
303 if (!operands) {
304 // We can't reason about the data-flow.
305 return setAllToEntryStates(lattices);
306 }
307
308 ValueRange inputs = predecessors->getSuccessorInputs(op);
309 assert(inputs.size() == operands->size() &&
310 "expected the same number of successor inputs as operands");
311
312 auto valueToLattices = [&](Value v) { return getLatticeElement(v); };
313 unsigned firstIndex = 0;
314 if (inputs.size() != lattices.size()) {
315 if (!point->isBlockStart()) {
316 if (!inputs.empty())
317 firstIndex = cast<OpResult>(inputs.front()).getResultNumber();
318 SmallVector<Value> nonSuccessorInputs =
319 branch.getNonSuccessorInputs(RegionSuccessor::parent());
320 SmallVector<AbstractSparseLattice *> nonSuccessorInputLattices =
321 llvm::map_to_vector(nonSuccessorInputs, valueToLattices);
323 nonSuccessorInputs,
324 nonSuccessorInputLattices);
325 } else {
326 if (!inputs.empty())
327 firstIndex = cast<BlockArgument>(inputs.front()).getArgNumber();
328 Region *region = point->getBlock()->getParent();
329 SmallVector<Value> nonSuccessorInputs =
330 branch.getNonSuccessorInputs(RegionSuccessor(region));
331 SmallVector<AbstractSparseLattice *> nonSuccessorInputLattices =
332 llvm::map_to_vector(nonSuccessorInputs, valueToLattices);
333 visitNonControlFlowArgumentsImpl(branch, RegionSuccessor(region),
334 nonSuccessorInputs,
335 nonSuccessorInputLattices);
336 }
337 }
338
339 for (auto [lattice, operand] :
340 llvm::zip(lattices.drop_front(firstIndex), *operands))
341 join(lattice, *getLatticeElementFor(point, operand));
342 }
343}
344
347 Value value) {
349 addDependency(state, point);
350 return state;
351}
352
358
363
364//===----------------------------------------------------------------------===//
365// AbstractSparseBackwardDataFlowAnalysis
366//===----------------------------------------------------------------------===//
367
373
374LogicalResult
376 return initializeRecursively(top);
377}
378
379LogicalResult
380AbstractSparseBackwardDataFlowAnalysis::initializeRecursively(Operation *op) {
381 if (failed(visitOperation(op)))
382 return failure();
383
384 for (Region &region : op->getRegions()) {
385 for (Block &block : region) {
387 ->blockContentSubscribe(this);
388 // Initialize ops in reverse order, so we can do as much initial
389 // propagation as possible without having to go through the
390 // solver queue.
391 for (auto it = block.rbegin(); it != block.rend(); it++)
392 if (failed(initializeRecursively(&*it)))
393 return failure();
394 }
395 }
396 return success();
397}
398
399LogicalResult
401 // For backward dataflow, we don't have to do any work for the blocks
402 // themselves. CFG edges between blocks are processed by the BranchOp
403 // logic in `visitOperation`, and entry blocks for functions are tied
404 // to the CallOp arguments by visitOperation.
405 if (point->isBlockStart())
406 return success();
407 return visitOperation(point->getPrevOp());
408}
409
413 resultLattices.reserve(values.size());
414 for (Value result : values) {
416 resultLattices.push_back(resultLattice);
417 }
418 return resultLattices;
419}
420
422AbstractSparseBackwardDataFlowAnalysis::getLatticeElementsFor(
423 ProgramPoint *point, ValueRange values) {
425 resultLattices.reserve(values.size());
426 for (Value result : values) {
427 const AbstractSparseLattice *resultLattice =
428 getLatticeElementFor(point, result);
429 resultLattices.push_back(resultLattice);
430 }
431 return resultLattices;
432}
433
435 return MutableArrayRef<OpOperand>(operands.getBase(), operands.size());
436}
437
438LogicalResult
439AbstractSparseBackwardDataFlowAnalysis::visitOperation(Operation *op) {
440 LDBG() << "Visiting operation: "
441 << OpWithFlags(op, OpPrintingFlags().skipRegions()) << " with "
442 << op->getNumOperands() << " operands and " << op->getNumResults()
443 << " results";
444
445 // If we're in a dead block, bail out.
446 if (op->getBlock() != nullptr &&
448 ->isLive()) {
449 LDBG() << "Operation is in dead block, bailing out";
450 return success();
451 }
452
453 LDBG() << "Creating lattice elements for " << op->getNumOperands()
454 << " operands and " << op->getNumResults() << " results";
455 SmallVector<AbstractSparseLattice *> operandLattices =
457 SmallVector<const AbstractSparseLattice *> resultLattices =
458 getLatticeElementsFor(getProgramPointAfter(op), op->getResults());
459
460 // Block arguments of region branch operations flow back into the operands
461 // of the parent op
462 if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
463 LDBG() << "Processing RegionBranchOpInterface operation";
464 visitRegionSuccessors(branch, operandLattices);
465 return success();
466 }
467
468 if (auto branch = dyn_cast<BranchOpInterface>(op)) {
469 LDBG() << "Processing BranchOpInterface operation with "
470 << op->getNumSuccessors() << " successors";
471
472 // Block arguments of successor blocks flow back into our operands.
473
474 // We remember all operands not forwarded to any block in a BitVector.
475 // We can't just cut out a range here, since the non-forwarded ops might
476 // be non-contiguous (if there's more than one successor).
477 BitVector unaccounted(op->getNumOperands(), true);
478
479 for (auto [index, block] : llvm::enumerate(op->getSuccessors())) {
480 SuccessorOperands successorOperands = branch.getSuccessorOperands(index);
481 OperandRange forwarded = successorOperands.getForwardedOperands();
482 if (!forwarded.empty()) {
483 MutableArrayRef<OpOperand> operands = op->getOpOperands().slice(
484 forwarded.getBeginOperandIndex(), forwarded.size());
485 for (OpOperand &operand : operands) {
486 unaccounted.reset(operand.getOperandNumber());
487 if (std::optional<BlockArgument> blockArg =
489 successorOperands, operand.getOperandNumber(), block)) {
490 meet(getLatticeElement(operand.get()),
491 *getLatticeElementFor(getProgramPointAfter(op), *blockArg));
492 }
493 }
494 }
495 }
496 // Operands not forwarded to successor blocks are typically parameters
497 // of the branch operation itself (for example the boolean for if/else).
498 for (int index : unaccounted.set_bits()) {
499 OpOperand &operand = op->getOpOperand(index);
500 visitBranchOperand(operand);
501 }
502 return success();
503 }
504
505 // For function calls, connect the arguments of the entry blocks to the
506 // operands of the call op that are forwarded to these arguments.
507 if (auto call = dyn_cast<CallOpInterface>(op)) {
508 LDBG() << "Processing CallOpInterface operation";
509 Operation *callableOp = call.resolveCallableInTable(&symbolTable);
510 if (auto callable = dyn_cast_or_null<CallableOpInterface>(callableOp)) {
511 // Not all operands of a call op forward to arguments. Such operands are
512 // stored in `unaccounted`.
513 BitVector unaccounted(op->getNumOperands(), true);
514
515 // If the call invokes an external function (or a function treated as
516 // external due to config), defer to the corresponding extension hook.
517 // By default, it just does `visitCallOperand` for all operands.
518 OperandRange argOperands = call.getArgOperands();
519 MutableArrayRef<OpOperand> argOpOperands =
520 operandsToOpOperands(argOperands);
521 Region *region = callable.getCallableRegion();
522 if (!region || region->empty() ||
523 !getSolverConfig().isInterprocedural()) {
524 visitExternalCallImpl(call, operandLattices, resultLattices);
525 return success();
526 }
527
528 // Otherwise, propagate information from the entry point of the function
529 // back to operands whenever possible.
530 Block &block = region->front();
531 for (auto [blockArg, argOpOperand] :
532 llvm::zip(block.getArguments(), argOpOperands)) {
533 meet(getLatticeElement(argOpOperand.get()),
534 *getLatticeElementFor(getProgramPointAfter(op), blockArg));
535 unaccounted.reset(argOpOperand.getOperandNumber());
536 }
537
538 // Handle the operands of the call op that aren't forwarded to any
539 // arguments.
540 for (int index : unaccounted.set_bits()) {
541 OpOperand &opOperand = op->getOpOperand(index);
542 visitCallOperand(opOperand);
543 }
544 return success();
545 }
546 }
547
548 // When the region of an op implementing `RegionBranchOpInterface` has a
549 // terminator implementing `RegionBranchTerminatorOpInterface` or a
550 // return-like terminator, the region's successors' arguments flow back into
551 // the "successor operands" of this terminator.
552 //
553 // A successor operand with respect to an op implementing
554 // `RegionBranchOpInterface` is an operand that is forwarded to a region
555 // successor's input. There are two types of successor operands: the operands
556 // of this op itself and the operands of the terminators of the regions of
557 // this op.
558 if (auto terminator = dyn_cast<RegionBranchTerminatorOpInterface>(op)) {
559 LDBG() << "Processing RegionBranchTerminatorOpInterface operation";
560 if (auto branch = dyn_cast<RegionBranchOpInterface>(op->getParentOp())) {
561 visitRegionSuccessorsFromTerminator(terminator, branch);
562 return success();
563 }
564 }
565
566 if (op->hasTrait<OpTrait::ReturnLike>()) {
567 LDBG() << "Processing ReturnLike operation";
568 // Going backwards, the operands of the return are derived from the
569 // results of all CallOps calling this CallableOp.
570 if (auto callable = dyn_cast<CallableOpInterface>(op->getParentOp())) {
571 LDBG() << "Callable parent found, visiting callable operation";
572 return visitCallableOperation(op, callable, operandLattices);
573 }
574 }
575
576 LDBG() << "Using default visitOperationImpl for operation: "
577 << OpWithFlags(op, OpPrintingFlags().skipRegions());
578 return visitOperationImpl(op, operandLattices, resultLattices);
579}
580
582 Operation *op, CallableOpInterface callable,
583 ArrayRef<AbstractSparseLattice *> operandLattices) {
586 if (callsites->allPredecessorsKnown()) {
587 for (Operation *call : callsites->getKnownPredecessors()) {
589 getLatticeElementsFor(getProgramPointAfter(op), call->getResults());
590 for (auto [op, result] : llvm::zip(operandLattices, callResultLattices))
591 meet(op, *result);
592 }
593 } else {
594 // If we don't know all the callers, we can't know where the
595 // returned values go. Note that, in particular, this will trigger
596 // for the return ops of any public functions.
597 setAllToExitStates(operandLattices);
598 }
599 return success();
600}
601
602void AbstractSparseBackwardDataFlowAnalysis::visitRegionSuccessors(
603 RegionBranchOpInterface branch,
604 ArrayRef<AbstractSparseLattice *> operandLattices) {
605 // Not all operands are forwarded to a successor. This set can be
606 // non-contiguous in the presence of multiple successors.
607 BitVector unaccounted(branch->getNumOperands(), true);
609 branch.getSuccessorOperandInputMapping(mapping, RegionBranchPoint::parent());
610 for (const auto &[operand, inputs] : mapping) {
611 for (Value input : inputs) {
612 meet(getLatticeElement(operand->get()),
613 *getLatticeElementFor(getProgramPointAfter(branch), input));
614 unaccounted.reset(operand->getOperandNumber());
615 }
616 }
617 Operation *op = branch.getOperation();
619 SmallVector<Attribute> operands(op->getNumOperands(), nullptr);
620 branch.getEntrySuccessorRegions(operands, successors);
621 for (RegionSuccessor &successor : successors) {
622 if (successor.isParent())
623 continue;
624 auto valueToArgument = [](Value value) {
625 return cast<BlockArgument>(value);
626 };
627 SmallVector<BlockArgument> noControlFlowArguments = llvm::map_to_vector(
628 branch.getNonSuccessorInputs(successor), valueToArgument);
629 visitNonControlFlowArguments(successor, noControlFlowArguments);
630 }
631
632 // All operands not forwarded to regions are typically parameters of the
633 // branch operation itself (for example the boolean for if/else).
634 for (int index : unaccounted.set_bits()) {
635 visitBranchOperand(branch->getOpOperand(index));
636 }
637}
638
639void AbstractSparseBackwardDataFlowAnalysis::
640 visitRegionSuccessorsFromTerminator(
641 RegionBranchTerminatorOpInterface terminator,
642 RegionBranchOpInterface branch) {
643 assert(terminator->getParentOp() == branch.getOperation() &&
644 "expected `branch` to be the parent op of `terminator`");
645
646 // Not all operands are forwarded to a successor. This set can be
647 // non-contiguous in the presence of multiple successors.
648 BitVector unaccounted(terminator->getNumOperands(), true);
649
651 branch.getSuccessorOperandInputMapping(mapping,
652 RegionBranchPoint(terminator));
653 for (const auto &[operand, inputs] : mapping) {
654 for (Value input : inputs) {
655 meet(getLatticeElement(operand->get()),
656 *getLatticeElementFor(getProgramPointAfter(terminator), input));
657 unaccounted.reset(operand->getOperandNumber());
658 }
659 }
660
661 // Visit operands of the branch op not forwarded to the next region.
662 // (Like e.g. the boolean of `scf.conditional`)
663 for (int index : unaccounted.set_bits()) {
664 visitBranchOperand(terminator->getOpOperand(index));
665 }
666}
667
669AbstractSparseBackwardDataFlowAnalysis::getLatticeElementFor(
670 ProgramPoint *point, Value value) {
671 AbstractSparseLattice *state = getLatticeElement(value);
672 addDependency(state, point);
673 return state;
674}
675
681
return success()
lhs
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.
friend class DataFlowSolver
Allow the framework to access the dependents.
Block represents an ordered list of Operations.
Definition Block.h:33
unsigned getNumArguments()
Definition Block.h:138
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition Block.cpp:27
Operation & front()
Definition Block.h:163
pred_iterator pred_begin()
Definition Block.h:246
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:249
reverse_iterator rend()
Definition Block.h:156
BlockArgListType getArguments()
Definition Block.h:97
PredecessorIterator pred_iterator
Definition Block.h:245
bool isEntryBlock()
Return if this block is the entry block in the parent region.
Definition Block.cpp:36
pred_iterator pred_end()
Definition Block.h:249
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
Definition Block.cpp:31
reverse_iterator rbegin()
Definition Block.h:155
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.
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.
const DataFlowConfig & getSolverConfig() const
Return the configuration of the solver used for this analysis.
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 * getProgramPointAfter(Operation *op)
Set of flags used to control the behavior of the various IR print methods (e.g.
A wrapper class that allows for printing an operation with a set of flags, useful to act as a "stream...
Definition Operation.h:1111
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
unsigned getNumSuccessors()
Definition Operation.h:706
Block * getBlock()
Returns the operation block that contains this operation.
Definition Operation.h:213
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:234
MutableArrayRef< OpOperand > getOpOperands()
Definition Operation.h:383
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
SuccessorRange getSuccessors()
Definition Operation.h:703
result_range getResults()
Definition Operation.h:415
OpOperand & getOpOperand(unsigned idx)
Definition Operation.h:388
unsigned getNumResults()
Return the number of results held by this operation.
Definition Operation.h:404
static constexpr RegionBranchPoint parent()
Returns an instance of RegionBranchPoint representing the parent operation.
This class represents a successor of a region.
static RegionSuccessor parent()
Initialize a successor that branches after/out of the parent operation.
bool isParent() const
Return true if the successor is the parent operation.
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
bool empty()
Definition Region.h:60
OperandRange getForwardedOperands() const
Get the range of operands that are simply forwarded to the successor.
This class represents a collection of SymbolTables.
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 AbstractSparseLattice * getLatticeElement(Value value)=0
Get the lattice element for a value.
virtual void visitBranchOperand(OpOperand &operand)=0
virtual void visitCallOperand(OpOperand &operand)=0
virtual void visitNonControlFlowArguments(RegionSuccessor &successor, ArrayRef< BlockArgument > arguments)=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.
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 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 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 AbstractSparseLattice * getLatticeElement(Value value)=0
Get the lattice element of a value.
virtual void visitNonControlFlowArgumentsImpl(Operation *op, const RegionSuccessor &successor, ValueRange nonSuccessorInputs, ArrayRef< AbstractSparseLattice * > nonSuccessorInputLattices)=0
Given an operation with region control-flow, the lattices of the operands, and a region successor,...
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...
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...
ArrayRef< Operation * > getKnownPredecessors() const
Get the known predecessors.
bool allPredecessorsKnown() const
Returns true if all predecessors are known.
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...
Include the generated interface declarations.
DenseMap< OpOperand *, SmallVector< Value > > RegionBranchSuccessorMapping
A mapping from successor operands to successor inputs.
Program point represents a specific location in the execution of a program.
Block * getBlock() const
Get the block contains this program point.
Operation * getPrevOp() const
Get the previous operation of this program point.