[Mlir-commits] [mlir] [mlir][dataflow] Fix LivenessAnalysis/RemoveDeadValues handling of loop induction variables (PR #161117)

lonely eagle llvmlistbot at llvm.org
Tue Oct 21 03:28:29 PDT 2025


https://github.com/linuxlonelyeagle updated https://github.com/llvm/llvm-project/pull/161117

>From 55321b8a9389d7ebf236819c8cfcd77df479aeef Mon Sep 17 00:00:00 2001
From: linuxlonelyeagle <2020382038 at qq.com>
Date: Mon, 29 Sep 2025 02:42:42 +0000
Subject: [PATCH 1/6] fix liveness-analysis in the loop ivs case.

---
 .../Analysis/DataFlow/LivenessAnalysis.cpp    | 23 +++++++++++++++----
 mlir/test/Transforms/remove-dead-values.mlir  | 13 +++++++++++
 2 files changed, 32 insertions(+), 4 deletions(-)

diff --git a/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp b/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp
index d705d8d4c7819..f540870113e3f 100644
--- a/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp
+++ b/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp
@@ -17,6 +17,7 @@
 #include <mlir/IR/Operation.h>
 #include <mlir/IR/Value.h>
 #include <mlir/Interfaces/CallInterfaces.h>
+#include <mlir/Interfaces/LoopLikeInterface.h>
 #include <mlir/Interfaces/SideEffectInterfaces.h>
 #include <mlir/Support/LLVM.h>
 
@@ -309,15 +310,29 @@ RunLivenessAnalysis::RunLivenessAnalysis(Operation *op) {
              << " has no liveness info (unreachable), mark dead";
       solver.getOrCreateState<Liveness>(result.value());
     }
+    SmallVector<Value> mustLiveValues;
+    if (auto loopOp = dyn_cast<LoopLikeOpInterface>(op)) {
+      std::optional<SmallVector<Value>> ivs = loopOp.getLoopInductionVars();
+      if (ivs.has_value())
+        mustLiveValues.append(*ivs);
+    }
     for (auto &region : op->getRegions()) {
       for (auto &block : region) {
         for (auto blockArg : llvm::enumerate(block.getArguments())) {
           if (getLiveness(blockArg.value()))
             continue;
-          LDBG() << "Block argument: " << blockArg.index() << " of "
-                 << OpWithFlags(op, OpPrintingFlags().skipRegions())
-                 << " has no liveness info, mark dead";
-          solver.getOrCreateState<Liveness>(blockArg.value());
+          if (llvm::find(mustLiveValues, blockArg.value())) {
+            LDBG() << "Block argument: " << blockArg.index() << " of "
+                   << OpWithFlags(op, OpPrintingFlags().skipRegions())
+                   << " is must value, mark live";
+            (void)solver.getOrCreateState<Liveness>(blockArg.value())
+                ->markLive();
+          } else {
+            LDBG() << "Block argument: " << blockArg.index() << " of "
+                   << OpWithFlags(op, OpPrintingFlags().skipRegions())
+                   << " has no liveness info, mark dead";
+            solver.getOrCreateState<Liveness>(blockArg.value());
+          }
         }
       }
     }
diff --git a/mlir/test/Transforms/remove-dead-values.mlir b/mlir/test/Transforms/remove-dead-values.mlir
index 56449469dc29f..979851bc3162d 100644
--- a/mlir/test/Transforms/remove-dead-values.mlir
+++ b/mlir/test/Transforms/remove-dead-values.mlir
@@ -649,3 +649,16 @@ func.func @callee(%arg0: index, %arg1: index, %arg2: index) -> index {
   %res = call @mutl_parameter(%arg0, %arg1, %arg2) : (index, index, index) -> (index)
   return %res : index
 }
+
+// -----
+
+// This test verifies that the induction variables in loops are not deleted.
+
+// CHECK-LABEL: func @dead_value_loop_ivs
+func.func @dead_value_loop_ivs(%lb: index, %ub: index, %step: index, %b : i1) -> i1 {
+  %loop_ret = scf.for %iv = %lb to %ub step %step iter_args(%iter = %b) -> (i1) {
+    cf.assert %b, "loop not dead"
+    scf.yield %b : i1
+  }
+  return %loop_ret : i1
+}

>From 1bf4f12940d8e2fbdb925393c97e734088ca7946 Mon Sep 17 00:00:00 2001
From: linuxlonelyeagle <2020382038 at qq.com>
Date: Thu, 2 Oct 2025 02:56:13 +0000
Subject: [PATCH 2/6] add LoopLikeOpInterface visit logic in the visit
 function.

---
 .../Analysis/DataFlow/LivenessAnalysis.cpp    | 35 +++++++++----------
 1 file changed, 17 insertions(+), 18 deletions(-)

diff --git a/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp b/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp
index f540870113e3f..3611d07695bf9 100644
--- a/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp
+++ b/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp
@@ -196,6 +196,18 @@ void LivenessAnalysis::visitBranchOperand(OpOperand &operand) {
           break;
         }
       }
