[mlir][Analysis] Add dependency declaration for dataflow analyses#193112
[mlir][Analysis] Add dependency declaration for dataflow analyses#193112Hardcode84 wants to merge 2 commits intollvm:mainfrom
Conversation
DataFlowAnalysis subclasses can now declare required sibling analyses via getDependentAnalyses(AnalysisDependencies &). The solver validates them by TypeID at initializeAndRun and emits a consolidated diagnostic listing every missing dep with its requester. Dependencies are not auto-loaded because load<T> accepts arbitrary constructor arguments the framework cannot synthesize; callers keep control of instantiation, the solver just enforces the contract. The four abstract driver bases (Sparse/Dense x Forward/Backward) declare DeadCodeAnalysis as a hard dependency, and additionally assert it in initialize() as a debug-only check against subclasses that override getDependentAnalyses without chaining to the base. Also: - New DataFlowSolver::lookupAnalysis<T>() (exact-TypeID match). - load<T>() asserts no duplicate load. - Consolidate the existing debugName (gated on LLVM_ENABLE_ABI_BREAKING_CHECKS) with the new analysisID/analysisName into always-present members populated by load() after construction. - Explicit TypeIDs (MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID) for analyses defined in anonymous namespaces. - Document the soft DeadCodeAnalysis <-> SparseConstantPropagation circular co-dependency in DeadCodeAnalysis.h and Utils.h. - Two lit tests covering single-requester and multi-requester missing-dep diagnostics. Made-with: Cursor
|
@llvm/pr-subscribers-flang-fir-hlfir @llvm/pr-subscribers-mlir-core Author: Ivan Butygin (Hardcode84) Changes
The four abstract driver bases (Sparse/Dense x Forward/Backward) declare DeadCodeAnalysis as a hard dependency, and additionally assert it in initialize() as a debug-only check against subclasses that override getDependentAnalyses without chaining to the base. Also:
Made-with: Cursor See also: https://discourse.llvm.org/t/mlir-dead-code-analysis/67568 and #192998 (comment) I'm planning to introduce a richer Patch is 29.53 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/193112.diff 17 Files Affected:
diff --git a/mlir/include/mlir/Analysis/DataFlow/DeadCodeAnalysis.h b/mlir/include/mlir/Analysis/DataFlow/DeadCodeAnalysis.h
index 3b2914cdd4c98..b05a323b2b3ec 100644
--- a/mlir/include/mlir/Analysis/DataFlow/DeadCodeAnalysis.h
+++ b/mlir/include/mlir/Analysis/DataFlow/DeadCodeAnalysis.h
@@ -173,6 +173,15 @@ class CFGEdge
/// for region entry blocks and region control-flow operations. For the
/// callgraph, this analysis determines the callsites and live returns of every
/// function.
+///
+/// This analysis reads `Lattice<ConstantValue>` state to refine branches on
+/// constant conditions. The producer of that lattice is *not* declared as a
+/// hard dependency: if no producer is loaded, operand constants remain
+/// uninitialized and `DeadCodeAnalysis` conservatively marks all successors
+/// live (still correct, just less precise). Pair with
+/// `SparseConstantPropagation` (see `loadBaselineAnalyses` in
+/// `mlir/Analysis/DataFlow/Utils.h`) or a custom `Lattice<ConstantValue>`
+/// producer for best precision.
class DeadCodeAnalysis : public DataFlowAnalysis {
public:
explicit DeadCodeAnalysis(DataFlowSolver &solver);
diff --git a/mlir/include/mlir/Analysis/DataFlow/DenseAnalysis.h b/mlir/include/mlir/Analysis/DataFlow/DenseAnalysis.h
index 82f86d06886b6..36a3b439dc2e1 100644
--- a/mlir/include/mlir/Analysis/DataFlow/DenseAnalysis.h
+++ b/mlir/include/mlir/Analysis/DataFlow/DenseAnalysis.h
@@ -73,6 +73,10 @@ class AbstractDenseForwardDataFlowAnalysis : public DataFlowAnalysis {
/// may modify the program state; that is, every operation and block.
LogicalResult initialize(Operation *top) override;
+ /// Dense forward analyses use `DeadCodeAnalysis` to skip dead blocks and
+ /// control-flow edges during propagation.
+ void getDependentAnalyses(AnalysisDependencies &deps) const override;
+
/// Initialize lattice anchor equivalence class from the provided top-level
/// operation.
///
@@ -367,6 +371,10 @@ class AbstractDenseBackwardDataFlowAnalysis : public DataFlowAnalysis {
/// may modify the program state; that is, every operation and block.
LogicalResult initialize(Operation *top) override;
+ /// Dense backward analyses use `DeadCodeAnalysis` to skip dead blocks and
+ /// control-flow edges during propagation.
+ void getDependentAnalyses(AnalysisDependencies &deps) const override;
+
/// Initialize lattice anchor equivalence class from the provided top-level
/// operation.
///
diff --git a/mlir/include/mlir/Analysis/DataFlow/SparseAnalysis.h b/mlir/include/mlir/Analysis/DataFlow/SparseAnalysis.h
index df50d8d193aeb..555efe8f802e9 100644
--- a/mlir/include/mlir/Analysis/DataFlow/SparseAnalysis.h
+++ b/mlir/include/mlir/Analysis/DataFlow/SparseAnalysis.h
@@ -193,6 +193,10 @@ class AbstractSparseForwardDataFlowAnalysis : public DataFlowAnalysis {
/// accordingly. Otherwise, the operation transfer function is invoked.
LogicalResult visit(ProgramPoint *point) override;
+ /// Sparse forward analyses use `DeadCodeAnalysis` to skip dead blocks and
+ /// control-flow edges during propagation.
+ void getDependentAnalyses(AnalysisDependencies &deps) const override;
+
protected:
explicit AbstractSparseForwardDataFlowAnalysis(DataFlowSolver &solver);
@@ -413,6 +417,10 @@ class AbstractSparseBackwardDataFlowAnalysis : public DataFlowAnalysis {
/// Otherwise, invokes the operation transfer function (`visitOperationImpl`).
LogicalResult visit(ProgramPoint *point) override;
+ /// Sparse backward analyses use `DeadCodeAnalysis` to skip dead blocks and
+ /// control-flow edges during propagation.
+ void getDependentAnalyses(AnalysisDependencies &deps) const override;
+
protected:
explicit AbstractSparseBackwardDataFlowAnalysis(
DataFlowSolver &solver, SymbolTableCollection &symbolTable);
diff --git a/mlir/include/mlir/Analysis/DataFlow/Utils.h b/mlir/include/mlir/Analysis/DataFlow/Utils.h
index e97f2f70f609c..43fd7810e2682 100644
--- a/mlir/include/mlir/Analysis/DataFlow/Utils.h
+++ b/mlir/include/mlir/Analysis/DataFlow/Utils.h
@@ -23,8 +23,15 @@ namespace dataflow {
/// Populates a DataFlowSolver with analyses that are required to ensure
/// user-defined analyses are run properly.
///
+/// `DeadCodeAnalysis` and `SparseConstantPropagation` have a circular
+/// co-dependency: SCP only propagates along live CFG edges (produced by DCA),
+/// and DCA refines branches using `Lattice<ConstantValue>` (produced by SCP).
+/// Only SCP's dep on DCA is declared (inherited from the sparse forward driver
+/// base); DCA works without SCP at reduced precision, so clients are free to
+/// substitute another `Lattice<ConstantValue>` producer.
+///
/// This helper is intended to be an interim fix until a more robust solution
-/// can be implemented in the DataFlow framework directly. Cf.
+/// can be implemented in the DataFlow framework directly. See
/// https://discourse.llvm.org/t/mlir-dead-code-analysis/67568
inline void loadBaselineAnalyses(DataFlowSolver &solver) {
solver.load<dataflow::DeadCodeAnalysis>();
@@ -34,4 +41,4 @@ inline void loadBaselineAnalyses(DataFlowSolver &solver) {
} // end namespace dataflow
} // end namespace mlir
-#endif // MLIR_ANALYSIS_DATAFLOW_INTEGERANGEANALYSIS_H
+#endif // MLIR_ANALYSIS_DATAFLOW_UTILS_H
diff --git a/mlir/include/mlir/Analysis/DataFlowFramework.h b/mlir/include/mlir/Analysis/DataFlowFramework.h
index 87ec01a918d90..0d766dffc77e9 100644
--- a/mlir/include/mlir/Analysis/DataFlowFramework.h
+++ b/mlir/include/mlir/Analysis/DataFlowFramework.h
@@ -28,6 +28,52 @@
namespace mlir {
+class DataFlowAnalysis;
+
+//===----------------------------------------------------------------------===//
+// AnalysisDependencies
+//===----------------------------------------------------------------------===//
+
+/// Helper used by `DataFlowAnalysis::getDependentAnalyses` to declare the
+/// analyses that must be loaded into the solver for an analysis to run
+/// correctly. Declared analyses are not auto-loaded by the solver; callers
+/// are expected to load them explicitly with the appropriate constructor
+/// arguments. `DataFlowSolver::initializeAndRun` fails with a diagnostic if
+/// any declared dependency is missing.
+///
+/// Dependencies are matched by `TypeID`. Analyses defined inside an
+/// anonymous namespace must therefore provide an explicit TypeID via
+/// `MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID` (see
+/// `mlir/Support/TypeID.h`).
+class AnalysisDependencies {
+public:
+ /// A declared dependency. `typeID` is used for matching; `name` is
+ /// purely informational and is used in diagnostics.
+ struct Dependency {
+ TypeID typeID;
+ StringRef name;
+ };
+
+ /// Declare that the enclosing analysis depends on `AnalysisT`. Repeated
+ /// declarations of the same dependency are deduplicated so validation
+ /// diagnostics do not carry redundant notes.
+ template <typename AnalysisT>
+ void insert() {
+ static_assert(std::is_base_of_v<DataFlowAnalysis, AnalysisT>,
+ "dependency must derive from DataFlowAnalysis");
+ TypeID id = TypeID::get<AnalysisT>();
+ for (const Dependency &dep : deps)
+ if (dep.typeID == id)
+ return;
+ deps.push_back({id, llvm::getTypeName<AnalysisT>()});
+ }
+
+ ArrayRef<Dependency> getDependencies() const { return deps; }
+
+private:
+ SmallVector<Dependency> deps;
+};
+
//===----------------------------------------------------------------------===//
// ChangeResult
//===----------------------------------------------------------------------===//
@@ -261,9 +307,6 @@ struct LatticeAnchor
Location getLoc() const;
};
-/// Forward declaration of the data-flow analysis class.
-class DataFlowAnalysis;
-
} // namespace mlir
template <>
@@ -331,8 +374,17 @@ class DataFlowSolver {
template <typename AnalysisT, typename... Args>
AnalysisT *load(Args &&...args);
+ /// Return the loaded analysis of type `AnalysisT`, or nullptr if no such
+ /// analysis is loaded. Matches are by exact TypeID: subclasses of
+ /// `AnalysisT` will not be returned when querying for `AnalysisT`.
+ template <typename AnalysisT>
+ AnalysisT *lookupAnalysis() const;
+
/// Initialize the children analyses starting from the provided top-level
/// operation and run the analysis until fixpoint.
+ ///
+ /// Fails with a diagnostic if any child analysis declares a dependency via
+ /// `DataFlowAnalysis::getDependentAnalyses` that has not been loaded.
LogicalResult initializeAndRun(Operation *top);
/// Lookup an analysis state for the given lattice anchor. Returns null if one
@@ -627,9 +679,29 @@ class DataFlowAnalysis {
///
/// This function will union lattice anchor to same equivalent class if the
/// analysis can determine the lattice content of lattice anchor is
- /// necessarily identical under the corrensponding lattice type.
+ /// necessarily identical under the corresponding lattice type.
virtual void initializeEquivalentLatticeAnchor(Operation *top) {}
+ /// Register the analyses that this analysis depends on. Each dependency
+ /// must be loaded into the solver before `initializeAndRun` is called.
+ ///
+ /// Dependencies are not auto-loaded, because `DataFlowSolver::load` accepts
+ /// arbitrary constructor arguments that the framework cannot synthesize.
+ /// Subclasses that override this method should call the parent's
+ /// `getDependentAnalyses` first to preserve inherited dependencies.
+ virtual void getDependentAnalyses(AnalysisDependencies &deps) const {}
+
+ /// Return the TypeID of this analysis. Valid only after
+ /// `DataFlowSolver::load<AnalysisT>` has returned; must not be read from
+ /// the analysis constructor body. Used by the solver to match declared
+ /// dependencies.
+ TypeID getTypeID() const { return analysisID; }
+
+ /// Return a printable name for the analysis. Valid only after
+ /// `DataFlowSolver::load<AnalysisT>` has returned; must not be read from
+ /// the analysis constructor body. Used for diagnostics and debug logging.
+ StringRef getName() const { return analysisName; }
+
protected:
/// Create a dependency between the given analysis state and lattice anchor
/// on this analysis.
@@ -696,28 +768,47 @@ class DataFlowAnalysis {
/// Return the configuration of the solver used for this analysis.
const DataFlowConfig &getSolverConfig() const { return solver.getConfig(); }
-#if LLVM_ENABLE_ABI_BREAKING_CHECKS
- /// When compiling with debugging, keep a name for the analyis.
- StringRef debugName;
-#endif // LLVM_ENABLE_ABI_BREAKING_CHECKS
+ /// Return the parent solver.
+ const DataFlowSolver &getSolver() const { return solver; }
private:
/// The parent data-flow solver.
DataFlowSolver &solver;
+ /// The TypeID of the concrete analysis type. Set by `DataFlowSolver::load`,
+ /// used to match declared dependencies.
+ TypeID analysisID;
+
+ /// Printable name of the analysis. Set by `DataFlowSolver::load`. Used for
+ /// diagnostics when reporting missing dependencies and for debug logging.
+ StringRef analysisName;
+
/// Allow the data-flow solver to access the internals of this class.
friend class DataFlowSolver;
};
template <typename AnalysisT, typename... Args>
AnalysisT *DataFlowSolver::load(Args &&...args) {
+ assert(lookupAnalysis<AnalysisT>() == nullptr &&
+ "analysis of this type already loaded into the solver");
childAnalyses.emplace_back(new AnalysisT(*this, std::forward<Args>(args)...));
-#if LLVM_ENABLE_ABI_BREAKING_CHECKS
- childAnalyses.back()->debugName = llvm::getTypeName<AnalysisT>();
-#endif // LLVM_ENABLE_ABI_BREAKING_CHECKS
+ DataFlowAnalysis &analysis = *childAnalyses.back();
+ analysis.analysisID = TypeID::get<AnalysisT>();
+ analysis.analysisName = llvm::getTypeName<AnalysisT>();
return static_cast<AnalysisT *>(childAnalyses.back().get());
}
+template <typename AnalysisT>
+AnalysisT *DataFlowSolver::lookupAnalysis() const {
+ // Linear search over `childAnalyses`. Solvers typically hold a handful of
+ // analyses, so a hash map would cost more than it saves.
+ TypeID id = TypeID::get<AnalysisT>();
+ for (const std::unique_ptr<DataFlowAnalysis> &analysis : childAnalyses)
+ if (analysis->getTypeID() == id)
+ return static_cast<AnalysisT *>(analysis.get());
+ return nullptr;
+}
+
template <typename StateT>
LatticeAnchor
DataFlowSolver::getLeaderAnchorOrSelf(LatticeAnchor latticeAnchor) const {
diff --git a/mlir/lib/Analysis/DataFlow/DenseAnalysis.cpp b/mlir/lib/Analysis/DataFlow/DenseAnalysis.cpp
index 22bc0b32a9bd1..37d7ce76e47ce 100644
--- a/mlir/lib/Analysis/DataFlow/DenseAnalysis.cpp
+++ b/mlir/lib/Analysis/DataFlow/DenseAnalysis.cpp
@@ -30,6 +30,11 @@ using namespace mlir::dataflow;
// AbstractDenseForwardDataFlowAnalysis
//===----------------------------------------------------------------------===//
+void AbstractDenseForwardDataFlowAnalysis::getDependentAnalyses(
+ AnalysisDependencies &deps) const {
+ deps.insert<DeadCodeAnalysis>();
+}
+
void AbstractDenseForwardDataFlowAnalysis::initializeEquivalentLatticeAnchor(
Operation *top) {
LDBG() << "initializeEquivalentLatticeAnchor: "
@@ -48,6 +53,8 @@ void AbstractDenseForwardDataFlowAnalysis::initializeEquivalentLatticeAnchor(
}
LogicalResult AbstractDenseForwardDataFlowAnalysis::initialize(Operation *top) {
+ assert(getSolver().lookupAnalysis<DeadCodeAnalysis>() &&
+ "DeadCodeAnalysis must be loaded alongside a dense forward analysis");
LDBG() << "initialize (forward): "
<< OpWithFlags(top, OpPrintingFlags().skipRegions());
// Visit every operation and block.
@@ -356,6 +363,11 @@ void AbstractDenseForwardDataFlowAnalysis::visitRegionBranchOperation(
// AbstractDenseBackwardDataFlowAnalysis
//===----------------------------------------------------------------------===//
+void AbstractDenseBackwardDataFlowAnalysis::getDependentAnalyses(
+ AnalysisDependencies &deps) const {
+ deps.insert<DeadCodeAnalysis>();
+}
+
void AbstractDenseBackwardDataFlowAnalysis::initializeEquivalentLatticeAnchor(
Operation *top) {
LDBG() << "initializeEquivalentLatticeAnchor (backward): "
@@ -375,6 +387,8 @@ void AbstractDenseBackwardDataFlowAnalysis::initializeEquivalentLatticeAnchor(
LogicalResult
AbstractDenseBackwardDataFlowAnalysis::initialize(Operation *top) {
+ assert(getSolver().lookupAnalysis<DeadCodeAnalysis>() &&
+ "DeadCodeAnalysis must be loaded alongside a dense backward analysis");
LDBG() << "initialize (backward): "
<< OpWithFlags(top, OpPrintingFlags().skipRegions());
// Visit every operation and block.
diff --git a/mlir/lib/Analysis/DataFlow/SparseAnalysis.cpp b/mlir/lib/Analysis/DataFlow/SparseAnalysis.cpp
index 90f2a588d1ca4..30f0a9c739533 100644
--- a/mlir/lib/Analysis/DataFlow/SparseAnalysis.cpp
+++ b/mlir/lib/Analysis/DataFlow/SparseAnalysis.cpp
@@ -51,8 +51,21 @@ AbstractSparseForwardDataFlowAnalysis::AbstractSparseForwardDataFlowAnalysis(
registerAnchorKind<CFGEdge>();
}
+void AbstractSparseForwardDataFlowAnalysis::getDependentAnalyses(
+ AnalysisDependencies &deps) const {
+ deps.insert<DeadCodeAnalysis>();
+}
+
LogicalResult
AbstractSparseForwardDataFlowAnalysis::initialize(Operation *top) {
+ // Sparse forward analyses require `DeadCodeAnalysis` to be loaded. This is
+ // normally enforced by the solver's dependency validator, but a concrete
+ // subclass can silently drop the base's declared dependency by overriding
+ // `getDependentAnalyses` without calling the parent. Assert here as a
+ // second line of defense.
+ assert(getSolver().lookupAnalysis<DeadCodeAnalysis>() &&
+ "DeadCodeAnalysis must be loaded alongside a sparse forward analysis");
+
// Mark the entry block arguments as having reached their pessimistic
// fixpoints.
for (Region ®ion : top->getRegions()) {
@@ -371,8 +384,16 @@ AbstractSparseBackwardDataFlowAnalysis::AbstractSparseBackwardDataFlowAnalysis(
registerAnchorKind<CFGEdge>();
}
+void AbstractSparseBackwardDataFlowAnalysis::getDependentAnalyses(
+ AnalysisDependencies &deps) const {
+ deps.insert<DeadCodeAnalysis>();
+}
+
LogicalResult
AbstractSparseBackwardDataFlowAnalysis::initialize(Operation *top) {
+ assert(getSolver().lookupAnalysis<DeadCodeAnalysis>() &&
+ "DeadCodeAnalysis must be loaded alongside a sparse backward "
+ "analysis");
return initializeRecursively(top);
}
diff --git a/mlir/lib/Analysis/DataFlowFramework.cpp b/mlir/lib/Analysis/DataFlowFramework.cpp
index 258bcf312afc5..a1a97097c36d9 100644
--- a/mlir/lib/Analysis/DataFlowFramework.cpp
+++ b/mlir/lib/Analysis/DataFlowFramework.cpp
@@ -7,10 +7,12 @@
//===----------------------------------------------------------------------===//
#include "mlir/Analysis/DataFlowFramework.h"
+#include "mlir/IR/Diagnostics.h"
#include "mlir/IR/Location.h"
#include "mlir/IR/Operation.h"
#include "mlir/IR/SymbolTable.h"
#include "mlir/IR/Value.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/iterator.h"
#include "llvm/Config/abi-breaking.h"
@@ -109,7 +111,48 @@ Location LatticeAnchor::getLoc() const {
// DataFlowSolver
//===----------------------------------------------------------------------===//
+/// Emit a diagnostic listing all analyses whose declared dependencies are not
+/// loaded in the solver. Returns failure if any dependency is missing.
+static LogicalResult
+validateDependencies(Operation *top,
+ ArrayRef<std::unique_ptr<DataFlowAnalysis>> analyses) {
+ llvm::SmallDenseSet<TypeID, 8> loaded;
+ for (const std::unique_ptr<DataFlowAnalysis> &analysis : analyses)
+ loaded.insert(analysis->getTypeID());
+
+ struct Missing {
+ StringRef requester;
+ AnalysisDependencies::Dependency dep;
+ };
+ SmallVector<Missing> missing;
+
+ for (const std::unique_ptr<DataFlowAnalysis> &analysis : analyses) {
+ AnalysisDependencies deps;
+ analysis->getDependentAnalyses(deps);
+ for (const AnalysisDependencies::Dependency &dep : deps.getDependencies()) {
+ if (!loaded.contains(dep.typeID))
+ missing.push_back({analysis->getName(), dep});
+ }
+ }
+
+ if (missing.empty())
+ return success();
+
+ InFlightDiagnostic err =
+ emitError(top->getLoc(),
+ "DataFlowSolver: missing required analyses; load each missing "
+ "analysis via `solver.load<T>()` before `initializeAndRun`");
+ for (const Missing &m : missing) {
+ err.attachNote() << "analysis '" << m.requester << "' requires '"
+ << m.dep.name << "' (not loaded)";
+ }
+ return err;
+}
+
LogicalResult DataFlowSolver::initializeAndRun(Operation *top) {
+ if (failed(validateDependencies(top, childAnalyses)))
+ return failure();
+
// Enable enqueue to the worklist.
isRunning = true;
llvm::scope_exit guard([&]() { isRunning = false; });
@@ -127,7 +170,7 @@ LogicalResult DataFlowSolver::initializeAndRun(Operation *top) {
// Initialize the analyses.
for (DataFlowAnalysis &analysis : llvm::make_pointee_range(childAnalyses)) {
- DATAFLOW_DEBUG(LDBG() << "Priming analysis: " << analysis.debugName);
+ DATAFLOW_DEBUG(LDBG() << "Priming analysis: " << analysis.getName());
if (failed(analysis.initialize(top)))
return failure();
}
@@ -139,7 +182,7 @@ LogicalResult DataFlowSolver::initializeAndRun(Operation *top) {
auto [point, analysis] = worklist.front();
worklist.pop();
- DATAFLOW_DEBUG(LDBG() << "Invoking '" << analysis->debugName
+ DATAFLOW_DEBUG(LDBG() << "Invoking '" << analysis->getName()
<< "' on: " << *point);
if (failed(analysis->visit(point)))
return failure();
diff --git a/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp b/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
index 686cb20e1976e..776b31efc167c 100644
--- a/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
+++ b/mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp
@@ -390,6 +390,8 @@ class LayoutInfoPropagation
bool hasParamsOfLayoutKind(xegpu::DistributeLayoutAttr anchorLayout);
public:
+ MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(LayoutInfoPropagation)
+
LayoutInfoPropagation(DataFlowSolver &solver,
SymbolTableCollection &symbolTable,
xegpu::LayoutKind lay...
[truncated]
|
DataFlowAnalysissubclasses can now declare required sibling analyses viagetDependentAnalyses(AnalysisDependencies &). The solver validates them by TypeID atinitializeAndRunand emits a consolidated diagnostic listing every missing dep with its requester. Dependencies are not auto-loaded because load accepts arbitrary constructor arguments the framework cannot synthesize; callers keep control of instantiation, the solver just enforces the contract.The four abstract driver bases (Sparse/Dense x Forward/Backward) declare DeadCodeAnalysis as a hard dependency, and additionally assert it in initialize() as a debug-only check against subclasses that override getDependentAnalyses without chaining to the base.
Also:
Made-with: Cursor
See also: https://discourse.llvm.org/t/mlir-dead-code-analysis/67568 and #192998 (comment)
I'm planning to introduce a richer
getDependentAnalysesAPI in follow-up PRs, which allow for more precise control between the dependent analyses (run concurrent vs run after all dependencies converged).