[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 ®ion : 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 ®ion : 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 ®ion : 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 ®ion : op->getRegions()) {
for (auto &block : region) {
for (auto blockArg : llvm::enumerate(block.getArguments())) {
More information about the Mlir-commits
mailing list