[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