Skip to content

[mlir][Analysis] Add dependency declaration for dataflow analyses#193112

Open
Hardcode84 wants to merge 2 commits intollvm:mainfrom
Hardcode84:dataflow-deps
Open

[mlir][Analysis] Add dependency declaration for dataflow analyses#193112
Hardcode84 wants to merge 2 commits intollvm:mainfrom
Hardcode84:dataflow-deps

Conversation

@Hardcode84
Copy link
Copy Markdown
Contributor

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 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() (exact-TypeID match).
  • load() 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

See also: https://discourse.llvm.org/t/mlir-dead-code-analysis/67568 and #192998 (comment)

I'm planning to introduce a richer getDependentAnalyses API in follow-up PRs, which allow for more precise control between the dependent analyses (run concurrent vs run after all dependencies converged).

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
@Hardcode84 Hardcode84 marked this pull request as ready for review April 20, 2026 23:15
@llvmbot llvmbot added mlir:core MLIR Core Infrastructure mlir:gpu mlir labels Apr 20, 2026
@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Apr 20, 2026

@llvm/pr-subscribers-flang-fir-hlfir
@llvm/pr-subscribers-mlir-gpu
@llvm/pr-subscribers-mlir

@llvm/pr-subscribers-mlir-core

Author: Ivan Butygin (Hardcode84)

Changes

DataFlowAnalysis subclasses can now declare required sibling analyses via getDependentAnalyses(AnalysisDependencies &amp;). 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

See also: https://discourse.llvm.org/t/mlir-dead-code-analysis/67568 and #192998 (comment)

I'm planning to introduce a richer getDependentAnalyses API in follow-up PRs, which allow for more precise control between the dependent analyses (run concurrent vs run after all dependencies converged).


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:

  • (modified) mlir/include/mlir/Analysis/DataFlow/DeadCodeAnalysis.h (+9)
  • (modified) mlir/include/mlir/Analysis/DataFlow/DenseAnalysis.h (+8)
  • (modified) mlir/include/mlir/Analysis/DataFlow/SparseAnalysis.h (+8)
  • (modified) mlir/include/mlir/Analysis/DataFlow/Utils.h (+9-2)
  • (modified) mlir/include/mlir/Analysis/DataFlowFramework.h (+102-11)
  • (modified) mlir/lib/Analysis/DataFlow/DenseAnalysis.cpp (+14)
  • (modified) mlir/lib/Analysis/DataFlow/SparseAnalysis.cpp (+21)
  • (modified) mlir/lib/Analysis/DataFlowFramework.cpp (+45-2)
  • (modified) mlir/lib/Dialect/XeGPU/Transforms/XeGPUPropagateLayout.cpp (+2)
  • (added) mlir/test/Analysis/DataFlow/test-analysis-deps-multi-requester.mlir (+11)
  • (added) mlir/test/Analysis/DataFlow/test-analysis-deps.mlir (+12)
  • (modified) mlir/test/lib/Analysis/DataFlow/TestDeadCodeAnalysis.cpp (+2)
  • (modified) mlir/test/lib/Analysis/DataFlow/TestDenseBackwardDataFlowAnalysis.cpp (+2)
  • (modified) mlir/test/lib/Analysis/DataFlow/TestDenseForwardDataFlowAnalysis.cpp (+2)
  • (modified) mlir/test/lib/Analysis/DataFlow/TestSparseBackwardDataFlowAnalysis.cpp (+3-1)
  • (modified) mlir/test/lib/Analysis/TestDataFlowFramework.cpp (+77)
  • (modified) mlir/tools/mlir-opt/mlir-opt.cpp (+4-2)
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 &region : 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]

@Hardcode84 Hardcode84 requested a review from Max191 April 20, 2026 23:15
@llvmbot llvmbot added flang Flang issues not falling into any other category flang:fir-hlfir labels Apr 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

flang:fir-hlfir flang Flang issues not falling into any other category mlir:core MLIR Core Infrastructure mlir:gpu mlir

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants