xref: /llvm-project/mlir/include/mlir/Analysis/DataFlow/DenseAnalysis.h (revision 4b3f251bada55cfc20a2c72321fa0bbfd7a759d5)
1 //===- DenseAnalysis.h - 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 //
9 // This file implements dense data-flow analysis using the data-flow analysis
10 // framework. The analysis is forward and conditional and uses the results of
11 // dead code analysis to prune dead code during the analysis.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #ifndef MLIR_ANALYSIS_DENSEDATAFLOWANALYSIS_H
16 #define MLIR_ANALYSIS_DENSEDATAFLOWANALYSIS_H
17 
18 #include "mlir/Analysis/DataFlowFramework.h"
19 #include "mlir/IR/SymbolTable.h"
20 #include "mlir/Interfaces/CallInterfaces.h"
21 #include "mlir/Interfaces/ControlFlowInterfaces.h"
22 
23 namespace mlir {
24 namespace dataflow {
25 
26 //===----------------------------------------------------------------------===//
27 // CallControlFlowAction
28 //===----------------------------------------------------------------------===//
29 
30 /// Indicates whether the control enters, exits, or skips over the callee (in
31 /// the case of external functions).
32 enum class CallControlFlowAction { EnterCallee, ExitCallee, ExternalCallee };
33 
34 //===----------------------------------------------------------------------===//
35 // AbstractDenseLattice
36 //===----------------------------------------------------------------------===//
37 
38 /// This class represents a dense lattice. A dense lattice is attached to
39 /// program point to represent the program state at the program point.
40 /// lattice is propagated through the IR by dense data-flow analysis.
41 class AbstractDenseLattice : public AnalysisState {
42 public:
43   /// A dense lattice can only be created for operations and blocks.
44   using AnalysisState::AnalysisState;
45 
46   /// Join the lattice across control-flow or callgraph edges.
47   virtual ChangeResult join(const AbstractDenseLattice &rhs) {
48     return ChangeResult::NoChange;
49   }
50 
51   virtual ChangeResult meet(const AbstractDenseLattice &rhs) {
52     return ChangeResult::NoChange;
53   }
54 };
55 
56 //===----------------------------------------------------------------------===//
57 // AbstractDenseForwardDataFlowAnalysis
58 //===----------------------------------------------------------------------===//
59 
60 /// Base class for dense forward data-flow analyses. Dense data-flow analysis
61 /// attaches a lattice to program points and implements a transfer function from
62 /// the lattice before each operation to the lattice after. The lattice contains
63 /// information about the state of the program at that program point.
64 ///
65 /// Visit a program point in forward dense data-flow analysis will invoke the
66 /// transfer function of the operation preceding the program point iterator.
67 /// Visit a program point at the begining of block will visit the block itself.
68 class AbstractDenseForwardDataFlowAnalysis : public DataFlowAnalysis {
69 public:
70   using DataFlowAnalysis::DataFlowAnalysis;
71 
72   /// Initialize the analysis by visiting every program point whose execution
73   /// may modify the program state; that is, every operation and block.
74   LogicalResult initialize(Operation *top) override;
75 
76   /// Visit a program point that modifies the state of the program. If the
77   /// program point is at the beginning of a block, then the state is propagated
78   /// from control-flow predecessors or callsites.  If the operation before
79   /// program point iterator is a call operation or region control-flow
80   /// operation, then the state after the execution of the operation is set by
81   /// control-flow or the callgraph. Otherwise, this function invokes the
82   /// operation transfer function before the program point iterator.
83   LogicalResult visit(ProgramPoint *point) override;
84 
85 protected:
86   /// Propagate the dense lattice before the execution of an operation to the
87   /// lattice after its execution.
88   virtual LogicalResult visitOperationImpl(Operation *op,
89                                            const AbstractDenseLattice &before,
90                                            AbstractDenseLattice *after) = 0;
91 
92   /// Get the dense lattice on the given lattice anchor.
93   virtual AbstractDenseLattice *getLattice(LatticeAnchor anchor) = 0;
94 
95   /// Get the dense lattice on the given lattice anchor and add dependent as its
96   /// dependency. That is, every time the lattice after anchor is updated, the
97   /// dependent program point must be visited, and the newly triggered visit
98   /// might update the lattice on dependent.
99   const AbstractDenseLattice *getLatticeFor(ProgramPoint *dependent,
100                                             LatticeAnchor anchor);
101 
102   /// Set the dense lattice at control flow entry point and propagate an update
103   /// if it changed.
104   virtual void setToEntryState(AbstractDenseLattice *lattice) = 0;
105 
106   /// Join a lattice with another and propagate an update if it changed.
107   void join(AbstractDenseLattice *lhs, const AbstractDenseLattice &rhs) {
108     propagateIfChanged(lhs, lhs->join(rhs));
109   }
110 
111   /// Visit an operation. If this is a call operation or region control-flow
112   /// operation, then the state after the execution of the operation is set by
113   /// control-flow or the callgraph. Otherwise, this function invokes the
114   /// operation transfer function.
115   virtual LogicalResult processOperation(Operation *op);
116 
117   /// Propagate the dense lattice forward along the control flow edge from
118   /// `regionFrom` to `regionTo` regions of the `branch` operation. `nullopt`
119   /// values correspond to control flow branches originating at or targeting the
120   /// `branch` operation itself. Default implementation just joins the states,
121   /// meaning that operations implementing `RegionBranchOpInterface` don't have
122   /// any effect on the lattice that isn't already expressed by the interface
123   /// itself.
124   virtual void visitRegionBranchControlFlowTransfer(
125       RegionBranchOpInterface branch, std::optional<unsigned> regionFrom,
126       std::optional<unsigned> regionTo, const AbstractDenseLattice &before,
127       AbstractDenseLattice *after) {
128     join(after, before);
129   }
130 
131   /// Propagate the dense lattice forward along the call control flow edge,
132   /// which can be either entering or exiting the callee. Default implementation
133   /// for enter and exit callee actions just meets the states, meaning that
134   /// operations implementing `CallOpInterface` don't have any effect on the
135   /// lattice that isn't already expressed by the interface itself. Default
136   /// implementation for the external callee action additionally sets the
137   /// "after" lattice to the entry state.
138   virtual void visitCallControlFlowTransfer(CallOpInterface call,
139                                             CallControlFlowAction action,
140                                             const AbstractDenseLattice &before,
141                                             AbstractDenseLattice *after) {
142     join(after, before);
143     // Note that `setToEntryState` may be a "partial fixpoint" for some
144     // lattices, e.g., lattices that are lists of maps of other lattices will
145     // only set fixpoint for "known" lattices.
146     if (action == CallControlFlowAction::ExternalCallee)
147       setToEntryState(after);
148   }
149 
150   /// Visit a program point within a region branch operation with predecessors
151   /// in it. This can either be an entry block of one of the regions of the
152   /// parent operation itself.
153   void visitRegionBranchOperation(ProgramPoint *point,
154                                   RegionBranchOpInterface branch,
155                                   AbstractDenseLattice *after);
156 
157 private:
158   /// Visit a block. The state at the start of the block is propagated from
159   /// control-flow predecessors or callsites.
160   void visitBlock(Block *block);
161 
162   /// Visit an operation for which the data flow is described by the
163   /// `CallOpInterface`.
164   void visitCallOperation(CallOpInterface call,
165                           const AbstractDenseLattice &before,
166                           AbstractDenseLattice *after);
167 };
168 
169 //===----------------------------------------------------------------------===//
170 // DenseForwardDataFlowAnalysis
171 //===----------------------------------------------------------------------===//
172 
173 /// A dense forward data-flow analysis for propagating lattices before and
174 /// after the execution of every operation across the IR by implementing
175 /// transfer functions for operations.
176 ///
177 /// `LatticeT` is expected to be a subclass of `AbstractDenseLattice`.
178 template <typename LatticeT>
179 class DenseForwardDataFlowAnalysis
180     : public AbstractDenseForwardDataFlowAnalysis {
181   static_assert(
182       std::is_base_of<AbstractDenseLattice, LatticeT>::value,
183       "analysis state class expected to subclass AbstractDenseLattice");
184 
185 public:
186   using AbstractDenseForwardDataFlowAnalysis::
187       AbstractDenseForwardDataFlowAnalysis;
188 
189   /// Visit an operation with the dense lattice before its execution. This
190   /// function is expected to set the dense lattice after its execution and
191   /// trigger change propagation in case of change.
192   virtual LogicalResult visitOperation(Operation *op, const LatticeT &before,
193                                        LatticeT *after) = 0;
194 
195   /// Hook for customizing the behavior of lattice propagation along the call
196   /// control flow edges. Two types of (forward) propagation are possible here:
197   ///   - `action == CallControlFlowAction::Enter` indicates that:
198   ///     - `before` is the state before the call operation;
199   ///     - `after` is the state at the beginning of the callee entry block;
200   ///   - `action == CallControlFlowAction::Exit` indicates that:
201   ///     - `before` is the state at the end of a callee exit block;
202   ///     - `after` is the state after the call operation.
203   /// By default, the `after` state is simply joined with the `before` state.
204   /// Concrete analyses can override this behavior or delegate to the parent
205   /// call for the default behavior. Specifically, if the `call` op may affect
206   /// the lattice prior to entering the callee, the custom behavior can be added
207   /// for `action == CallControlFlowAction::Enter`. If the `call` op may affect
208   /// the lattice post exiting the callee, the custom behavior can be added for
209   /// `action == CallControlFlowAction::Exit`.
210   virtual void visitCallControlFlowTransfer(CallOpInterface call,
211                                             CallControlFlowAction action,
212                                             const LatticeT &before,
213                                             LatticeT *after) {
214     AbstractDenseForwardDataFlowAnalysis::visitCallControlFlowTransfer(
215         call, action, before, after);
216   }
217 
218   /// Hook for customizing the behavior of lattice propagation along the control
219   /// flow edges between regions and their parent op. The control flows from
220   /// `regionFrom` to `regionTo`, both of which may be `nullopt` to indicate the
221   /// parent op. The lattice is propagated forward along this edge. The lattices
222   /// are as follows:
223   ///   - `before:`
224   ///     - if `regionFrom` is a region, this is the lattice at the end of the
225   ///       block that exits the region; note that for multi-exit regions, the
226   ///       lattices are equal at the end of all exiting blocks, but they are
227   ///       associated with different program points.
228   ///     - otherwise, this is the lattice before the parent op.
229   ///   - `after`:
230   ///     - if `regionTo` is a region, this is the lattice at the beginning of
231   ///       the entry block of that region;
232   ///     - otherwise, this is the lattice after the parent op.
233   /// By default, the `after` state is simply joined with the `before` state.
234   /// Concrete analyses can override this behavior or delegate to the parent
235   /// call for the default behavior. Specifically, if the `branch` op may affect
236   /// the lattice before entering any region, the custom behavior can be added
237   /// for `regionFrom == nullopt`. If the `branch` op may affect the lattice
238   /// after all terminated, the custom behavior can be added for `regionTo ==
239   /// nullptr`. The behavior can be further refined for specific pairs of "from"
240   /// and "to" regions.
241   virtual void visitRegionBranchControlFlowTransfer(
242       RegionBranchOpInterface branch, std::optional<unsigned> regionFrom,
243       std::optional<unsigned> regionTo, const LatticeT &before,
244       LatticeT *after) {
245     AbstractDenseForwardDataFlowAnalysis::visitRegionBranchControlFlowTransfer(
246         branch, regionFrom, regionTo, before, after);
247   }
248 
249 protected:
250   /// Get the dense lattice on this lattice anchor.
251   LatticeT *getLattice(LatticeAnchor anchor) override {
252     return getOrCreate<LatticeT>(anchor);
253   }
254 
255   /// Set the dense lattice at control flow entry point and propagate an update
256   /// if it changed.
257   virtual void setToEntryState(LatticeT *lattice) = 0;
258   void setToEntryState(AbstractDenseLattice *lattice) override {
259     setToEntryState(static_cast<LatticeT *>(lattice));
260   }
261 
262   /// Type-erased wrappers that convert the abstract dense lattice to a derived
263   /// lattice and invoke the virtual hooks operating on the derived lattice.
264   LogicalResult visitOperationImpl(Operation *op,
265                                    const AbstractDenseLattice &before,
266                                    AbstractDenseLattice *after) final {
267     return visitOperation(op, static_cast<const LatticeT &>(before),
268                           static_cast<LatticeT *>(after));
269   }
270   void visitCallControlFlowTransfer(CallOpInterface call,
271                                     CallControlFlowAction action,
272                                     const AbstractDenseLattice &before,
273                                     AbstractDenseLattice *after) final {
274     visitCallControlFlowTransfer(call, action,
275                                  static_cast<const LatticeT &>(before),
276                                  static_cast<LatticeT *>(after));
277   }
278   void visitRegionBranchControlFlowTransfer(RegionBranchOpInterface branch,
279                                             std::optional<unsigned> regionFrom,
280                                             std::optional<unsigned> regionTo,
281                                             const AbstractDenseLattice &before,
282                                             AbstractDenseLattice *after) final {
283     visitRegionBranchControlFlowTransfer(branch, regionFrom, regionTo,
284                                          static_cast<const LatticeT &>(before),
285                                          static_cast<LatticeT *>(after));
286   }
287 };
288 
289 //===----------------------------------------------------------------------===//
290 // AbstractDenseBackwardDataFlowAnalysis
291 //===----------------------------------------------------------------------===//
292 
293 /// Base class for dense backward dataflow analyses. Such analyses attach a
294 /// lattice to program point and implement a transfer function from the lattice
295 /// after the operation to the lattice before it, thus propagating backward.
296 ///
297 /// Visit a program point in dense backward data-flow analysis will invoke the
298 /// transfer function of the operation following the program point iterator.
299 /// Visit a program point at the end of block will visit the block itself.
300 class AbstractDenseBackwardDataFlowAnalysis : public DataFlowAnalysis {
301 public:
302   /// Construct the analysis in the given solver. Takes a symbol table
303   /// collection that is used to cache symbol resolution in interprocedural part
304   /// of the analysis. The symbol table need not be prefilled.
305   AbstractDenseBackwardDataFlowAnalysis(DataFlowSolver &solver,
306                                         SymbolTableCollection &symbolTable)
307       : DataFlowAnalysis(solver), symbolTable(symbolTable) {}
308 
309   /// Initialize the analysis by visiting every program point whose execution
310   /// may modify the program state; that is, every operation and block.
311   LogicalResult initialize(Operation *top) override;
312 
313   /// Visit a program point that modifies the state of the program. The state is
314   /// propagated along control flow directions for branch-, region- and
315   /// call-based control flow using the respective interfaces. For other
316   /// operations, the state is propagated using the transfer function
317   /// (visitOperation).
318   ///
319   /// Note: the transfer function is currently *not* invoked before operations
320   /// with region or call interface, but *is* invoked before block terminators.
321   LogicalResult visit(ProgramPoint *point) override;
322 
323 protected:
324   /// Propagate the dense lattice after the execution of an operation to the
325   /// lattice before its execution.
326   virtual LogicalResult visitOperationImpl(Operation *op,
327                                            const AbstractDenseLattice &after,
328                                            AbstractDenseLattice *before) = 0;
329 
330   /// Get the dense lattice before the execution of the lattice anchor. That is,
331   /// before the execution of the given operation or after the execution of the
332   /// block.
333   virtual AbstractDenseLattice *getLattice(LatticeAnchor anchor) = 0;
334 
335   /// Get the dense lattice on the given lattice anchor and add dependent as its
336   /// dependency. That is, every time the lattice after anchor is updated, the
337   /// dependent program point must be visited, and the newly triggered visit
338   /// might update the lattice before dependent.
339   const AbstractDenseLattice *getLatticeFor(ProgramPoint *dependent,
340                                             LatticeAnchor anchor);
341 
342   /// Set the dense lattice before at the control flow exit point and propagate
343   /// the update if it changed.
344   virtual void setToExitState(AbstractDenseLattice *lattice) = 0;
345 
346   /// Meet a lattice with another lattice and propagate an update if it changed.
347   void meet(AbstractDenseLattice *lhs, const AbstractDenseLattice &rhs) {
348     propagateIfChanged(lhs, lhs->meet(rhs));
349   }
350 
351   /// Visit an operation. Dispatches to specialized methods for call or region
352   /// control-flow operations. Otherwise, this function invokes the operation
353   /// transfer function.
354   virtual LogicalResult processOperation(Operation *op);
355 
356   /// Propagate the dense lattice backwards along the control flow edge from
357   /// `regionFrom` to `regionTo` regions of the `branch` operation. `nullopt`
358   /// values correspond to control flow branches originating at or targeting the
359   /// `branch` operation itself. Default implementation just meets the states,
360   /// meaning that operations implementing `RegionBranchOpInterface` don't have
361   /// any effect on the lattice that isn't already expressed by the interface
362   /// itself.
363   virtual void visitRegionBranchControlFlowTransfer(
364       RegionBranchOpInterface branch, RegionBranchPoint regionFrom,
365       RegionBranchPoint regionTo, const AbstractDenseLattice &after,
366       AbstractDenseLattice *before) {
367     meet(before, after);
368   }
369 
370   /// Propagate the dense lattice backwards along the call control flow edge,
371   /// which can be either entering or exiting the callee. Default implementation
372   /// for enter and exit callee action just meets the states, meaning that
373   /// operations implementing `CallOpInterface` don't have any effect on the
374   /// lattice that isn't already expressed by the interface itself. Default
375   /// implementation for external callee action additional sets the result to
376   /// the exit (fixpoint) state.
377   virtual void visitCallControlFlowTransfer(CallOpInterface call,
378                                             CallControlFlowAction action,
379                                             const AbstractDenseLattice &after,
380                                             AbstractDenseLattice *before) {
381     meet(before, after);
382 
383     // Note that `setToExitState` may be a "partial fixpoint" for some lattices,
384     // e.g., lattices that are lists of maps of other lattices will only
385     // set fixpoint for "known" lattices.
386     if (action == CallControlFlowAction::ExternalCallee)
387       setToExitState(before);
388   }
389 
390 private:
391   /// Visit a block. The state and the end of the block is propagated from
392   /// control-flow successors of the block or callsites.
393   void visitBlock(Block *block);
394 
395   /// Visit a program point within a region branch operation with successors
396   /// (from which the state is propagated) in or after it. `regionNo` indicates
397   /// the region that contains the successor, `nullopt` indicating the successor
398   /// of the branch operation itself.
399   void visitRegionBranchOperation(ProgramPoint *point,
400                                   RegionBranchOpInterface branch,
401                                   RegionBranchPoint branchPoint,
402                                   AbstractDenseLattice *before);
403 
404   /// Visit an operation for which the data flow is described by the
405   /// `CallOpInterface`. Performs inter-procedural data flow as follows:
406   ///
407   ///   - find the callable (resolve via the symbol table),
408   ///   - get the entry block of the callable region,
409   ///   - take the state before the first operation if present or at block end
410   ///     otherwise,
411   ///   - meet that state with the state before the call-like op, or use the
412   ///     custom logic if overridden by concrete analyses.
413   void visitCallOperation(CallOpInterface call,
414                           const AbstractDenseLattice &after,
415                           AbstractDenseLattice *before);
416 
417   /// Symbol table for call-level control flow.
418   SymbolTableCollection &symbolTable;
419 };
420 
421 //===----------------------------------------------------------------------===//
422 // DenseBackwardDataFlowAnalysis
423 //===----------------------------------------------------------------------===//
424 
425 /// A dense backward dataflow analysis propagating lattices after and before the
426 /// execution of every operation across the IR by implementing transfer
427 /// functions for opreations.
428 ///
429 /// `LatticeT` is expected to be a subclass of `AbstractDenseLattice`.
430 template <typename LatticeT>
431 class DenseBackwardDataFlowAnalysis
432     : public AbstractDenseBackwardDataFlowAnalysis {
433   static_assert(std::is_base_of_v<AbstractDenseLattice, LatticeT>,
434                 "analysis state expected to subclass AbstractDenseLattice");
435 
436 public:
437   using AbstractDenseBackwardDataFlowAnalysis::
438       AbstractDenseBackwardDataFlowAnalysis;
439 
440   /// Transfer function. Visits an operation with the dense lattice after its
441   /// execution. This function is expected to set the dense lattice before its
442   /// execution and trigger propagation in case of change.
443   virtual LogicalResult visitOperation(Operation *op, const LatticeT &after,
444                                        LatticeT *before) = 0;
445 
446   /// Hook for customizing the behavior of lattice propagation along the call
447   /// control flow edges. Two types of (back) propagation are possible here:
448   ///   - `action == CallControlFlowAction::Enter` indicates that:
449   ///     - `after` is the state at the top of the callee entry block;
450   ///     - `before` is the state before the call operation;
451   ///   - `action == CallControlFlowAction::Exit` indicates that:
452   ///     - `after` is the state after the call operation;
453   ///     - `before` is the state of exit blocks of the callee.
454   /// By default, the `before` state is simply met with the `after` state.
455   /// Concrete analyses can override this behavior or delegate to the parent
456   /// call for the default behavior. Specifically, if the `call` op may affect
457   /// the lattice prior to entering the callee, the custom behavior can be added
458   /// for `action == CallControlFlowAction::Enter`. If the `call` op may affect
459   /// the lattice post exiting the callee, the custom behavior can be added for
460   /// `action == CallControlFlowAction::Exit`.
461   virtual void visitCallControlFlowTransfer(CallOpInterface call,
462                                             CallControlFlowAction action,
463                                             const LatticeT &after,
464                                             LatticeT *before) {
465     AbstractDenseBackwardDataFlowAnalysis::visitCallControlFlowTransfer(
466         call, action, after, before);
467   }
468 
469   /// Hook for customizing the behavior of lattice propagation along the control
470   /// flow edges between regions and their parent op. The control flows from
471   /// `regionFrom` to `regionTo`, both of which may be `nullopt` to indicate the
472   /// parent op. The lattice is propagated back along this edge. The lattices
473   /// are as follows:
474   ///   - `after`:
475   ///     - if `regionTo` is a region, this is the lattice at the beginning of
476   ///       the entry block of that region;
477   ///     - otherwise, this is the lattice after the parent op.
478   ///   - `before:`
479   ///     - if `regionFrom` is a region, this is the lattice at the end of the
480   ///       block that exits the region; note that for multi-exit regions, the
481   ///       lattices are equal at the end of all exiting blocks, but they are
482   ///       associated with different program points.
483   ///     - otherwise, this is the lattice before the parent op.
484   /// By default, the `before` state is simply met with the `after` state.
485   /// Concrete analyses can override this behavior or delegate to the parent
486   /// call for the default behavior. Specifically, if the `branch` op may affect
487   /// the lattice before entering any region, the custom behavior can be added
488   /// for `regionFrom == nullopt`. If the `branch` op may affect the lattice
489   /// after all terminated, the custom behavior can be added for `regionTo ==
490   /// nullptr`. The behavior can be further refined for specific pairs of "from"
491   /// and "to" regions.
492   virtual void visitRegionBranchControlFlowTransfer(
493       RegionBranchOpInterface branch, RegionBranchPoint regionFrom,
494       RegionBranchPoint regionTo, const LatticeT &after, LatticeT *before) {
495     AbstractDenseBackwardDataFlowAnalysis::visitRegionBranchControlFlowTransfer(
496         branch, regionFrom, regionTo, after, before);
497   }
498 
499 protected:
500   /// Get the dense lattice at the given lattice anchor.
501   LatticeT *getLattice(LatticeAnchor anchor) override {
502     return getOrCreate<LatticeT>(anchor);
503   }
504 
505   /// Set the dense lattice at control flow exit point (after the terminator)
506   /// and propagate an update if it changed.
507   virtual void setToExitState(LatticeT *lattice) = 0;
508   void setToExitState(AbstractDenseLattice *lattice) final {
509     setToExitState(static_cast<LatticeT *>(lattice));
510   }
511 
512   /// Type-erased wrappers that convert the abstract dense lattice to a derived
513   /// lattice and invoke the virtual hooks operating on the derived lattice.
514   LogicalResult visitOperationImpl(Operation *op,
515                                    const AbstractDenseLattice &after,
516                                    AbstractDenseLattice *before) final {
517     return visitOperation(op, static_cast<const LatticeT &>(after),
518                           static_cast<LatticeT *>(before));
519   }
520   void visitCallControlFlowTransfer(CallOpInterface call,
521                                     CallControlFlowAction action,
522                                     const AbstractDenseLattice &after,
523                                     AbstractDenseLattice *before) final {
524     visitCallControlFlowTransfer(call, action,
525                                  static_cast<const LatticeT &>(after),
526                                  static_cast<LatticeT *>(before));
527   }
528   void visitRegionBranchControlFlowTransfer(
529       RegionBranchOpInterface branch, RegionBranchPoint regionForm,
530       RegionBranchPoint regionTo, const AbstractDenseLattice &after,
531       AbstractDenseLattice *before) final {
532     visitRegionBranchControlFlowTransfer(branch, regionForm, regionTo,
533                                          static_cast<const LatticeT &>(after),
534                                          static_cast<LatticeT *>(before));
535   }
536 };
537 
538 } // end namespace dataflow
539 } // end namespace mlir
540 
541 #endif // MLIR_ANALYSIS_DENSEDATAFLOWANALYSIS_H
542