#include "mlir/Analysis/DataFlow/DenseAnalysis.h"
#include "mlir/Analysis/DataFlow/DeadCodeAnalysis.h"
#include "mlir/Analysis/DataFlowFramework.h"
#include "mlir/IR/Block.h"
#include "mlir/IR/OpDefinition.h"
#include "mlir/IR/Operation.h"
#include "mlir/IR/Region.h"
#include "mlir/Interfaces/CallInterfaces.h"
#include "mlir/Interfaces/ControlFlowInterfaces.h"
#include "mlir/Support/LLVM.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Casting.h"
#include <cassert>
#include <optional>
using namespace mlir;
using namespace mlir::dataflow;
LogicalResult AbstractDenseForwardDataFlowAnalysis::initialize(Operation *top) {
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 AbstractDenseForwardDataFlowAnalysis::visit(ProgramPoint point) {
if (auto *op = llvm::dyn_cast_if_present<Operation *>(point))
processOperation(op);
else if (auto *block = llvm::dyn_cast_if_present<Block *>(point))
visitBlock(block);
else
return failure();
return success();
}
void AbstractDenseForwardDataFlowAnalysis::visitCallOperation(
CallOpInterface call, const AbstractDenseLattice &before,
AbstractDenseLattice *after) {
auto callable =
dyn_cast_if_present<CallableOpInterface>(call.resolveCallable());
if (!getSolverConfig().isInterprocedural() ||
(callable && !callable.getCallableRegion())) {
return visitCallControlFlowTransfer(
call, CallControlFlowAction::ExternalCallee, before, after);
}
const auto *predecessors =
getOrCreateFor<PredecessorState>(call.getOperation(), call);
if (!predecessors->allPredecessorsKnown())
return setToEntryState(after);
for (Operation *predecessor : predecessors->getKnownPredecessors()) {
AbstractDenseLattice *latticeAfterCall = after;
const AbstractDenseLattice *latticeAtCalleeReturn =
getLatticeFor(call.getOperation(), predecessor);
visitCallControlFlowTransfer(call, CallControlFlowAction::ExitCallee,
*latticeAtCalleeReturn, latticeAfterCall);
}
}
void AbstractDenseForwardDataFlowAnalysis::processOperation(Operation *op) {
if (!getOrCreateFor<Executable>(op, op->getBlock())->isLive())
return;
AbstractDenseLattice *after = getLattice(op);
const AbstractDenseLattice *before;
if (Operation *prev = op->getPrevNode())
before = getLatticeFor(op, prev);
else
before = getLatticeFor(op, op->getBlock());
if (auto branch = dyn_cast<RegionBranchOpInterface>(op))
return visitRegionBranchOperation(op, branch, after);
if (auto call = dyn_cast<CallOpInterface>(op))
return visitCallOperation(call, *before, after);
visitOperationImpl(op, *before, after);
}
void AbstractDenseForwardDataFlowAnalysis::visitBlock(Block *block) {
if (!getOrCreateFor<Executable>(block, block)->isLive())
return;
AbstractDenseLattice *after = getLattice(block);
if (block->isEntryBlock()) {
auto callable = dyn_cast<CallableOpInterface>(block->getParentOp());
if (callable && callable.getCallableRegion() == block->getParent()) {
const auto *callsites = getOrCreateFor<PredecessorState>(block, callable);
if (!callsites->allPredecessorsKnown() ||
!getSolverConfig().isInterprocedural())
return setToEntryState(after);
for (Operation *callsite : callsites->getKnownPredecessors()) {
const AbstractDenseLattice *before;
if (Operation *prev = callsite->getPrevNode())
before = getLatticeFor(block, prev);
else
before = getLatticeFor(block, callsite->getBlock());
visitCallControlFlowTransfer(cast<CallOpInterface>(callsite),
CallControlFlowAction::EnterCallee,
*before, after);
}
return;
}
if (auto branch = dyn_cast<RegionBranchOpInterface>(block->getParentOp()))
return visitRegionBranchOperation(block, branch, after);
return setToEntryState(after);
}
for (Block::pred_iterator it = block->pred_begin(), e = block->pred_end();
it != e; ++it) {
Block *predecessor = *it;
if (!getOrCreateFor<Executable>(
block, getProgramPoint<CFGEdge>(predecessor, block))
->isLive())
continue;
join(after, *getLatticeFor(block, predecessor->getTerminator()));
}
}
void AbstractDenseForwardDataFlowAnalysis::visitRegionBranchOperation(
ProgramPoint point, RegionBranchOpInterface branch,
AbstractDenseLattice *after) {
const auto *predecessors = getOrCreateFor<PredecessorState>(point, point);
assert(predecessors->allPredecessorsKnown() &&
"unexpected unresolved region successors");
for (Operation *op : predecessors->getKnownPredecessors()) {
const AbstractDenseLattice *before;
if (op == branch) {
if (Operation *prev = op->getPrevNode())
before = getLatticeFor(point, prev);
else
before = getLatticeFor(point, op->getBlock());
} else {
before = getLatticeFor(point, op);
}
std::optional<unsigned> regionFrom =
op == branch ? std::optional<unsigned>()
: op->getBlock()->getParent()->getRegionNumber();
if (auto *toBlock = point.dyn_cast<Block *>()) {
unsigned regionTo = toBlock->getParent()->getRegionNumber();
visitRegionBranchControlFlowTransfer(branch, regionFrom, regionTo,
*before, after);
} else {
assert(point.get<Operation *>() == branch &&
"expected to be visiting the branch itself");
if (op->getParentOp() == branch || op == branch) {
visitRegionBranchControlFlowTransfer(
branch, regionFrom, std::nullopt, *before, after);
} else {
join(after, *before);
}
}
}
}
const AbstractDenseLattice *
AbstractDenseForwardDataFlowAnalysis::getLatticeFor(ProgramPoint dependent,
ProgramPoint point) {
AbstractDenseLattice *state = getLattice(point);
addDependency(state, dependent);
return state;
}
LogicalResult
AbstractDenseBackwardDataFlowAnalysis::initialize(Operation *top) {
processOperation(top);
for (Region ®ion : top->getRegions()) {
for (Block &block : region) {
visitBlock(&block);
for (Operation &op : llvm::reverse(block)) {
if (failed(initialize(&op)))
return failure();
}
}
}
return success();
}
LogicalResult AbstractDenseBackwardDataFlowAnalysis::visit(ProgramPoint point) {
if (auto *op = llvm::dyn_cast_if_present<Operation *>(point))
processOperation(op);
else if (auto *block = llvm::dyn_cast_if_present<Block *>(point))
visitBlock(block);
else
return failure();
return success();
}
void AbstractDenseBackwardDataFlowAnalysis::visitCallOperation(
CallOpInterface call, const AbstractDenseLattice &after,
AbstractDenseLattice *before) {
Operation *callee = call.resolveCallable(&symbolTable);
auto callable = dyn_cast_or_null<CallableOpInterface>(callee);
if (!getSolverConfig().isInterprocedural() ||
(callable && (!callable.getCallableRegion() ||
callable.getCallableRegion()->empty()))) {
return visitCallControlFlowTransfer(
call, CallControlFlowAction::ExternalCallee, after, before);
}
if (!callable)
return setToExitState(before);
Region *region = callable.getCallableRegion();
Block *calleeEntryBlock = ®ion->front();
ProgramPoint calleeEntry = calleeEntryBlock->empty()
? ProgramPoint(calleeEntryBlock)
: &calleeEntryBlock->front();
const AbstractDenseLattice &latticeAtCalleeEntry =
*getLatticeFor(call.getOperation(), calleeEntry);
AbstractDenseLattice *latticeBeforeCall = before;
visitCallControlFlowTransfer(call, CallControlFlowAction::EnterCallee,
latticeAtCalleeEntry, latticeBeforeCall);
}
void AbstractDenseBackwardDataFlowAnalysis::processOperation(Operation *op) {
if (!getOrCreateFor<Executable>(op, op->getBlock())->isLive())
return;
AbstractDenseLattice *before = getLattice(op);
const AbstractDenseLattice *after;
if (Operation *next = op->getNextNode())
after = getLatticeFor(op, next);
else
after = getLatticeFor(op, op->getBlock());
if (auto branch = dyn_cast<RegionBranchOpInterface>(op))
return visitRegionBranchOperation(op, branch, RegionBranchPoint::parent(),
before);
if (auto call = dyn_cast<CallOpInterface>(op))
return visitCallOperation(call, *after, before);
visitOperationImpl(op, *after, before);
}
void AbstractDenseBackwardDataFlowAnalysis::visitBlock(Block *block) {
if (!getOrCreateFor<Executable>(block, block)->isLive())
return;
AbstractDenseLattice *before = getLattice(block);
auto isExitBlock = [](Block *b) {
if (b->empty() || !b->back().mightHaveTrait<OpTrait::IsTerminator>())
return true;
return isa_and_nonnull<RegionBranchTerminatorOpInterface>(
b->getTerminator());
};
if (isExitBlock(block)) {
auto callable = dyn_cast<CallableOpInterface>(block->getParentOp());
if (callable && callable.getCallableRegion() == block->getParent()) {
const auto *callsites = getOrCreateFor<PredecessorState>(block, callable);
if (!callsites->allPredecessorsKnown() ||
!getSolverConfig().isInterprocedural()) {
return setToExitState(before);
}
for (Operation *callsite : callsites->getKnownPredecessors()) {
const AbstractDenseLattice *after;
if (Operation *next = callsite->getNextNode())
after = getLatticeFor(block, next);
else
after = getLatticeFor(block, callsite->getBlock());
visitCallControlFlowTransfer(cast<CallOpInterface>(callsite),
CallControlFlowAction::ExitCallee, *after,
before);
}
return;
}
if (auto branch = dyn_cast<RegionBranchOpInterface>(block->getParentOp())) {
visitRegionBranchOperation(block, branch, block->getParent(), before);
return;
}
return setToExitState(before);
}
for (Block *successor : block->getSuccessors()) {
if (!getOrCreateFor<Executable>(block,
getProgramPoint<CFGEdge>(block, successor))
->isLive())
continue;
if (successor->empty())
meet(before, *getLatticeFor(block, successor));
else
meet(before, *getLatticeFor(block, &successor->front()));
}
}
void AbstractDenseBackwardDataFlowAnalysis::visitRegionBranchOperation(
ProgramPoint point, RegionBranchOpInterface branch,
RegionBranchPoint branchPoint, AbstractDenseLattice *before) {
SmallVector<RegionSuccessor> successors;
branch.getSuccessorRegions(branchPoint, successors);
for (const RegionSuccessor &successor : successors) {
const AbstractDenseLattice *after;
if (successor.isParent() || successor.getSuccessor()->empty()) {
if (Operation *next = branch->getNextNode())
after = getLatticeFor(point, next);
else
after = getLatticeFor(point, branch->getBlock());
} else {
Region *successorRegion = successor.getSuccessor();
assert(!successorRegion->empty() && "unexpected empty successor region");
Block *successorBlock = &successorRegion->front();
if (!getOrCreateFor<Executable>(point, successorBlock)->isLive())
continue;
if (successorBlock->empty())
after = getLatticeFor(point, successorBlock);
else
after = getLatticeFor(point, &successorBlock->front());
}
visitRegionBranchControlFlowTransfer(branch, branchPoint, successor, *after,
before);
}
}
const AbstractDenseLattice *
AbstractDenseBackwardDataFlowAnalysis::getLatticeFor(ProgramPoint dependent,
ProgramPoint point) {
AbstractDenseLattice *state = getLattice(point);
addDependency(state, dependent);
return state;
}