mirror of
https://github.com/intel/llvm.git
synced 2026-01-20 10:58:11 +08:00
[MLIR] Add more logging to DenseAnalysis/DeaDCodeAnalysis/TestDenseBackwardDataFlowAnalysis (NFC) (#161503)
Just some more debugging help here, it may need more tweaking in the future.
This commit is contained in:
@@ -522,14 +522,14 @@ void DeadCodeAnalysis::visitRegionBranchEdges(
|
||||
// Mark the entry block as executable.
|
||||
auto *state = getOrCreate<Executable>(point);
|
||||
propagateIfChanged(state, state->setToLive());
|
||||
LDBG() << "Marked region successor live: " << point;
|
||||
LDBG() << "Marked region successor live: " << *point;
|
||||
|
||||
// Add the parent op as a predecessor.
|
||||
auto *predecessors = getOrCreate<PredecessorState>(point);
|
||||
propagateIfChanged(
|
||||
predecessors,
|
||||
predecessors->join(predecessorOp, successor.getSuccessorInputs()));
|
||||
LDBG() << "Added region branch as predecessor for successor: " << point;
|
||||
LDBG() << "Added region branch as predecessor for successor: " << *point;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,44 +17,74 @@
|
||||
#include "mlir/Interfaces/ControlFlowInterfaces.h"
|
||||
#include "mlir/Support/LLVM.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Support/DebugLog.h"
|
||||
#include <cassert>
|
||||
#include <optional>
|
||||
|
||||
using namespace mlir;
|
||||
using namespace mlir::dataflow;
|
||||
|
||||
#define DEBUG_TYPE "dense-analysis"
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// AbstractDenseForwardDataFlowAnalysis
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
void AbstractDenseForwardDataFlowAnalysis::initializeEquivalentLatticeAnchor(
|
||||
Operation *top) {
|
||||
LDBG() << "initializeEquivalentLatticeAnchor: "
|
||||
<< OpWithFlags(top, OpPrintingFlags().skipRegions());
|
||||
top->walk([&](Operation *op) {
|
||||
if (isa<RegionBranchOpInterface, CallOpInterface>(op))
|
||||
if (isa<RegionBranchOpInterface, CallOpInterface>(op)) {
|
||||
LDBG() << " Skipping "
|
||||
<< OpWithFlags(op, OpPrintingFlags().skipRegions())
|
||||
<< " (region branch or call)";
|
||||
return;
|
||||
}
|
||||
LDBG() << " Building equivalent lattice anchor for "
|
||||
<< OpWithFlags(op, OpPrintingFlags().skipRegions());
|
||||
buildOperationEquivalentLatticeAnchor(op);
|
||||
});
|
||||
}
|
||||
|
||||
LogicalResult AbstractDenseForwardDataFlowAnalysis::initialize(Operation *top) {
|
||||
LDBG() << "initialize (forward): "
|
||||
<< OpWithFlags(top, OpPrintingFlags().skipRegions());
|
||||
// Visit every operation and block.
|
||||
if (failed(processOperation(top)))
|
||||
if (failed(processOperation(top))) {
|
||||
LDBG() << " Failed to process top-level operation";
|
||||
return failure();
|
||||
}
|
||||
|
||||
for (Region ®ion : top->getRegions()) {
|
||||
LDBG() << " Processing region with " << region.getBlocks().size()
|
||||
<< " blocks";
|
||||
for (Block &block : region) {
|
||||
LDBG() << " Processing block with " << block.getOperations().size()
|
||||
<< " operations";
|
||||
visitBlock(&block);
|
||||
for (Operation &op : block)
|
||||
if (failed(initialize(&op)))
|
||||
for (Operation &op : block) {
|
||||
LDBG() << " Initializing operation: "
|
||||
<< OpWithFlags(&op, OpPrintingFlags().skipRegions());
|
||||
if (failed(initialize(&op))) {
|
||||
LDBG() << " Failed to initialize operation";
|
||||
return failure();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LDBG() << " Forward initialization completed successfully";
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult AbstractDenseForwardDataFlowAnalysis::visit(ProgramPoint *point) {
|
||||
if (!point->isBlockStart())
|
||||
LDBG() << "visit (forward): " << *point;
|
||||
if (!point->isBlockStart()) {
|
||||
LDBG() << " Processing operation: "
|
||||
<< OpWithFlags(point->getPrevOp(), OpPrintingFlags().skipRegions());
|
||||
return processOperation(point->getPrevOp());
|
||||
}
|
||||
LDBG() << " Visiting block: " << point->getBlock();
|
||||
visitBlock(point->getBlock());
|
||||
return success();
|
||||
}
|
||||
@@ -62,6 +92,11 @@ LogicalResult AbstractDenseForwardDataFlowAnalysis::visit(ProgramPoint *point) {
|
||||
void AbstractDenseForwardDataFlowAnalysis::visitCallOperation(
|
||||
CallOpInterface call, const AbstractDenseLattice &before,
|
||||
AbstractDenseLattice *after) {
|
||||
LDBG() << "visitCallOperation (forward): "
|
||||
<< OpWithFlags(call.getOperation(), OpPrintingFlags().skipRegions());
|
||||
LDBG() << " before state: " << before;
|
||||
LDBG() << " after state: " << *after;
|
||||
|
||||
// Allow for customizing the behavior of calls to external symbols, including
|
||||
// when the analysis is explicitly marked as non-interprocedural.
|
||||
auto isExternalCallable = [&]() {
|
||||
@@ -70,6 +105,7 @@ void AbstractDenseForwardDataFlowAnalysis::visitCallOperation(
|
||||
return callable && !callable.getCallableRegion();
|
||||
};
|
||||
if (!getSolverConfig().isInterprocedural() || isExternalCallable()) {
|
||||
LDBG() << " Handling as external callee (non-interprocedural or external)";
|
||||
return visitCallControlFlowTransfer(
|
||||
call, CallControlFlowAction::ExternalCallee, before, after);
|
||||
}
|
||||
@@ -78,10 +114,16 @@ void AbstractDenseForwardDataFlowAnalysis::visitCallOperation(
|
||||
getProgramPointAfter(call.getOperation()), getProgramPointAfter(call));
|
||||
// Otherwise, if not all return sites are known, then conservatively assume we
|
||||
// can't reason about the data-flow.
|
||||
if (!predecessors->allPredecessorsKnown())
|
||||
if (!predecessors->allPredecessorsKnown()) {
|
||||
LDBG() << " Not all predecessors known, setting to entry state";
|
||||
return setToEntryState(after);
|
||||
}
|
||||
|
||||
LDBG() << " Processing " << predecessors->getKnownPredecessors().size()
|
||||
<< " known predecessors";
|
||||
for (Operation *predecessor : predecessors->getKnownPredecessors()) {
|
||||
LDBG() << " Processing predecessor: "
|
||||
<< OpWithFlags(predecessor, OpPrintingFlags().skipRegions());
|
||||
// Get the lattices at callee return:
|
||||
//
|
||||
// func.func @callee() {
|
||||
@@ -99,6 +141,7 @@ void AbstractDenseForwardDataFlowAnalysis::visitCallOperation(
|
||||
const AbstractDenseLattice *latticeAtCalleeReturn =
|
||||
getLatticeFor(getProgramPointAfter(call.getOperation()),
|
||||
getProgramPointAfter(predecessor));
|
||||
LDBG() << " Lattice at callee return: " << *latticeAtCalleeReturn;
|
||||
visitCallControlFlowTransfer(call, CallControlFlowAction::ExitCallee,
|
||||
*latticeAtCalleeReturn, latticeAfterCall);
|
||||
}
|
||||
@@ -106,12 +149,16 @@ void AbstractDenseForwardDataFlowAnalysis::visitCallOperation(
|
||||
|
||||
LogicalResult
|
||||
AbstractDenseForwardDataFlowAnalysis::processOperation(Operation *op) {
|
||||
LDBG() << "processOperation (forward): "
|
||||
<< OpWithFlags(op, OpPrintingFlags().skipRegions());
|
||||
ProgramPoint *point = getProgramPointAfter(op);
|
||||
// If the containing block is not executable, bail out.
|
||||
if (op->getBlock() != nullptr &&
|
||||
!getOrCreateFor<Executable>(point, getProgramPointBefore(op->getBlock()))
|
||||
->isLive())
|
||||
->isLive()) {
|
||||
LDBG() << " Block not executable, skipping operation";
|
||||
return success();
|
||||
}
|
||||
|
||||
// Get the dense lattice to update.
|
||||
AbstractDenseLattice *after = getLattice(point);
|
||||
@@ -119,10 +166,13 @@ AbstractDenseForwardDataFlowAnalysis::processOperation(Operation *op) {
|
||||
// Get the dense state before the execution of the op.
|
||||
const AbstractDenseLattice *before =
|
||||
getLatticeFor(point, getProgramPointBefore(op));
|
||||
LDBG() << " before state: " << *before;
|
||||
LDBG() << " after state: " << *after;
|
||||
|
||||
// If this op implements region control-flow, then control-flow dictates its
|
||||
// transfer function.
|
||||
if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
|
||||
LDBG() << " Processing as region branch operation";
|
||||
visitRegionBranchOperation(point, branch, after);
|
||||
return success();
|
||||
}
|
||||
@@ -130,41 +180,57 @@ AbstractDenseForwardDataFlowAnalysis::processOperation(Operation *op) {
|
||||
// If this is a call operation, then join its lattices across known return
|
||||
// sites.
|
||||
if (auto call = dyn_cast<CallOpInterface>(op)) {
|
||||
LDBG() << " Processing as call operation";
|
||||
visitCallOperation(call, *before, after);
|
||||
return success();
|
||||
}
|
||||
|
||||
// Invoke the operation transfer function.
|
||||
LDBG() << " Invoking operation transfer function";
|
||||
return visitOperationImpl(op, *before, after);
|
||||
}
|
||||
|
||||
void AbstractDenseForwardDataFlowAnalysis::visitBlock(Block *block) {
|
||||
LDBG() << "visitBlock (forward): " << block;
|
||||
// If the block is not executable, bail out.
|
||||
ProgramPoint *point = getProgramPointBefore(block);
|
||||
if (!getOrCreateFor<Executable>(point, point)->isLive())
|
||||
if (!getOrCreateFor<Executable>(point, point)->isLive()) {
|
||||
LDBG() << " Block not executable, skipping";
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the dense lattice to update.
|
||||
AbstractDenseLattice *after = getLattice(point);
|
||||
LDBG() << " Block lattice state: " << *after;
|
||||
|
||||
// The dense lattices of entry blocks are set by region control-flow or the
|
||||
// callgraph.
|
||||
if (block->isEntryBlock()) {
|
||||
LDBG() << " Processing entry block";
|
||||
// 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()) {
|
||||
LDBG() << " Entry block of callable region";
|
||||
const auto *callsites = getOrCreateFor<PredecessorState>(
|
||||
point, getProgramPointAfter(callable));
|
||||
// If not all callsites are known, conservatively mark all lattices as
|
||||
// having reached their pessimistic fixpoints. Do the same if
|
||||
// interprocedural analysis is not enabled.
|
||||
if (!callsites->allPredecessorsKnown() ||
|
||||
!getSolverConfig().isInterprocedural())
|
||||
!getSolverConfig().isInterprocedural()) {
|
||||
LDBG() << " Not all callsites known or non-interprocedural, setting "
|
||||
"to entry state";
|
||||
return setToEntryState(after);
|
||||
}
|
||||
LDBG() << " Processing " << callsites->getKnownPredecessors().size()
|
||||
<< " known callsites";
|
||||
for (Operation *callsite : callsites->getKnownPredecessors()) {
|
||||
LDBG() << " Processing callsite: "
|
||||
<< OpWithFlags(callsite, OpPrintingFlags().skipRegions());
|
||||
// Get the dense lattice before the callsite.
|
||||
const AbstractDenseLattice *before;
|
||||
before = getLatticeFor(point, getProgramPointBefore(callsite));
|
||||
LDBG() << " Lattice before callsite: " << *before;
|
||||
|
||||
visitCallControlFlowTransfer(cast<CallOpInterface>(callsite),
|
||||
CallControlFlowAction::EnterCallee,
|
||||
@@ -174,23 +240,32 @@ void AbstractDenseForwardDataFlowAnalysis::visitBlock(Block *block) {
|
||||
}
|
||||
|
||||
// Check if we can reason about the control-flow.
|
||||
if (auto branch = dyn_cast<RegionBranchOpInterface>(block->getParentOp()))
|
||||
if (auto branch = dyn_cast<RegionBranchOpInterface>(block->getParentOp())) {
|
||||
LDBG() << " Entry block of region branch operation";
|
||||
return visitRegionBranchOperation(point, branch, after);
|
||||
}
|
||||
|
||||
// Otherwise, we can't reason about the data-flow.
|
||||
LDBG() << " Cannot reason about data-flow, setting to entry state";
|
||||
return setToEntryState(after);
|
||||
}
|
||||
|
||||
// Join the state with the state after the block's predecessors.
|
||||
LDBG() << " Joining state from "
|
||||
<< std::distance(block->pred_begin(), block->pred_end())
|
||||
<< " 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>(
|
||||
point, getLatticeAnchor<CFGEdge>(predecessor, block))
|
||||
->isLive())
|
||||
->isLive()) {
|
||||
LDBG() << " Skipping non-executable edge from " << predecessor;
|
||||
continue;
|
||||
}
|
||||
|
||||
LDBG() << " Joining state from predecessor " << predecessor;
|
||||
// Merge in the state from the predecessor's terminator.
|
||||
join(after, *getLatticeFor(
|
||||
point, getProgramPointAfter(predecessor->getTerminator())));
|
||||
@@ -200,20 +275,34 @@ void AbstractDenseForwardDataFlowAnalysis::visitBlock(Block *block) {
|
||||
void AbstractDenseForwardDataFlowAnalysis::visitRegionBranchOperation(
|
||||
ProgramPoint *point, RegionBranchOpInterface branch,
|
||||
AbstractDenseLattice *after) {
|
||||
LDBG() << "visitRegionBranchOperation (forward): "
|
||||
<< OpWithFlags(branch.getOperation(), OpPrintingFlags().skipRegions());
|
||||
LDBG() << " point: " << *point;
|
||||
LDBG() << " after state: " << *after;
|
||||
|
||||
// Get the terminator predecessors.
|
||||
const auto *predecessors = getOrCreateFor<PredecessorState>(point, point);
|
||||
assert(predecessors->allPredecessorsKnown() &&
|
||||
"unexpected unresolved region successors");
|
||||
|
||||
LDBG() << " Processing " << predecessors->getKnownPredecessors().size()
|
||||
<< " known predecessors";
|
||||
for (Operation *op : predecessors->getKnownPredecessors()) {
|
||||
LDBG() << " Processing predecessor: "
|
||||
<< OpWithFlags(op, OpPrintingFlags().skipRegions());
|
||||
const AbstractDenseLattice *before;
|
||||
// If the predecessor is the parent, get the state before the parent.
|
||||
if (op == branch) {
|
||||
LDBG() << " Predecessor is the branch itself, getting state before "
|
||||
"parent";
|
||||
before = getLatticeFor(point, getProgramPointBefore(op));
|
||||
// Otherwise, get the state after the terminator.
|
||||
} else {
|
||||
LDBG()
|
||||
<< " Predecessor is terminator, getting state after terminator";
|
||||
before = getLatticeFor(point, getProgramPointAfter(op));
|
||||
}
|
||||
LDBG() << " before state: " << *before;
|
||||
|
||||
// This function is called in two cases:
|
||||
// 1. when visiting the block (point = block start);
|
||||
@@ -231,19 +320,31 @@ void AbstractDenseForwardDataFlowAnalysis::visitRegionBranchOperation(
|
||||
std::optional<unsigned> regionFrom =
|
||||
op == branch ? std::optional<unsigned>()
|
||||
: op->getBlock()->getParent()->getRegionNumber();
|
||||
LDBG() << " regionFrom: "
|
||||
<< (regionFrom ? std::to_string(*regionFrom) : "parent");
|
||||
|
||||
if (point->isBlockStart()) {
|
||||
unsigned regionTo = point->getBlock()->getParent()->getRegionNumber();
|
||||
LDBG() << " Point is block start, regionTo: " << regionTo;
|
||||
LDBG() << " Calling visitRegionBranchControlFlowTransfer with "
|
||||
"regionFrom/regionTo";
|
||||
visitRegionBranchControlFlowTransfer(branch, regionFrom, regionTo,
|
||||
*before, after);
|
||||
} else {
|
||||
assert(point->getPrevOp() == branch &&
|
||||
"expected to be visiting the branch itself");
|
||||
LDBG() << " Point is not block start, checking if predecessor is "
|
||||
"region or op itself";
|
||||
// Only need to call the arc transfer when the predecessor is the region
|
||||
// or the op itself, not the previous op.
|
||||
if (op->getParentOp() == branch || op == branch) {
|
||||
LDBG() << " Predecessor is region or op itself, calling "
|
||||
"visitRegionBranchControlFlowTransfer";
|
||||
visitRegionBranchControlFlowTransfer(
|
||||
branch, regionFrom, /*regionTo=*/std::nullopt, *before, after);
|
||||
} else {
|
||||
LDBG()
|
||||
<< " Predecessor is not region or op itself, performing join";
|
||||
join(after, *before);
|
||||
}
|
||||
}
|
||||
@@ -256,35 +357,61 @@ void AbstractDenseForwardDataFlowAnalysis::visitRegionBranchOperation(
|
||||
|
||||
void AbstractDenseBackwardDataFlowAnalysis::initializeEquivalentLatticeAnchor(
|
||||
Operation *top) {
|
||||
LDBG() << "initializeEquivalentLatticeAnchor (backward): "
|
||||
<< OpWithFlags(top, OpPrintingFlags().skipRegions());
|
||||
top->walk([&](Operation *op) {
|
||||
if (isa<RegionBranchOpInterface, CallOpInterface>(op))
|
||||
if (isa<RegionBranchOpInterface, CallOpInterface>(op)) {
|
||||
LDBG() << " Skipping "
|
||||
<< OpWithFlags(op, OpPrintingFlags().skipRegions())
|
||||
<< " (region branch or call)";
|
||||
return;
|
||||
}
|
||||
LDBG() << " Building equivalent lattice anchor for "
|
||||
<< OpWithFlags(op, OpPrintingFlags().skipRegions());
|
||||
buildOperationEquivalentLatticeAnchor(op);
|
||||
});
|
||||
}
|
||||
|
||||
LogicalResult
|
||||
AbstractDenseBackwardDataFlowAnalysis::initialize(Operation *top) {
|
||||
LDBG() << "initialize (backward): "
|
||||
<< OpWithFlags(top, OpPrintingFlags().skipRegions());
|
||||
// Visit every operation and block.
|
||||
if (failed(processOperation(top)))
|
||||
if (failed(processOperation(top))) {
|
||||
LDBG() << " Failed to process top-level operation";
|
||||
return failure();
|
||||
}
|
||||
|
||||
for (Region ®ion : top->getRegions()) {
|
||||
LDBG() << " Processing region with " << region.getBlocks().size()
|
||||
<< " blocks";
|
||||
for (Block &block : region) {
|
||||
LDBG() << " Processing block with " << block.getOperations().size()
|
||||
<< " operations";
|
||||
visitBlock(&block);
|
||||
for (Operation &op : llvm::reverse(block)) {
|
||||
if (failed(initialize(&op)))
|
||||
LDBG() << " Initializing operation (backward): "
|
||||
<< OpWithFlags(&op, OpPrintingFlags().skipRegions());
|
||||
if (failed(initialize(&op))) {
|
||||
LDBG() << " Failed to initialize operation";
|
||||
return failure();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LDBG() << " Backward initialization completed successfully";
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult
|
||||
AbstractDenseBackwardDataFlowAnalysis::visit(ProgramPoint *point) {
|
||||
if (!point->isBlockEnd())
|
||||
LDBG() << "visit (backward): " << *point;
|
||||
if (!point->isBlockEnd()) {
|
||||
LDBG() << " Processing operation: "
|
||||
<< OpWithFlags(point->getNextOp(), OpPrintingFlags().skipRegions());
|
||||
return processOperation(point->getNextOp());
|
||||
}
|
||||
LDBG() << " Visiting block: " << point->getBlock();
|
||||
visitBlock(point->getBlock());
|
||||
return success();
|
||||
}
|
||||
@@ -292,28 +419,47 @@ AbstractDenseBackwardDataFlowAnalysis::visit(ProgramPoint *point) {
|
||||
void AbstractDenseBackwardDataFlowAnalysis::visitCallOperation(
|
||||
CallOpInterface call, const AbstractDenseLattice &after,
|
||||
AbstractDenseLattice *before) {
|
||||
LDBG() << "visitCallOperation (backward): "
|
||||
<< OpWithFlags(call.getOperation(), OpPrintingFlags().skipRegions());
|
||||
LDBG() << " after state: " << after;
|
||||
LDBG() << " before state: " << *before;
|
||||
|
||||
// If the solver is not interprocedural, let the hook handle it as an external
|
||||
// callee.
|
||||
if (!getSolverConfig().isInterprocedural())
|
||||
if (!getSolverConfig().isInterprocedural()) {
|
||||
LDBG() << " Non-interprocedural analysis, handling as external callee";
|
||||
return visitCallControlFlowTransfer(
|
||||
call, CallControlFlowAction::ExternalCallee, after, before);
|
||||
}
|
||||
|
||||
// Find the callee.
|
||||
Operation *callee = call.resolveCallableInTable(&symbolTable);
|
||||
if (callee) {
|
||||
LDBG() << " Resolved callee: "
|
||||
<< OpWithFlags(callee, OpPrintingFlags().skipRegions());
|
||||
} else {
|
||||
LDBG() << " Resolved callee: null";
|
||||
}
|
||||
|
||||
auto callable = dyn_cast_or_null<CallableOpInterface>(callee);
|
||||
// No region means the callee is only declared in this module.
|
||||
// If that is the case or if the solver is not interprocedural,
|
||||
// let the hook handle it.
|
||||
if (callable &&
|
||||
(!callable.getCallableRegion() || callable.getCallableRegion()->empty()))
|
||||
if (callable && (!callable.getCallableRegion() ||
|
||||
callable.getCallableRegion()->empty())) {
|
||||
LDBG() << " Callee has no region or empty region, handling as external "
|
||||
"callee";
|
||||
return visitCallControlFlowTransfer(
|
||||
call, CallControlFlowAction::ExternalCallee, after, before);
|
||||
}
|
||||
|
||||
if (!callable)
|
||||
if (!callable) {
|
||||
LDBG() << " No callable found, setting to exit state";
|
||||
return setToExitState(before);
|
||||
}
|
||||
|
||||
Region *region = callable.getCallableRegion();
|
||||
LDBG() << " Processing callable with region";
|
||||
|
||||
// Call-level control flow specifies the data flow here.
|
||||
//
|
||||
@@ -332,6 +478,7 @@ void AbstractDenseBackwardDataFlowAnalysis::visitCallOperation(
|
||||
ProgramPoint *calleeEntry = getProgramPointBefore(calleeEntryBlock);
|
||||
const AbstractDenseLattice &latticeAtCalleeEntry =
|
||||
*getLatticeFor(getProgramPointBefore(call.getOperation()), calleeEntry);
|
||||
LDBG() << " Lattice at callee entry: " << latticeAtCalleeEntry;
|
||||
AbstractDenseLattice *latticeBeforeCall = before;
|
||||
visitCallControlFlowTransfer(call, CallControlFlowAction::EnterCallee,
|
||||
latticeAtCalleeEntry, latticeBeforeCall);
|
||||
@@ -339,12 +486,16 @@ void AbstractDenseBackwardDataFlowAnalysis::visitCallOperation(
|
||||
|
||||
LogicalResult
|
||||
AbstractDenseBackwardDataFlowAnalysis::processOperation(Operation *op) {
|
||||
LDBG() << "processOperation (backward): "
|
||||
<< OpWithFlags(op, OpPrintingFlags().skipRegions());
|
||||
ProgramPoint *point = getProgramPointBefore(op);
|
||||
// If the containing block is not executable, bail out.
|
||||
if (op->getBlock() != nullptr &&
|
||||
!getOrCreateFor<Executable>(point, getProgramPointBefore(op->getBlock()))
|
||||
->isLive())
|
||||
->isLive()) {
|
||||
LDBG() << " Block not executable, skipping operation";
|
||||
return success();
|
||||
}
|
||||
|
||||
// Get the dense lattice to update.
|
||||
AbstractDenseLattice *before = getLattice(point);
|
||||
@@ -352,30 +503,39 @@ AbstractDenseBackwardDataFlowAnalysis::processOperation(Operation *op) {
|
||||
// Get the dense state after execution of this op.
|
||||
const AbstractDenseLattice *after =
|
||||
getLatticeFor(point, getProgramPointAfter(op));
|
||||
LDBG() << " before state: " << *before;
|
||||
LDBG() << " after state: " << *after;
|
||||
|
||||
// Special cases where control flow may dictate data flow.
|
||||
if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
|
||||
LDBG() << " Processing as region branch operation";
|
||||
visitRegionBranchOperation(point, branch, RegionBranchPoint::parent(),
|
||||
before);
|
||||
return success();
|
||||
}
|
||||
if (auto call = dyn_cast<CallOpInterface>(op)) {
|
||||
LDBG() << " Processing as call operation";
|
||||
visitCallOperation(call, *after, before);
|
||||
return success();
|
||||
}
|
||||
|
||||
// Invoke the operation transfer function.
|
||||
LDBG() << " Invoking operation transfer function";
|
||||
return visitOperationImpl(op, *after, before);
|
||||
}
|
||||
|
||||
void AbstractDenseBackwardDataFlowAnalysis::visitBlock(Block *block) {
|
||||
LDBG() << "visitBlock (backward): " << block;
|
||||
ProgramPoint *point = getProgramPointAfter(block);
|
||||
// If the block is not executable, bail out.
|
||||
if (!getOrCreateFor<Executable>(point, getProgramPointBefore(block))
|
||||
->isLive())
|
||||
->isLive()) {
|
||||
LDBG() << " Block not executable, skipping";
|
||||
return;
|
||||
}
|
||||
|
||||
AbstractDenseLattice *before = getLattice(point);
|
||||
LDBG() << " Block lattice state: " << *before;
|
||||
|
||||
// We need "exit" blocks, i.e. the blocks that may return control to the
|
||||
// parent operation.
|
||||
@@ -391,23 +551,32 @@ void AbstractDenseBackwardDataFlowAnalysis::visitBlock(Block *block) {
|
||||
b->getTerminator());
|
||||
};
|
||||
if (isExitBlock(block)) {
|
||||
LDBG() << " Processing exit block";
|
||||
// If this block is exiting from a callable, the successors of exiting from
|
||||
// a callable are the successors of all call sites. And the call sites
|
||||
// themselves are predecessors of the callable.
|
||||
auto callable = dyn_cast<CallableOpInterface>(block->getParentOp());
|
||||
if (callable && callable.getCallableRegion() == block->getParent()) {
|
||||
LDBG() << " Exit block of callable region";
|
||||
const auto *callsites = getOrCreateFor<PredecessorState>(
|
||||
point, getProgramPointAfter(callable));
|
||||
// If not all call sites are known, conservative mark all lattices as
|
||||
// having reached their pessimistic fix points.
|
||||
if (!callsites->allPredecessorsKnown() ||
|
||||
!getSolverConfig().isInterprocedural()) {
|
||||
LDBG() << " Not all callsites known or non-interprocedural, setting "
|
||||
"to exit state";
|
||||
return setToExitState(before);
|
||||
}
|
||||
|
||||
LDBG() << " Processing " << callsites->getKnownPredecessors().size()
|
||||
<< " known callsites";
|
||||
for (Operation *callsite : callsites->getKnownPredecessors()) {
|
||||
LDBG() << " Processing callsite: "
|
||||
<< OpWithFlags(callsite, OpPrintingFlags().skipRegions());
|
||||
const AbstractDenseLattice *after =
|
||||
getLatticeFor(point, getProgramPointAfter(callsite));
|
||||
LDBG() << " Lattice after callsite: " << *after;
|
||||
visitCallControlFlowTransfer(cast<CallOpInterface>(callsite),
|
||||
CallControlFlowAction::ExitCallee, *after,
|
||||
before);
|
||||
@@ -418,22 +587,29 @@ void AbstractDenseBackwardDataFlowAnalysis::visitBlock(Block *block) {
|
||||
// If this block is exiting from an operation with region-based control
|
||||
// flow, propagate the lattice back along the control flow edge.
|
||||
if (auto branch = dyn_cast<RegionBranchOpInterface>(block->getParentOp())) {
|
||||
LDBG() << " Exit block of region branch operation";
|
||||
visitRegionBranchOperation(point, branch, block->getParent(), before);
|
||||
return;
|
||||
}
|
||||
|
||||
// Cannot reason about successors of an exit block, set the pessimistic
|
||||
// fixpoint.
|
||||
LDBG() << " Cannot reason about successors, setting to exit state";
|
||||
return setToExitState(before);
|
||||
}
|
||||
|
||||
// Meet the state with the state before block's successors.
|
||||
LDBG() << " Meeting state from " << block->getSuccessors().size()
|
||||
<< " successors";
|
||||
for (Block *successor : block->getSuccessors()) {
|
||||
if (!getOrCreateFor<Executable>(point,
|
||||
getLatticeAnchor<CFGEdge>(block, successor))
|
||||
->isLive())
|
||||
->isLive()) {
|
||||
LDBG() << " Skipping non-executable edge to " << successor;
|
||||
continue;
|
||||
}
|
||||
|
||||
LDBG() << " Meeting state from successor " << successor;
|
||||
// Merge in the state from the successor: either the first operation, or the
|
||||
// block itself when empty.
|
||||
meet(before, *getLatticeFor(point, getProgramPointBefore(successor)));
|
||||
@@ -443,28 +619,39 @@ void AbstractDenseBackwardDataFlowAnalysis::visitBlock(Block *block) {
|
||||
void AbstractDenseBackwardDataFlowAnalysis::visitRegionBranchOperation(
|
||||
ProgramPoint *point, RegionBranchOpInterface branch,
|
||||
RegionBranchPoint branchPoint, AbstractDenseLattice *before) {
|
||||
LDBG() << "visitRegionBranchOperation (backward): "
|
||||
<< OpWithFlags(branch.getOperation(), OpPrintingFlags().skipRegions());
|
||||
LDBG() << " branchPoint: " << (branchPoint.isParent() ? "parent" : "region");
|
||||
LDBG() << " before state: " << *before;
|
||||
|
||||
// The successors of the operation may be either the first operation of the
|
||||
// entry block of each possible successor region, or the next operation when
|
||||
// the branch is a successor of itself.
|
||||
SmallVector<RegionSuccessor> successors;
|
||||
branch.getSuccessorRegions(branchPoint, successors);
|
||||
LDBG() << " Processing " << successors.size() << " successor regions";
|
||||
for (const RegionSuccessor &successor : successors) {
|
||||
const AbstractDenseLattice *after;
|
||||
if (successor.isParent() || successor.getSuccessor()->empty()) {
|
||||
LDBG() << " Successor is parent or empty region";
|
||||
after = getLatticeFor(point, getProgramPointAfter(branch));
|
||||
} else {
|
||||
Region *successorRegion = successor.getSuccessor();
|
||||
assert(!successorRegion->empty() && "unexpected empty successor region");
|
||||
Block *successorBlock = &successorRegion->front();
|
||||
LDBG() << " Successor region with "
|
||||
<< successorRegion->getBlocks().size() << " blocks";
|
||||
|
||||
if (!getOrCreateFor<Executable>(point,
|
||||
getProgramPointBefore(successorBlock))
|
||||
->isLive())
|
||||
->isLive()) {
|
||||
LDBG() << " Successor block not executable, skipping";
|
||||
continue;
|
||||
}
|
||||
|
||||
after = getLatticeFor(point, getProgramPointBefore(successorBlock));
|
||||
}
|
||||
LDBG() << " After state: " << *after;
|
||||
|
||||
visitRegionBranchControlFlowTransfer(branch, branchPoint, successor, *after,
|
||||
before);
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "TestDenseDataFlowAnalysis.h"
|
||||
#include "TestDialect.h"
|
||||
#include "TestOps.h"
|
||||
#include "mlir/Analysis/DataFlow/DenseAnalysis.h"
|
||||
#include "mlir/Analysis/DataFlow/Utils.h"
|
||||
@@ -23,12 +22,15 @@
|
||||
#include "mlir/Interfaces/SideEffectInterfaces.h"
|
||||
#include "mlir/Pass/Pass.h"
|
||||
#include "mlir/Support/TypeID.h"
|
||||
#include "llvm/Support/DebugLog.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace mlir;
|
||||
using namespace mlir::dataflow;
|
||||
using namespace mlir::dataflow::test;
|
||||
|
||||
#define DEBUG_TYPE "test-next-access"
|
||||
|
||||
namespace {
|
||||
|
||||
class NextAccess : public AbstractDenseLattice, public AccessLatticeBase {
|
||||
@@ -72,6 +74,7 @@ public:
|
||||
// means "we don't know what the next access is" rather than "there is no next
|
||||
// access". But it's unclear how to differentiate the two cases...
|
||||
void setToExitState(NextAccess *lattice) override {
|
||||
LDBG() << "setToExitState: setting lattice to unknown state";
|
||||
propagateIfChanged(lattice, lattice->setKnownToUnknown());
|
||||
}
|
||||
|
||||
@@ -87,16 +90,23 @@ public:
|
||||
LogicalResult NextAccessAnalysis::visitOperation(Operation *op,
|
||||
const NextAccess &after,
|
||||
NextAccess *before) {
|
||||
LDBG() << "visitOperation: "
|
||||
<< OpWithFlags(op, OpPrintingFlags().skipRegions());
|
||||
LDBG() << " after state: " << after;
|
||||
LDBG() << " before state: " << *before;
|
||||
|
||||
auto memory = dyn_cast<MemoryEffectOpInterface>(op);
|
||||
// If we can't reason about the memory effects, conservatively assume we can't
|
||||
// say anything about the next access.
|
||||
if (!memory) {
|
||||
LDBG() << " No memory effect interface, setting to exit state";
|
||||
setToExitState(before);
|
||||
return success();
|
||||
}
|
||||
|
||||
SmallVector<MemoryEffects::EffectInstance> effects;
|
||||
memory.getEffects(effects);
|
||||
LDBG() << " Found " << effects.size() << " memory effects";
|
||||
|
||||
// First, check if all underlying values are already known. Otherwise, avoid
|
||||
// propagating and stay in the "undefined" state to avoid incorrectly
|
||||
@@ -110,6 +120,7 @@ LogicalResult NextAccessAnalysis::visitOperation(Operation *op,
|
||||
// Effects with unspecified value are treated conservatively and we cannot
|
||||
// assume anything about the next access.
|
||||
if (!value) {
|
||||
LDBG() << " Effect has unspecified value, setting to exit state";
|
||||
setToExitState(before);
|
||||
return success();
|
||||
}
|
||||
@@ -124,38 +135,63 @@ LogicalResult NextAccessAnalysis::visitOperation(Operation *op,
|
||||
});
|
||||
|
||||
// If the underlying value is not known yet, don't propagate.
|
||||
if (!underlyingValue)
|
||||
if (!underlyingValue) {
|
||||
LDBG() << " Underlying value not known for " << value
|
||||
<< ", skipping propagation";
|
||||
return success();
|
||||
}
|
||||
|
||||
LDBG() << " Found underlying value " << *underlyingValue << " for "
|
||||
<< value;
|
||||
underlyingValues.push_back(*underlyingValue);
|
||||
}
|
||||
|
||||
// Update the state if all underlying values are known.
|
||||
LDBG() << " All underlying values known, updating state";
|
||||
ChangeResult result = before->meet(after);
|
||||
for (const auto &[effect, value] : llvm::zip(effects, underlyingValues)) {
|
||||
// If the underlying value is known to be unknown, set to fixpoint.
|
||||
if (!value) {
|
||||
LDBG() << " Underlying value is unknown, setting to exit state";
|
||||
setToExitState(before);
|
||||
return success();
|
||||
}
|
||||
|
||||
LDBG() << " Setting next access for value " << value << " to operation "
|
||||
<< OpWithFlags(op, OpPrintingFlags().skipRegions());
|
||||
result |= before->set(value, op);
|
||||
}
|
||||
LDBG() << " Final result: "
|
||||
<< (result == ChangeResult::Change ? "changed" : "no change");
|
||||
propagateIfChanged(before, result);
|
||||
return success();
|
||||
}
|
||||
|
||||
void NextAccessAnalysis::buildOperationEquivalentLatticeAnchor(Operation *op) {
|
||||
LDBG() << "buildOperationEquivalentLatticeAnchor: "
|
||||
<< OpWithFlags(op, OpPrintingFlags().skipRegions());
|
||||
if (isMemoryEffectFree(op)) {
|
||||
LDBG() << " Operation is memory effect free, unioning lattice anchors";
|
||||
unionLatticeAnchors<NextAccess>(getProgramPointBefore(op),
|
||||
getProgramPointAfter(op));
|
||||
} else {
|
||||
LDBG() << " Operation has memory effects, not unioning lattice anchors";
|
||||
}
|
||||
}
|
||||
|
||||
void NextAccessAnalysis::visitCallControlFlowTransfer(
|
||||
CallOpInterface call, CallControlFlowAction action, const NextAccess &after,
|
||||
NextAccess *before) {
|
||||
LDBG() << "visitCallControlFlowTransfer: "
|
||||
<< OpWithFlags(call.getOperation(), OpPrintingFlags().skipRegions());
|
||||
LDBG() << " action: "
|
||||
<< (action == CallControlFlowAction::ExternalCallee ? "ExternalCallee"
|
||||
: action == CallControlFlowAction::EnterCallee ? "EnterCallee"
|
||||
: "ExitCallee");
|
||||
LDBG() << " assumeFuncReads: " << assumeFuncReads;
|
||||
|
||||
if (action == CallControlFlowAction::ExternalCallee && assumeFuncReads) {
|
||||
LDBG() << " Handling external callee with assumed function reads";
|
||||
SmallVector<Value> underlyingValues;
|
||||
underlyingValues.reserve(call->getNumOperands());
|
||||
for (Value operand : call.getArgOperands()) {
|
||||
@@ -165,15 +201,26 @@ void NextAccessAnalysis::visitCallControlFlowTransfer(
|
||||
return getOrCreateFor<UnderlyingValueLattice>(
|
||||
getProgramPointBefore(call.getOperation()), value);
|
||||
});
|
||||
if (!underlyingValue)
|
||||
if (!underlyingValue) {
|
||||
LDBG() << " Underlying value not known for operand " << operand
|
||||
<< ", returning";
|
||||
return;
|
||||
}
|
||||
LDBG() << " Found underlying value " << *underlyingValue
|
||||
<< " for operand " << operand;
|
||||
underlyingValues.push_back(*underlyingValue);
|
||||
}
|
||||
|
||||
LDBG() << " Setting next access for " << underlyingValues.size()
|
||||
<< " operands";
|
||||
ChangeResult result = before->meet(after);
|
||||
for (Value operand : underlyingValues) {
|
||||
LDBG() << " Setting next access for operand " << operand << " to call "
|
||||
<< call;
|
||||
result |= before->set(operand, call);
|
||||
}
|
||||
LDBG() << " Call control flow result: "
|
||||
<< (result == ChangeResult::Change ? "changed" : "no change");
|
||||
return propagateIfChanged(before, result);
|
||||
}
|
||||
auto testCallAndStore =
|
||||
@@ -182,8 +229,10 @@ void NextAccessAnalysis::visitCallControlFlowTransfer(
|
||||
testCallAndStore.getStoreBeforeCall()) ||
|
||||
(action == CallControlFlowAction::ExitCallee &&
|
||||
!testCallAndStore.getStoreBeforeCall()))) {
|
||||
LDBG() << " Handling TestCallAndStoreOp with special logic";
|
||||
(void)visitOperation(call, after, before);
|
||||
} else {
|
||||
LDBG() << " Using default call control flow transfer logic";
|
||||
AbstractDenseBackwardDataFlowAnalysis::visitCallControlFlowTransfer(
|
||||
call, action, after, before);
|
||||
}
|
||||
@@ -192,6 +241,11 @@ void NextAccessAnalysis::visitCallControlFlowTransfer(
|
||||
void NextAccessAnalysis::visitRegionBranchControlFlowTransfer(
|
||||
RegionBranchOpInterface branch, RegionBranchPoint regionFrom,
|
||||
RegionBranchPoint regionTo, const NextAccess &after, NextAccess *before) {
|
||||
LDBG() << "visitRegionBranchControlFlowTransfer: "
|
||||
<< OpWithFlags(branch.getOperation(), OpPrintingFlags().skipRegions());
|
||||
LDBG() << " regionFrom: " << (regionFrom.isParent() ? "parent" : "region");
|
||||
LDBG() << " regionTo: " << (regionTo.isParent() ? "parent" : "region");
|
||||
|
||||
auto testStoreWithARegion =
|
||||
dyn_cast<::test::TestStoreWithARegion>(branch.getOperation());
|
||||
|
||||
@@ -199,9 +253,11 @@ void NextAccessAnalysis::visitRegionBranchControlFlowTransfer(
|
||||
((regionTo.isParent() && !testStoreWithARegion.getStoreBeforeRegion()) ||
|
||||
(regionFrom.isParent() &&
|
||||
testStoreWithARegion.getStoreBeforeRegion()))) {
|
||||
LDBG() << " Handling TestStoreWithARegion with special logic";
|
||||
(void)visitOperation(branch, static_cast<const NextAccess &>(after),
|
||||
static_cast<NextAccess *>(before));
|
||||
} else {
|
||||
LDBG() << " Using default region branch control flow transfer logic";
|
||||
propagateIfChanged(before, before->meet(after));
|
||||
}
|
||||
}
|
||||
@@ -278,6 +334,11 @@ struct TestNextAccessPass
|
||||
|
||||
void runOnOperation() override {
|
||||
Operation *op = getOperation();
|
||||
LDBG() << "runOnOperation: Starting test-next-access pass on "
|
||||
<< OpWithFlags(op, OpPrintingFlags().skipRegions());
|
||||
LDBG() << " interprocedural: " << interprocedural;
|
||||
LDBG() << " assumeFuncReads: " << assumeFuncReads;
|
||||
|
||||
SymbolTableCollection symbolTable;
|
||||
|
||||
auto config = DataFlowConfig().setInterprocedural(interprocedural);
|
||||
@@ -285,15 +346,20 @@ struct TestNextAccessPass
|
||||
loadBaselineAnalyses(solver);
|
||||
solver.load<NextAccessAnalysis>(symbolTable, assumeFuncReads);
|
||||
solver.load<UnderlyingValueAnalysis>();
|
||||
LDBG() << " Initializing and running dataflow solver";
|
||||
if (failed(solver.initializeAndRun(op))) {
|
||||
emitError(op->getLoc(), "dataflow solver failed");
|
||||
return signalPassFailure();
|
||||
}
|
||||
LDBG() << " Dataflow solver completed successfully";
|
||||
LDBG() << " Walking operations to set next access attributes";
|
||||
op->walk([&](Operation *op) {
|
||||
auto tag = op->getAttrOfType<StringAttr>(kTagAttrName);
|
||||
if (!tag)
|
||||
return;
|
||||
|
||||
LDBG() << " Processing tagged operation: "
|
||||
<< OpWithFlags(op, OpPrintingFlags().skipRegions());
|
||||
const NextAccess *nextAccess =
|
||||
solver.lookupState<NextAccess>(solver.getProgramPointAfter(op));
|
||||
op->setAttr(kNextAccessAttrName,
|
||||
|
||||
Reference in New Issue
Block a user