[llvm] [LoopFusion] NFC: Add tests for same-iteration scalar dependence fusion (PR #216243)

Anchu Rajendran S via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 14 14:03:37 PDT 2026


https://github.com/anchuraj updated https://github.com/llvm/llvm-project/pull/216243

>From 162f8c68d3eb1c71a515c54d81cf2a8cbf05566c Mon Sep 17 00:00:00 2001
From: Anchu Rajendran <asudhaku at amd.com>
Date: Fri, 14 Aug 2026 02:22:03 +0530
Subject: [PATCH] [LoopFusion] NFC: Add tests for same-iteration scalar
 dependence fusion

Add coverage for multi-loop same-array accumulation and negative cases for
loop-carried scalar deps and may-alias cross-pointer patterns.
---
 .../LoopFusion/accumulate_same_array.ll       | 143 ++++++++++++++++++
 .../LoopFusion/scalar_eq_cannot_fuse.ll       |  89 +++++++++++
 2 files changed, 232 insertions(+)
 create mode 100644 llvm/test/Transforms/LoopFusion/accumulate_same_array.ll
 create mode 100644 llvm/test/Transforms/LoopFusion/scalar_eq_cannot_fuse.ll

diff --git a/llvm/test/Transforms/LoopFusion/accumulate_same_array.ll b/llvm/test/Transforms/LoopFusion/accumulate_same_array.ll
new file mode 100644
index 0000000000000..6a789f676a7f7
--- /dev/null
+++ b/llvm/test/Transforms/LoopFusion/accumulate_same_array.ll
@@ -0,0 +1,143 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes=loop-simplify,loop-fusion < %s | FileCheck %s
+
+; Four adjacent loops accumulating into the same array element. Each adjacent
+; pair has a same-iteration flow dependence on hist[i], which is preserved by
+; placing the earlier loop's body first, so all four loops are fused into one.
+
+; C Code
+;
+;;  for (long int i = 0; i < n; i++)
+;;    hist[i] = A[i];
+;;  for (long int i = 0; i < n; i++)
+;;    hist[i] += B[i];
+;;  for (long int i = 0; i < n; i++)
+;;    hist[i] += C[i];
+;;  for (long int i = 0; i < n; i++)
+;;    hist[i] += D[i];
+
+define void @accumulate_same_array(ptr noalias %hist, ptr %A, ptr %B, ptr %C, ptr %D, i64 %n) {
+; CHECK-LABEL: define void @accumulate_same_array(
+; CHECK-SAME: ptr noalias [[HIST:%.*]], ptr [[A:%.*]], ptr [[B:%.*]], ptr [[C:%.*]], ptr [[D:%.*]], i64 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP1:.*]]
+; CHECK:       [[LOOP1]]:
+; CHECK-NEXT:    [[I1:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[I1_NEXT:%.*]], %[[LOOP4_LATCH:.*]] ]
+; CHECK-NEXT:    [[I2:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[I2_NEXT:%.*]], %[[LOOP4_LATCH]] ]
+; CHECK-NEXT:    [[I3:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[I3_NEXT:%.*]], %[[LOOP4_LATCH]] ]
+; CHECK-NEXT:    [[I4:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[I4_NEXT:%.*]], %[[LOOP4_LATCH]] ]
+; CHECK-NEXT:    [[A_GEP:%.*]] = getelementptr inbounds float, ptr [[A]], i64 [[I1]]
+; CHECK-NEXT:    [[A_VAL:%.*]] = load float, ptr [[A_GEP]], align 4
+; CHECK-NEXT:    [[H1_GEP:%.*]] = getelementptr inbounds float, ptr [[HIST]], i64 [[I1]]
+; CHECK-NEXT:    store float [[A_VAL]], ptr [[H1_GEP]], align 4
+; CHECK-NEXT:    br label %[[LOOP1_LATCH:.*]]
+; CHECK:       [[LOOP1_LATCH]]:
+; CHECK-NEXT:    [[B_GEP:%.*]] = getelementptr inbounds float, ptr [[B]], i64 [[I2]]
+; CHECK-NEXT:    [[B_VAL:%.*]] = load float, ptr [[B_GEP]], align 4
+; CHECK-NEXT:    [[H2_GEP:%.*]] = getelementptr inbounds float, ptr [[HIST]], i64 [[I2]]
+; CHECK-NEXT:    [[OLD2:%.*]] = load float, ptr [[H2_GEP]], align 4
+; CHECK-NEXT:    [[SUM2:%.*]] = fadd float [[OLD2]], [[B_VAL]]
+; CHECK-NEXT:    store float [[SUM2]], ptr [[H2_GEP]], align 4
+; CHECK-NEXT:    br label %[[LOOP2_LATCH:.*]]
+; CHECK:       [[LOOP2_LATCH]]:
+; CHECK-NEXT:    [[C_GEP:%.*]] = getelementptr inbounds float, ptr [[C]], i64 [[I3]]
+; CHECK-NEXT:    [[C_VAL:%.*]] = load float, ptr [[C_GEP]], align 4
+; CHECK-NEXT:    [[H3_GEP:%.*]] = getelementptr inbounds float, ptr [[HIST]], i64 [[I3]]
+; CHECK-NEXT:    [[OLD3:%.*]] = load float, ptr [[H3_GEP]], align 4
+; CHECK-NEXT:    [[SUM3:%.*]] = fadd float [[OLD3]], [[C_VAL]]
+; CHECK-NEXT:    store float [[SUM3]], ptr [[H3_GEP]], align 4
+; CHECK-NEXT:    br label %[[LOOP3_LATCH:.*]]
+; CHECK:       [[LOOP3_LATCH]]:
+; CHECK-NEXT:    [[D_GEP:%.*]] = getelementptr inbounds float, ptr [[D]], i64 [[I4]]
+; CHECK-NEXT:    [[D_VAL:%.*]] = load float, ptr [[D_GEP]], align 4
+; CHECK-NEXT:    [[H4_GEP:%.*]] = getelementptr inbounds float, ptr [[HIST]], i64 [[I4]]
+; CHECK-NEXT:    [[OLD4:%.*]] = load float, ptr [[H4_GEP]], align 4
+; CHECK-NEXT:    [[SUM4:%.*]] = fadd float [[OLD4]], [[D_VAL]]
+; CHECK-NEXT:    store float [[SUM4]], ptr [[H4_GEP]], align 4
+; CHECK-NEXT:    br label %[[LOOP4_LATCH]]
+; CHECK:       [[LOOP4_LATCH]]:
+; CHECK-NEXT:    [[I1_NEXT]] = add nuw nsw i64 [[I1]], 1
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ult i64 [[I1_NEXT]], [[N]]
+; CHECK-NEXT:    [[I2_NEXT]] = add nuw nsw i64 [[I2]], 1
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp ult i64 [[I2_NEXT]], [[N]]
+; CHECK-NEXT:    [[I3_NEXT]] = add nuw nsw i64 [[I3]], 1
+; CHECK-NEXT:    [[CMP3:%.*]] = icmp ult i64 [[I3_NEXT]], [[N]]
+; CHECK-NEXT:    [[I4_NEXT]] = add nuw nsw i64 [[I4]], 1
+; CHECK-NEXT:    [[CMP4:%.*]] = icmp ult i64 [[I4_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[CMP4]], label %[[LOOP1]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop1
+
+loop1:
+  %i1 = phi i64 [ 0, %entry ], [ %i1.next, %loop1.latch ]
+  %a.gep = getelementptr inbounds float, ptr %A, i64 %i1
+  %a.val = load float, ptr %a.gep, align 4
+  %h1.gep = getelementptr inbounds float, ptr %hist, i64 %i1
+  store float %a.val, ptr %h1.gep, align 4
+  br label %loop1.latch
+
+loop1.latch:
+  %i1.next = add nuw nsw i64 %i1, 1
+  %cmp1 = icmp ult i64 %i1.next, %n
+  br i1 %cmp1, label %loop1, label %loop2.preheader
+
+loop2.preheader:
+  br label %loop2
+
+loop2:
+  %i2 = phi i64 [ 0, %loop2.preheader ], [ %i2.next, %loop2.latch ]
+  %b.gep = getelementptr inbounds float, ptr %B, i64 %i2
+  %b.val = load float, ptr %b.gep, align 4
+  %h2.gep = getelementptr inbounds float, ptr %hist, i64 %i2
+  %old2 = load float, ptr %h2.gep, align 4
+  %sum2 = fadd float %old2, %b.val
+  store float %sum2, ptr %h2.gep, align 4
+  br label %loop2.latch
+
+loop2.latch:
+  %i2.next = add nuw nsw i64 %i2, 1
+  %cmp2 = icmp ult i64 %i2.next, %n
+  br i1 %cmp2, label %loop2, label %loop3.preheader
+
+loop3.preheader:
+  br label %loop3
+
+loop3:
+  %i3 = phi i64 [ 0, %loop3.preheader ], [ %i3.next, %loop3.latch ]
+  %c.gep = getelementptr inbounds float, ptr %C, i64 %i3
+  %c.val = load float, ptr %c.gep, align 4
+  %h3.gep = getelementptr inbounds float, ptr %hist, i64 %i3
+  %old3 = load float, ptr %h3.gep, align 4
+  %sum3 = fadd float %old3, %c.val
+  store float %sum3, ptr %h3.gep, align 4
+  br label %loop3.latch
+
+loop3.latch:
+  %i3.next = add nuw nsw i64 %i3, 1
+  %cmp3 = icmp ult i64 %i3.next, %n
+  br i1 %cmp3, label %loop3, label %loop4.preheader
+
+loop4.preheader:
+  br label %loop4
+
+loop4:
+  %i4 = phi i64 [ 0, %loop4.preheader ], [ %i4.next, %loop4.latch ]
+  %d.gep = getelementptr inbounds float, ptr %D, i64 %i4
+  %d.val = load float, ptr %d.gep, align 4
+  %h4.gep = getelementptr inbounds float, ptr %hist, i64 %i4
+  %old4 = load float, ptr %h4.gep, align 4
+  %sum4 = fadd float %old4, %d.val
+  store float %sum4, ptr %h4.gep, align 4
+  br label %loop4.latch
+
+loop4.latch:
+  %i4.next = add nuw nsw i64 %i4, 1
+  %cmp4 = icmp ult i64 %i4.next, %n
+  br i1 %cmp4, label %loop4, label %exit
+
+exit:
+  ret void
+}
diff --git a/llvm/test/Transforms/LoopFusion/scalar_eq_cannot_fuse.ll b/llvm/test/Transforms/LoopFusion/scalar_eq_cannot_fuse.ll
new file mode 100644
index 0000000000000..457f71c5f489a
--- /dev/null
+++ b/llvm/test/Transforms/LoopFusion/scalar_eq_cannot_fuse.ll
@@ -0,0 +1,89 @@
+; RUN: opt -passes=loop-simplify,loop-fusion -stats -disable-output < %s 2>&1 | FileCheck %s --check-prefix=STAT
+; RUN: opt -passes=loop-simplify,loop-fusion -pass-remarks-missed=loop-fusion -disable-output < %s 2>&1 | FileCheck %s
+; STAT-NOT: loop-fusion{{.*}} - Loops fused
+; STAT: 2 loop-fusion{{.*}} - Dependencies prevent fusion
+
+; Negative tests: the scalar EQ dependence change must not fuse these patterns.
+
+; Loop 1 stores A[i]; loop 2 loads A[i + 1] — forward loop-carried dependence.
+; CHECK: remark: {{.*}}store_then_load_next{{.*}}: Dependencies prevent fusion
+
+define void @store_then_load_next(ptr noalias nocapture %A, i64 %n, float %w0) {
+entry:
+  br label %loop1
+
+loop1:
+  %i1 = phi i64 [ 0, %entry ], [ %i1.next, %loop1.latch ]
+  %gep1 = getelementptr inbounds float, ptr %A, i64 %i1
+  store float %w0, ptr %gep1, align 4
+  br label %loop1.latch
+
+loop1.latch:
+  %i1.next = add nuw nsw i64 %i1, 1
+  %cmp1 = icmp ult i64 %i1.next, %n
+  br i1 %cmp1, label %loop1, label %loop2.preheader
+
+loop2.preheader:
+  br label %loop2
+
+loop2:
+  %i2 = phi i64 [ 0, %loop2.preheader ], [ %i2.next, %loop2.latch ]
+  %next = add nuw nsw i64 %i2, 1
+  %gep2 = getelementptr inbounds float, ptr %A, i64 %next
+  %v = load float, ptr %gep2, align 4
+  %gep3 = getelementptr inbounds float, ptr %A, i64 %i2
+  store float %v, ptr %gep3, align 4
+  br label %loop2.latch
+
+loop2.latch:
+  %i2.next = add nuw nsw i64 %i2, 1
+  %cmp2 = icmp ult i64 %i2.next, %n
+  br i1 %cmp2, label %loop2, label %exit
+
+exit:
+  ret void
+}
+
+; Cross-pointer store-then-accumulate without noalias on the output pointer.
+; CHECK: remark: {{.*}}cross_pointer_may_alias{{.*}}: Dependencies prevent fusion
+
+define void @cross_pointer_may_alias(ptr nocapture %hist, ptr nocapture readonly %input, i64 %n, float %w0, float %w1) {
+entry:
+  br label %loop1
+
+loop1:
+  %i1 = phi i64 [ 0, %entry ], [ %i1.next, %loop1.latch ]
+  %a = getelementptr inbounds float, ptr %input, i64 %i1
+  %v0 = load float, ptr %a, align 4
+  %p = fmul float %w0, %v0
+  %h1 = getelementptr inbounds float, ptr %hist, i64 %i1
+  store float %p, ptr %h1, align 4
+  br label %loop1.latch
+
+loop1.latch:
+  %i1.next = add nuw nsw i64 %i1, 1
+  %cmp1 = icmp ult i64 %i1.next, %n
+  br i1 %cmp1, label %loop1, label %loop2.preheader
+
+loop2.preheader:
+  br label %loop2
+
+loop2:
+  %i2 = phi i64 [ 0, %loop2.preheader ], [ %i2.next, %loop2.latch ]
+  %b = getelementptr inbounds float, ptr %input, i64 %i2
+  %v1 = load float, ptr %b, align 4
+  %h2 = getelementptr inbounds float, ptr %hist, i64 %i2
+  %old = load float, ptr %h2, align 4
+  %p1 = fmul float %w1, %v1
+  %sum = fadd float %old, %p1
+  store float %sum, ptr %h2, align 4
+  br label %loop2.latch
+
+loop2.latch:
+  %i2.next = add nuw nsw i64 %i2, 1
+  %cmp2 = icmp ult i64 %i2.next, %n
+  br i1 %cmp2, label %loop2, label %exit
+
+exit:
+  ret void
+}



More information about the llvm-commits mailing list