//===- DenseAnalysis.cpp - 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 // //===----------------------------------------------------------------------===// #include "mlir/Analysis/DataFlow/DenseAnalysis.h" #include "mlir/Analysis/DataFlow/DeadCodeAnalysis.h" #include "mlir/Interfaces/CallInterfaces.h" #include "mlir/Interfaces/ControlFlowInterfaces.h" using namespace mlir; using namespace mlir::dataflow; //===----------------------------------------------------------------------===// // AbstractDenseDataFlowAnalysis //===----------------------------------------------------------------------===// LogicalResult AbstractDenseDataFlowAnalysis::initialize(Operation *top) { // Visit every operation and block. processOperation(top); for (Region ®ion : top->getRegions()) { for (Block &block : region) { visitBlock(&block); for (Operation &op : block) if (failed(initialize(&op))) return failure(); } } return success(); } LogicalResult AbstractDenseDataFlowAnalysis::visit(ProgramPoint point) { if (auto *op = point.dyn_cast()) processOperation(op); else if (auto *block = point.dyn_cast()) visitBlock(block); else return failure(); return success(); } void AbstractDenseDataFlowAnalysis::processOperation(Operation *op) { // If the containing block is not executable, bail out. if (!getOrCreateFor(op, op->getBlock())->isLive()) return; // Get the dense lattice to update. AbstractDenseLattice *after = getLattice(op); // If this op implements region control-flow, then control-flow dictates its // transfer function. if (auto branch = dyn_cast(op)) return visitRegionBranchOperation(op, branch, after); // If this is a call operation, then join its lattices across known return // sites. if (auto call = dyn_cast(op)) { const auto *predecessors = getOrCreateFor(op, call); // If not all return sites are known, then conservatively assume we can't // reason about the data-flow. if (!predecessors->allPredecessorsKnown()) return setToEntryState(after); for (Operation *predecessor : predecessors->getKnownPredecessors()) join(after, *getLatticeFor(op, predecessor)); return; } // Get the dense state before the execution of the op. const AbstractDenseLattice *before; if (Operation *prev = op->getPrevNode()) before = getLatticeFor(op, prev); else before = getLatticeFor(op, op->getBlock()); // Invoke the operation transfer function. visitOperationImpl(op, *before, after); } void AbstractDenseDataFlowAnalysis::visitBlock(Block *block) { // If the block is not executable, bail out. if (!getOrCreateFor(block, block)->isLive()) return; // Get the dense lattice to update. AbstractDenseLattice *after = getLattice(block); // The dense lattices of entry blocks are set by region control-flow or the // callgraph. if (block->isEntryBlock()) { // Check if this block is the entry block of a callable region. auto callable = dyn_cast(block->getParentOp()); if (callable && callable.getCallableRegion() == block->getParent()) { const auto *callsites = getOrCreateFor(block, callable); // If not all callsites are known, conservatively mark all lattices as // having reached their pessimistic fixpoints. if (!callsites->allPredecessorsKnown()) return setToEntryState(after); for (Operation *callsite : callsites->getKnownPredecessors()) { // Get the dense lattice before the callsite. if (Operation *prev = callsite->getPrevNode()) join(after, *getLatticeFor(block, prev)); else join(after, *getLatticeFor(block, callsite->getBlock())); } return; } // Check if we can reason about the control-flow. if (auto branch = dyn_cast(block->getParentOp())) return visitRegionBranchOperation(block, branch, after); // Otherwise, we can't reason about the data-flow. return setToEntryState(after); } // Join the state with the state after the block's predecessors. for (Block::pred_iterator it = block->pred_begin(), e = block->pred_end(); it != e; ++it) { // Skip control edges that aren't executable. Block *predecessor = *it; if (!getOrCreateFor( block, getProgramPoint(predecessor, block)) ->isLive()) continue; // Merge in the state from the predecessor's terminator. join(after, *getLatticeFor(block, predecessor->getTerminator())); } } void AbstractDenseDataFlowAnalysis::visitRegionBranchOperation( ProgramPoint point, RegionBranchOpInterface branch, AbstractDenseLattice *after) { // Get the terminator predecessors. const auto *predecessors = getOrCreateFor(point, point); assert(predecessors->allPredecessorsKnown() && "unexpected unresolved region successors"); for (Operation *op : predecessors->getKnownPredecessors()) { const AbstractDenseLattice *before; // If the predecessor is the parent, get the state before the parent. if (op == branch) { if (Operation *prev = op->getPrevNode()) before = getLatticeFor(point, prev); else before = getLatticeFor(point, op->getBlock()); // Otherwise, get the state after the terminator. } else { before = getLatticeFor(point, op); } join(after, *before); } } const AbstractDenseLattice * AbstractDenseDataFlowAnalysis::getLatticeFor(ProgramPoint dependent, ProgramPoint point) { AbstractDenseLattice *state = getLattice(point); addDependency(state, dependent); return state; }