20#include <mlir/Analysis/DataFlow/DeadCodeAnalysis.h>
21#include <mlir/Analysis/DataFlowFramework.h>
22#include <mlir/IR/Block.h>
23#include <mlir/IR/Operation.h>
24#include <mlir/IR/Region.h>
25#include <mlir/Interfaces/CallInterfaces.h>
26#include <mlir/Interfaces/ControlFlowInterfaces.h>
27#include <mlir/Support/LLVM.h>
28#include <mlir/Support/LogicalResult.h>
30#include <llvm/ADT/STLExtras.h>
31#include <llvm/Support/Casting.h>
41using namespace function;
54 for (Region ®ion : top->getRegions()) {
55 for (Block &block : region) {
57 for (Operation &op : block) {
68 if (!point->isBlockStart()) {
71 visitBlock(point->getBlock());
77void AbstractDenseForwardDataFlowAnalysis::visitCallOperation(
83 if (!getSolverConfig().isInterprocedural() ||
84 (succeeded(callable) && !callable->get().getCallableRegion())) {
90 SmallVector<Operation *> predecessors;
91 callable->get().walk([&predecessors](
ReturnOp ret)
mutable { predecessors.push_back(ret); });
95 if (predecessors.empty()) {
99 for (Operation *predecessor : predecessors) {
115 getLatticeFor(getProgramPointAfter(call.getOperation()), getProgramPointAfter(predecessor));
117 call, CallControlFlowAction::ExitCallee, *latticeAtCalleeReturn, latticeAfterCall
123 ProgramPoint *point = getProgramPointAfter(op);
125 if (op->getBlock() !=
nullptr &&
126 !getOrCreateFor<Executable>(point, getProgramPointBefore(op->getBlock()))->isLive()) {
138 if (
auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
145 if (
auto call = dyn_cast<CallOpInterface>(op)) {
146 visitCallOperation(call, *before, after);
156void AbstractDenseForwardDataFlowAnalysis::visitBlock(Block *block) {
158 ProgramPoint *point = getProgramPointBefore(block);
159 if (!getOrCreateFor<Executable>(point, point)->isLive()) {
168 if (block->isEntryBlock()) {
170 auto callable = dyn_cast<CallableOpInterface>(block->getParentOp());
171 if (callable && callable.getCallableRegion() == block->getParent()) {
172 if (!getSolverConfig().isInterprocedural()) {
177 ensure(succeeded(moduleOpRes),
"could not get root module from callable");
178 SmallVector<Operation *> callsites;
179 moduleOpRes->walk([
this, &callable, &callsites](CallOp call)
mutable {
181 if (succeeded(calledFnRes) &&
182 calledFnRes->get().getCallableRegion() == callable.getCallableRegion()) {
183 callsites.push_back(call);
187 for (Operation *callsite : callsites) {
192 llvm::cast<CallOpInterface>(callsite), CallControlFlowAction::EnterCallee, *before,
200 if (
auto branch = dyn_cast<RegionBranchOpInterface>(block->getParentOp())) {
209 for (Block::pred_iterator it = block->pred_begin(), e = block->pred_end(); it != e; ++it) {
211 Block *predecessor = *it;
212 if (!getOrCreateFor<Executable>(point, getLatticeAnchor<CFGEdge>(predecessor, block))
218 join(after, *
getLatticeFor(point, getProgramPointAfter(predecessor->getTerminator())));
226 Operation *op = point->isBlockStart() ? point->getBlock()->getParentOp() : point->getPrevOp();
250 std::optional<unsigned> regionFrom =
251 op == branch ? std::optional<unsigned>() : op->getBlock()->getParent()->getRegionNumber();
252 if (point->isBlockStart()) {
253 unsigned regionTo = point->getBlock()->getParent()->getRegionNumber();
256 assert(point->getPrevOp() == branch &&
"expected to be visiting the branch itself");
259 if (op->getParentOp() == branch || op == branch) {
261 branch, regionFrom, std::nullopt, *before, after
264 join(after, *before);
273 addDependency(state, dependent);
mlir::dataflow::Executable Executable
mlir::dataflow::CFGEdge CFGEdge
This file implements (LLZK-tailored) dense data-flow analysis using the data-flow analysis framework.
void join(AbstractDenseLattice *lhs, const AbstractDenseLattice &rhs)
Join a lattice with another and propagate an update if it changed.
virtual void visitCallControlFlowTransfer(mlir::CallOpInterface, 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 mlir::LogicalResult processOperation(mlir::Operation *op)
Visit an operation.
virtual AbstractDenseLattice * getLattice(mlir::LatticeAnchor anchor)=0
Get the dense lattice on the given lattice anchor.
mlir::LogicalResult visit(mlir::ProgramPoint *point) override
Visit a program point that modifies the state of the program.
void visitRegionBranchOperation(mlir::ProgramPoint *point, mlir::RegionBranchOpInterface branch, AbstractDenseLattice *after)
Visit a program point within a region branch operation with predecessors in it.
mlir::SymbolTableCollection tables
LLZK: Added for use of symbol helper caching.
virtual void setToEntryState(AbstractDenseLattice *lattice)=0
Set the dense lattice at control flow entry point and propagate an update if it changed.
mlir::LogicalResult initialize(mlir::Operation *top) override
Initialize the analysis by visiting every program point whose execution may modify the program state;...
virtual void visitRegionBranchControlFlowTransfer(mlir::RegionBranchOpInterface, 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...
const AbstractDenseLattice * getLatticeFor(mlir::ProgramPoint *dependent, mlir::LatticeAnchor anchor)
Get the dense lattice on the given lattice anchor and add dependent as its dependency.
virtual mlir::LogicalResult visitOperationImpl(mlir::Operation *op, const AbstractDenseLattice &before, AbstractDenseLattice *after)=0
Propagate the dense lattice before the execution of an operation to the lattice after its execution.
mlir::dataflow::AbstractDenseLattice AbstractDenseLattice
void ensure(bool condition, const llvm::Twine &errMsg)
mlir::FailureOr< SymbolLookupResult< T > > resolveCallable(mlir::SymbolTableCollection &symbolTable, mlir::CallOpInterface call)
Based on mlir::CallOpInterface::resolveCallable, but using LLZK lookup helpers.
FailureOr< ModuleOp > getTopRootModule(Operation *from)