### Rationale
For a program point where we cannot reason about incoming dataflow (e.g. an argument of an entry block), the framework needs to initialize the state.
Currently, `AbstractSparseDataFlowAnalysis` initializes such state to the "pessimistic fixpoint", and `AbstractDenseDataFlowAnalysis` calls the state's `reset()` function.
However, entry states aren't necessarily the pessimistic fixpoint. Example: in reaching definition, the pessimistic fixpoint is `{all definitions}`, but the entry state is `{}`.
This awkwardness might be why the dense analysis API currently uses `reset()` instead of `markPessimisticFixpoint()`.
This patch consolidates entry point initialization into a single function `setToEntryState()`.
### API Location
Note that `setToEntryState()` is defined in the analysis rather than the lattice, so that we allow different analyses to use the same lattice but different entry states.
### Removal of the concept of optimistic/known value
The concept of optimistic/known value is too specific to SCCP.
Furthermore, the known value is not really used: In the current SCCP implementation, the known value (pessimistic fixpoint) is always `Attribute{}` (non-constant). This means there's no point storing a `knownValue` in each state.
If we do need to re-introduce optimistic/known value, we should put it in the SCCP analysis, not the sparse analysis API.
### Terminology
Please let me know if "entry state" is a good terminology.
I chose "entry" from Wikipedia (https://en.wikipedia.org/wiki/Data-flow_analysis#Basic_principles).
Another term I can think of is "boundary" (https://suif.stanford.edu/~courses/cs243/lectures/L3-DFA2-revised.pdf) which might be better since it also makes sense for backward analysis.
Reviewed By: Mogball
Differential Revision: https://reviews.llvm.org/D132086
169 lines
6.0 KiB
C++
169 lines
6.0 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 ®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<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());
|
|
// If the incoming lattice is uninitialized, bail out.
|
|
if (before->isUninitialized())
|
|
return;
|
|
|
|
// 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;
|
|
}
|