llvm-project/mlir/lib/Analysis/DataFlow/DenseAnalysis.cpp

166 lines
5.9 KiB
C++

//===- 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.
visitOperation(top);
for (Region &region : 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<Operation *>())
visitOperation(op);
else if (auto *block = point.dyn_cast<Block *>())
visitBlock(block);
else
return failure();
return success();
}
void AbstractDenseDataFlowAnalysis::visitOperation(Operation *op) {
// If the containing block is not executable, bail out.
if (!getOrCreateFor<Executable>(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<RegionBranchOpInterface>(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<CallOpInterface>(op)) {
const auto *predecessors = getOrCreateFor<PredecessorState>(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<Executable>(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<CallableOpInterface>(block->getParentOp());
if (callable && callable.getCallableRegion() == block->getParent()) {
const auto *callsites = getOrCreateFor<PredecessorState>(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<RegionBranchOpInterface>(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<Executable>(
block, getProgramPoint<CFGEdge>(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<PredecessorState>(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;
}