MLIR  20.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/Casting.h"
21 #include <cassert>
22 #include <optional>
23 
24 using namespace mlir;
25 using namespace mlir::dataflow;
26 
27 //===----------------------------------------------------------------------===//
28 // AbstractDenseForwardDataFlowAnalysis
29 //===----------------------------------------------------------------------===//
30 
32  // Visit every operation and block.
33  processOperation(top);
34  for (Region &region : top->getRegions()) {
35  for (Block &block : region) {
36  visitBlock(&block);
37  for (Operation &op : block)
38  if (failed(initialize(&op)))
39  return failure();
40  }
41  }
42  return success();
43 }
44 
46  if (auto *op = llvm::dyn_cast_if_present<Operation *>(point))
47  processOperation(op);
48  else if (auto *block = llvm::dyn_cast_if_present<Block *>(point))
49  visitBlock(block);
50  else
51  return failure();
52  return success();
53 }
54 
55 void AbstractDenseForwardDataFlowAnalysis::visitCallOperation(
56  CallOpInterface call, const AbstractDenseLattice &before,
57  AbstractDenseLattice *after) {
58  // Allow for customizing the behavior of calls to external symbols, including
59  // when the analysis is explicitly marked as non-interprocedural.
60  auto callable =
61  dyn_cast_if_present<CallableOpInterface>(call.resolveCallable());
62  if (!getSolverConfig().isInterprocedural() ||
63  (callable && !callable.getCallableRegion())) {
65  call, CallControlFlowAction::ExternalCallee, before, after);
66  }
67 
68  const auto *predecessors =
69  getOrCreateFor<PredecessorState>(call.getOperation(), call);
70  // Otherwise, if not all return sites are known, then conservatively assume we
71  // can't reason about the data-flow.
72  if (!predecessors->allPredecessorsKnown())
73  return setToEntryState(after);
74 
75  for (Operation *predecessor : predecessors->getKnownPredecessors()) {
76  // Get the lattices at callee return:
77  //
78  // func.func @callee() {
79  // ...
80  // return // predecessor
81  // // latticeAtCalleeReturn
82  // }
83  // func.func @caller() {
84  // ...
85  // call @callee
86  // // latticeAfterCall
87  // ...
88  // }
89  AbstractDenseLattice *latticeAfterCall = after;
90  const AbstractDenseLattice *latticeAtCalleeReturn =
91  getLatticeFor(call.getOperation(), predecessor);
93  *latticeAtCalleeReturn, latticeAfterCall);
94  }
95 }
96 
98  // If the containing block is not executable, bail out.
99  if (!getOrCreateFor<Executable>(op, op->getBlock())->isLive())
100  return;
101 
102  // Get the dense lattice to update.
103  AbstractDenseLattice *after = getLattice(op);
104 
105  // Get the dense state before the execution of the op.
106  const AbstractDenseLattice *before;
107  if (Operation *prev = op->getPrevNode())
108  before = getLatticeFor(op, prev);
109  else
110  before = getLatticeFor(op, op->getBlock());
111 
112  // If this op implements region control-flow, then control-flow dictates its
113  // transfer function.
114  if (auto branch = dyn_cast<RegionBranchOpInterface>(op))
115  return visitRegionBranchOperation(op, branch, after);
116 
117  // If this is a call operation, then join its lattices across known return
118  // sites.
119  if (auto call = dyn_cast<CallOpInterface>(op))
120  return visitCallOperation(call, *before, after);
121 
122  // Invoke the operation transfer function.
123  visitOperationImpl(op, *before, after);
124 }
125 
126 void AbstractDenseForwardDataFlowAnalysis::visitBlock(Block *block) {
127  // If the block is not executable, bail out.
128  if (!getOrCreateFor<Executable>(block, block)->isLive())
129  return;
130 
131  // Get the dense lattice to update.
132  AbstractDenseLattice *after = getLattice(block);
133 
134  // The dense lattices of entry blocks are set by region control-flow or the
135  // callgraph.
136  if (block->isEntryBlock()) {
137  // Check if this block is the entry block of a callable region.
138  auto callable = dyn_cast<CallableOpInterface>(block->getParentOp());
139  if (callable && callable.getCallableRegion() == block->getParent()) {
140  const auto *callsites = getOrCreateFor<PredecessorState>(block, callable);
141  // If not all callsites are known, conservatively mark all lattices as
142  // having reached their pessimistic fixpoints. Do the same if
143  // interprocedural analysis is not enabled.
144  if (!callsites->allPredecessorsKnown() ||
145  !getSolverConfig().isInterprocedural())
146  return setToEntryState(after);
147  for (Operation *callsite : callsites->getKnownPredecessors()) {
148  // Get the dense lattice before the callsite.
149  const AbstractDenseLattice *before;
150  if (Operation *prev = callsite->getPrevNode())
151  before = getLatticeFor(block, prev);
152  else
153  before = getLatticeFor(block, callsite->getBlock());
154 
155  visitCallControlFlowTransfer(cast<CallOpInterface>(callsite),
157  *before, after);
158  }
159  return;
160  }
161 
162  // Check if we can reason about the control-flow.
163  if (auto branch = dyn_cast<RegionBranchOpInterface>(block->getParentOp()))
164  return visitRegionBranchOperation(block, branch, after);
165 
166  // Otherwise, we can't reason about the data-flow.
167  return setToEntryState(after);
168  }
169 
170  // Join the state with the state after the block's predecessors.
171  for (Block::pred_iterator it = block->pred_begin(), e = block->pred_end();
172  it != e; ++it) {
173  // Skip control edges that aren't executable.
174  Block *predecessor = *it;
175  if (!getOrCreateFor<Executable>(
176  block, getProgramPoint<CFGEdge>(predecessor, block))
177  ->isLive())
178  continue;
179 
180  // Merge in the state from the predecessor's terminator.
181  join(after, *getLatticeFor(block, predecessor->getTerminator()));
182  }
183 }
184 
186  ProgramPoint point, RegionBranchOpInterface branch,
187  AbstractDenseLattice *after) {
188  // Get the terminator predecessors.
189  const auto *predecessors = getOrCreateFor<PredecessorState>(point, point);
190  assert(predecessors->allPredecessorsKnown() &&
191  "unexpected unresolved region successors");
192 
193  for (Operation *op : predecessors->getKnownPredecessors()) {
194  const AbstractDenseLattice *before;
195  // If the predecessor is the parent, get the state before the parent.
196  if (op == branch) {
197  if (Operation *prev = op->getPrevNode())
198  before = getLatticeFor(point, prev);
199  else
200  before = getLatticeFor(point, op->getBlock());
201 
202  // Otherwise, get the state after the terminator.
203  } else {
204  before = getLatticeFor(point, op);
205  }
206 
207  // This function is called in two cases:
208  // 1. when visiting the block (point = block);
209  // 2. when visiting the parent operation (point = parent op).
210  // In both cases, we are looking for predecessor operations of the point,
211  // 1. predecessor may be the terminator of another block from another
212  // region (assuming that the block does belong to another region via an
213  // assertion) or the parent (when parent can transfer control to this
214  // region);
215  // 2. predecessor may be the terminator of a block that exits the
216  // region (when region transfers control to the parent) or the operation
217  // before the parent.
218  // In the latter case, just perform the join as it isn't the control flow
219  // affected by the region.
220  std::optional<unsigned> regionFrom =
221  op == branch ? std::optional<unsigned>()
222  : op->getBlock()->getParent()->getRegionNumber();
223  if (auto *toBlock = point.dyn_cast<Block *>()) {
224  unsigned regionTo = toBlock->getParent()->getRegionNumber();
225  visitRegionBranchControlFlowTransfer(branch, regionFrom, regionTo,
226  *before, after);
227  } else {
228  assert(point.get<Operation *>() == branch &&
229  "expected to be visiting the branch itself");
230  // Only need to call the arc transfer when the predecessor is the region
231  // or the op itself, not the previous op.
232  if (op->getParentOp() == branch || op == branch) {
234  branch, regionFrom, /*regionTo=*/std::nullopt, *before, after);
235  } else {
236  join(after, *before);
237  }
238  }
239  }
240 }
241 
242 const AbstractDenseLattice *
244  ProgramPoint point) {
245  AbstractDenseLattice *state = getLattice(point);
246  addDependency(state, dependent);
247  return state;
248 }
249 
250 //===----------------------------------------------------------------------===//
251 // AbstractDenseBackwardDataFlowAnalysis
252 //===----------------------------------------------------------------------===//
253 
254 LogicalResult
256  // Visit every operation and block.
257  processOperation(top);
258  for (Region &region : top->getRegions()) {
259  for (Block &block : region) {
260  visitBlock(&block);
261  for (Operation &op : llvm::reverse(block)) {
262  if (failed(initialize(&op)))
263  return failure();
264  }
265  }
266  }
267  return success();
268 }
269 
271  if (auto *op = llvm::dyn_cast_if_present<Operation *>(point))
272  processOperation(op);
273  else if (auto *block = llvm::dyn_cast_if_present<Block *>(point))
274  visitBlock(block);
275  else
276  return failure();
277  return success();
278 }
279 
280 void AbstractDenseBackwardDataFlowAnalysis::visitCallOperation(
281  CallOpInterface call, const AbstractDenseLattice &after,
282  AbstractDenseLattice *before) {
283  // Find the callee.
284  Operation *callee = call.resolveCallable(&symbolTable);
285 
286  auto callable = dyn_cast_or_null<CallableOpInterface>(callee);
287  // No region means the callee is only declared in this module.
288  // If that is the case or if the solver is not interprocedural,
289  // let the hook handle it.
290  if (!getSolverConfig().isInterprocedural() ||
291  (callable && (!callable.getCallableRegion() ||
292  callable.getCallableRegion()->empty()))) {
294  call, CallControlFlowAction::ExternalCallee, after, before);
295  }
296 
297  if (!callable)
298  return setToExitState(before);
299 
300  Region *region = callable.getCallableRegion();
301 
302  // Call-level control flow specifies the data flow here.
303  //
304  // func.func @callee() {
305  // ^calleeEntryBlock:
306  // // latticeAtCalleeEntry
307  // ...
308  // }
309  // func.func @caller() {
310  // ...
311  // // latticeBeforeCall
312  // call @callee
313  // ...
314  // }
315  Block *calleeEntryBlock = &region->front();
316  ProgramPoint calleeEntry = calleeEntryBlock->empty()
317  ? ProgramPoint(calleeEntryBlock)
318  : &calleeEntryBlock->front();
319  const AbstractDenseLattice &latticeAtCalleeEntry =
320  *getLatticeFor(call.getOperation(), calleeEntry);
321  AbstractDenseLattice *latticeBeforeCall = before;
323  latticeAtCalleeEntry, latticeBeforeCall);
324 }
325 
327  // If the containing block is not executable, bail out.
328  if (!getOrCreateFor<Executable>(op, op->getBlock())->isLive())
329  return;
330 
331  // Get the dense lattice to update.
332  AbstractDenseLattice *before = getLattice(op);
333 
334  // Get the dense state after execution of this op.
335  const AbstractDenseLattice *after;
336  if (Operation *next = op->getNextNode())
337  after = getLatticeFor(op, next);
338  else
339  after = getLatticeFor(op, op->getBlock());
340 
341  // Special cases where control flow may dictate data flow.
342  if (auto branch = dyn_cast<RegionBranchOpInterface>(op))
343  return visitRegionBranchOperation(op, branch, RegionBranchPoint::parent(),
344  before);
345  if (auto call = dyn_cast<CallOpInterface>(op))
346  return visitCallOperation(call, *after, before);
347 
348  // Invoke the operation transfer function.
349  visitOperationImpl(op, *after, before);
350 }
351 
352 void AbstractDenseBackwardDataFlowAnalysis::visitBlock(Block *block) {
353  // If the block is not executable, bail out.
354  if (!getOrCreateFor<Executable>(block, block)->isLive())
355  return;
356 
357  AbstractDenseLattice *before = getLattice(block);
358 
359  // We need "exit" blocks, i.e. the blocks that may return control to the
360  // parent operation.
361  auto isExitBlock = [](Block *b) {
362  // Treat empty and terminator-less blocks as exit blocks.
363  if (b->empty() || !b->back().mightHaveTrait<OpTrait::IsTerminator>())
364  return true;
365 
366  // There may be a weird case where a terminator may be transferring control
367  // either to the parent or to another block, so exit blocks and successors
368  // are not mutually exclusive.
369  return isa_and_nonnull<RegionBranchTerminatorOpInterface>(
370  b->getTerminator());
371  };
372  if (isExitBlock(block)) {
373  // If this block is exiting from a callable, the successors of exiting from
374  // a callable are the successors of all call sites. And the call sites
375  // themselves are predecessors of the callable.
376  auto callable = dyn_cast<CallableOpInterface>(block->getParentOp());
377  if (callable && callable.getCallableRegion() == block->getParent()) {
378  const auto *callsites = getOrCreateFor<PredecessorState>(block, callable);
379  // If not all call sites are known, conservative mark all lattices as
380  // having reached their pessimistic fix points.
381  if (!callsites->allPredecessorsKnown() ||
382  !getSolverConfig().isInterprocedural()) {
383  return setToExitState(before);
384  }
385 
386  for (Operation *callsite : callsites->getKnownPredecessors()) {
387  const AbstractDenseLattice *after;
388  if (Operation *next = callsite->getNextNode())
389  after = getLatticeFor(block, next);
390  else
391  after = getLatticeFor(block, callsite->getBlock());
392  visitCallControlFlowTransfer(cast<CallOpInterface>(callsite),
394  before);
395  }
396  return;
397  }
398 
399  // If this block is exiting from an operation with region-based control
400  // flow, propagate the lattice back along the control flow edge.
401  if (auto branch = dyn_cast<RegionBranchOpInterface>(block->getParentOp())) {
402  visitRegionBranchOperation(block, branch, block->getParent(), before);
403  return;
404  }
405 
406  // Cannot reason about successors of an exit block, set the pessimistic
407  // fixpoint.
408  return setToExitState(before);
409  }
410 
411  // Meet the state with the state before block's successors.
412  for (Block *successor : block->getSuccessors()) {
413  if (!getOrCreateFor<Executable>(block,
414  getProgramPoint<CFGEdge>(block, successor))
415  ->isLive())
416  continue;
417 
418  // Merge in the state from the successor: either the first operation, or the
419  // block itself when empty.
420  if (successor->empty())
421  meet(before, *getLatticeFor(block, successor));
422  else
423  meet(before, *getLatticeFor(block, &successor->front()));
424  }
425 }
426 
427 void AbstractDenseBackwardDataFlowAnalysis::visitRegionBranchOperation(
428  ProgramPoint point, RegionBranchOpInterface branch,
429  RegionBranchPoint branchPoint, AbstractDenseLattice *before) {
430 
431  // The successors of the operation may be either the first operation of the
432  // entry block of each possible successor region, or the next operation when
433  // the branch is a successor of itself.
434  SmallVector<RegionSuccessor> successors;
435  branch.getSuccessorRegions(branchPoint, successors);
436  for (const RegionSuccessor &successor : successors) {
437  const AbstractDenseLattice *after;
438  if (successor.isParent() || successor.getSuccessor()->empty()) {
439  if (Operation *next = branch->getNextNode())
440  after = getLatticeFor(point, next);
441  else
442  after = getLatticeFor(point, branch->getBlock());
443  } else {
444  Region *successorRegion = successor.getSuccessor();
445  assert(!successorRegion->empty() && "unexpected empty successor region");
446  Block *successorBlock = &successorRegion->front();
447 
448  if (!getOrCreateFor<Executable>(point, successorBlock)->isLive())
449  continue;
450 
451  if (successorBlock->empty())
452  after = getLatticeFor(point, successorBlock);
453  else
454  after = getLatticeFor(point, &successorBlock->front());
455  }
456 
457  visitRegionBranchControlFlowTransfer(branch, branchPoint, successor, *after,
458  before);
459  }
460 }
461 
462 const AbstractDenseLattice *
464  ProgramPoint point) {
465  AbstractDenseLattice *state = getLattice(point);
466  addDependency(state, dependent);
467  return state;
468 }
Block represents an ordered list of Operations.
Definition: Block.h:31
bool empty()
Definition: Block.h:146
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition: Block.cpp:26
pred_iterator pred_begin()
Definition: Block.h:231
SuccessorRange getSuccessors()
Definition: Block.h:265
Operation * getTerminator()
Get the terminator operation of this block.
Definition: Block.cpp:243
Operation & front()
Definition: Block.h:151
bool isEntryBlock()
Return if this block is the entry block in the parent region.
Definition: Block.cpp:35
pred_iterator pred_end()
Definition: Block.h:234
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
Definition: Block.cpp:30
const DataFlowConfig & getSolverConfig() const
Return the configuration of the solver used for this analysis.
void addDependency(AnalysisState *state, ProgramPoint point)
Create a dependency between the given analysis state and program point on this analysis.
This class provides the API for ops that are known to be terminators.
Definition: OpDefinition.h:764
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition: Operation.h:234
Block * getBlock()
Returns the operation block that contains this operation.
Definition: Operation.h:213
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition: Operation.h:672
Implement a predecessor iterator for blocks.
Definition: BlockSupport.h:51
This class represents a point being branched from in the methods of the RegionBranchOpInterface.
static constexpr RegionBranchPoint parent()
Returns an instance of RegionBranchPoint representing the parent operation.
This class represents a successor of a region.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition: Region.h:26
unsigned getRegionNumber()
Return the number of this region in the parent operation.
Definition: Region.cpp:62
bool empty()
Definition: Region.h:60
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 ...
LogicalResult visit(ProgramPoint point) override
Visit a program point that modifies the state of the program.
virtual void 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 void processOperation(Operation *op)
Visit an operation.
const AbstractDenseLattice * getLatticeFor(ProgramPoint dependent, ProgramPoint point)
Get the dense lattice before the execution of the program point point and declare that the dependent ...
virtual AbstractDenseLattice * getLattice(ProgramPoint point)=0
Get the dense lattice before the execution of the program point.
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...
const AbstractDenseLattice * getLatticeFor(ProgramPoint dependent, ProgramPoint point)
Get the dense lattice after the execution of the given program point and add it as a dependency to a ...
virtual AbstractDenseLattice * getLattice(ProgramPoint point)=0
Get the dense lattice after the execution of the given program point.
void join(AbstractDenseLattice *lhs, const AbstractDenseLattice &rhs)
Join a lattice with another and propagate an update if it changed.
void visitRegionBranchOperation(ProgramPoint point, RegionBranchOpInterface branch, AbstractDenseLattice *after)
Visit a program point within a region branch operation with predecessors in it.
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...
LogicalResult visit(ProgramPoint point) override
Visit a program point that modifies the state of the program.
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 processOperation(Operation *op)
Visit an operation.
virtual void 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.
This class represents a dense lattice.
Definition: DenseAnalysis.h:42
Include the generated interface declarations.
Fundamental IR components are supported as first-class program points.