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"
13 #include "mlir/IR/OpDefinition.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 
24 using namespace mlir;
25 using 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 
92 void 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 
150 LogicalResult
152  LDBG() << "processOperation (forward): "
153  << OpWithFlags(op, OpPrintingFlags().skipRegions());
154  ProgramPoint *point = getProgramPointAfter(op);
155  // If the containing block is not executable, bail out.
156  if (op->getBlock() != nullptr &&
157  !getOrCreateFor<Executable>(point, getProgramPointBefore(op->getBlock()))
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 
193 void 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;
261  if (!getOrCreateFor<Executable>(
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  // Merge in the state from the predecessor's terminator.
270  join(after, *getLatticeFor(
271  point, getProgramPointAfter(predecessor->getTerminator())));
272  }
273 }
274 
276  ProgramPoint *point, RegionBranchOpInterface branch,
277  AbstractDenseLattice *after) {
278  LDBG() << "visitRegionBranchOperation (forward): "
279  << OpWithFlags(branch.getOperation(), OpPrintingFlags().skipRegions());
280  LDBG() << " point: " << *point;
281  LDBG() << " after state: " << *after;
282 
283  // Get the terminator predecessors.
284  const auto *predecessors = getOrCreateFor<PredecessorState>(point, point);
285  assert(predecessors->allPredecessorsKnown() &&
286  "unexpected unresolved region successors");
287 
288  LDBG() << " Processing " << predecessors->getKnownPredecessors().size()
289  << " known predecessors";
290  for (Operation *op : predecessors->getKnownPredecessors()) {
291  LDBG() << " Processing predecessor: "
292  << OpWithFlags(op, OpPrintingFlags().skipRegions());
293  const AbstractDenseLattice *before;
294  // If the predecessor is the parent, get the state before the parent.
295  if (op == branch) {
296  LDBG() << " Predecessor is the branch itself, getting state before "
297  "parent";
298  before = getLatticeFor(point, getProgramPointBefore(op));
299  // Otherwise, get the state after the terminator.
300  } else {
301  LDBG()
302  << " Predecessor is terminator, getting state after terminator";
303  before = getLatticeFor(point, getProgramPointAfter(op));
304  }
305  LDBG() << " before state: " << *before;
306 
307  // This function is called in two cases:
308  // 1. when visiting the block (point = block start);
309  // 2. when visiting the parent operation (point = iter after parent op).
310  // In both cases, we are looking for predecessor operations of the point,
311  // 1. predecessor may be the terminator of another block from another
312  // region (assuming that the block does belong to another region via an
313  // assertion) or the parent (when parent can transfer control to this
314  // region);
315  // 2. predecessor may be the terminator of a block that exits the
316  // region (when region transfers control to the parent) or the operation
317  // before the parent.
318  // In the latter case, just perform the join as it isn't the control flow
319  // affected by the region.
320  std::optional<unsigned> regionFrom =
321  op == branch ? std::optional<unsigned>()
322  : op->getBlock()->getParent()->getRegionNumber();
323  LDBG() << " regionFrom: "
324  << (regionFrom ? std::to_string(*regionFrom) : "parent");
325 
326  if (point->isBlockStart()) {
327  unsigned regionTo = point->getBlock()->getParent()->getRegionNumber();
328  LDBG() << " Point is block start, regionTo: " << regionTo;
329  LDBG() << " Calling visitRegionBranchControlFlowTransfer with "
330  "regionFrom/regionTo";
331  visitRegionBranchControlFlowTransfer(branch, regionFrom, regionTo,
332  *before, after);
333  } else {
334  assert(point->getPrevOp() == branch &&
335  "expected to be visiting the branch itself");
336  LDBG() << " Point is not block start, checking if predecessor is "
337  "region or op itself";
338  // Only need to call the arc transfer when the predecessor is the region
339  // or the op itself, not the previous op.
340  if (op->getParentOp() == branch || op == branch) {
341  LDBG() << " Predecessor is region or op itself, calling "
342  "visitRegionBranchControlFlowTransfer";
344  branch, regionFrom, /*regionTo=*/std::nullopt, *before, after);
345  } else {
346  LDBG()
347  << " Predecessor is not region or op itself, performing join";
348  join(after, *before);
349  }
350  }
351  }
352 }
353 
354 //===----------------------------------------------------------------------===//
355 // AbstractDenseBackwardDataFlowAnalysis
356 //===----------------------------------------------------------------------===//
357 
359  Operation *top) {
360  LDBG() << "initializeEquivalentLatticeAnchor (backward): "
361  << OpWithFlags(top, OpPrintingFlags().skipRegions());
362  top->walk([&](Operation *op) {
363  if (isa<RegionBranchOpInterface, CallOpInterface>(op)) {
364  LDBG() << " Skipping "
365  << OpWithFlags(op, OpPrintingFlags().skipRegions())
366  << " (region branch or call)";
367  return;
368  }
369  LDBG() << " Building equivalent lattice anchor for "
370  << OpWithFlags(op, OpPrintingFlags().skipRegions());
372  });
373 }
374 
375 LogicalResult
377  LDBG() << "initialize (backward): "
378  << OpWithFlags(top, OpPrintingFlags().skipRegions());
379  // Visit every operation and block.
380  if (failed(processOperation(top))) {
381  LDBG() << " Failed to process top-level operation";
382  return failure();
383  }
384 
385  for (Region &region : top->getRegions()) {
386  LDBG() << " Processing region with " << region.getBlocks().size()
387  << " blocks";
388  for (Block &block : region) {
389  LDBG() << " Processing block with " << block.getOperations().size()
390  << " operations";
391  visitBlock(&block);
392  for (Operation &op : llvm::reverse(block)) {
393  LDBG() << " Initializing operation (backward): "
394  << OpWithFlags(&op, OpPrintingFlags().skipRegions());
395  if (failed(initialize(&op))) {
396  LDBG() << " Failed to initialize operation";
397  return failure();
398  }
399  }
400  }
401  }
402  LDBG() << " Backward initialization completed successfully";
403  return success();
404 }
405 
406 LogicalResult
408  LDBG() << "visit (backward): " << *point;
409  if (!point->isBlockEnd()) {
410  LDBG() << " Processing operation: "
411  << OpWithFlags(point->getNextOp(), OpPrintingFlags().skipRegions());
412  return processOperation(point->getNextOp());
413  }
414  LDBG() << " Visiting block: " << point->getBlock();
415  visitBlock(point->getBlock());
416  return success();
417 }
418 
419 void AbstractDenseBackwardDataFlowAnalysis::visitCallOperation(
420  CallOpInterface call, const AbstractDenseLattice &after,
421  AbstractDenseLattice *before) {
422  LDBG() << "visitCallOperation (backward): "
423  << OpWithFlags(call.getOperation(), OpPrintingFlags().skipRegions());
424  LDBG() << " after state: " << after;
425  LDBG() << " before state: " << *before;
426 
427  // If the solver is not interprocedural, let the hook handle it as an external
428  // callee.
429  if (!getSolverConfig().isInterprocedural()) {
430  LDBG() << " Non-interprocedural analysis, handling as external callee";
432  call, CallControlFlowAction::ExternalCallee, after, before);
433  }
434 
435  // Find the callee.
436  Operation *callee = call.resolveCallableInTable(&symbolTable);
437  if (callee) {
438  LDBG() << " Resolved callee: "
439  << OpWithFlags(callee, OpPrintingFlags().skipRegions());
440  } else {
441  LDBG() << " Resolved callee: null";
442  }
443 
444  auto callable = dyn_cast_or_null<CallableOpInterface>(callee);
445  // No region means the callee is only declared in this module.
446  // If that is the case or if the solver is not interprocedural,
447  // let the hook handle it.
448  if (callable && (!callable.getCallableRegion() ||
449  callable.getCallableRegion()->empty())) {
450  LDBG() << " Callee has no region or empty region, handling as external "
451  "callee";
453  call, CallControlFlowAction::ExternalCallee, after, before);
454  }
455 
456  if (!callable) {
457  LDBG() << " No callable found, setting to exit state";
458  return setToExitState(before);
459  }
460 
461  Region *region = callable.getCallableRegion();
462  LDBG() << " Processing callable with region";
463 
464  // Call-level control flow specifies the data flow here.
465  //
466  // func.func @callee() {
467  // ^calleeEntryBlock:
468  // // latticeAtCalleeEntry
469  // ...
470  // }
471  // func.func @caller() {
472  // ...
473  // // latticeBeforeCall
474  // call @callee
475  // ...
476  // }
477  Block *calleeEntryBlock = &region->front();
478  ProgramPoint *calleeEntry = getProgramPointBefore(calleeEntryBlock);
479  const AbstractDenseLattice &latticeAtCalleeEntry =
480  *getLatticeFor(getProgramPointBefore(call.getOperation()), calleeEntry);
481  LDBG() << " Lattice at callee entry: " << latticeAtCalleeEntry;
482  AbstractDenseLattice *latticeBeforeCall = before;
484  latticeAtCalleeEntry, latticeBeforeCall);
485 }
486 
487 LogicalResult
489  LDBG() << "processOperation (backward): "
490  << OpWithFlags(op, OpPrintingFlags().skipRegions());
491  ProgramPoint *point = getProgramPointBefore(op);
492  // If the containing block is not executable, bail out.
493  if (op->getBlock() != nullptr &&
494  !getOrCreateFor<Executable>(point, getProgramPointBefore(op->getBlock()))
495  ->isLive()) {
496  LDBG() << " Block not executable, skipping operation";
497  return success();
498  }
499 
500  // Get the dense lattice to update.
501  AbstractDenseLattice *before = getLattice(point);
502 
503  // Get the dense state after execution of this op.
504  const AbstractDenseLattice *after =
506  LDBG() << " before state: " << *before;
507  LDBG() << " after state: " << *after;
508 
509  // Special cases where control flow may dictate data flow.
510  if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
511  LDBG() << " Processing as region branch operation";
512  visitRegionBranchOperation(point, branch, RegionBranchPoint::parent(),
513  before);
514  return success();
515  }
516  if (auto call = dyn_cast<CallOpInterface>(op)) {
517  LDBG() << " Processing as call operation";
518  visitCallOperation(call, *after, before);
519  return success();
520  }
521 
522  // Invoke the operation transfer function.
523  LDBG() << " Invoking operation transfer function";
524  return visitOperationImpl(op, *after, before);
525 }
526 
527 void AbstractDenseBackwardDataFlowAnalysis::visitBlock(Block *block) {
528  LDBG() << "visitBlock (backward): " << block;
529  ProgramPoint *point = getProgramPointAfter(block);
530  // If the block is not executable, bail out.
531  if (!getOrCreateFor<Executable>(point, getProgramPointBefore(block))
532  ->isLive()) {
533  LDBG() << " Block not executable, skipping";
534  return;
535  }
536 
537  AbstractDenseLattice *before = getLattice(point);
538  LDBG() << " Block lattice state: " << *before;
539 
540  // We need "exit" blocks, i.e. the blocks that may return control to the
541  // parent operation.
542  auto isExitBlock = [](Block *b) {
543  // Treat empty and terminator-less blocks as exit blocks.
544  if (b->empty() || !b->back().mightHaveTrait<OpTrait::IsTerminator>())
545  return true;
546 
547  // There may be a weird case where a terminator may be transferring control
548  // either to the parent or to another block, so exit blocks and successors
549  // are not mutually exclusive.
550  return isa_and_nonnull<RegionBranchTerminatorOpInterface>(
551  b->getTerminator());
552  };
553  if (isExitBlock(block)) {
554  LDBG() << " Processing exit block";
555  // If this block is exiting from a callable, the successors of exiting from
556  // a callable are the successors of all call sites. And the call sites
557  // themselves are predecessors of the callable.
558  auto callable = dyn_cast<CallableOpInterface>(block->getParentOp());
559  if (callable && callable.getCallableRegion() == block->getParent()) {
560  LDBG() << " Exit block of callable region";
561  const auto *callsites = getOrCreateFor<PredecessorState>(
562  point, getProgramPointAfter(callable));
563  // If not all call sites are known, conservative mark all lattices as
564  // having reached their pessimistic fix points.
565  if (!callsites->allPredecessorsKnown() ||
566  !getSolverConfig().isInterprocedural()) {
567  LDBG() << " Not all callsites known or non-interprocedural, setting "
568  "to exit state";
569  return setToExitState(before);
570  }
571 
572  LDBG() << " Processing " << callsites->getKnownPredecessors().size()
573  << " known callsites";
574  for (Operation *callsite : callsites->getKnownPredecessors()) {
575  LDBG() << " Processing callsite: "
576  << OpWithFlags(callsite, OpPrintingFlags().skipRegions());
577  const AbstractDenseLattice *after =
578  getLatticeFor(point, getProgramPointAfter(callsite));
579  LDBG() << " Lattice after callsite: " << *after;
580  visitCallControlFlowTransfer(cast<CallOpInterface>(callsite),
582  before);
583  }
584  return;
585  }
586 
587  // If this block is exiting from an operation with region-based control
588  // flow, propagate the lattice back along the control flow edge.
589  if (auto branch = dyn_cast<RegionBranchOpInterface>(block->getParentOp())) {
590  LDBG() << " Exit block of region branch operation";
591  visitRegionBranchOperation(point, branch, block->getParent(), before);
592  return;
593  }
594 
595  // Cannot reason about successors of an exit block, set the pessimistic
596  // fixpoint.
597  LDBG() << " Cannot reason about successors, setting to exit state";
598  return setToExitState(before);
599  }
600 
601  // Meet the state with the state before block's successors.
602  LDBG() << " Meeting state from " << block->getSuccessors().size()
603  << " successors";
604  for (Block *successor : block->getSuccessors()) {
605  if (!getOrCreateFor<Executable>(point,
606  getLatticeAnchor<CFGEdge>(block, successor))
607  ->isLive()) {
608  LDBG() << " Skipping non-executable edge to " << successor;
609  continue;
610  }
611 
612  LDBG() << " Meeting state from successor " << successor;
613  // Merge in the state from the successor: either the first operation, or the
614  // block itself when empty.
615  meet(before, *getLatticeFor(point, getProgramPointBefore(successor)));
616  }
617 }
618 
619 void AbstractDenseBackwardDataFlowAnalysis::visitRegionBranchOperation(
620  ProgramPoint *point, RegionBranchOpInterface branch,
621  RegionBranchPoint branchPoint, AbstractDenseLattice *before) {
622  LDBG() << "visitRegionBranchOperation (backward): "
623  << OpWithFlags(branch.getOperation(), OpPrintingFlags().skipRegions());
624  LDBG() << " branchPoint: " << (branchPoint.isParent() ? "parent" : "region");
625  LDBG() << " before state: " << *before;
626 
627  // The successors of the operation may be either the first operation of the
628  // entry block of each possible successor region, or the next operation when
629  // the branch is a successor of itself.
630  SmallVector<RegionSuccessor> successors;
631  branch.getSuccessorRegions(branchPoint, successors);
632  LDBG() << " Processing " << successors.size() << " successor regions";
633  for (const RegionSuccessor &successor : successors) {
634  const AbstractDenseLattice *after;
635  if (successor.isParent() || successor.getSuccessor()->empty()) {
636  LDBG() << " Successor is parent or empty region";
637  after = getLatticeFor(point, getProgramPointAfter(branch));
638  } else {
639  Region *successorRegion = successor.getSuccessor();
640  assert(!successorRegion->empty() && "unexpected empty successor region");
641  Block *successorBlock = &successorRegion->front();
642  LDBG() << " Successor region with "
643  << successorRegion->getBlocks().size() << " blocks";
644 
645  if (!getOrCreateFor<Executable>(point,
646  getProgramPointBefore(successorBlock))
647  ->isLive()) {
648  LDBG() << " Successor block not executable, skipping";
649  continue;
650  }
651 
652  after = getLatticeFor(point, getProgramPointBefore(successorBlock));
653  }
654  LDBG() << " After state: " << *after;
655 
656  visitRegionBranchControlFlowTransfer(branch, branchPoint, successor, *after,
657  before);
658  }
659 }
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
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
OpListType & getOperations()
Definition: Block.h:137
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 * getProgramPointAfter(Operation *op)
ProgramPoint * getProgramPointBefore(Operation *op)
Get a uniqued program point instance.
const DataFlowConfig & getSolverConfig() const
Return the configuration of the solver used for this analysis.
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.
Definition: OpDefinition.h:773
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
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
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
Implement a predecessor iterator for blocks.
Definition: BlockSupport.h:51
This class represents a point being branched from in the methods of the RegionBranchOpInterface.
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 represents a successor of a region.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition: Region.h:26
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
Block & front()
Definition: Region.h:65
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 const AbstractDenseLattice * getLatticeFor(ProgramPoint *dependent, LatticeAnchor anchor)=0
Get the dense lattice on the given lattice anchor and add dependent as its dependency.
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.
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.
void meet(AbstractDenseLattice *lhs, const AbstractDenseLattice &rhs)
Meet a lattice with another lattice and propagate an update if it changed.
virtual LogicalResult processOperation(Operation *op)
Visit an operation.
virtual AbstractDenseLattice * getLattice(LatticeAnchor anchor)=0
Get the dense lattice before the execution of the lattice anchor.
virtual void visitRegionBranchControlFlowTransfer(RegionBranchOpInterface branch, RegionBranchPoint regionFrom, RegionBranchPoint regionTo, const AbstractDenseLattice &after, AbstractDenseLattice *before)
Propagate the dense lattice backwards along the control flow edge from regionFrom to regionTo regions...
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.
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 LogicalResult processOperation(Operation *op)
Visit an operation.
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.
virtual AbstractDenseLattice * getLattice(LatticeAnchor anchor)=0
Get the dense lattice on the given lattice anchor.
LogicalResult visit(ProgramPoint *point) override
Visit a program point that modifies the state of the program.
virtual const AbstractDenseLattice * getLatticeFor(ProgramPoint *dependent, LatticeAnchor anchor)=0
Get the dense lattice on the given lattice anchor and add dependent as its dependency.
This class represents a dense lattice.
Definition: DenseAnalysis.h:41
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition: Remarks.h:491
Include the generated interface declarations.
Program point represents a specific location in the execution of a program.
bool isBlockStart() const
Operation * getPrevOp() const
Get the previous operation of this program point.
Operation * getNextOp() const
Get the next operation of this program point.
Block * getBlock() const
Get the block contains this program point.