MLIR 22.0.0git
DenseAnalysis.cpp
Go to the documentation of this file.
1//===- DenseAnalysis.cpp - Dense 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/Block.h"
14#include "mlir/IR/Operation.h"
15#include "mlir/IR/Region.h"
18#include "mlir/Support/LLVM.h"
19#include "llvm/ADT/STLExtras.h"
20#include "llvm/Support/DebugLog.h"
21#include <cassert>
22#include <optional>
23
24using namespace mlir;
25using namespace mlir::dataflow;
26
27#define DEBUG_TYPE "dense-analysis"
28
29//===----------------------------------------------------------------------===//
30// AbstractDenseForwardDataFlowAnalysis
31//===----------------------------------------------------------------------===//
32
34 Operation *top) {
35 LDBG() << "initializeEquivalentLatticeAnchor: "
36 << OpWithFlags(top, OpPrintingFlags().skipRegions());
37 top->walk([&](Operation *op) {
38 if (isa<RegionBranchOpInterface, CallOpInterface>(op)) {
39 LDBG() << " Skipping "
40 << OpWithFlags(op, OpPrintingFlags().skipRegions())
41 << " (region branch or call)";
42 return;
43 }
44 LDBG() << " Building equivalent lattice anchor for "
45 << OpWithFlags(op, OpPrintingFlags().skipRegions());
47 });
48}
49
51 LDBG() << "initialize (forward): "
52 << OpWithFlags(top, OpPrintingFlags().skipRegions());
53 // Visit every operation and block.
54 if (failed(processOperation(top))) {
55 LDBG() << " Failed to process top-level operation";
56 return failure();
57 }
58
59 for (Region &region : top->getRegions()) {
60 LDBG() << " Processing region with " << region.getBlocks().size()
61 << " blocks";
62 for (Block &block : region) {
63 LDBG() << " Processing block with " << block.getOperations().size()
64 << " operations";
65 visitBlock(&block);
66 for (Operation &op : block) {
67 LDBG() << " Initializing operation: "
68 << OpWithFlags(&op, OpPrintingFlags().skipRegions());
69 if (failed(initialize(&op))) {
70 LDBG() << " Failed to initialize operation";
71 return failure();
72 }
73 }
74 }
75 }
76 LDBG() << " Forward initialization completed successfully";
77 return success();
78}
79
81 LDBG() << "visit (forward): " << *point;
82 if (!point->isBlockStart()) {
83 LDBG() << " Processing operation: "
84 << OpWithFlags(point->getPrevOp(), OpPrintingFlags().skipRegions());
85 return processOperation(point->getPrevOp());
86 }
87 LDBG() << " Visiting block: " << point->getBlock();
88 visitBlock(point->getBlock());
89 return success();
90}
91
92void AbstractDenseForwardDataFlowAnalysis::visitCallOperation(
93 CallOpInterface call, const AbstractDenseLattice &before,
94 AbstractDenseLattice *after) {
95 LDBG() << "visitCallOperation (forward): "
96 << OpWithFlags(call.getOperation(), OpPrintingFlags().skipRegions());
97 LDBG() << " before state: " << before;
98 LDBG() << " after state: " << *after;
99
100 // Allow for customizing the behavior of calls to external symbols, including
101 // when the analysis is explicitly marked as non-interprocedural.
102 auto isExternalCallable = [&]() {
103 auto callable =
104 dyn_cast_if_present<CallableOpInterface>(call.resolveCallable());
105 return callable && !callable.getCallableRegion();
106 };
107 if (!getSolverConfig().isInterprocedural() || isExternalCallable()) {
108 LDBG() << " Handling as external callee (non-interprocedural or external)";
110 call, CallControlFlowAction::ExternalCallee, before, after);
111 }
112
113 const auto *predecessors = getOrCreateFor<PredecessorState>(
114 getProgramPointAfter(call.getOperation()), getProgramPointAfter(call));
115 // Otherwise, if not all return sites are known, then conservatively assume we
116 // can't reason about the data-flow.
117 if (!predecessors->allPredecessorsKnown()) {
118 LDBG() << " Not all predecessors known, setting to entry state";
119 return setToEntryState(after);
120 }
121
122 LDBG() << " Processing " << predecessors->getKnownPredecessors().size()
123 << " known predecessors";
124 for (Operation *predecessor : predecessors->getKnownPredecessors()) {
125 LDBG() << " Processing predecessor: "
126 << OpWithFlags(predecessor, OpPrintingFlags().skipRegions());
127 // Get the lattices at callee return:
128 //
129 // func.func @callee() {
130 // ...
131 // return // predecessor
132 // // latticeAtCalleeReturn
133 // }
134 // func.func @caller() {
135 // ...
136 // call @callee
137 // // latticeAfterCall
138 // ...
139 // }
140 AbstractDenseLattice *latticeAfterCall = after;
141 const AbstractDenseLattice *latticeAtCalleeReturn =
142 getLatticeFor(getProgramPointAfter(call.getOperation()),
143 getProgramPointAfter(predecessor));
144 LDBG() << " Lattice at callee return: " << *latticeAtCalleeReturn;
146 *latticeAtCalleeReturn, latticeAfterCall);
147 }
148}
149
150LogicalResult
152 LDBG() << "processOperation (forward): "
153 << OpWithFlags(op, OpPrintingFlags().skipRegions());
155 // If the containing block is not executable, bail out.
156 if (op->getBlock() != nullptr &&
158 ->isLive()) {
159 LDBG() << " Block not executable, skipping operation";
160 return success();
161 }
162
163 // Get the dense lattice to update.
164 AbstractDenseLattice *after = getLattice(point);
165
166 // Get the dense state before the execution of the op.
167 const AbstractDenseLattice *before =
169 LDBG() << " before state: " << *before;
170 LDBG() << " after state: " << *after;
171
172 // If this op implements region control-flow, then control-flow dictates its
173 // transfer function.
174 if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
175 LDBG() << " Processing as region branch operation";
176 visitRegionBranchOperation(point, branch, after);
177 return success();
178 }
179
180 // If this is a call operation, then join its lattices across known return
181 // sites.
182 if (auto call = dyn_cast<CallOpInterface>(op)) {
183 LDBG() << " Processing as call operation";
184 visitCallOperation(call, *before, after);
185 return success();
186 }
187
188 // Invoke the operation transfer function.
189 LDBG() << " Invoking operation transfer function";
190 return visitOperationImpl(op, *before, after);
191}
192
193void AbstractDenseForwardDataFlowAnalysis::visitBlock(Block *block) {
194 LDBG() << "visitBlock (forward): " << block;
195 // If the block is not executable, bail out.
196 ProgramPoint *point = getProgramPointBefore(block);
197 if (!getOrCreateFor<Executable>(point, point)->isLive()) {
198 LDBG() << " Block not executable, skipping";
199 return;
200 }
201
202 // Get the dense lattice to update.
203 AbstractDenseLattice *after = getLattice(point);
204 LDBG() << " Block lattice state: " << *after;
205
206 // The dense lattices of entry blocks are set by region control-flow or the
207 // callgraph.
208 if (block->isEntryBlock()) {
209 LDBG() << " Processing entry block";
210 // Check if this block is the entry block of a callable region.
211 auto callable = dyn_cast<CallableOpInterface>(block->getParentOp());
212 if (callable && callable.getCallableRegion() == block->getParent()) {
213 LDBG() << " Entry block of callable region";
214 const auto *callsites = getOrCreateFor<PredecessorState>(
215 point, getProgramPointAfter(callable));
216 // If not all callsites are known, conservatively mark all lattices as
217 // having reached their pessimistic fixpoints. Do the same if
218 // interprocedural analysis is not enabled.
219 if (!callsites->allPredecessorsKnown() ||
220 !getSolverConfig().isInterprocedural()) {
221 LDBG() << " Not all callsites known or non-interprocedural, setting "
222 "to entry state";
223 return setToEntryState(after);
224 }
225 LDBG() << " Processing " << callsites->getKnownPredecessors().size()
226 << " known callsites";
227 for (Operation *callsite : callsites->getKnownPredecessors()) {
228 LDBG() << " Processing callsite: "
229 << OpWithFlags(callsite, OpPrintingFlags().skipRegions());
230 // Get the dense lattice before the callsite.
231 const AbstractDenseLattice *before;
232 before = getLatticeFor(point, getProgramPointBefore(callsite));
233 LDBG() << " Lattice before callsite: " << *before;
234
235 visitCallControlFlowTransfer(cast<CallOpInterface>(callsite),
237 *before, after);
238 }
239 return;
240 }
241
242 // Check if we can reason about the control-flow.
243 if (auto branch = dyn_cast<RegionBranchOpInterface>(block->getParentOp())) {
244 LDBG() << " Entry block of region branch operation";
245 return visitRegionBranchOperation(point, branch, after);
246 }
247
248 // Otherwise, we can't reason about the data-flow.
249 LDBG() << " Cannot reason about data-flow, setting to entry state";
250 return setToEntryState(after);
251 }
252
253 // Join the state with the state after the block's predecessors.
254 LDBG() << " Joining state from "
255 << std::distance(block->pred_begin(), block->pred_end())
256 << " predecessors";
257 for (Block::pred_iterator it = block->pred_begin(), e = block->pred_end();
258 it != e; ++it) {
259 // Skip control edges that aren't executable.
260 Block *predecessor = *it;
262 point, getLatticeAnchor<CFGEdge>(predecessor, block))
263 ->isLive()) {
264 LDBG() << " Skipping non-executable edge from " << predecessor;
265 continue;
266 }
267
268 LDBG() << " Joining state from predecessor " << predecessor;
269 const AbstractDenseLattice &before = *getLatticeFor(
270 point, getProgramPointAfter(predecessor->getTerminator()));
271 // Merge in the state from the predecessor's terminator.
272 visitBlockTransfer(block, point, predecessor, before, after);
273 }
274}
275
277 ProgramPoint *point, RegionBranchOpInterface branch,
278 AbstractDenseLattice *after) {
279 LDBG() << "visitRegionBranchOperation (forward): "
280 << OpWithFlags(branch.getOperation(), OpPrintingFlags().skipRegions());
281 LDBG() << " point: " << *point;
282 LDBG() << " after state: " << *after;
283
284 // Get the terminator predecessors.
285 const auto *predecessors = getOrCreateFor<PredecessorState>(point, point);
286 assert(predecessors->allPredecessorsKnown() &&
287 "unexpected unresolved region successors");
288
289 LDBG() << " Processing " << predecessors->getKnownPredecessors().size()
290 << " known predecessors";
291 for (Operation *op : predecessors->getKnownPredecessors()) {
292 LDBG() << " Processing predecessor: "
293 << OpWithFlags(op, OpPrintingFlags().skipRegions());
294 const AbstractDenseLattice *before;
295 // If the predecessor is the parent, get the state before the parent.
296 if (op == branch) {
297 LDBG() << " Predecessor is the branch itself, getting state before "
298 "parent";
299 before = getLatticeFor(point, getProgramPointBefore(op));
300 // Otherwise, get the state after the terminator.
301 } else {
302 LDBG()
303 << " Predecessor is terminator, getting state after terminator";
304 before = getLatticeFor(point, getProgramPointAfter(op));
305 }
306 LDBG() << " before state: " << *before;
307
308 // This function is called in two cases:
309 // 1. when visiting the block (point = block start);
310 // 2. when visiting the parent operation (point = iter after parent op).
311 // In both cases, we are looking for predecessor operations of the point,
312 // 1. predecessor may be the terminator of another block from another
313 // region (assuming that the block does belong to another region via an
314 // assertion) or the parent (when parent can transfer control to this
315 // region);
316 // 2. predecessor may be the terminator of a block that exits the
317 // region (when region transfers control to the parent) or the operation
318 // before the parent.
319 // In the latter case, just perform the join as it isn't the control flow
320 // affected by the region.
321 std::optional<unsigned> regionFrom =
322 op == branch ? std::optional<unsigned>()
323 : op->getBlock()->getParent()->getRegionNumber();
324 LDBG() << " regionFrom: "
325 << (regionFrom ? std::to_string(*regionFrom) : "parent");
326
327 if (point->isBlockStart()) {
328 unsigned regionTo = point->getBlock()->getParent()->getRegionNumber();
329 LDBG() << " Point is block start, regionTo: " << regionTo;
330 LDBG() << " Calling visitRegionBranchControlFlowTransfer with "
331 "regionFrom/regionTo";
332 visitRegionBranchControlFlowTransfer(branch, regionFrom, regionTo,
333 *before, after);
334 } else {
335 assert(point->getPrevOp() == branch &&
336 "expected to be visiting the branch itself");
337 LDBG() << " Point is not block start, checking if predecessor is "
338 "region or op itself";
339 // Only need to call the arc transfer when the predecessor is the region
340 // or the op itself, not the previous op.
341 if (op->getParentOp() == branch || op == branch) {
342 LDBG() << " Predecessor is region or op itself, calling "
343 "visitRegionBranchControlFlowTransfer";
345 branch, regionFrom, /*regionTo=*/std::nullopt, *before, after);
346 } else {
347 LDBG()
348 << " Predecessor is not region or op itself, performing join";
349 join(after, *before);
350 }
351 }
352 }
353}
354
355//===----------------------------------------------------------------------===//
356// AbstractDenseBackwardDataFlowAnalysis
357//===----------------------------------------------------------------------===//
358
360 Operation *top) {
361 LDBG() << "initializeEquivalentLatticeAnchor (backward): "
362 << OpWithFlags(top, OpPrintingFlags().skipRegions());
363 top->walk([&](Operation *op) {
364 if (isa<RegionBranchOpInterface, CallOpInterface>(op)) {
365 LDBG() << " Skipping "
366 << OpWithFlags(op, OpPrintingFlags().skipRegions())
367 << " (region branch or call)";
368 return;
369 }
370 LDBG() << " Building equivalent lattice anchor for "
371 << OpWithFlags(op, OpPrintingFlags().skipRegions());
373 });
374}
375
376LogicalResult
378 LDBG() << "initialize (backward): "
379 << OpWithFlags(top, OpPrintingFlags().skipRegions());
380 // Visit every operation and block.
381 if (failed(processOperation(top))) {
382 LDBG() << " Failed to process top-level operation";
383 return failure();
384 }
385
386 for (Region &region : top->getRegions()) {
387 LDBG() << " Processing region with " << region.getBlocks().size()
388 << " blocks";
389 for (Block &block : region) {
390 LDBG() << " Processing block with " << block.getOperations().size()
391 << " operations";
392 visitBlock(&block);
393 for (Operation &op : llvm::reverse(block)) {
394 LDBG() << " Initializing operation (backward): "
395 << OpWithFlags(&op, OpPrintingFlags().skipRegions());
396 if (failed(initialize(&op))) {
397 LDBG() << " Failed to initialize operation";
398 return failure();
399 }
400 }
401 }
402 }
403 LDBG() << " Backward initialization completed successfully";
404 return success();
405}
406
407LogicalResult
409 LDBG() << "visit (backward): " << *point;
410 if (!point->isBlockEnd()) {
411 LDBG() << " Processing operation: "
412 << OpWithFlags(point->getNextOp(), OpPrintingFlags().skipRegions());
413 return processOperation(point->getNextOp());
414 }
415 LDBG() << " Visiting block: " << point->getBlock();
416 visitBlock(point->getBlock());
417 return success();
418}
419
420void AbstractDenseBackwardDataFlowAnalysis::visitCallOperation(
421 CallOpInterface call, const AbstractDenseLattice &after,
422 AbstractDenseLattice *before) {
423 LDBG() << "visitCallOperation (backward): "
424 << OpWithFlags(call.getOperation(), OpPrintingFlags().skipRegions());
425 LDBG() << " after state: " << after;
426 LDBG() << " before state: " << *before;
427
428 // If the solver is not interprocedural, let the hook handle it as an external
429 // callee.
430 if (!getSolverConfig().isInterprocedural()) {
431 LDBG() << " Non-interprocedural analysis, handling as external callee";
433 call, CallControlFlowAction::ExternalCallee, after, before);
434 }
435
436 // Find the callee.
437 Operation *callee = call.resolveCallableInTable(&symbolTable);
438 if (callee) {
439 LDBG() << " Resolved callee: "
440 << OpWithFlags(callee, OpPrintingFlags().skipRegions());
441 } else {
442 LDBG() << " Resolved callee: null";
443 }
444
445 auto callable = dyn_cast_or_null<CallableOpInterface>(callee);
446 // No region means the callee is only declared in this module.
447 // If that is the case or if the solver is not interprocedural,
448 // let the hook handle it.
449 if (callable && (!callable.getCallableRegion() ||
450 callable.getCallableRegion()->empty())) {
451 LDBG() << " Callee has no region or empty region, handling as external "
452 "callee";
454 call, CallControlFlowAction::ExternalCallee, after, before);
455 }
456
457 if (!callable) {
458 LDBG() << " No callable found, setting to exit state";
459 return setToExitState(before);
460 }
461
462 Region *region = callable.getCallableRegion();
463 LDBG() << " Processing callable with region";
464
465 // Call-level control flow specifies the data flow here.
466 //
467 // func.func @callee() {
468 // ^calleeEntryBlock:
469 // // latticeAtCalleeEntry
470 // ...
471 // }
472 // func.func @caller() {
473 // ...
474 // // latticeBeforeCall
475 // call @callee
476 // ...
477 // }
478 Block *calleeEntryBlock = &region->front();
479 ProgramPoint *calleeEntry = getProgramPointBefore(calleeEntryBlock);
480 const AbstractDenseLattice &latticeAtCalleeEntry =
481 *getLatticeFor(getProgramPointBefore(call.getOperation()), calleeEntry);
482 LDBG() << " Lattice at callee entry: " << latticeAtCalleeEntry;
483 AbstractDenseLattice *latticeBeforeCall = before;
485 latticeAtCalleeEntry, latticeBeforeCall);
486}
487
488LogicalResult
490 LDBG() << "processOperation (backward): "
491 << OpWithFlags(op, OpPrintingFlags().skipRegions());
493 // If the containing block is not executable, bail out.
494 if (op->getBlock() != nullptr &&
496 ->isLive()) {
497 LDBG() << " Block not executable, skipping operation";
498 return success();
499 }
500
501 // Get the dense lattice to update.
502 AbstractDenseLattice *before = getLattice(point);
503
504 // Get the dense state after execution of this op.
505 const AbstractDenseLattice *after =
507 LDBG() << " before state: " << *before;
508 LDBG() << " after state: " << *after;
509
510 // Special cases where control flow may dictate data flow.
511 if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
512 LDBG() << " Processing as region branch operation";
513 visitRegionBranchOperation(point, branch, RegionBranchPoint::parent(),
514 before);
515 return success();
516 }
517 if (auto call = dyn_cast<CallOpInterface>(op)) {
518 LDBG() << " Processing as call operation";
519 visitCallOperation(call, *after, before);
520 return success();
521 }
522
523 // Invoke the operation transfer function.
524 LDBG() << " Invoking operation transfer function";
525 return visitOperationImpl(op, *after, before);
526}
527
528void AbstractDenseBackwardDataFlowAnalysis::visitBlock(Block *block) {
529 LDBG() << "visitBlock (backward): " << block;
530 ProgramPoint *point = getProgramPointAfter(block);
531 // If the block is not executable, bail out.
533 ->isLive()) {
534 LDBG() << " Block not executable, skipping";
535 return;
536 }
537
538 AbstractDenseLattice *before = getLattice(point);
539 LDBG() << " Block lattice state: " << *before;
540
541 // We need "exit" blocks, i.e. the blocks that may return control to the
542 // parent operation.
543 auto isExitBlock = [](Block *b) {
544 // Treat empty and terminator-less blocks as exit blocks.
545 if (b->empty() || !b->back().mightHaveTrait<OpTrait::IsTerminator>())
546 return true;
547
548 // There may be a weird case where a terminator may be transferring control
549 // either to the parent or to another block, so exit blocks and successors
550 // are not mutually exclusive.
551 return isa_and_nonnull<RegionBranchTerminatorOpInterface>(
552 b->getTerminator());
553 };
554 if (isExitBlock(block)) {
555 LDBG() << " Processing exit block";
556 // If this block is exiting from a callable, the successors of exiting from
557 // a callable are the successors of all call sites. And the call sites
558 // themselves are predecessors of the callable.
559 auto callable = dyn_cast<CallableOpInterface>(block->getParentOp());
560 if (callable && callable.getCallableRegion() == block->getParent()) {
561 LDBG() << " Exit block of callable region";
562 const auto *callsites = getOrCreateFor<PredecessorState>(
563 point, getProgramPointAfter(callable));
564 // If not all call sites are known, conservative mark all lattices as
565 // having reached their pessimistic fix points.
566 if (!callsites->allPredecessorsKnown() ||
567 !getSolverConfig().isInterprocedural()) {
568 LDBG() << " Not all callsites known or non-interprocedural, setting "
569 "to exit state";
570 return setToExitState(before);
571 }
572
573 LDBG() << " Processing " << callsites->getKnownPredecessors().size()
574 << " known callsites";
575 for (Operation *callsite : callsites->getKnownPredecessors()) {
576 LDBG() << " Processing callsite: "
577 << OpWithFlags(callsite, OpPrintingFlags().skipRegions());
578 const AbstractDenseLattice *after =
579 getLatticeFor(point, getProgramPointAfter(callsite));
580 LDBG() << " Lattice after callsite: " << *after;
581 visitCallControlFlowTransfer(cast<CallOpInterface>(callsite),
583 before);
584 }
585 return;
586 }
587
588 // If this block is exiting from an operation with region-based control
589 // flow, propagate the lattice back along the control flow edge.
590 if (auto branch = dyn_cast<RegionBranchOpInterface>(block->getParentOp())) {
591 LDBG() << " Exit block of region branch operation";
592 auto terminator =
593 cast<RegionBranchTerminatorOpInterface>(block->getTerminator());
594 visitRegionBranchOperation(point, branch, terminator, before);
595 return;
596 }
597
598 // Cannot reason about successors of an exit block, set the pessimistic
599 // fixpoint.
600 LDBG() << " Cannot reason about successors, setting to exit state";
601 return setToExitState(before);
602 }
603
604 // Meet the state with the state before block's successors.
605 LDBG() << " Meeting state from " << block->getSuccessors().size()
606 << " successors";
607 for (Block *successor : block->getSuccessors()) {
609 getLatticeAnchor<CFGEdge>(block, successor))
610 ->isLive()) {
611 LDBG() << " Skipping non-executable edge to " << successor;
612 continue;
613 }
614
615 LDBG() << " Meeting state from successor " << successor;
616 // Merge in the state from the successor: either the first operation, or the
617 // block itself when empty.
618 visitBlockTransfer(block, point, successor,
619 *getLatticeFor(point, getProgramPointBefore(successor)),
620 before);
621 }
622}
623
624void AbstractDenseBackwardDataFlowAnalysis::visitRegionBranchOperation(
625 ProgramPoint *point, RegionBranchOpInterface branch,
626 RegionBranchPoint branchPoint, AbstractDenseLattice *before) {
627 LDBG() << "visitRegionBranchOperation (backward): "
628 << OpWithFlags(branch.getOperation(), OpPrintingFlags().skipRegions());
629 LDBG() << " branchPoint: " << (branchPoint.isParent() ? "parent" : "region");
630 LDBG() << " before state: " << *before;
631
632 // The successors of the operation may be either the first operation of the
633 // entry block of each possible successor region, or the next operation when
634 // the branch is a successor of itself.
635 SmallVector<RegionSuccessor> successors;
636 branch.getSuccessorRegions(branchPoint, successors);
637 LDBG() << " Processing " << successors.size() << " successor regions";
638 for (const RegionSuccessor &successor : successors) {
639 const AbstractDenseLattice *after;
640 if (successor.isParent() || successor.getSuccessor()->empty()) {
641 LDBG() << " Successor is parent or empty region";
642 after = getLatticeFor(point, getProgramPointAfter(branch));
643 } else {
644 Region *successorRegion = successor.getSuccessor();
645 assert(!successorRegion->empty() && "unexpected empty successor region");
646 Block *successorBlock = &successorRegion->front();
647 LDBG() << " Successor region with "
648 << successorRegion->getBlocks().size() << " blocks";
649
651 getProgramPointBefore(successorBlock))
652 ->isLive()) {
653 LDBG() << " Successor block not executable, skipping";
654 continue;
655 }
656
657 after = getLatticeFor(point, getProgramPointBefore(successorBlock));
658 }
659 LDBG() << " After state: " << *after;
660
661 visitRegionBranchControlFlowTransfer(branch, branchPoint, successor, *after,
662 before);
663 }
664}
return success()
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
Block represents an ordered list of Operations.
Definition Block.h:33
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition Block.cpp:27
OpListType & getOperations()
Definition Block.h:137
pred_iterator pred_begin()
Definition Block.h:236
SuccessorRange getSuccessors()
Definition Block.h:270
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:244
PredecessorIterator pred_iterator
Definition Block.h:235
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:239
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
Definition Block.cpp:31
ProgramPoint * getProgramPointBefore(Operation *op)
Get a uniqued program point instance.
const DataFlowConfig & getSolverConfig() const
Return the configuration of the solver used for this analysis.
ProgramPoint * getProgramPointAfter(Operation *op)
AnchorT * getLatticeAnchor(Args &&...args)
Get or create a custom lattice anchor.
const StateT * getOrCreateFor(ProgramPoint *dependent, AnchorT anchor)
Get a read-only analysis state for the given point and create a dependency on dependent.
Set of flags used to control the behavior of the various IR print methods (e.g.
This class provides the API for ops that are known to be terminators.
A wrapper class that allows for printing an operation with a set of flags, useful to act as a "stream...
Definition Operation.h:1111
Operation is the basic unit of execution within MLIR.
Definition Operation.h:88
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
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
Definition Operation.h:797
bool isParent() const
Returns true if branching from the parent op.
static constexpr RegionBranchPoint parent()
Returns an instance of RegionBranchPoint representing 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
unsigned getRegionNumber()
Return the number of this region in the parent operation.
Definition Region.cpp:62
bool empty()
Definition Region.h:60
BlockListType & getBlocks()
Definition Region.h:45
virtual void setToExitState(AbstractDenseLattice *lattice)=0
Set the dense lattice before at the control flow exit point and propagate the update if it changed.
LogicalResult initialize(Operation *top) override
Initialize the analysis by visiting every program point whose execution may modify the program state;...
virtual void visitCallControlFlowTransfer(CallOpInterface call, CallControlFlowAction action, const AbstractDenseLattice &after, AbstractDenseLattice *before)
Propagate the dense lattice backwards along the call control flow edge, which can be either entering ...
virtual void initializeEquivalentLatticeAnchor(Operation *top) override
Initialize lattice anchor equivalence class from the provided top-level operation.
virtual void buildOperationEquivalentLatticeAnchor(Operation *op)
Visit an operation.
virtual void visitBlockTransfer(Block *block, ProgramPoint *point, Block *successor, const AbstractDenseLattice &after, AbstractDenseLattice *before)
Visit a block and propagate the dense lattice backward along the control flow edge from successor to ...
virtual AbstractDenseLattice * getLattice(LatticeAnchor anchor)=0
Get the dense lattice before the execution of the lattice anchor.
LogicalResult visit(ProgramPoint *point) override
Visit a program point that modifies the state of the program.
virtual LogicalResult visitOperationImpl(Operation *op, const AbstractDenseLattice &after, AbstractDenseLattice *before)=0
Propagate the dense lattice after the execution of an operation to the lattice before its execution.
virtual LogicalResult processOperation(Operation *op)
Visit an operation.
virtual void visitRegionBranchControlFlowTransfer(RegionBranchOpInterface branch, RegionBranchPoint regionFrom, RegionSuccessor regionTo, const AbstractDenseLattice &after, AbstractDenseLattice *before)
Propagate the dense lattice backwards along the control flow edge from regionFrom to regionTo regions...
virtual const AbstractDenseLattice * getLatticeFor(ProgramPoint *dependent, LatticeAnchor anchor)=0
Get the dense lattice on the given lattice anchor and add dependent as its dependency.
virtual LogicalResult visitOperationImpl(Operation *op, const AbstractDenseLattice &before, AbstractDenseLattice *after)=0
Propagate the dense lattice before the execution of an operation to the lattice after its execution.
virtual const AbstractDenseLattice * getLatticeFor(ProgramPoint *dependent, LatticeAnchor anchor)=0
Get the dense lattice on the given lattice anchor and add dependent as its dependency.
void visitRegionBranchOperation(ProgramPoint *point, RegionBranchOpInterface branch, AbstractDenseLattice *after)
Visit a program point within a region branch operation with predecessors in it.
virtual void initializeEquivalentLatticeAnchor(Operation *top) override
Initialize lattice anchor equivalence class from the provided top-level operation.
void join(AbstractDenseLattice *lhs, const AbstractDenseLattice &rhs)
Join a lattice with another and propagate an update if it changed.
virtual void setToEntryState(AbstractDenseLattice *lattice)=0
Set the dense lattice at control flow entry point and propagate an update if it changed.
LogicalResult initialize(Operation *top) override
Initialize the analysis by visiting every program point whose execution may modify the program state;...
virtual void visitCallControlFlowTransfer(CallOpInterface call, CallControlFlowAction action, const AbstractDenseLattice &before, AbstractDenseLattice *after)
Propagate the dense lattice forward along the call control flow edge, which can be either entering or...
virtual AbstractDenseLattice * getLattice(LatticeAnchor anchor)=0
Get the dense lattice on the given lattice anchor.
virtual LogicalResult processOperation(Operation *op)
Visit an operation.
virtual void visitBlockTransfer(Block *block, ProgramPoint *point, Block *predecessor, const AbstractDenseLattice &before, AbstractDenseLattice *after)
Visit a block and propagate the dense lattice forward along the control flow edge from predecessor to...
virtual void visitRegionBranchControlFlowTransfer(RegionBranchOpInterface branch, std::optional< unsigned > regionFrom, std::optional< unsigned > regionTo, const AbstractDenseLattice &before, AbstractDenseLattice *after)
Propagate the dense lattice forward along the control flow edge from regionFrom to regionTo regions o...
virtual void buildOperationEquivalentLatticeAnchor(Operation *op)
Visit an operation.
LogicalResult visit(ProgramPoint *point) override
Visit a program point that modifies the state of the program.
This class represents a dense lattice.
Include the generated interface declarations.
Program point represents a specific location in the execution of a program.
Block * getBlock() const
Get the block contains this program point.
Operation * getNextOp() const
Get the next operation of this program point.
Operation * getPrevOp() const
Get the previous operation of this program point.