[llvm] da4894c - [LoopFusion] reject unsafe scalar flow dependences (#195895)

via llvm-commits llvm-commits at lists.llvm.org
Wed May 27 20:01:27 PDT 2026


Author: Arda Serdar Pektezol
Date: 2026-05-28T08:31:22+05:30
New Revision: da4894c874edb5fc9b9b82a8a99ed471a49153bf

URL: https://github.com/llvm/llvm-project/commit/da4894c874edb5fc9b9b82a8a99ed471a49153bf
DIFF: https://github.com/llvm/llvm-project/commit/da4894c874edb5fc9b9b82a8a99ed471a49153bf.diff

LOG: [LoopFusion] reject unsafe scalar flow dependences (#195895)

`loop-fusion` treats any loop-invariant scalar non-anti dependence as
safe to fuse. In the linked issue, it incorrectly allows scalar flow
dependences where the first loop writes a loop-invariant location and
the second loop later reads that same location. Fusion interleaves the
producer and consumer and this changes the value observed by the second
loop.

Example C source would look like:
```C
for (int i = 0; i < N; i++) {
    ptr[0] = i;
}
for (int j = 0; j < N; j++) {
    out[j] = ptr[0];
}
=>
for (int i = 0; i < N; i++) {
    ptr[0] = i;
    out[i] = ptr[0];
}
```

This patch makes the DA scalar-dependence shortcut **_more
conservative_** by rejecting scalar non-anti and allowing input/output
dependences. This preserves the existing safe read and write cases while
preventing the miscompile above.

The patch also updates the `loop-fusion` debug message to reflect the
narrower accepted case, updates the existing regression to check the new
debug message, and adds a new regression from the linked issue.

Fixes #191238

Added: 
    llvm/test/Transforms/LoopFusion/pr191238.ll

Modified: 
    llvm/lib/Transforms/Scalar/LoopFuse.cpp
    llvm/test/Transforms/LoopFusion/loop_invariant.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Scalar/LoopFuse.cpp b/llvm/lib/Transforms/Scalar/LoopFuse.cpp
index 723dce9dec468..47bed80cb84ea 100644
--- a/llvm/lib/Transforms/Scalar/LoopFuse.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopFuse.cpp
@@ -1141,11 +1141,17 @@ struct LoopFuser {
 
     assert(CurLoopLevel > Levels && "Fusion candidates are not separated");
 
-    if (DepResult->isScalar(CurLoopLevel, true) && !DepResult->isAnti()) {
-      LLVM_DEBUG(dbgs() << "Safe to fuse due to a loop-invariant non-anti "
-                           "dependency\n");
-      NumDA++;
-      return true;
+    if (DepResult->isScalar(CurLoopLevel, true)) {
+      if (DepResult->isInput() || DepResult->isOutput()) {
+        LLVM_DEBUG(dbgs() << "Safe to fuse due to a loop-invariant "
+                          << (DepResult->isInput() ? "input" : "output")
+                          << " dependency\n");
+        NumDA++;
+        return true;
+      }
+      LLVM_DEBUG(
+          dbgs() << "Not safe to fuse due to a scalar flow dependency\n");
+      return false;
     }
 
     unsigned CurDir = DepResult->getDirection(CurLoopLevel, true);

diff  --git a/llvm/test/Transforms/LoopFusion/loop_invariant.ll b/llvm/test/Transforms/LoopFusion/loop_invariant.ll
index 7de4e7122464a..ea682ac363aad 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 non-anti dependency
+; CHECK-DA: Safe to fuse due to a loop-invariant output dependency
 ;
 pre1:
   %ptr = alloca i32, align 4

diff  --git a/llvm/test/Transforms/LoopFusion/pr191238.ll b/llvm/test/Transforms/LoopFusion/pr191238.ll
new file mode 100644
index 0000000000000..9fded5775c73d
--- /dev/null
+++ b/llvm/test/Transforms/LoopFusion/pr191238.ll
@@ -0,0 +1,57 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes=loop-fusion < %s | FileCheck %s
+
+; The expected output keeps body1 and body2 as separate loops.
+; body2 must execute only after body1 finishes so every store to %out
+; uses the final value left in %ptr.
+
+define void @fill(i32 %N, ptr %out) {
+; CHECK-LABEL: define void @fill(
+; CHECK-SAME: i32 [[N:%.*]], ptr [[OUT:%.*]]) {
+; CHECK-NEXT:  [[PRE1:.*]]:
+; CHECK-NEXT:    [[PTR:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    br label %[[BODY1:.*]]
+; CHECK:       [[BODY1]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ 0, %[[PRE1]] ], [ [[I_NEXT:%.*]], %[[BODY1]] ]
+; CHECK-NEXT:    store i32 [[I]], ptr [[PTR]], align 4
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
+; CHECK-NEXT:    [[COND_NOT:%.*]] = icmp eq i32 [[I_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[COND_NOT]], label %[[BODY2_PREHEADER:.*]], label %[[BODY1]]
+; CHECK:       [[BODY2_PREHEADER]]:
+; CHECK-NEXT:    br label %[[BODY2:.*]]
+; CHECK:       [[BODY2]]:
+; CHECK-NEXT:    [[J:%.*]] = phi i32 [ [[J_NEXT:%.*]], %[[BODY2]] ], [ 0, %[[BODY2_PREHEADER]] ]
+; CHECK-NEXT:    [[VAL:%.*]] = load i32, ptr [[PTR]], align 4
+; CHECK-NEXT:    [[TMP0:%.*]] = sext i32 [[J]] to i64
+; CHECK-NEXT:    [[OUT_PTR:%.*]] = getelementptr [4 x i8], ptr [[OUT]], i64 [[TMP0]]
+; CHECK-NEXT:    store i32 [[VAL]], ptr [[OUT_PTR]], align 4
+; CHECK-NEXT:    [[J_NEXT]] = add i32 [[J]], 1
+; CHECK-NEXT:    [[COND2_NOT:%.*]] = icmp eq i32 [[J_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[COND2_NOT]], label %[[EXIT:.*]], label %[[BODY2]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+pre1:
+  %ptr = alloca i32, align 4
+  br label %body1
+
+body1:                                            ; preds = %body1, %pre1
+  %i = phi i32 [ 0, %pre1 ], [ %i_next, %body1 ]
+  store i32 %i, ptr %ptr, align 4
+  %i_next = add i32 %i, 1
+  %cond.not = icmp eq i32 %i_next, %N
+  br i1 %cond.not, label %body2, label %body1
+
+body2:                                            ; preds = %body2, %body1
+  %j = phi i32 [ %j_next, %body2 ], [ 0, %body1 ]
+  %val = load i32, ptr %ptr, align 4
+  %0 = sext i32 %j to i64
+  %out_ptr = getelementptr [4 x i8], ptr %out, i64 %0
+  store i32 %val, ptr %out_ptr, align 4
+  %j_next = add i32 %j, 1
+  %cond2.not = icmp eq i32 %j_next, %N
+  br i1 %cond2.not, label %exit, label %body2
+
+exit:                                             ; preds = %body2
+  ret void
+}


        


More information about the llvm-commits mailing list