MLIR 24.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(branch.getOperation()),
138 resultLattices);
139 return success();
140 }
141
142 // Grab the lattice elements of the operands.
144 operandLattices.reserve(op->getNumOperands());
145 for (Value operand : op->getOperands()) {
146 AbstractSparseLattice *operandLattice = getLatticeElement(operand);
147 operandLattice->useDefSubscribe(this);
148 operandLattices.push_back(operandLattice);
149 }
150
151 if (auto call = dyn_cast<CallOpInterface>(op))
152 return visitCallOperation(call, operandLattices, resultLattices);
153
154 // Invoke the operation transfer function.
155 return visitOperationImpl(op, operandLattices, resultLattices);
156}
157
158void AbstractSparseForwardDataFlowAnalysis::visitBlock(Block *block) {
159 // Exit early on blocks with no arguments.
160 if (block->getNumArguments() == 0)
161 return;
162
163 // If the block is not executable, bail out.
164 if (!getOrCreate<Executable>(getProgramPointBefore(block))->isLive())
165 return;
166
167 // Get the argument lattices.
168 SmallVector<AbstractSparseLattice *> argLattices;
169 argLattices.reserve(block->getNumArguments());
170 for (BlockArgument argument : block->getArguments()) {
171 AbstractSparseLattice *argLattice = getLatticeElement(argument);
172 argLattices.push_back(argLattice);
173 }
174
175 // The argument lattices of entry blocks are set by region control-flow or the
176 // callgraph.
177 if (block->isEntryBlock()) {
178 // Check if this block is the entry block of a callable region.
179 auto callable = dyn_cast<CallableOpInterface>(block->getParentOp());
180 if (callable && callable.getCallableRegion() == block->getParent())
181 return visitCallableOperation(callable, argLattices);
182
183 // Check if the lattices can be determined from region control flow.
184 if (auto branch = dyn_cast<RegionBranchOpInterface>(block->getParentOp())) {
185 return visitRegionSuccessors(getProgramPointBefore(block), branch,
186 block->getParent(), argLattices);
187 }
188
189 // All block arguments are non-successor-inputs.
191 RegionSuccessor(block->getParent()),
192 block->getArguments(), argLattices);
193 }
194
195 // Iterate over the predecessors of the non-entry block.
196 for (Block::pred_iterator it = block->pred_begin(), e = block->pred_end();
197 it != e; ++it) {
198 Block *predecessor = *it;
199
200 // If the edge from the predecessor block to the current block is not live,
201 // bail out.
202 auto *edgeExecutable =
204 edgeExecutable->blockContentSubscribe(this);
205 if (!edgeExecutable->isLive())
206 continue;
207
208 // Check if we can reason about the data-flow from the predecessor.
209 if (auto branch =
210 dyn_cast<BranchOpInterface>(predecessor->getTerminator())) {
211 SuccessorOperands operands =
212 branch.getSuccessorOperands(it.getSuccessorIndex());
213 for (auto [idx, lattice] : llvm::enumerate(argLattices)) {
214 if (Value operand = operands[idx]) {
215 join(lattice,
217 } else {
218 // Conservatively consider internally produced arguments as entry
219 // points.
220 setAllToEntryStates(lattice);
221 }
222 }
223 } else {
224 return setAllToEntryStates(argLattices);
225 }
226 }
227}
228
230 CallOpInterface call,
232 ArrayRef<AbstractSparseLattice *> resultLattices) {
233 // If the call operation is to an external function, attempt to infer the
234 // results from the call arguments.
235 auto isExternalCallable = [&]() {
236 auto callable =
237 dyn_cast_if_present<CallableOpInterface>(call.resolveCallable());
238 return callable && !callable.getCallableRegion();
239 };
240 if (!getSolverConfig().isInterprocedural() || isExternalCallable()) {
241 visitExternalCallImpl(call, operandLattices, resultLattices);
242 return success();
243 }
244
245 // Otherwise, the results of a call operation are determined by the
246 // callgraph.
247 const auto *predecessors = getOrCreateFor<PredecessorState>(
249 // If not all return sites are known, then conservatively assume we can't
250 // reason about the data-flow.
251 if (!predecessors->allPredecessorsKnown()) {
252 setAllToEntryStates(resultLattices);
253 return success();
254 }
255
256 // Only the forwarded results receive the values returned by the callee. Any
257 // other result is produced by the call operation itself and nothing is known
258 // about it here.
259 ResultRange forwardedResults = call.getForwardedResults();
260 unsigned firstForwarded = forwardedResults.empty()
261 ? resultLattices.size()
262 : forwardedResults[0].getResultNumber();
263 setAllToEntryStates(resultLattices.take_front(firstForwarded));
265 resultLattices.drop_front(firstForwarded + forwardedResults.size()));
266 ArrayRef<AbstractSparseLattice *> forwardedResultLattices =
267 resultLattices.slice(firstForwarded, forwardedResults.size());
268
269 for (Operation *predecessor : predecessors->getKnownPredecessors())
270 for (auto &&[operand, resLattice] :
271 llvm::zip_equal(predecessor->getOperands(), forwardedResultLattices))
272 join(resLattice,
274 return success();
275}
276
278 CallableOpInterface callable,
280 Block *entryBlock = &callable.getCallableRegion()->front();
281 const auto *callsites = getOrCreateFor<PredecessorState>(
282 getProgramPointBefore(entryBlock), getProgramPointAfter(callable));
283 // If not all callsites are known, conservatively mark all lattices as
284 // having reached their pessimistic fixpoints.
285 if (!callsites->allPredecessorsKnown() ||
286 !getSolverConfig().isInterprocedural()) {
287 return setAllToEntryStates(argLattices);
288 }
289 for (Operation *callsite : callsites->getKnownPredecessors()) {
290 auto call = cast<CallOpInterface>(callsite);
291 for (auto it : llvm::zip(call.getArgOperands(), argLattices))
292 join(std::get<1>(it),
294 std::get<0>(it)));
295 }
296}
297
298void AbstractSparseForwardDataFlowAnalysis::visitRegionSuccessors(
299 ProgramPoint *point, RegionBranchOpInterface branch,
301 const auto *predecessors = getOrCreateFor<PredecessorState>(point, point);
302 assert(predecessors->allPredecessorsKnown() &&
303 "unexpected unresolved region successors");
304
305 for (Operation *op : predecessors->getKnownPredecessors()) {
306 // Get the incoming successor operands.
307 std::optional<OperandRange> operands;
308
309 // Check if the predecessor is the parent op.
310 if (op == branch) {
311 operands = branch.getEntrySuccessorOperands(successor);
312 // Otherwise, try to deduce the operands from a region return-like op.
313 } else if (auto regionTerminator =
314 dyn_cast<RegionBranchTerminatorOpInterface>(op)) {
315 operands = regionTerminator.getSuccessorOperands(successor);
316 }
317
318 if (!operands) {
319 // We can't reason about the data-flow.
320 return setAllToEntryStates(lattices);
321 }
322
323 ValueRange inputs = predecessors->getSuccessorInputs(op);
324 assert(inputs.size() == operands->size() &&
325 "expected the same number of successor inputs as operands");
326
327 auto valueToLattices = [&](Value v) { return getLatticeElement(v); };
328 unsigned firstIndex = 0;
329 if (inputs.size() != lattices.size()) {
330 if (!point->isBlockStart()) {
331 if (!inputs.empty())
332 firstIndex = cast<OpResult>(inputs.front()).getResultNumber();
333 SmallVector<Value> nonSuccessorInputs =
334 branch.getNonSuccessorInputs(successor);
335 SmallVector<AbstractSparseLattice *> nonSuccessorInputLattices =
336 llvm::map_to_vector(nonSuccessorInputs, valueToLattices);
337 visitNonControlFlowArgumentsImpl(branch, successor, nonSuccessorInputs,
338 nonSuccessorInputLattices);
339 } else {
340 if (!inputs.empty())
341 firstIndex = cast<BlockArgument>(inputs.front()).getArgNumber();
342 Region *region = point->getBlock()->getParent();
343 SmallVector<Value> nonSuccessorInputs =
344 branch.getNonSuccessorInputs(RegionSuccessor(region));
345 SmallVector<AbstractSparseLattice *> nonSuccessorInputLattices =
346 llvm::map_to_vector(nonSuccessorInputs, valueToLattices);
347 visitNonControlFlowArgumentsImpl(branch, RegionSuccessor(region),
348 nonSuccessorInputs,
349 nonSuccessorInputLattices);
350 }
351 }
352
353 for (auto [lattice, operand] :
354 llvm::zip(lattices.drop_front(firstIndex), *operands))
355 join(lattice, *getLatticeElementFor(point, operand));
356 }
357}
358
361 Value value) {
363 addDependency(state, point);
364 return state;
365}
366
372
377
378//===----------------------------------------------------------------------===//
379// AbstractSparseBackwardDataFlowAnalysis
380//===----------------------------------------------------------------------===//
381
387
388LogicalResult
390 return initializeRecursively(top);
391}
392
393LogicalResult
394AbstractSparseBackwardDataFlowAnalysis::initializeRecursively(Operation *op) {
395 if (failed(visitOperation(op)))
396 return failure();
397
398 for (Region &region : op->getRegions()) {
399 for (Block &block : region) {
401 ->blockContentSubscribe(this);
402 // Initialize ops in reverse order, so we can do as much initial
403 // propagation as possible without having to go through the
404 // solver queue.
405 for (auto it = block.rbegin(); it != block.rend(); it++)
406 if (failed(initializeRecursively(&*it)))
407 return failure();
408 }
409 }
410 return success();
411}
412
413LogicalResult
415 // For backward dataflow, we don't have to do any work for the blocks
416 // themselves. CFG edges between blocks are processed by the BranchOp
417 // logic in `visitOperation`, and entry blocks for functions are tied
418 // to the CallOp arguments by visitOperation.
419 if (point->isBlockStart())
420 return success();
421 return visitOperation(point->getPrevOp());
422}
423
427 resultLattices.reserve(values.size());
428 for (Value result : values) {
430 resultLattices.push_back(resultLattice);
431 }
432 return resultLattices;
433}
434
436AbstractSparseBackwardDataFlowAnalysis::getLatticeElementsFor(
437 ProgramPoint *point, ValueRange values) {
439 resultLattices.reserve(values.size());
440 for (Value result : values) {
441 const AbstractSparseLattice *resultLattice =
442 getLatticeElementFor(point, result);
443 resultLattices.push_back(resultLattice);
444 }
445 return resultLattices;
446}
447
449 return MutableArrayRef<OpOperand>(operands.getBase(), operands.size());
450}
451
452LogicalResult
453AbstractSparseBackwardDataFlowAnalysis::visitOperation(Operation *op) {
454 LDBG() << "Visiting operation: "
455 << OpWithFlags(op, OpPrintingFlags().skipRegions()) << " with "
456 << op->getNumOperands() << " operands and " << op->getNumResults()
457 << " results";
458
459 // If we're in a dead block, bail out.
460 if (op->getBlock() != nullptr &&
462 ->isLive()) {
463 LDBG() << "Operation is in dead block, bailing out";
464 return success();
465 }
466
467 LDBG() << "Creating lattice elements for " << op->getNumOperands()
468 << " operands and " << op->getNumResults() << " results";
469 SmallVector<AbstractSparseLattice *> operandLattices =
471 SmallVector<const AbstractSparseLattice *> resultLattices =
472 getLatticeElementsFor(getProgramPointAfter(op), op->getResults());
473
474 // Block arguments of region branch operations flow back into the operands
475 // of the parent op
476 if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
477 LDBG() << "Processing RegionBranchOpInterface operation";
478 visitRegionSuccessors(branch, operandLattices);
479 return success();
480 }
481
482 if (auto branch = dyn_cast<BranchOpInterface>(op)) {
483 LDBG() << "Processing BranchOpInterface operation with "
484 << op->getNumSuccessors() << " successors";
485
486 // Block arguments of successor blocks flow back into our operands.
487
488 // We remember all operands not forwarded to any block in a BitVector.
489 // We can't just cut out a range here, since the non-forwarded ops might
490 // be non-contiguous (if there's more than one successor).
491 BitVector unaccounted(op->getNumOperands(), true);
492
493 for (auto [index, block] : llvm::enumerate(op->getSuccessors())) {
494 SuccessorOperands successorOperands = branch.getSuccessorOperands(index);
495 OperandRange forwarded = successorOperands.getForwardedOperands();
496 if (!forwarded.empty()) {
497 MutableArrayRef<OpOperand> operands = op->getOpOperands().slice(
498 forwarded.getBeginOperandIndex(), forwarded.size());
499 for (OpOperand &operand : operands) {
500 unaccounted.reset(operand.getOperandNumber());
501 if (std::optional<BlockArgument> blockArg =
503 successorOperands, operand.getOperandNumber(), block)) {
504 meet(getLatticeElement(operand.get()),
505 *getLatticeElementFor(getProgramPointAfter(op), *blockArg));
506 }
507 }
508 }
509 }
510 // Operands not forwarded to successor blocks are typically parameters
511 // of the branch operation itself (for example the boolean for if/else).
512 for (int index : unaccounted.set_bits()) {
513 OpOperand &operand = op->getOpOperand(index);
514 visitBranchOperand(operand);
515 }
516 return success();
517 }
518
519 // For function calls, connect the arguments of the entry blocks to the
520 // operands of the call op that are forwarded to these arguments.
521 if (auto call = dyn_cast<CallOpInterface>(op)) {
522 LDBG() << "Processing CallOpInterface operation";
523 Operation *callableOp = call.resolveCallableInTable(&symbolTable);
524 if (auto callable = dyn_cast_or_null<CallableOpInterface>(callableOp)) {
525 // Not all operands of a call op forward to arguments. Such operands are
526 // stored in `unaccounted`.
527 BitVector unaccounted(op->getNumOperands(), true);
528
529 // If the call invokes an external function (or a function treated as
530 // external due to config), defer to the corresponding extension hook.
531 // By default, it just does `visitCallOperand` for all operands.
532 OperandRange argOperands = call.getArgOperands();
533 MutableArrayRef<OpOperand> argOpOperands =
534 operandsToOpOperands(argOperands);
535 Region *region = callable.getCallableRegion();
536 if (!region || region->empty() ||
537 !getSolverConfig().isInterprocedural()) {
538 visitExternalCallImpl(call, operandLattices, resultLattices);
539 return success();
540 }
541
542 // Otherwise, propagate information from the entry point of the function
543 // back to operands whenever possible.
544 Block &block = region->front();
545 for (auto [blockArg, argOpOperand] :
546 llvm::zip(block.getArguments(), argOpOperands)) {
547 meet(getLatticeElement(argOpOperand.get()),
548 *getLatticeElementFor(getProgramPointAfter(op), blockArg));
549 unaccounted.reset(argOpOperand.getOperandNumber());
550 }
551
552 // Handle the operands of the call op that aren't forwarded to any
553 // arguments.
554 for (int index : unaccounted.set_bits()) {
555 OpOperand &opOperand = op->getOpOperand(index);
556 visitCallOperand(opOperand);
557 }
558 return success();
559 }
560 }
561
562 // When the region of an op implementing `RegionBranchOpInterface` has a
563 // terminator implementing `RegionBranchTerminatorOpInterface` or a
564 // return-like terminator, the region's successors' arguments flow back into
565 // the "successor operands" of this terminator.
566 //
567 // A successor operand with respect to an op implementing
568 // `RegionBranchOpInterface` is an operand that is forwarded to a region
569 // successor's input. There are two types of successor operands: the operands
570 // of this op itself and the operands of the terminators of the regions of
571 // this op.
572 if (auto terminator = dyn_cast<RegionBranchTerminatorOpInterface>(op)) {
573 LDBG() << "Processing RegionBranchTerminatorOpInterface operation";
574 if (auto branch = dyn_cast<RegionBranchOpInterface>(op->getParentOp())) {
575 visitRegionSuccessorsFromTerminator(terminator, branch);
576 return success();
577 }
578 }
579
580 if (op->hasTrait<OpTrait::ReturnLike>()) {
581 LDBG() << "Processing ReturnLike operation";
582 // Going backwards, the operands of the return are derived from the
583 // results of all CallOps calling this CallableOp.
584 if (auto callable = dyn_cast<CallableOpInterface>(op->getParentOp())) {
585 LDBG() << "Callable parent found, visiting callable operation";
586 return visitCallableOperation(op, callable, operandLattices);
587 }
588 }
589
590 LDBG() << "Using default visitOperationImpl for operation: "
591 << OpWithFlags(op, OpPrintingFlags().skipRegions());
592 return visitOperationImpl(op, operandLattices, resultLattices);
593}
594
596 Operation *op, CallableOpInterface callable,
597 ArrayRef<AbstractSparseLattice *> operandLattices) {
600 if (callsites->allPredecessorsKnown()) {
601 for (Operation *call : callsites->getKnownPredecessors()) {
602 // Only the forwarded results of the call receive the values returned by
603 // the callee.
604 ResultRange forwardedResults =
605 cast<CallOpInterface>(call).getForwardedResults();
607 getLatticeElementsFor(getProgramPointAfter(op), forwardedResults);
608 for (auto [op, result] : llvm::zip(operandLattices, callResultLattices))
609 meet(op, *result);
610 }
611 } else {
612 // If we don't know all the callers, we can't know where the
613 // returned values go. Note that, in particular, this will trigger
614 // for the return ops of any public functions.
615 setAllToExitStates(operandLattices);
616 }
617 return success();
618}
619
620void AbstractSparseBackwardDataFlowAnalysis::visitRegionSuccessors(
621 RegionBranchOpInterface branch,
622 ArrayRef<AbstractSparseLattice *> operandLattices) {
623 // Not all operands are forwarded to a successor. This set can be
624 // non-contiguous in the presence of multiple successors.
625 BitVector unaccounted(branch->getNumOperands(), true);
627 branch.getSuccessorOperandInputMapping(mapping, RegionBranchPoint::parent());
628 for (const auto &[operand, inputs] : mapping) {
629 for (Value input : inputs) {
630 meet(getLatticeElement(operand->get()),
631 *getLatticeElementFor(getProgramPointAfter(branch), input));
632 unaccounted.reset(operand->getOperandNumber());
633 }
634 }
635 Operation *op = branch.getOperation();
637 SmallVector<Attribute> operands(op->getNumOperands(), nullptr);
638 branch.getEntrySuccessorRegions(operands, successors);
639 for (RegionSuccessor &successor : successors) {
640 if (successor.isOperation())
641 continue;
642 auto valueToArgument = [](Value value) {
643 return cast<BlockArgument>(value);
644 };
645 SmallVector<BlockArgument> noControlFlowArguments = llvm::map_to_vector(
646 branch.getNonSuccessorInputs(successor), valueToArgument);
647 visitNonControlFlowArguments(successor, noControlFlowArguments);
648 }
649
650 // All operands not forwarded to regions are typically parameters of the
651 // branch operation itself (for example the boolean for if/else).
652 for (int index : unaccounted.set_bits()) {
653 visitBranchOperand(branch->getOpOperand(index));
654 }
655}
656
657void AbstractSparseBackwardDataFlowAnalysis::
658 visitRegionSuccessorsFromTerminator(
659 RegionBranchTerminatorOpInterface terminator,
660 RegionBranchOpInterface branch) {
661 assert(terminator->getParentOp() == branch.getOperation() &&
662 "expected `branch` to be the parent op of `terminator`");
663
664 // Not all operands are forwarded to a successor. This set can be
665 // non-contiguous in the presence of multiple successors.
666 BitVector unaccounted(terminator->getNumOperands(), true);
667
669 branch.getSuccessorOperandInputMapping(mapping,
670 RegionBranchPoint(terminator));
671 for (const auto &[operand, inputs] : mapping) {
672 for (Value input : inputs) {
673 meet(getLatticeElement(operand->get()),
674 *getLatticeElementFor(getProgramPointAfter(terminator), input));
675 unaccounted.reset(operand->getOperandNumber());
676 }
677 }
678
679 // Visit operands of the branch op not forwarded to the next region.
680 // (Like e.g. the boolean of `scf.conditional`)
681 for (int index : unaccounted.set_bits()) {
682 visitBranchOperand(terminator->getOpOperand(index));
683 }
684}
685
687AbstractSparseBackwardDataFlowAnalysis::getLatticeElementFor(
688 ProgramPoint *point, Value value) {
689 AbstractSparseLattice *state = getLatticeElement(value);
690 addDependency(state, point);
691 return state;
692}
693
699
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:152
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition Block.cpp:27
Operation & front()
Definition Block.h:177
pred_iterator pred_begin()
Definition Block.h:260
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:249
reverse_iterator rend()
Definition Block.h:170
BlockArgListType getArguments()
Definition Block.h:111
PredecessorIterator pred_iterator
Definition Block.h:259
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:263
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
Definition Block.cpp:31
reverse_iterator rbegin()
Definition Block.h:169
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:1162
This class implements the operand iterators for the Operation class.
Definition ValueRange.h:44
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:87
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:794
unsigned getNumSuccessors()
Definition Operation.h:751
Block * getBlock()
Returns the operation block that contains this operation.
Definition Operation.h:230
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:251
MutableArrayRef< OpOperand > getOpOperands()
Definition Operation.h:408
unsigned getNumOperands()
Definition Operation.h:371
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition Operation.h:722
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:403
SuccessorRange getSuccessors()
Definition Operation.h:748
result_range getResults()
Definition Operation.h:440
OpOperand & getOpOperand(unsigned idx)
Definition Operation.h:413
unsigned getNumResults()
Return the number of results held by this operation.
Definition Operation.h:429
static constexpr RegionBranchPoint parent()
Returns an instance of RegionBranchPoint representing the parent operation.
This class represents a successor of a region.
bool isOperation() const
Return true if the successor is an 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
This class implements the result iterators for the Operation class.
Definition ValueRange.h:248
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:389
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.