[llvm] [LoopFusion] Fix false-positive dependency blocking fusion of idempot… (PR #206401)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 31 08:17:20 PDT 2026
https://github.com/AntonyCJ30 updated https://github.com/llvm/llvm-project/pull/206401
>From 09595f1de0a774b2f1e1287a313a653e4e66784c Mon Sep 17 00:00:00 2001
From: AntonyCJ30 <cj6186609 at gmail@gmail.com>
Date: Mon, 29 Jun 2026 10:40:22 +0530
Subject: [PATCH] [LoopFusion] Fix false-positive dependency blocking fusion of
idempotent stores
---
llvm/lib/Transforms/Scalar/LoopFuse.cpp | 8 ++
.../Transforms/LoopFusion/loop_invariant.ll | 83 ++++++++++++++++++-
2 files changed, 90 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/Scalar/LoopFuse.cpp b/llvm/lib/Transforms/Scalar/LoopFuse.cpp
index f15bf026f2b2d..6ce82a0dd7684 100644
--- a/llvm/lib/Transforms/Scalar/LoopFuse.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopFuse.cpp
@@ -1113,6 +1113,14 @@ struct LoopFuser {
auto DepResult = DI.depends(&I0, &I1);
if (!DepResult)
return true;
+ // If two stores write the same SSA value, fusion is safe regardless of
+ // aliasing — writing the same value twice is idempotent.
+ if (isa<StoreInst>(I0) && isa<StoreInst>(I1)) {
+ auto *S0 = cast<StoreInst>(&I0);
+ auto *S1 = cast<StoreInst>(&I1);
+ if (S0->getValueOperand() == S1->getValueOperand())
+ return true;
+ }
#ifndef NDEBUG
if (VerboseFusionDebugging) {
LLVM_DEBUG(dbgs() << "DA res: "; DepResult->dump(dbgs());
diff --git a/llvm/test/Transforms/LoopFusion/loop_invariant.ll b/llvm/test/Transforms/LoopFusion/loop_invariant.ll
index ea682ac363aad..637ee64c311e8 100644
--- a/llvm/test/Transforms/LoopFusion/loop_invariant.ll
+++ b/llvm/test/Transforms/LoopFusion/loop_invariant.ll
@@ -4,7 +4,7 @@
define void @loop_invariant(i32 %N) {
; CHECK-DA: Performing Loop Fusion on function loop_invariant
-; CHECK-DA: Safe to fuse due to a loop-invariant output dependency
+; CHECK-DA: Fusion is performed
;
pre1:
%ptr = alloca i32, align 4
@@ -60,3 +60,84 @@ body2: ; preds = %pre2, %body2
exit:
ret void
}
+
+; Test idempotent store-store pairs: both loops write the same value to
+; Safe to fuse despite MayAlias.
+
+define void @idempotent_same_arg(ptr %a, ptr %b, i32 %n, i32 %val) {
+; CHECK-DA: Performing Loop Fusion on function idempotent_same_arg
+; CHECK-DA: Fusion is performed
+entry:
+ %cmp = icmp sgt i32 %n, 0
+ br i1 %cmp, label %for.body.preheader, label %for.cond2.preheader
+
+for.body.preheader:
+ %wide.trip.count = zext nneg i32 %n to i64
+ br label %for.body
+
+for.body:
+ %iv = phi i64 [ 0, %for.body.preheader ], [ %iv.next, %for.body ]
+ %gep.a = getelementptr inbounds i32, ptr %a, i64 %iv
+ store i32 %val, ptr %gep.a, align 4
+ %iv.next = add nuw nsw i64 %iv, 1
+ %exit = icmp eq i64 %iv.next, %wide.trip.count
+ br i1 %exit, label %for.cond2.preheader, label %for.body
+
+for.cond2.preheader:
+ %cmp2 = icmp sgt i32 %n, 0
+ br i1 %cmp2, label %for.body5.preheader, label %for.cond.cleanup4
+
+for.body5.preheader:
+ %wide.trip.count2 = zext nneg i32 %n to i64
+ br label %for.body5
+
+for.body5:
+ %iv2 = phi i64 [ 0, %for.body5.preheader ], [ %iv2.next, %for.body5 ]
+ %gep.b = getelementptr inbounds i32, ptr %b, i64 %iv2
+ store i32 %val, ptr %gep.b, align 4
+ %iv2.next = add nuw nsw i64 %iv2, 1
+ %exit2 = icmp eq i64 %iv2.next, %wide.trip.count2
+ br i1 %exit2, label %for.cond.cleanup4, label %for.body5
+
+for.cond.cleanup4:
+ ret void
+}
+
+define void @different_values_no_fusion(ptr %a, ptr %b, i32 %n, i32 %t) {
+; CHECK-DA: Performing Loop Fusion on function different_values_no_fusion
+; CHECK-DA: Memory dependencies do not allow fusion!
+entry:
+ %cmp = icmp sgt i32 %n, 0
+ br i1 %cmp, label %for.body.preheader, label %for.cond2.preheader
+
+for.body.preheader:
+ %wide.trip.count = zext nneg i32 %n to i64
+ br label %for.body
+
+for.body:
+ %iv = phi i64 [ 0, %for.body.preheader ], [ %iv.next, %for.body ]
+ %gep.a = getelementptr inbounds i32, ptr %a, i64 %iv
+ store i32 %t, ptr %gep.a, align 4
+ %iv.next = add nuw nsw i64 %iv, 1
+ %exit = icmp eq i64 %iv.next, %wide.trip.count
+ br i1 %exit, label %for.cond2.preheader, label %for.body
+
+for.cond2.preheader:
+ %cmp2 = icmp sgt i32 %n, 0
+ br i1 %cmp2, label %for.body5.preheader, label %for.cond.cleanup4
+
+for.body5.preheader:
+ %wide.trip.count2 = zext nneg i32 %n to i64
+ br label %for.body5
+
+for.body5:
+ %iv2 = phi i64 [ 0, %for.body5.preheader ], [ %iv2.next, %for.body5 ]
+ %gep.b = getelementptr inbounds i32, ptr %b, i64 %iv2
+ store i32 42, ptr %gep.b, align 4
+ %iv2.next = add nuw nsw i64 %iv2, 1
+ %exit2 = icmp eq i64 %iv2.next, %wide.trip.count2
+ br i1 %exit2, label %for.cond.cleanup4, label %for.body5
+
+for.cond.cleanup4:
+ ret void
+}
More information about the llvm-commits
mailing list