Files
gps/tools/local/circt/include/mlir/Analysis/DataFlow/DenseAnalysis.h
T
2026-07-12 11:01:51 +03:00

655 lines
33 KiB
C++

//===- DenseAnalysis.h - Dense data-flow analysis -------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements dense data-flow analysis using the data-flow analysis
// framework. The analysis is forward and conditional and uses the results of
// dead code analysis to prune dead code during the analysis.
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_ANALYSIS_DENSEDATAFLOWANALYSIS_H
#define MLIR_ANALYSIS_DENSEDATAFLOWANALYSIS_H
#include "mlir/Analysis/DataFlowFramework.h"
#include "mlir/IR/SymbolTable.h"
#include "mlir/Interfaces/CallInterfaces.h"
#include "mlir/Interfaces/ControlFlowInterfaces.h"
namespace mlir {
namespace dataflow {
//===----------------------------------------------------------------------===//
// CallControlFlowAction
//===----------------------------------------------------------------------===//
/// Indicates whether the control enters, exits, or skips over the callee (in
/// the case of external functions).
enum class CallControlFlowAction { EnterCallee, ExitCallee, ExternalCallee };
//===----------------------------------------------------------------------===//
// AbstractDenseLattice
//===----------------------------------------------------------------------===//
/// This class represents a dense lattice. A dense lattice is attached to
/// program point to represent the program state at the program point.
/// lattice is propagated through the IR by dense data-flow analysis.
class AbstractDenseLattice : public AnalysisState {
public:
/// A dense lattice can only be created for operations and blocks.
using AnalysisState::AnalysisState;
/// Join the lattice across control-flow or callgraph edges.
virtual ChangeResult join(const AbstractDenseLattice &rhs) {
return ChangeResult::NoChange;
}
virtual ChangeResult meet(const AbstractDenseLattice &rhs) {
return ChangeResult::NoChange;
}
};
//===----------------------------------------------------------------------===//
// AbstractDenseForwardDataFlowAnalysis
//===----------------------------------------------------------------------===//
/// Base class for dense forward data-flow analyses. Dense data-flow analysis
/// attaches a lattice to program points and implements a transfer function from
/// the lattice before each operation to the lattice after. The lattice contains
/// information about the state of the program at that program point.
///
/// Visit a program point in forward dense data-flow analysis will invoke the
/// transfer function of the operation preceding the program point iterator.
/// Visit a program point at the begining of block will visit the block itself.
class AbstractDenseForwardDataFlowAnalysis : public DataFlowAnalysis {
public:
using DataFlowAnalysis::DataFlowAnalysis;
/// Initialize the analysis by visiting every program point whose execution
/// may modify the program state; that is, every operation and block.
LogicalResult initialize(Operation *top) override;
/// Initialize lattice anchor equivalence class from the provided top-level
/// operation.
///
/// This function will union lattice anchor to same equivalent class if the
/// analysis can determine the lattice content of lattice anchor is
/// necessarily identical under the corrensponding lattice type.
virtual void initializeEquivalentLatticeAnchor(Operation *top) override;
/// Visit a program point that modifies the state of the program. If the
/// program point is at the beginning of a block, then the state is propagated
/// from control-flow predecessors or callsites. If the operation before
/// program point iterator is a call operation or region control-flow
/// operation, then the state after the execution of the operation is set by
/// control-flow or the callgraph. Otherwise, this function invokes the
/// operation transfer function before the program point iterator.
LogicalResult visit(ProgramPoint *point) override;
protected:
/// Propagate the dense lattice before the execution of an operation to the
/// lattice after its execution.
virtual LogicalResult visitOperationImpl(Operation *op,
const AbstractDenseLattice &before,
AbstractDenseLattice *after) = 0;
/// Get the dense lattice on the given lattice anchor.
virtual AbstractDenseLattice *getLattice(LatticeAnchor anchor) = 0;
/// Get the dense lattice on the given lattice anchor and add dependent as its
/// dependency. That is, every time the lattice after anchor is updated, the
/// dependent program point must be visited, and the newly triggered visit
/// might update the lattice on dependent.
virtual const AbstractDenseLattice *getLatticeFor(ProgramPoint *dependent,
LatticeAnchor anchor) = 0;
/// Set the dense lattice at control flow entry point and propagate an update
/// if it changed.
virtual void setToEntryState(AbstractDenseLattice *lattice) = 0;
/// Join a lattice with another and propagate an update if it changed.
void join(AbstractDenseLattice *lhs, const AbstractDenseLattice &rhs) {
propagateIfChanged(lhs, lhs->join(rhs));
}
/// Visit an operation. If this is a call operation or region control-flow
/// operation, then the state after the execution of the operation is set by
/// control-flow or the callgraph. Otherwise, this function invokes the
/// operation transfer function.
virtual LogicalResult processOperation(Operation *op);
/// Visit an operation. If this analysis can confirm that lattice content
/// of lattice anchors around operation are necessarily identical, join
/// them into the same equivalent class.
virtual void buildOperationEquivalentLatticeAnchor(Operation *op) {}
/// Visit a block and propagate the dense lattice forward along the control
/// flow edge from predecessor to block. `point` corresponds to the program
/// point before `block`. The default implementation merges in the state from
/// the predecessor's terminator.
virtual void visitBlockTransfer(Block *block, ProgramPoint *point,
Block *predecessor,
const AbstractDenseLattice &before,
AbstractDenseLattice *after) {
// Merge in the state from the predecessor's terminator.
join(after, before);
}
/// Propagate the dense lattice forward along the control flow edge from
/// `regionFrom` to `regionTo` regions of the `branch` operation. `nullopt`
/// values correspond to control flow branches originating at or targeting the
/// `branch` operation itself. Default implementation just joins the states,
/// meaning that operations implementing `RegionBranchOpInterface` don't have
/// any effect on the lattice that isn't already expressed by the interface
/// itself.
virtual void visitRegionBranchControlFlowTransfer(
RegionBranchOpInterface branch, std::optional<unsigned> regionFrom,
std::optional<unsigned> regionTo, const AbstractDenseLattice &before,
AbstractDenseLattice *after) {
join(after, before);
}
/// Propagate the dense lattice forward along the call control flow edge,
/// which can be either entering or exiting the callee. Default implementation
/// for enter and exit callee actions just meets the states, meaning that
/// operations implementing `CallOpInterface` don't have any effect on the
/// lattice that isn't already expressed by the interface itself. Default
/// implementation for the external callee action additionally sets the
/// "after" lattice to the entry state.
virtual void visitCallControlFlowTransfer(CallOpInterface call,
CallControlFlowAction action,
const AbstractDenseLattice &before,
AbstractDenseLattice *after) {
join(after, before);
// Note that `setToEntryState` may be a "partial fixpoint" for some
// lattices, e.g., lattices that are lists of maps of other lattices will
// only set fixpoint for "known" lattices.
if (action == CallControlFlowAction::ExternalCallee)
setToEntryState(after);
}
/// Visit a program point within a region branch operation with predecessors
/// in it. This can either be an entry block of one of the regions of the
/// parent operation itself.
void visitRegionBranchOperation(ProgramPoint *point,
RegionBranchOpInterface branch,
AbstractDenseLattice *after);
private:
/// Visit a block. The state at the start of the block is propagated from
/// control-flow predecessors or callsites.
void visitBlock(Block *block);
/// Visit an operation for which the data flow is described by the
/// `CallOpInterface`.
void visitCallOperation(CallOpInterface call,
const AbstractDenseLattice &before,
AbstractDenseLattice *after);
};
//===----------------------------------------------------------------------===//
// DenseForwardDataFlowAnalysis
//===----------------------------------------------------------------------===//
/// A dense forward data-flow analysis for propagating lattices before and
/// after the execution of every operation across the IR by implementing
/// transfer functions for operations.
///
/// `LatticeT` is expected to be a subclass of `AbstractDenseLattice`.
template <typename LatticeT>
class DenseForwardDataFlowAnalysis
: public AbstractDenseForwardDataFlowAnalysis {
static_assert(
std::is_base_of<AbstractDenseLattice, LatticeT>::value,
"analysis state class expected to subclass AbstractDenseLattice");
public:
using AbstractDenseForwardDataFlowAnalysis::
AbstractDenseForwardDataFlowAnalysis;
/// Visit an operation with the dense lattice before its execution. This
/// function is expected to set the dense lattice after its execution and
/// trigger change propagation in case of change.
virtual LogicalResult visitOperation(Operation *op, const LatticeT &before,
LatticeT *after) = 0;
/// Hook for customizing the behavior of lattice propagation along the call
/// control flow edges. Two types of (forward) propagation are possible here:
/// - `action == CallControlFlowAction::Enter` indicates that:
/// - `before` is the state before the call operation;
/// - `after` is the state at the beginning of the callee entry block;
/// - `action == CallControlFlowAction::Exit` indicates that:
/// - `before` is the state at the end of a callee exit block;
/// - `after` is the state after the call operation.
/// By default, the `after` state is simply joined with the `before` state.
/// Concrete analyses can override this behavior or delegate to the parent
/// call for the default behavior. Specifically, if the `call` op may affect
/// the lattice prior to entering the callee, the custom behavior can be added
/// for `action == CallControlFlowAction::Enter`. If the `call` op may affect
/// the lattice post exiting the callee, the custom behavior can be added for
/// `action == CallControlFlowAction::Exit`.
virtual void visitCallControlFlowTransfer(CallOpInterface call,
CallControlFlowAction action,
const LatticeT &before,
LatticeT *after) {
AbstractDenseForwardDataFlowAnalysis::visitCallControlFlowTransfer(
call, action, before, after);
}
/// Hook for customizing the behavior of lattice propagation along the control
/// flow edges between regions and their parent op. The control flows from
/// `regionFrom` to `regionTo`, both of which may be `nullopt` to indicate the
/// parent op. The lattice is propagated forward along this edge. The lattices
/// are as follows:
/// - `before:`
/// - if `regionFrom` is a region, this is the lattice at the end of the
/// block that exits the region; note that for multi-exit regions, the
/// lattices are equal at the end of all exiting blocks, but they are
/// associated with different program points.
/// - otherwise, this is the lattice before the parent op.
/// - `after`:
/// - if `regionTo` is a region, this is the lattice at the beginning of
/// the entry block of that region;
/// - otherwise, this is the lattice after the parent op.
/// By default, the `after` state is simply joined with the `before` state.
/// Concrete analyses can override this behavior or delegate to the parent
/// call for the default behavior. Specifically, if the `branch` op may affect
/// the lattice before entering any region, the custom behavior can be added
/// for `regionFrom == nullopt`. If the `branch` op may affect the lattice
/// after all terminated, the custom behavior can be added for `regionTo ==
/// nullptr`. The behavior can be further refined for specific pairs of "from"
/// and "to" regions.
virtual void visitRegionBranchControlFlowTransfer(
RegionBranchOpInterface branch, std::optional<unsigned> regionFrom,
std::optional<unsigned> regionTo, const LatticeT &before,
LatticeT *after) {
AbstractDenseForwardDataFlowAnalysis::visitRegionBranchControlFlowTransfer(
branch, regionFrom, regionTo, before, after);
}
/// Hook for customizing the behavior of lattice propagation along the control
/// flow edges between blocks. The control flows from `predecessor` to
/// `block`. The lattice is propagated forward along this edge. The lattices
/// are as follows:
/// - `before` is the lattice at the end of the predecessor block;
/// - `after` is the lattice at the beginning of the block.
/// By default, the `after` state is simply joined with the `before` state.
/// Concrete analyses can override this behavior or delegate to the parent
/// call for the default behavior.
virtual void visitBlockTransfer(Block *block, ProgramPoint *point,
Block *predecessor, const LatticeT &before,
LatticeT *after) {
AbstractDenseForwardDataFlowAnalysis::visitBlockTransfer(
block, point, predecessor, before, after);
}
protected:
/// Get the dense lattice on this lattice anchor.
LatticeT *getLattice(LatticeAnchor anchor) override {
return getOrCreate<LatticeT>(anchor);
}
/// Get the dense lattice on the given lattice anchor and add dependent as its
/// dependency. That is, every time the lattice after anchor is updated, the
/// dependent program point must be visited, and the newly triggered visit
/// might update the lattice on dependent.
const AbstractDenseLattice *getLatticeFor(ProgramPoint *dependent,
LatticeAnchor anchor) override {
return getOrCreateFor<LatticeT>(dependent, anchor);
}
/// Set the dense lattice at control flow entry point and propagate an update
/// if it changed.
virtual void setToEntryState(LatticeT *lattice) = 0;
void setToEntryState(AbstractDenseLattice *lattice) override {
setToEntryState(static_cast<LatticeT *>(lattice));
}
/// Type-erased wrappers that convert the abstract dense lattice to a derived
/// lattice and invoke the virtual hooks operating on the derived lattice.
LogicalResult visitOperationImpl(Operation *op,
const AbstractDenseLattice &before,
AbstractDenseLattice *after) final {
return visitOperation(op, static_cast<const LatticeT &>(before),
static_cast<LatticeT *>(after));
}
void visitCallControlFlowTransfer(CallOpInterface call,
CallControlFlowAction action,
const AbstractDenseLattice &before,
AbstractDenseLattice *after) final {
visitCallControlFlowTransfer(call, action,
static_cast<const LatticeT &>(before),
static_cast<LatticeT *>(after));
}
void visitRegionBranchControlFlowTransfer(RegionBranchOpInterface branch,
std::optional<unsigned> regionFrom,
std::optional<unsigned> regionTo,
const AbstractDenseLattice &before,
AbstractDenseLattice *after) final {
visitRegionBranchControlFlowTransfer(branch, regionFrom, regionTo,
static_cast<const LatticeT &>(before),
static_cast<LatticeT *>(after));
}
void visitBlockTransfer(Block *block, ProgramPoint *point, Block *predecessor,
const AbstractDenseLattice &before,
AbstractDenseLattice *after) final {
visitBlockTransfer(block, point, predecessor,
static_cast<const LatticeT &>(before),
static_cast<LatticeT *>(after));
}
};
//===----------------------------------------------------------------------===//
// AbstractDenseBackwardDataFlowAnalysis
//===----------------------------------------------------------------------===//
/// Base class for dense backward dataflow analyses. Such analyses attach a
/// lattice to program point and implement a transfer function from the lattice
/// after the operation to the lattice before it, thus propagating backward.
///
/// Visit a program point in dense backward data-flow analysis will invoke the
/// transfer function of the operation following the program point iterator.
/// Visit a program point at the end of block will visit the block itself.
class AbstractDenseBackwardDataFlowAnalysis : public DataFlowAnalysis {
public:
/// Construct the analysis in the given solver. Takes a symbol table
/// collection that is used to cache symbol resolution in interprocedural part
/// of the analysis. The symbol table need not be prefilled.
AbstractDenseBackwardDataFlowAnalysis(DataFlowSolver &solver,
SymbolTableCollection &symbolTable)
: DataFlowAnalysis(solver), symbolTable(symbolTable) {}
/// Initialize the analysis by visiting every program point whose execution
/// may modify the program state; that is, every operation and block.
LogicalResult initialize(Operation *top) override;
/// Initialize lattice anchor equivalence class from the provided top-level
/// operation.
///
/// This function will union lattice anchor to same equivalent class if the
/// analysis can determine the lattice content of lattice anchor is
/// necessarily identical under the corrensponding lattice type.
virtual void initializeEquivalentLatticeAnchor(Operation *top) override;
/// Visit a program point that modifies the state of the program. The state is
/// propagated along control flow directions for branch-, region- and
/// call-based control flow using the respective interfaces. For other
/// operations, the state is propagated using the transfer function
/// (visitOperation).
///
/// Note: the transfer function is currently *not* invoked before operations
/// with region or call interface, but *is* invoked before block terminators.
LogicalResult visit(ProgramPoint *point) override;
protected:
/// Propagate the dense lattice after the execution of an operation to the
/// lattice before its execution.
virtual LogicalResult visitOperationImpl(Operation *op,
const AbstractDenseLattice &after,
AbstractDenseLattice *before) = 0;
/// Get the dense lattice before the execution of the lattice anchor. That is,
/// before the execution of the given operation or after the execution of the
/// block.
virtual AbstractDenseLattice *getLattice(LatticeAnchor anchor) = 0;
/// Get the dense lattice on the given lattice anchor and add dependent as its
/// dependency. That is, every time the lattice after anchor is updated, the
/// dependent program point must be visited, and the newly triggered visit
/// might update the lattice before dependent.
virtual const AbstractDenseLattice *getLatticeFor(ProgramPoint *dependent,
LatticeAnchor anchor) = 0;
/// Set the dense lattice before at the control flow exit point and propagate
/// the update if it changed.
virtual void setToExitState(AbstractDenseLattice *lattice) = 0;
/// Meet a lattice with another lattice and propagate an update if it changed.
void meet(AbstractDenseLattice *lhs, const AbstractDenseLattice &rhs) {
propagateIfChanged(lhs, lhs->meet(rhs));
}
/// Visit an operation. Dispatches to specialized methods for call or region
/// control-flow operations. Otherwise, this function invokes the operation
/// transfer function.
virtual LogicalResult processOperation(Operation *op);
/// Visit an operation. If this analysis can confirm that lattice content
/// of lattice anchors around operation are necessarily identical, join
/// them into the same equivalent class.
virtual void buildOperationEquivalentLatticeAnchor(Operation *op) {}
/// Visit a block and propagate the dense lattice backward along the control
/// flow edge from successor to block. `point` corresponds to the program
/// point after `block`. The default implementation merges in the state from
/// the successor's first operation or the block itself when empty.
virtual void visitBlockTransfer(Block *block, ProgramPoint *point,
Block *successor,
const AbstractDenseLattice &after,
AbstractDenseLattice *before) {
meet(before, after);
}
/// Propagate the dense lattice backwards along the control flow edge from
/// `regionFrom` to `regionTo` regions of the `branch` operation. `nullopt`
/// values correspond to control flow branches originating at or targeting the
/// `branch` operation itself. Default implementation just meets the states,
/// meaning that operations implementing `RegionBranchOpInterface` don't have
/// any effect on the lattice that isn't already expressed by the interface
/// itself.
virtual void visitRegionBranchControlFlowTransfer(
RegionBranchOpInterface branch, RegionBranchPoint regionFrom,
RegionSuccessor regionTo, const AbstractDenseLattice &after,
AbstractDenseLattice *before) {
meet(before, after);
}
/// Propagate the dense lattice backwards along the call control flow edge,
/// which can be either entering or exiting the callee. Default implementation
/// for enter and exit callee action just meets the states, meaning that
/// operations implementing `CallOpInterface` don't have any effect on the
/// lattice that isn't already expressed by the interface itself. Default
/// implementation for external callee action additional sets the result to
/// the exit (fixpoint) state.
virtual void visitCallControlFlowTransfer(CallOpInterface call,
CallControlFlowAction action,
const AbstractDenseLattice &after,
AbstractDenseLattice *before) {
meet(before, after);
// Note that `setToExitState` may be a "partial fixpoint" for some lattices,
// e.g., lattices that are lists of maps of other lattices will only
// set fixpoint for "known" lattices.
if (action == CallControlFlowAction::ExternalCallee)
setToExitState(before);
}
private:
/// Visit a block. The state and the end of the block is propagated from
/// control-flow successors of the block or callsites.
void visitBlock(Block *block);
/// Visit a program point within a region branch operation with successors
/// (from which the state is propagated) in or after it. `regionNo` indicates
/// the region that contains the successor, `nullopt` indicating the successor
/// of the branch operation itself.
void visitRegionBranchOperation(ProgramPoint *point,
RegionBranchOpInterface branch,
RegionBranchPoint branchPoint,
AbstractDenseLattice *before);
/// Visit an operation for which the data flow is described by the
/// `CallOpInterface`. Performs inter-procedural data flow as follows:
///
/// - find the callable (resolve via the symbol table),
/// - get the entry block of the callable region,
/// - take the state before the first operation if present or at block end
/// otherwise,
/// - meet that state with the state before the call-like op, or use the
/// custom logic if overridden by concrete analyses.
void visitCallOperation(CallOpInterface call,
const AbstractDenseLattice &after,
AbstractDenseLattice *before);
/// Symbol table for call-level control flow.
SymbolTableCollection &symbolTable;
};
//===----------------------------------------------------------------------===//
// DenseBackwardDataFlowAnalysis
//===----------------------------------------------------------------------===//
/// A dense backward dataflow analysis propagating lattices after and before the
/// execution of every operation across the IR by implementing transfer
/// functions for operations.
///
/// `LatticeT` is expected to be a subclass of `AbstractDenseLattice`.
template <typename LatticeT>
class DenseBackwardDataFlowAnalysis
: public AbstractDenseBackwardDataFlowAnalysis {
static_assert(std::is_base_of_v<AbstractDenseLattice, LatticeT>,
"analysis state expected to subclass AbstractDenseLattice");
public:
using AbstractDenseBackwardDataFlowAnalysis::
AbstractDenseBackwardDataFlowAnalysis;
/// Transfer function. Visits an operation with the dense lattice after its
/// execution. This function is expected to set the dense lattice before its
/// execution and trigger propagation in case of change.
virtual LogicalResult visitOperation(Operation *op, const LatticeT &after,
LatticeT *before) = 0;
/// Hook for customizing the behavior of lattice propagation along the call
/// control flow edges. Two types of (back) propagation are possible here:
/// - `action == CallControlFlowAction::Enter` indicates that:
/// - `after` is the state at the top of the callee entry block;
/// - `before` is the state before the call operation;
/// - `action == CallControlFlowAction::Exit` indicates that:
/// - `after` is the state after the call operation;
/// - `before` is the state of exit blocks of the callee.
/// By default, the `before` state is simply met with the `after` state.
/// Concrete analyses can override this behavior or delegate to the parent
/// call for the default behavior. Specifically, if the `call` op may affect
/// the lattice prior to entering the callee, the custom behavior can be added
/// for `action == CallControlFlowAction::Enter`. If the `call` op may affect
/// the lattice post exiting the callee, the custom behavior can be added for
/// `action == CallControlFlowAction::Exit`.
virtual void visitCallControlFlowTransfer(CallOpInterface call,
CallControlFlowAction action,
const LatticeT &after,
LatticeT *before) {
AbstractDenseBackwardDataFlowAnalysis::visitCallControlFlowTransfer(
call, action, after, before);
}
/// Hook for customizing the behavior of lattice propagation along the control
/// flow edges between regions and their parent op. The control flows from
/// `regionFrom` to `regionTo`, both of which may be `nullopt` to indicate the
/// parent op. The lattice is propagated back along this edge. The lattices
/// are as follows:
/// - `after`:
/// - if `regionTo` is a region, this is the lattice at the beginning of
/// the entry block of that region;
/// - otherwise, this is the lattice after the parent op.
/// - `before:`
/// - if `regionFrom` is a region, this is the lattice at the end of the
/// block that exits the region; note that for multi-exit regions, the
/// lattices are equal at the end of all exiting blocks, but they are
/// associated with different program points.
/// - otherwise, this is the lattice before the parent op.
/// By default, the `before` state is simply met with the `after` state.
/// Concrete analyses can override this behavior or delegate to the parent
/// call for the default behavior. Specifically, if the `branch` op may affect
/// the lattice before entering any region, the custom behavior can be added
/// for `regionFrom == nullopt`. If the `branch` op may affect the lattice
/// after all terminated, the custom behavior can be added for `regionTo ==
/// nullptr`. The behavior can be further refined for specific pairs of "from"
/// and "to" regions.
virtual void visitRegionBranchControlFlowTransfer(
RegionBranchOpInterface branch, RegionBranchPoint regionFrom,
RegionSuccessor regionTo, const LatticeT &after, LatticeT *before) {
AbstractDenseBackwardDataFlowAnalysis::visitRegionBranchControlFlowTransfer(
branch, regionFrom, regionTo, after, before);
}
/// Hook for customizing the behavior of lattice propagation along the control
/// flow edges between blocks. The control flows from `successor` to
/// `block`. The lattice is propagated back along this edge. The lattices
/// are as follows:
/// - `after` is the lattice at the beginning of the successor block;
/// - `before` is the lattice at the end of the block.
/// By default, the `before` state is simply met with the `after` state.
/// Concrete analyses can override this behavior or delegate to the parent
/// call for the default behavior.
virtual void visitBlockTransfer(Block *block, ProgramPoint *point,
Block *successor, const LatticeT &after,
LatticeT *before) {
AbstractDenseBackwardDataFlowAnalysis::visitBlockTransfer(
block, point, successor, after, before);
}
protected:
/// Get the dense lattice at the given lattice anchor.
LatticeT *getLattice(LatticeAnchor anchor) override {
return getOrCreate<LatticeT>(anchor);
}
/// Get the dense lattice on the given lattice anchor and add dependent as its
/// dependency. That is, every time the lattice after anchor is updated, the
/// dependent program point must be visited, and the newly triggered visit
/// might update the lattice before dependent.
virtual const AbstractDenseLattice *
getLatticeFor(ProgramPoint *dependent, LatticeAnchor anchor) override {
return getOrCreateFor<LatticeT>(dependent, anchor);
}
/// Set the dense lattice at control flow exit point (after the terminator)
/// and propagate an update if it changed.
virtual void setToExitState(LatticeT *lattice) = 0;
void setToExitState(AbstractDenseLattice *lattice) final {
setToExitState(static_cast<LatticeT *>(lattice));
}
/// Type-erased wrappers that convert the abstract dense lattice to a derived
/// lattice and invoke the virtual hooks operating on the derived lattice.
LogicalResult visitOperationImpl(Operation *op,
const AbstractDenseLattice &after,
AbstractDenseLattice *before) final {
return visitOperation(op, static_cast<const LatticeT &>(after),
static_cast<LatticeT *>(before));
}
void visitCallControlFlowTransfer(CallOpInterface call,
CallControlFlowAction action,
const AbstractDenseLattice &after,
AbstractDenseLattice *before) final {
visitCallControlFlowTransfer(call, action,
static_cast<const LatticeT &>(after),
static_cast<LatticeT *>(before));
}
void visitRegionBranchControlFlowTransfer(
RegionBranchOpInterface branch, RegionBranchPoint regionForm,
RegionSuccessor regionTo, const AbstractDenseLattice &after,
AbstractDenseLattice *before) final {
visitRegionBranchControlFlowTransfer(branch, regionForm, regionTo,
static_cast<const LatticeT &>(after),
static_cast<LatticeT *>(before));
}
void visitBlockTransfer(Block *block, ProgramPoint *point, Block *successor,
const AbstractDenseLattice &after,
AbstractDenseLattice *before) final {
visitBlockTransfer(block, point, successor,
static_cast<const LatticeT &>(after),
static_cast<LatticeT *>(before));
}
};
} // end namespace dataflow
} // end namespace mlir
#endif // MLIR_ANALYSIS_DENSEDATAFLOWANALYSIS_H