+
+      // If the parentOp is live and it implements the LoopLiveOpInterface, then
+      // set its IV as live.
+      if (mayLive && isa<LoopLikeOpInterface>(parentOp)) {
+        auto loopOp = cast<LoopLikeOpInterface>(parentOp);
+        std::optional<SmallVector<Value>> ivs = loopOp.getLoopInductionVars();
+        if (ivs.has_value()) {
+          for (auto iv : *ivs) {
+            getLatticeElement(iv)->markLive();
+          }
+        }
+      }
     } else {
       // When the op is a `RegionBranchTerminatorOpInterface`, like an
       // `scf.condition` op or return-like, like an `scf.yield` op, its branch
@@ -310,29 +322,16 @@ RunLivenessAnalysis::RunLivenessAnalysis(Operation *op) {
              << " has no liveness info (unreachable), mark dead";
       solver.getOrCreateState<Liveness>(result.value());
     }
-    SmallVector<Value> mustLiveValues;
-    if (auto loopOp = dyn_cast<LoopLikeOpInterface>(op)) {
-      std::optional<SmallVector<Value>> ivs = loopOp.getLoopInductionVars();
-      if (ivs.has_value())
-        mustLiveValues.append(*ivs);
-    }
+
     for (auto &region : op->getRegions()) {
       for (auto &block : region) {
         for (auto blockArg : llvm::enumerate(block.getArguments())) {
           if (getLiveness(blockArg.value()))
             continue;
-          if (llvm::find(mustLiveValues, blockArg.value())) {
-            LDBG() << "Block argument: " << blockArg.index() << " of "
-                   << OpWithFlags(op, OpPrintingFlags().skipRegions())
-                   << " is must value, mark live";
-            (void)solver.getOrCreateState<Liveness>(blockArg.value())
-                ->markLive();
-          } else {
-            LDBG() << "Block argument: " << blockArg.index() << " of "
-                   << OpWithFlags(op, OpPrintingFlags().skipRegions())
-                   << " has no liveness info, mark dead";
-            solver.getOrCreateState<Liveness>(blockArg.value());
-          }
+          LDBG() << "Block argument: " << blockArg.index() << " of "
+                 << OpWithFlags(op, OpPrintingFlags().skipRegions())
+                 << " has no liveness info, mark dead";
+          solver.getOrCreateState<Liveness>(blockArg.value());
         }
       }
     }

>From e1cfe4ddc616e6cc45408a61aa3dc2f2234f36fe Mon Sep 17 00:00:00 2001
From: linuxlonelyeagle <2020382038 at qq.com>
Date: Thu, 2 Oct 2025 16:35:01 +0000
Subject: [PATCH 3/6] use RegionBranchOpInterface fix the bug.

---
 .../Analysis/DataFlow/LivenessAnalysis.cpp    | 35 ++++++++++++-------
 mlir/test/Transforms/remove-dead-values.mlir  | 16 +++++++--
 2 files changed, 36 insertions(+), 15 deletions(-)

diff --git a/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp b/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp
index 3611d07695bf9..b66072fd8c6ad 100644
--- a/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp
+++ b/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp
@@ -17,7 +17,6 @@
 #include <mlir/IR/Operation.h>
 #include <mlir/IR/Value.h>
 #include <mlir/Interfaces/CallInterfaces.h>
-#include <mlir/Interfaces/LoopLikeInterface.h>
 #include <mlir/Interfaces/SideEffectInterfaces.h>
 #include <mlir/Support/LLVM.h>
 
@@ -166,6 +165,28 @@ void LivenessAnalysis::visitBranchOperand(OpOperand &operand) {
           blocks.push_back(&block);
       }
     }
