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