llvm-project/mlir/lib/Analysis/DataFlowFramework.cpp
donald chen ccbbb1722f
[mlir] [dataflow] : Improve the time and space footprint of data flow. (#135325)
MLIR's data flow analysis (especially dense data flow analysis)
constructs a lattice at every lattice anchor (which, for dense data
flow, means every program point). As the program grows larger, the time
and space complexity can become unmanageable.

However, in many programs, the lattice values at numerous lattice
anchors are actually identical. We can leverage this observation to
improve the complexity of data flow analysis. This patch introducing
equivalence lattice anchor to group lattice anchors that must contains
identical lattice on certain state to improve the time and space
footprint of data flow.
2025-04-15 16:20:37 +08:00

170 lines
5.7 KiB
C++

//===- DataFlowFramework.cpp - A generic framework for 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/DataFlowFramework.h"
#include "mlir/IR/Location.h"
#include "mlir/IR/Operation.h"
#include "mlir/IR/Value.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/iterator.h"
#include "llvm/Config/abi-breaking.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#define DEBUG_TYPE "dataflow"
#if LLVM_ENABLE_ABI_BREAKING_CHECKS
#define DATAFLOW_DEBUG(X) LLVM_DEBUG(X)
#else
#define DATAFLOW_DEBUG(X)
#endif // LLVM_ENABLE_ABI_BREAKING_CHECKS
using namespace mlir;
//===----------------------------------------------------------------------===//
// GenericLatticeAnchor
//===----------------------------------------------------------------------===//
GenericLatticeAnchor::~GenericLatticeAnchor() = default;
//===----------------------------------------------------------------------===//
// AnalysisState
//===----------------------------------------------------------------------===//
AnalysisState::~AnalysisState() = default;
void AnalysisState::addDependency(ProgramPoint *dependent,
DataFlowAnalysis *analysis) {
auto inserted = dependents.insert({dependent, analysis});
(void)inserted;
DATAFLOW_DEBUG({
if (inserted) {
llvm::dbgs() << "Creating dependency between " << debugName << " of "
<< anchor << "\nand " << debugName << " on " << dependent
<< "\n";
}
});
}
void AnalysisState::dump() const { print(llvm::errs()); }
//===----------------------------------------------------------------------===//
// ProgramPoint
//===----------------------------------------------------------------------===//
void ProgramPoint::print(raw_ostream &os) const {
if (isNull()) {
os << "<NULL POINT>";
return;
}
if (!isBlockStart()) {
os << "<after operation>:";
return getPrevOp()->print(os, OpPrintingFlags().skipRegions());
}
os << "<before operation>:";
return getNextOp()->print(os, OpPrintingFlags().skipRegions());
}
//===----------------------------------------------------------------------===//
// LatticeAnchor
//===----------------------------------------------------------------------===//
void LatticeAnchor::print(raw_ostream &os) const {
if (isNull()) {
os << "<NULL POINT>";
return;
}
if (auto *LatticeAnchor = llvm::dyn_cast<GenericLatticeAnchor *>(*this))
return LatticeAnchor->print(os);
if (auto value = llvm::dyn_cast<Value>(*this)) {
return value.print(os, OpPrintingFlags().skipRegions());
}
return llvm::cast<ProgramPoint *>(*this)->print(os);
}
Location LatticeAnchor::getLoc() const {
if (auto *LatticeAnchor = llvm::dyn_cast<GenericLatticeAnchor *>(*this))
return LatticeAnchor->getLoc();
if (auto value = llvm::dyn_cast<Value>(*this))
return value.getLoc();
ProgramPoint *pp = llvm::cast<ProgramPoint *>(*this);
if (!pp->isBlockStart())
return pp->getPrevOp()->getLoc();
return pp->getBlock()->getParent()->getLoc();
}
//===----------------------------------------------------------------------===//
// DataFlowSolver
//===----------------------------------------------------------------------===//
LogicalResult DataFlowSolver::initializeAndRun(Operation *top) {
// Enable enqueue to the worklist.
isRunning = true;
auto guard = llvm::make_scope_exit([&]() { isRunning = false; });
// Initialize equivalent lattice anchors.
for (DataFlowAnalysis &analysis : llvm::make_pointee_range(childAnalyses)) {
analysis.initializeEquivalentLatticeAnchor(top);
}
// Initialize the analyses.
for (DataFlowAnalysis &analysis : llvm::make_pointee_range(childAnalyses)) {
DATAFLOW_DEBUG(llvm::dbgs()
<< "Priming analysis: " << analysis.debugName << "\n");
if (failed(analysis.initialize(top)))
return failure();
}
// Run the analysis until fixpoint.
// Iterate until all states are in some initialized state and the worklist
// is exhausted.
while (!worklist.empty()) {
auto [point, analysis] = worklist.front();
worklist.pop();
DATAFLOW_DEBUG(llvm::dbgs() << "Invoking '" << analysis->debugName
<< "' on: " << point << "\n");
if (failed(analysis->visit(point)))
return failure();
}
return success();
}
void DataFlowSolver::propagateIfChanged(AnalysisState *state,
ChangeResult changed) {
assert(isRunning &&
"DataFlowSolver is not running, should not use propagateIfChanged");
if (changed == ChangeResult::Change) {
DATAFLOW_DEBUG(llvm::dbgs() << "Propagating update to " << state->debugName
<< " of " << state->anchor << "\n"
<< "Value: " << *state << "\n");
state->onUpdate(this);
}
}
//===----------------------------------------------------------------------===//
// DataFlowAnalysis
//===----------------------------------------------------------------------===//
DataFlowAnalysis::~DataFlowAnalysis() = default;
DataFlowAnalysis::DataFlowAnalysis(DataFlowSolver &solver) : solver(solver) {}
void DataFlowAnalysis::addDependency(AnalysisState *state,
ProgramPoint *point) {
state->addDependency(point, this);
}
void DataFlowAnalysis::propagateIfChanged(AnalysisState *state,
ChangeResult changed) {
solver.propagateIfChanged(state, changed);
}