+
+    // In the block of the successor block argument of RegionBranchOpInterface,
+    // there may be arguments of RegionBranchOpInterface, such as the IV of
+    // scf.forOp. Explicitly set this argument to live.
+    auto regionBranchOp = cast<RegionBranchOpInterface>(op);
+    for (size_t i = 0, e = op->getNumRegions(); i < e; ++i) {
+      SmallVector<RegionSuccessor> successors;
+      regionBranchOp.getSuccessorRegions(op->getRegion(i), successors);
+      for (RegionSuccessor successor : successors) {
+        if (successor.isParent())
+          continue;
+        auto arguments = successor.getSuccessor()->getArguments();
+        ValueRange regionInputs = successor.getSuccessorInputs();
+        for (auto argument : arguments) {
+          if (llvm::find(regionInputs, argument) == regionInputs.end()) {
+            (void)getLatticeElement(argument)->markLive();
+            LDBG() << "Marking RegionBranchOp's success argument live: "
+                   << argument;
+          }
+        }
+      }
+    }
   } else if (isa<BranchOpInterface>(op)) {
     // We cannot track all successor blocks of the branch operation(More
     // specifically, it's the successor's successor). Additionally, different
@@ -196,18 +217,6 @@ void LivenessAnalysis::visitBranchOperand(OpOperand &operand) {
           break;
         }
       }
