[Mlir-commits] [mlir] [MLIR] Add more logging to DenseAnalysis/DeaDCodeAnalysis/TestDenseBackwardDataFlowAnalysis (NFC) (PR #161503)

Mehdi Amini llvmlistbot at llvm.org
Wed Oct 1 03:53:50 PDT 2025


https://github.com/joker-eph created https://github.com/llvm/llvm-project/pull/161503

Just some more debugging help here.

>From eaeef096aff76071ed881b501ac515bfa45fd0f1 Mon Sep 17 00:00:00 2001
From: Mehdi Amini <joker.eph at gmail.com>
Date: Wed, 1 Oct 2025 03:52:30 -0700
Subject: [PATCH] [MLIR] Add more logging to
 DenseAnalysis/DeaDCodeAnalysis/TestDenseBackwardDataFlowAnalysis (NFC)

Just some more debugging help here.
---
 .../Analysis/DataFlow/DeadCodeAnalysis.cpp    |   4 +-
 mlir/lib/Analysis/DataFlow/DenseAnalysis.cpp  | 233 ++++++++++++++++--
 .../TestDenseBackwardDataFlowAnalysis.cpp     |  72 +++++-
 3 files changed, 281 insertions(+), 28 deletions(-)

diff --git a/mlir/lib/Analysis/DataFlow/DeadCodeAnalysis.cpp b/mlir/lib/Analysis/DataFlow/DeadCodeAnalysis.cpp
index de1ed39ed4fdb..377f7ebe06750 100644
--- a/mlir/lib/Analysis/DataFlow/DeadCodeAnalysis.cpp
+++ b/mlir/lib/Analysis/DataFlow/DeadCodeAnalysis.cpp
@@ -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;
   }
 }
 
diff --git a/mlir/lib/Analysis/DataFlow/DenseAnalysis.cpp b/mlir/lib/Analysis/DataFlow/DenseAnalysis.cpp
index b51465bc31ec3..daa3db55b2852 100644
--- a/mlir/lib/Analysis/DataFlow/DenseAnalysis.cpp
+++ b/mlir/lib/Analysis/DataFlow/DenseAnalysis.cpp
@@ -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 &region : 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 &region : 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);
diff --git a/mlir/test/lib/Analysis/DataFlow/TestDenseBackwardDataFlowAnalysis.cpp b/mlir/test/lib/Analysis/DataFlow/TestDenseBackwardDataFlowAnalysis.cpp
index d57b41c41de64..eb0d9801e7d3f 100644
--- a/mlir/test/lib/Analysis/DataFlow/TestDenseBackwardDataFlowAnalysis.cpp
+++ b/mlir/test/lib/Analysis/DataFlow/TestDenseBackwardDataFlowAnalysis.cpp
@@ -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 @@ class NextAccessAnalysis : public DenseBackwardDataFlowAnalysis<NextAccess> {
   // 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 @@ class NextAccessAnalysis : public DenseBackwardDataFlowAnalysis<NextAccess> {
 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,



More information about the Mlir-commits mailing list