[llvm] [LoopFusion] Fix false-positive dependency blocking fusion of idempot… (PR #206401)

via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 15 08:46:38 PDT 2026


https://github.com/AntonyCJ30 updated https://github.com/llvm/llvm-project/pull/206401

>From 559465616d15090690620f26f9bc03766a799ca1 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       | 16 +++-
 .../Transforms/LoopFusion/loop_invariant.ll   | 81 +++++++++++++++++++
 2 files changed, 95 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/LoopFuse.cpp b/llvm/lib/Transforms/Scalar/LoopFuse.cpp
index 1b83c971c01bf..38da52af06275 100644
--- a/llvm/lib/Transforms/Scalar/LoopFuse.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopFuse.cpp
@@ -54,6 +54,7 @@
 #include "llvm/Analysis/PostDominators.h"
 #include "llvm/Analysis/ScalarEvolution.h"
 #include "llvm/Analysis/TargetTransformInfo.h"
+#include "llvm/Analysis/ValueTracking.h"
 #include "llvm/IR/Function.h"
 #include "llvm/IR/Verifier.h"
 #include "llvm/Support/CommandLine.h"
@@ -64,7 +65,6 @@
 #include "llvm/Transforms/Utils/LoopPeel.h"
 #include "llvm/Transforms/Utils/LoopSimplify.h"
 #include <list>
-
 using namespace llvm;
 
 #define DEBUG_TYPE "loop-fusion"
@@ -1113,6 +1113,18 @@ 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);
+      
+      Value *P0 = getUnderlyingObject(S0->getPointerOperand());
+      Value *P1 = getUnderlyingObject(S1->getPointerOperand());
+      
+      if (P0 != P1 && S0->getValueOperand() == S1->getValueOperand())
+        return true; 
+    } 
 #ifndef NDEBUG
     if (VerboseFusionDebugging) {
       LLVM_DEBUG(dbgs() << "DA res: "; DepResult->dump(dbgs());
@@ -1148,7 +1160,6 @@ struct LoopFuser {
     }
 
     assert(CurLoopLevel > Levels && "Fusion candidates are not separated");
-
     if (DepResult->isScalar(CurLoopLevel, true)) {
       if (DepResult->isInput() || DepResult->isOutput()) {
         LLVM_DEBUG(dbgs() << "Safe to fuse due to a loop-invariant "
@@ -1157,6 +1168,7 @@ struct LoopFuser {
         NumDA++;
         return true;
       }
+
       LLVM_DEBUG(
           dbgs() << "Not safe to fuse due to a scalar flow dependency\n");
       return false;
diff --git a/llvm/test/Transforms/LoopFusion/loop_invariant.ll b/llvm/test/Transforms/LoopFusion/loop_invariant.ll
index ea682ac363aad..5b06e5c85e504 100644
--- a/llvm/test/Transforms/LoopFusion/loop_invariant.ll
+++ b/llvm/test/Transforms/LoopFusion/loop_invariant.ll
@@ -60,3 +60,84 @@ body2:  ; preds = %pre2, %body2
 exit:
   ret void
 }
+
+; Test idempotent store-store pairs: both loops write the same value to
+; provably different underlying objects. 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 @idempotent_different_values(ptr %a, ptr %b, i32 %n, i32 %t) {
+; CHECK-DA: Performing Loop Fusion on function idempotent_different_values
+; 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