-
-      // If the parentOp is live and it implements the LoopLiveOpInterface, then
-      // set its IV as live.
-      if (mayLive && isa<LoopLikeOpInterface>(parentOp)) {
-        auto loopOp = cast<LoopLikeOpInterface>(parentOp);
-        std::optional<SmallVector<Value>> ivs = loopOp.getLoopInductionVars();
-        if (ivs.has_value()) {
-          for (auto iv : *ivs) {
-            getLatticeElement(iv)->markLive();
-          }
-        }
-      }
     } else {
       // When the op is a `RegionBranchTerminatorOpInterface`, like an
       // `scf.condition` op or return-like, like an `scf.yield` op, its branch
diff --git a/mlir/test/Transforms/remove-dead-values.mlir b/mlir/test/Transforms/remove-dead-values.mlir
index 979851bc3162d..e7304505c809e 100644
--- a/mlir/test/Transforms/remove-dead-values.mlir
+++ b/mlir/test/Transforms/remove-dead-values.mlir
@@ -652,13 +652,25 @@ func.func @callee(%arg0: index, %arg1: index, %arg2: index) -> index {
 
 // -----
 
-// This test verifies that the induction variables in loops are not deleted.
+// This test verifies that the induction variables in loops are not deleted, the loop has results.
 
 // CHECK-LABEL: func @dead_value_loop_ivs
-func.func @dead_value_loop_ivs(%lb: index, %ub: index, %step: index, %b : i1) -> i1 {
+func.func @dead_value_loop_ivs_has_result(%lb: index, %ub: index, %step: index, %b: i1) -> i1 {
   %loop_ret = scf.for %iv = %lb to %ub step %step iter_args(%iter = %b) -> (i1) {
     cf.assert %b, "loop not dead"
     scf.yield %b : i1
   }
   return %loop_ret : i1
 }
+
+// -----
+
+// This test verifies that the induction variables in loops are not deleted, the loop has no results.
+
+// CHECK-LABEL: func @dead_value_loop_ivs_no_result
+func.func @dead_value_loop_ivs_no_result(%lb: index, %ub: index, %step: index, %input: memref<?xf32>, %value: f32, %pos: index) {
+  scf.for %iv = %lb to %ub step %step {
+    memref.store %value, %input[%pos] : memref<?xf32>
+  }
+  return
+}

>From d81f19ed46ee659aa9da7f23fc33b86c66a3352e Mon Sep 17 00:00:00 2001
From: linuxlonelyeagle <2020382038 at qq.com>
Date: Mon, 20 Oct 2025 16:50:27 +0000
Subject: [PATCH 4/6] update code.

---
 .../Analysis/DataFlow/LivenessAnalysis.cpp    | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp b/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp
index b66072fd8c6ad..d8c324cff4c19 100644
--- a/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp
+++ b/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp
@@ -137,7 +137,8 @@ void LivenessAnalysis::visitBranchOperand(OpOperand &operand) {
   // Populating such blocks in `blocks`.
   bool mayLive = false;
   SmallVector<Block *, 4> blocks;
-  if (isa<RegionBranchOpInterface>(op)) {
+  SmallVector<BlockArgument> argumentNotOperand;
+  if (auto regionBranchOp = dyn_cast<RegionBranchOpInterface>(op)) {
     if (op->getNumResults() != 0) {
       // This mark value of type 1.c liveness as may live, because the region
       // branch operation has a return value, and the non-forwarded operand can
@@ -169,10 +170,9 @@ void LivenessAnalysis::visitBranchOperand(OpOperand &operand) {
     // In the block of the successor block argument of RegionBranchOpInterface,
     // there may be arguments of RegionBranchOpInterface, such as the IV of
     // scf.forOp. Explicitly set this argument to live.
-    auto regionBranchOp = cast<RegionBranchOpInterface>(op);
-    for (size_t i = 0, e = op->getNumRegions(); i < e; ++i) {
+    for (Region &region : op->getRegions()) {
       SmallVector<RegionSuccessor> successors;
-      regionBranchOp.getSuccessorRegions(op->getRegion(i), successors);
+      regionBranchOp.getSuccessorRegions(region, successors);
       for (RegionSuccessor successor : successors) {
         if (successor.isParent())
           continue;
@@ -180,9 +180,7 @@ void LivenessAnalysis::visitBranchOperand(OpOperand &operand) {
         ValueRange regionInputs = successor.getSuccessorInputs();
         for (auto argument : arguments) {
           if (llvm::find(regionInputs, argument) == regionInputs.end()) {
-            (void)getLatticeElement(argument)->markLive();
-            LDBG() << "Marking RegionBranchOp's success argument live: "
-                   << argument;
+            argumentNotOperand.push_back(argument);
           }
         }
       }
@@ -246,6 +244,11 @@ void LivenessAnalysis::visitBranchOperand(OpOperand &operand) {
     Liveness *operandLiveness = getLatticeElement(operand.get());
     LDBG() << "Marking branch operand live: " << operand.get();
     propagateIfChanged(operandLiveness, operandLiveness->markLive());
+    for (BlockArgument argument : argumentNotOperand) {
+      Liveness *argumentLiveness = getLatticeElement(argument);
+      LDBG() << "Marking RegionBranchOp's argument live: " << argument;
+      propagateIfChanged(argumentLiveness, argumentLiveness->markLive());
+    }
   }
 
   // Now that we have checked for memory-effecting ops in the blocks of concern,
@@ -253,6 +256,8 @@ void LivenessAnalysis::visitBranchOperand(OpOperand &operand) {
   // mark it "live" due to type (1.a/3) liveness.
   SmallVector<Liveness *, 4> operandLiveness;
   operandLiveness.push_back(getLatticeElement(operand.get()));
+  for (BlockArgument argument : argumentNotOperand)
+    operandLiveness.push_back(getLatticeElement(argument));
   SmallVector<const Liveness *, 4> resultsLiveness;
   for (const Value result : op->getResults())
     resultsLiveness.push_back(getLatticeElement(result));

>From 46cdb02db00436ccc48ff713aa6034e15dd39e4c Mon Sep 17 00:00:00 2001
From: lonely eagle <2020382038 at qq.com>
Date: Tue, 21 Oct 2025 18:28:08 +0800
Subject: [PATCH 5/6] Update mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp

Co-authored-by: Mehdi Amini <joker.eph at gmail.com>
---
 mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp b/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp
index d8c324cff4c19..70e058d2bc2b4 100644
--- a/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp
+++ b/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp
@@ -247,6 +247,9 @@ void LivenessAnalysis::visitBranchOperand(OpOperand &operand) {
     for (BlockArgument argument : argumentNotOperand) {
       Liveness *argumentLiveness = getLatticeElement(argument);
       LDBG() << "Marking RegionBranchOp's argument live: " << argument;
+      // TODO: this is overly conservative: we should be able to eliminate unused 
+      // values in a RegionBranchOpInterface operation but that may requires removing
+      // operation results which is beyond current capabilities of this pass right now.
       propagateIfChanged(argumentLiveness, argumentLiveness->markLive());
     }
   }

>From fca73127940eef1899642020931e138a103a4ec9 Mon Sep 17 00:00:00 2001
From: lonely eagle <2020382038 at qq.com>
Date: Tue, 21 Oct 2025 18:28:19 +0800
Subject: [PATCH 6/6] Update mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp

Co-authored-by: Mehdi Amini <joker.eph at gmail.com>
---
 mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp b/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp
index 70e058d2bc2b4..312fe12960368 100644
--- a/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp
+++ b/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp
@@ -339,7 +339,6 @@ RunLivenessAnalysis::RunLivenessAnalysis(Operation *op) {
              << " has no liveness info (unreachable), mark dead";
       solver.getOrCreateState<Liveness>(result.value());
     }
-
     for (auto &region : op->getRegions()) {
       for (auto &block : region) {
         for (auto blockArg : llvm::enumerate(block.getArguments())) {



More information about the Mlir-commits mailing list