[llvm] [LAA] Allow zero-distance dependences between mixed-width accesses (PR #221531)

Hadong Lee via llvm-commits llvm-commits at lists.llvm.org
Sat Sep 12 17:34:38 PDT 2026


https://github.com/ChrisLee02 updated https://github.com/llvm/llvm-project/pull/221531

>From 396a30366cc12da8bceeb808b38688a03397d29f Mon Sep 17 00:00:00 2001
From: Chrislee02 <hdchris765 at snu.ac.kr>
Date: Sun, 6 Sep 2026 18:48:48 +0900
Subject: [PATCH 1/3] [LAA] Add tests for zero-distance mixed-width accesses

---
 .../zero-distance-mixed-width.ll              | 563 ++++++++++++++++++
 1 file changed, 563 insertions(+)
 create mode 100644 llvm/test/Analysis/LoopAccessAnalysis/zero-distance-mixed-width.ll

diff --git a/llvm/test/Analysis/LoopAccessAnalysis/zero-distance-mixed-width.ll b/llvm/test/Analysis/LoopAccessAnalysis/zero-distance-mixed-width.ll
new file mode 100644
index 0000000000000..1679f2ca5accc
--- /dev/null
+++ b/llvm/test/Analysis/LoopAccessAnalysis/zero-distance-mixed-width.ll
@@ -0,0 +1,563 @@
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes='print<access-info>' -disable-output < %s 2>&1 | FileCheck %s
+
+; Mixed-width accesses at zero distance with a common byte stride
+; are Forward when different iterations are disjoint.
+
+; Same address, load 4 bytes / store 8 bytes, byte stride 8.
+; Different iterations are disjoint --> Forward.
+define void @load32_store64_stride8(ptr %base, i32 %before, i64 %after) {
+; CHECK-LABEL: 'load32_store64_stride8'
+; CHECK-NEXT:    loop:
+; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
+; CHECK-NEXT:  Unknown data dependence.
+; CHECK-NEXT:      Dependences:
+; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:            %value = load i32, ptr %p, align 1 ->
+; CHECK-NEXT:            store i64 %after, ptr %p, align 1
+; CHECK-EMPTY:
+; CHECK-NEXT:      Run-time memory checks:
+; CHECK-NEXT:      Grouped accesses:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Non vectorizable stores to invariant address were not found in loop.
+; CHECK-NEXT:      SCEV assumptions:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Expressions re-written:
+;
+entry:
+  br label %loop
+
+loop:
+  %offset = phi i64 [ 0, %entry ], [ %next, %latch ]
+  %p = getelementptr inbounds i8, ptr %base, i64 %offset
+  %value = load i32, ptr %p, align 1
+  %match = icmp eq i32 %value, %before
+  br i1 %match, label %store, label %latch
+
+store:
+  store i64 %after, ptr %p, align 1
+  br label %latch
+
+latch:
+  %next = add nsw i64 %offset, 8
+  %done = icmp eq i64 %next, 128
+  br i1 %done, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; Load 1 byte / store 2 bytes, byte stride 2.
+; Smaller access types remain disjoint across iterations --> Forward.
+define void @load8_store16_stride2(ptr %base, i8 %before, i16 %after) {
+; CHECK-LABEL: 'load8_store16_stride2'
+; CHECK-NEXT:    loop:
+; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
+; CHECK-NEXT:  Unknown data dependence.
+; CHECK-NEXT:      Dependences:
+; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:            %value = load i8, ptr %p, align 1 ->
+; CHECK-NEXT:            store i16 %after, ptr %p, align 1
+; CHECK-EMPTY:
+; CHECK-NEXT:      Run-time memory checks:
+; CHECK-NEXT:      Grouped accesses:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Non vectorizable stores to invariant address were not found in loop.
+; CHECK-NEXT:      SCEV assumptions:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Expressions re-written:
+;
+entry:
+  br label %loop
+
+loop:
+  %offset = phi i64 [ 0, %entry ], [ %next, %latch ]
+  %p = getelementptr inbounds i8, ptr %base, i64 %offset
+  %value = load i8, ptr %p, align 1
+  %match = icmp eq i8 %value, %before
+  br i1 %match, label %store, label %latch
+
+store:
+  store i16 %after, ptr %p, align 1
+  br label %latch
+
+latch:
+  %next = add nsw i64 %offset, 2
+  %done = icmp eq i64 %next, 32
+  br i1 %done, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; Load 1 byte / store 8 bytes, byte stride 8.
+; Access sizes need not differ by a factor of two --> Forward.
+define void @load8_store64_stride8(ptr %base, i8 %before, i64 %after) {
+; CHECK-LABEL: 'load8_store64_stride8'
+; CHECK-NEXT:    loop:
+; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
+; CHECK-NEXT:  Unknown data dependence.
+; CHECK-NEXT:      Dependences:
+; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:            %value = load i8, ptr %p, align 1 ->
+; CHECK-NEXT:            store i64 %after, ptr %p, align 1
+; CHECK-EMPTY:
+; CHECK-NEXT:      Run-time memory checks:
+; CHECK-NEXT:      Grouped accesses:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Non vectorizable stores to invariant address were not found in loop.
+; CHECK-NEXT:      SCEV assumptions:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Expressions re-written:
+;
+entry:
+  br label %loop
+
+loop:
+  %offset = phi i64 [ 0, %entry ], [ %next, %latch ]
+  %p = getelementptr inbounds i8, ptr %base, i64 %offset
+  %value = load i8, ptr %p, align 1
+  %match = icmp eq i8 %value, %before
+  br i1 %match, label %store, label %latch
+
+store:
+  store i64 %after, ptr %p, align 1
+  br label %latch
+
+latch:
+  %next = add nsw i64 %offset, 8
+  %done = icmp eq i64 %next, 128
+  br i1 %done, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; Load i24 (store size 3, allocation size 4) / store i64, byte stride 8.
+; Different store and allocation sizes still fit within the stride --> Forward.
+define void @load24_store64_stride8(ptr %base, i24 %before, i64 %after) {
+; CHECK-LABEL: 'load24_store64_stride8'
+; CHECK-NEXT:    loop:
+; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
+; CHECK-NEXT:  Unknown data dependence.
+; CHECK-NEXT:      Dependences:
+; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:            %value = load i24, ptr %p, align 1 ->
+; CHECK-NEXT:            store i64 %after, ptr %p, align 1
+; CHECK-EMPTY:
+; CHECK-NEXT:      Run-time memory checks:
+; CHECK-NEXT:      Grouped accesses:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Non vectorizable stores to invariant address were not found in loop.
+; CHECK-NEXT:      SCEV assumptions:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Expressions re-written:
+;
+entry:
+  br label %loop
+
+loop:
+  %offset = phi i64 [ 0, %entry ], [ %next, %latch ]
+  %p = getelementptr inbounds i8, ptr %base, i64 %offset
+  %value = load i24, ptr %p, align 1
+  %match = icmp eq i24 %value, %before
+  br i1 %match, label %store, label %latch
+
+store:
+  store i64 %after, ptr %p, align 1
+  br label %latch
+
+latch:
+  %next = add nsw i64 %offset, 8
+  %done = icmp eq i64 %next, 128
+  br i1 %done, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; Increase the common byte stride from 8 to 16.
+; Gaps between iterations do not prevent classification --> Forward.
+define void @load32_store64_stride16(ptr %base, i32 %before, i64 %after) {
+; CHECK-LABEL: 'load32_store64_stride16'
+; CHECK-NEXT:    loop:
+; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
+; CHECK-NEXT:  Unknown data dependence.
+; CHECK-NEXT:      Dependences:
+; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:            %value = load i32, ptr %p, align 1 ->
+; CHECK-NEXT:            store i64 %after, ptr %p, align 1
+; CHECK-EMPTY:
+; CHECK-NEXT:      Run-time memory checks:
+; CHECK-NEXT:      Grouped accesses:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Non vectorizable stores to invariant address were not found in loop.
+; CHECK-NEXT:      SCEV assumptions:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Expressions re-written:
+;
+entry:
+  br label %loop
+
+loop:
+  %offset = phi i64 [ 0, %entry ], [ %next, %latch ]
+  %p = getelementptr inbounds i8, ptr %base, i64 %offset
+  %value = load i32, ptr %p, align 1
+  %match = icmp eq i32 %value, %before
+  br i1 %match, label %store, label %latch
+
+store:
+  store i64 %after, ptr %p, align 1
+  br label %latch
+
+latch:
+  %next = add nsw i64 %offset, 16
+  %done = icmp eq i64 %next, 256
+  br i1 %done, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; Reverse the common byte stride from +8 to -8.
+; Different iterations remain disjoint --> Forward.
+define void @load32_store64_reverse(ptr %base, i32 %before, i64 %after) {
+; CHECK-LABEL: 'load32_store64_reverse'
+; CHECK-NEXT:    loop:
+; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
+; CHECK-NEXT:  Unknown data dependence.
+; CHECK-NEXT:      Dependences:
+; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:            %value = load i32, ptr %p, align 1 ->
+; CHECK-NEXT:            store i64 %after, ptr %p, align 1
+; CHECK-EMPTY:
+; CHECK-NEXT:      Run-time memory checks:
+; CHECK-NEXT:      Grouped accesses:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Non vectorizable stores to invariant address were not found in loop.
+; CHECK-NEXT:      SCEV assumptions:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Expressions re-written:
+;
+entry:
+  br label %loop
+
+loop:
+  %offset = phi i64 [ 120, %entry ], [ %next, %latch ]
+  %p = getelementptr inbounds i8, ptr %base, i64 %offset
+  %value = load i32, ptr %p, align 1
+  %match = icmp eq i32 %value, %before
+  br i1 %match, label %store, label %latch
+
+store:
+  store i64 %after, ptr %p, align 1
+  br label %latch
+
+latch:
+  %next = add nsw i64 %offset, -8
+  %done = icmp eq i64 %next, -8
+  br i1 %done, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; Swap the access widths: load 8 bytes / store 4 bytes, byte stride 8.
+; The load may be wider than the store --> Forward.
+define void @load64_store32_stride8(ptr %base, i64 %before, i32 %after) {
+; CHECK-LABEL: 'load64_store32_stride8'
+; CHECK-NEXT:    loop:
+; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
+; CHECK-NEXT:  Unknown data dependence.
+; CHECK-NEXT:      Dependences:
+; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:            %value = load i64, ptr %p, align 1 ->
+; CHECK-NEXT:            store i32 %after, ptr %p, align 1
+; CHECK-EMPTY:
+; CHECK-NEXT:      Run-time memory checks:
+; CHECK-NEXT:      Grouped accesses:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Non vectorizable stores to invariant address were not found in loop.
+; CHECK-NEXT:      SCEV assumptions:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Expressions re-written:
+;
+entry:
+  br label %loop
+
+loop:
+  %offset = phi i64 [ 0, %entry ], [ %next, %latch ]
+  %p = getelementptr inbounds i8, ptr %base, i64 %offset
+  %value = load i64, ptr %p, align 1
+  %match = icmp eq i64 %value, %before
+  br i1 %match, label %store, label %latch
+
+store:
+  store i32 %after, ptr %p, align 1
+  br label %latch
+
+latch:
+  %next = add nsw i64 %offset, 8
+  %done = icmp eq i64 %next, 128
+  br i1 %done, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; Reverse the access order: store 8 bytes before loading 4 bytes.
+; The dependence stays within each iteration --> Forward.
+define void @store64_load32_stride8(ptr %base, i32 %before, i64 %after) {
+; CHECK-LABEL: 'store64_load32_stride8'
+; CHECK-NEXT:    loop:
+; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
+; CHECK-NEXT:  Unknown data dependence.
+; CHECK-NEXT:      Dependences:
+; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:            store i64 %after, ptr %p, align 1 ->
+; CHECK-NEXT:            %value = load i32, ptr %p, align 1
+; CHECK-EMPTY:
+; CHECK-NEXT:      Run-time memory checks:
+; CHECK-NEXT:      Grouped accesses:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Non vectorizable stores to invariant address were not found in loop.
+; CHECK-NEXT:      SCEV assumptions:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Expressions re-written:
+;
+entry:
+  br label %loop
+
+loop:
+  %offset = phi i64 [ 0, %entry ], [ %next, %latch ]
+  %p = getelementptr inbounds i8, ptr %base, i64 %offset
+  store i64 %after, ptr %p, align 1
+  %value = load i32, ptr %p, align 1
+  br label %latch
+
+latch:
+  %next = add nsw i64 %offset, 8
+  %done = icmp eq i64 %next, 128
+  br i1 %done, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; Store 4 bytes then 8 bytes at equal addresses, with byte stride 8.
+; Two writes are also safe across iterations --> Forward.
+; Distinct pointer values with equal address expressions ensure that the
+; writes reach dependence analysis instead of sharing one access key.
+define void @store32_store64_stride8(ptr %base, i32 %first, i64 %second) {
+; CHECK-LABEL: 'store32_store64_stride8'
+; CHECK-NEXT:    loop:
+; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
+; CHECK-NEXT:  Unknown data dependence.
+; CHECK-NEXT:      Dependences:
+; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:            store i32 %first, ptr %p, align 1 ->
+; CHECK-NEXT:            store i64 %second, ptr %q, align 1
+; CHECK-EMPTY:
+; CHECK-NEXT:      Run-time memory checks:
+; CHECK-NEXT:      Grouped accesses:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Non vectorizable stores to invariant address were not found in loop.
+; CHECK-NEXT:      SCEV assumptions:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Expressions re-written:
+;
+entry:
+  br label %loop
+
+loop:
+  %index = phi i64 [ 0, %entry ], [ %next, %loop ]
+  %p = getelementptr inbounds i64, ptr %base, i64 %index
+  %offset = shl nuw nsw i64 %index, 3
+  %q = getelementptr inbounds i8, ptr %base, i64 %offset
+  store i32 %first, ptr %p, align 1
+  store i64 %second, ptr %q, align 1
+  %next = add nuw nsw i64 %index, 1
+  %done = icmp eq i64 %next, 16
+  br i1 %done, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; Let the trip count be a runtime value; keep the common byte stride at 8.
+; Independence does not require a constant trip count --> Forward.
+define void @load32_store64_dynamic_trip_count(ptr %base, i64 %n,
+; CHECK-LABEL: 'load32_store64_dynamic_trip_count'
+; CHECK-NEXT:    loop:
+; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
+; CHECK-NEXT:  Unknown data dependence.
+; CHECK-NEXT:      Dependences:
+; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:            %value = load i32, ptr %p, align 1 ->
+; CHECK-NEXT:            store i64 %after, ptr %p, align 1
+; CHECK-EMPTY:
+; CHECK-NEXT:      Run-time memory checks:
+; CHECK-NEXT:      Grouped accesses:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Non vectorizable stores to invariant address were not found in loop.
+; CHECK-NEXT:      SCEV assumptions:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Expressions re-written:
+;
+                                              i32 %before, i64 %after) {
+entry:
+  %empty = icmp eq i64 %n, 0
+  br i1 %empty, label %exit, label %loop
+
+loop:
+  %index = phi i64 [ 0, %entry ], [ %next, %latch ]
+  %p = getelementptr inbounds i64, ptr %base, i64 %index
+  %value = load i32, ptr %p, align 1
+  %match = icmp eq i32 %value, %before
+  br i1 %match, label %store, label %latch
+
+store:
+  store i64 %after, ptr %p, align 1
+  br label %latch
+
+latch:
+  %next = add nuw i64 %index, 1
+  %done = icmp eq i64 %next, %n
+  br i1 %done, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; Reduce the byte stride to 4 while retaining the 8-byte store.
+; The store overlaps the next iteration's load --> IndirectUnsafe.
+; Stride analysis rejects the access before the zero-distance check.
+define void @load32_store64_overlap(ptr %base, i32 %before, i64 %after) {
+; CHECK-LABEL: 'load32_store64_overlap'
+; CHECK-NEXT:    loop:
+; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
+; CHECK-NEXT:  Unsafe indirect dependence.
+; CHECK-NEXT:      Dependences:
+; CHECK-NEXT:        IndirectUnsafe:
+; CHECK-NEXT:            %value = load i32, ptr %p, align 1 ->
+; CHECK-NEXT:            store i64 %after, ptr %p, align 1
+; CHECK-EMPTY:
+; CHECK-NEXT:      Run-time memory checks:
+; CHECK-NEXT:      Grouped accesses:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Non vectorizable stores to invariant address were not found in loop.
+; CHECK-NEXT:      SCEV assumptions:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Expressions re-written:
+;
+entry:
+  br label %loop
+
+loop:
+  %offset = phi i64 [ 0, %entry ], [ %next, %latch ]
+  %p = getelementptr inbounds i8, ptr %base, i64 %offset
+  %value = load i32, ptr %p, align 1
+  %match = icmp eq i32 %value, %before
+  br i1 %match, label %store, label %latch
+
+store:
+  store i64 %after, ptr %p, align 1
+  br label %latch
+
+latch:
+  %next = add nsw i64 %offset, 4
+  %done = icmp eq i64 %next, 64
+  br i1 %done, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; Load 8 bytes / store 4 bytes with byte stride 4.
+; The load overlaps the next iteration's store --> IndirectUnsafe.
+; Stride analysis rejects the access before the zero-distance check.
+define void @load64_store32_overlap(ptr %base, i64 %before, i32 %after) {
+; CHECK-LABEL: 'load64_store32_overlap'
+; CHECK-NEXT:    loop:
+; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
+; CHECK-NEXT:  Unsafe indirect dependence.
+; CHECK-NEXT:      Dependences:
+; CHECK-NEXT:        IndirectUnsafe:
+; CHECK-NEXT:            %value = load i64, ptr %p, align 1 ->
+; CHECK-NEXT:            store i32 %after, ptr %p, align 1
+; CHECK-EMPTY:
+; CHECK-NEXT:      Run-time memory checks:
+; CHECK-NEXT:      Grouped accesses:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Non vectorizable stores to invariant address were not found in loop.
+; CHECK-NEXT:      SCEV assumptions:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Expressions re-written:
+;
+entry:
+  br label %loop
+
+loop:
+  %offset = phi i64 [ 0, %entry ], [ %next, %latch ]
+  %p = getelementptr inbounds i8, ptr %base, i64 %offset
+  %value = load i64, ptr %p, align 1
+  %match = icmp eq i64 %value, %before
+  br i1 %match, label %store, label %latch
+
+store:
+  store i32 %after, ptr %p, align 1
+  br label %latch
+
+latch:
+  %next = add nsw i64 %offset, 4
+  %done = icmp eq i64 %next, 64
+  br i1 %done, label %exit, label %loop
+
+exit:
+  ret void
+}
+
+; Keep the common byte stride at 8, but move the store 8 bytes ahead.
+; The store overlaps the next iteration's load --> Unknown.
+; A common stride alone must not bypass the positive-distance checks.
+define void @load32_store64_distance8(ptr %base, i32 %before, i64 %after) {
+; CHECK-LABEL: 'load32_store64_distance8'
+; CHECK-NEXT:    loop:
+; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
+; CHECK-NEXT:  Unknown data dependence.
+; CHECK-NEXT:      Dependences:
+; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:            %value = load i32, ptr %p, align 1 ->
+; CHECK-NEXT:            store i64 %after, ptr %q, align 1
+; CHECK-EMPTY:
+; CHECK-NEXT:      Run-time memory checks:
+; CHECK-NEXT:      Grouped accesses:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Non vectorizable stores to invariant address were not found in loop.
+; CHECK-NEXT:      SCEV assumptions:
+; CHECK-EMPTY:
+; CHECK-NEXT:      Expressions re-written:
+;
+entry:
+  br label %loop
+
+loop:
+  %index = phi i64 [ 0, %entry ], [ %next, %latch ]
+  %p = getelementptr inbounds i64, ptr %base, i64 %index
+  %q = getelementptr inbounds i8, ptr %p, i64 8
+  %value = load i32, ptr %p, align 1
+  %match = icmp eq i32 %value, %before
+  br i1 %match, label %store, label %latch
+
+store:
+  store i64 %after, ptr %q, align 1
+  br label %latch
+
+latch:
+  %next = add nuw nsw i64 %index, 1
+  %done = icmp eq i64 %next, 16
+  br i1 %done, label %exit, label %loop
+
+exit:
+  ret void
+}

>From f3ea1cdcaf96f988d36e858cc3851aca6fb17801 Mon Sep 17 00:00:00 2001
From: Chrislee02 <hdchris765 at snu.ac.kr>
Date: Sun, 6 Sep 2026 18:53:42 +0900
Subject: [PATCH 2/3] [LAA] Allow zero-distance mixed-width accesses with a
 common stride

---
 llvm/lib/Analysis/LoopAccessAnalysis.cpp      | 13 ++++-
 .../LoopAccessAnalysis/depend_diff_types.ll   |  9 ++--
 ...bolic-max-backedge-taken-count-may-wrap.ll |  2 +-
 .../zero-distance-mixed-width.ll              | 50 ++++++++-----------
 4 files changed, 36 insertions(+), 38 deletions(-)

diff --git a/llvm/lib/Analysis/LoopAccessAnalysis.cpp b/llvm/lib/Analysis/LoopAccessAnalysis.cpp
index 3b4025bf20a4a..4239cb88da5c8 100644
--- a/llvm/lib/Analysis/LoopAccessAnalysis.cpp
+++ b/llvm/lib/Analysis/LoopAccessAnalysis.cpp
@@ -2377,8 +2377,17 @@ MemoryDepChecker::isDependent(const MemAccessInfo &A, unsigned AIdx,
   // Negative distances are not plausible dependencies.
   if (SE.isKnownNonPositive(Dist)) {
     if (SE.isKnownNonNegative(Dist)) {
-      if (HasSameSize) {
-        // Write to the same location with the same size.
+      if (HasSameSize || CommonStride) {
+        // Equal-sized accesses to the same location are forward.
+        //
+        // Invariant from getDependenceDistanceStrideAndSize: both accesses have
+        // non-zero strides in the same direction, each a multiple of its type's
+        // allocation size in bytes.
+        //
+        // When CommonStride is present, its value is therefore at least as
+        // large as either access size. With equal starting addresses, different
+        // iterations cannot overlap, leaving only a loop-independent forward
+        // dependence, even for mixed sizes.
         return Dependence::Forward;
       }
       LLVM_DEBUG(dbgs() << "LAA: possibly zero dependence difference but "
diff --git a/llvm/test/Analysis/LoopAccessAnalysis/depend_diff_types.ll b/llvm/test/Analysis/LoopAccessAnalysis/depend_diff_types.ll
index 84e9d70a16d3b..babf4a59255b7 100644
--- a/llvm/test/Analysis/LoopAccessAnalysis/depend_diff_types.ll
+++ b/llvm/test/Analysis/LoopAccessAnalysis/depend_diff_types.ll
@@ -76,16 +76,15 @@ exit:
 
 ; In the function below one of the accesses is done as i19 type, which has a
 ; different store size than the i32 type, even though their alloc sizes are
-; equivalent. This is a negative test to ensure that they are not analyzed as
-; in the tests above.
+; equivalent. Both accesses fit within the common 8-byte stride, so the
+; zero-distance dependence is forward despite the different store sizes.
 
 define void @backdep_type_store_size_equivalence(ptr nocapture %vec, i64 %n) {
 ; CHECK-LABEL: 'backdep_type_store_size_equivalence'
 ; CHECK-NEXT:    loop:
-; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
-; CHECK-NEXT:  Unknown data dependence.
+; CHECK-NEXT:      Memory dependences are safe
 ; CHECK-NEXT:      Dependences:
-; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:        Forward:
 ; CHECK-NEXT:            %ld.f32 = load float, ptr %gep.iv, align 8 ->
 ; CHECK-NEXT:            store i19 %indvars.iv.i19, ptr %gep.iv, align 8
 ; CHECK-EMPTY:
diff --git a/llvm/test/Analysis/LoopAccessAnalysis/evaluate-at-symbolic-max-backedge-taken-count-may-wrap.ll b/llvm/test/Analysis/LoopAccessAnalysis/evaluate-at-symbolic-max-backedge-taken-count-may-wrap.ll
index ebbb435bc2264..9ca53333d02d6 100644
--- a/llvm/test/Analysis/LoopAccessAnalysis/evaluate-at-symbolic-max-backedge-taken-count-may-wrap.ll
+++ b/llvm/test/Analysis/LoopAccessAnalysis/evaluate-at-symbolic-max-backedge-taken-count-may-wrap.ll
@@ -94,7 +94,7 @@ define i32 @check_no_dep_via_bounds_compare_symbolic_max_btc_neg_1(ptr %P, i32 %
 ; CHECK-NEXT:            store i64 0, ptr %gep.iv, align 4 ->
 ; CHECK-NEXT:            %l.2 = load i32, ptr %gep.P.4, align 4
 ; CHECK-EMPTY:
-; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:        Forward:
 ; CHECK-NEXT:            %l = load i32, ptr %gep.iv, align 4 ->
 ; CHECK-NEXT:            store i64 0, ptr %gep.iv, align 4
 ; CHECK-EMPTY:
diff --git a/llvm/test/Analysis/LoopAccessAnalysis/zero-distance-mixed-width.ll b/llvm/test/Analysis/LoopAccessAnalysis/zero-distance-mixed-width.ll
index 1679f2ca5accc..514f76fff2f4d 100644
--- a/llvm/test/Analysis/LoopAccessAnalysis/zero-distance-mixed-width.ll
+++ b/llvm/test/Analysis/LoopAccessAnalysis/zero-distance-mixed-width.ll
@@ -9,10 +9,9 @@
 define void @load32_store64_stride8(ptr %base, i32 %before, i64 %after) {
 ; CHECK-LABEL: 'load32_store64_stride8'
 ; CHECK-NEXT:    loop:
-; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
-; CHECK-NEXT:  Unknown data dependence.
+; CHECK-NEXT:      Memory dependences are safe
 ; CHECK-NEXT:      Dependences:
-; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:        Forward:
 ; CHECK-NEXT:            %value = load i32, ptr %p, align 1 ->
 ; CHECK-NEXT:            store i64 %after, ptr %p, align 1
 ; CHECK-EMPTY:
@@ -52,10 +51,9 @@ exit:
 define void @load8_store16_stride2(ptr %base, i8 %before, i16 %after) {
 ; CHECK-LABEL: 'load8_store16_stride2'
 ; CHECK-NEXT:    loop:
-; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
-; CHECK-NEXT:  Unknown data dependence.
+; CHECK-NEXT:      Memory dependences are safe
 ; CHECK-NEXT:      Dependences:
-; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:        Forward:
 ; CHECK-NEXT:            %value = load i8, ptr %p, align 1 ->
 ; CHECK-NEXT:            store i16 %after, ptr %p, align 1
 ; CHECK-EMPTY:
@@ -95,10 +93,9 @@ exit:
 define void @load8_store64_stride8(ptr %base, i8 %before, i64 %after) {
 ; CHECK-LABEL: 'load8_store64_stride8'
 ; CHECK-NEXT:    loop:
-; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
-; CHECK-NEXT:  Unknown data dependence.
+; CHECK-NEXT:      Memory dependences are safe
 ; CHECK-NEXT:      Dependences:
-; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:        Forward:
 ; CHECK-NEXT:            %value = load i8, ptr %p, align 1 ->
 ; CHECK-NEXT:            store i64 %after, ptr %p, align 1
 ; CHECK-EMPTY:
@@ -138,10 +135,9 @@ exit:
 define void @load24_store64_stride8(ptr %base, i24 %before, i64 %after) {
 ; CHECK-LABEL: 'load24_store64_stride8'
 ; CHECK-NEXT:    loop:
-; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
-; CHECK-NEXT:  Unknown data dependence.
+; CHECK-NEXT:      Memory dependences are safe
 ; CHECK-NEXT:      Dependences:
-; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:        Forward:
 ; CHECK-NEXT:            %value = load i24, ptr %p, align 1 ->
 ; CHECK-NEXT:            store i64 %after, ptr %p, align 1
 ; CHECK-EMPTY:
@@ -181,10 +177,9 @@ exit:
 define void @load32_store64_stride16(ptr %base, i32 %before, i64 %after) {
 ; CHECK-LABEL: 'load32_store64_stride16'
 ; CHECK-NEXT:    loop:
-; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
-; CHECK-NEXT:  Unknown data dependence.
+; CHECK-NEXT:      Memory dependences are safe
 ; CHECK-NEXT:      Dependences:
-; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:        Forward:
 ; CHECK-NEXT:            %value = load i32, ptr %p, align 1 ->
 ; CHECK-NEXT:            store i64 %after, ptr %p, align 1
 ; CHECK-EMPTY:
@@ -224,10 +219,9 @@ exit:
 define void @load32_store64_reverse(ptr %base, i32 %before, i64 %after) {
 ; CHECK-LABEL: 'load32_store64_reverse'
 ; CHECK-NEXT:    loop:
-; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
-; CHECK-NEXT:  Unknown data dependence.
+; CHECK-NEXT:      Memory dependences are safe
 ; CHECK-NEXT:      Dependences:
-; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:        Forward:
 ; CHECK-NEXT:            %value = load i32, ptr %p, align 1 ->
 ; CHECK-NEXT:            store i64 %after, ptr %p, align 1
 ; CHECK-EMPTY:
@@ -267,10 +261,9 @@ exit:
 define void @load64_store32_stride8(ptr %base, i64 %before, i32 %after) {
 ; CHECK-LABEL: 'load64_store32_stride8'
 ; CHECK-NEXT:    loop:
-; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
-; CHECK-NEXT:  Unknown data dependence.
+; CHECK-NEXT:      Memory dependences are safe
 ; CHECK-NEXT:      Dependences:
-; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:        Forward:
 ; CHECK-NEXT:            %value = load i64, ptr %p, align 1 ->
 ; CHECK-NEXT:            store i32 %after, ptr %p, align 1
 ; CHECK-EMPTY:
@@ -310,10 +303,9 @@ exit:
 define void @store64_load32_stride8(ptr %base, i32 %before, i64 %after) {
 ; CHECK-LABEL: 'store64_load32_stride8'
 ; CHECK-NEXT:    loop:
-; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
-; CHECK-NEXT:  Unknown data dependence.
+; CHECK-NEXT:      Memory dependences are safe
 ; CHECK-NEXT:      Dependences:
-; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:        Forward:
 ; CHECK-NEXT:            store i64 %after, ptr %p, align 1 ->
 ; CHECK-NEXT:            %value = load i32, ptr %p, align 1
 ; CHECK-EMPTY:
@@ -351,10 +343,9 @@ exit:
 define void @store32_store64_stride8(ptr %base, i32 %first, i64 %second) {
 ; CHECK-LABEL: 'store32_store64_stride8'
 ; CHECK-NEXT:    loop:
-; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
-; CHECK-NEXT:  Unknown data dependence.
+; CHECK-NEXT:      Memory dependences are safe
 ; CHECK-NEXT:      Dependences:
-; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:        Forward:
 ; CHECK-NEXT:            store i32 %first, ptr %p, align 1 ->
 ; CHECK-NEXT:            store i64 %second, ptr %q, align 1
 ; CHECK-EMPTY:
@@ -389,10 +380,9 @@ exit:
 define void @load32_store64_dynamic_trip_count(ptr %base, i64 %n,
 ; CHECK-LABEL: 'load32_store64_dynamic_trip_count'
 ; CHECK-NEXT:    loop:
-; CHECK-NEXT:      Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
-; CHECK-NEXT:  Unknown data dependence.
+; CHECK-NEXT:      Memory dependences are safe
 ; CHECK-NEXT:      Dependences:
-; CHECK-NEXT:        Unknown:
+; CHECK-NEXT:        Forward:
 ; CHECK-NEXT:            %value = load i32, ptr %p, align 1 ->
 ; CHECK-NEXT:            store i64 %after, ptr %p, align 1
 ; CHECK-EMPTY:

>From 0fdc11e2e03d4b6481f43f83a5e8057d35df64e4 Mon Sep 17 00:00:00 2001
From: Chrislee02 <hdchris765 at snu.ac.kr>
Date: Fri, 11 Sep 2026 19:48:22 +0900
Subject: [PATCH 3/3] [LAA] Address review comments for zero-distance
 mixed-width accesses with a common stride

---
 llvm/lib/Analysis/LoopAccessAnalysis.cpp      | 25 +++++++++++--------
 .../zero-distance-mixed-width.ll              |  3 +--
 2 files changed, 15 insertions(+), 13 deletions(-)

diff --git a/llvm/lib/Analysis/LoopAccessAnalysis.cpp b/llvm/lib/Analysis/LoopAccessAnalysis.cpp
index 4239cb88da5c8..16950e92d6f48 100644
--- a/llvm/lib/Analysis/LoopAccessAnalysis.cpp
+++ b/llvm/lib/Analysis/LoopAccessAnalysis.cpp
@@ -2377,17 +2377,20 @@ MemoryDepChecker::isDependent(const MemAccessInfo &A, unsigned AIdx,
   // Negative distances are not plausible dependencies.
   if (SE.isKnownNonPositive(Dist)) {
     if (SE.isKnownNonNegative(Dist)) {
-      if (HasSameSize || CommonStride) {
-        // Equal-sized accesses to the same location are forward.
-        //
-        // Invariant from getDependenceDistanceStrideAndSize: both accesses have
-        // non-zero strides in the same direction, each a multiple of its type's
-        // allocation size in bytes.
-        //
-        // When CommonStride is present, its value is therefore at least as
-        // large as either access size. With equal starting addresses, different
-        // iterations cannot overlap, leaving only a loop-independent forward
-        // dependence, even for mixed sizes.
+      // Equal-sized accesses to the same location are forward.
+      if (HasSameSize)
+        return Dependence::Forward;
+
+      if (CommonStride) {
+        // For mixed sizes, CommonStride is asserted to cover both accesses when
+        // computed in getDependenceDistanceStrideAndSize, so different
+        // iterations cannot overlap.
+        [[maybe_unused]] uint64_t ASz =
+            DL.getTypeAllocSize(getLoadStoreType(InstMap[AIdx]));
+        [[maybe_unused]] uint64_t BSz =
+            DL.getTypeAllocSize(getLoadStoreType(InstMap[BIdx]));
+        assert(*CommonStride >= std::max(ASz, BSz) &&
+               "Invariant from getDependenceDistanceStrideAndSize broken!");
         return Dependence::Forward;
       }
       LLVM_DEBUG(dbgs() << "LAA: possibly zero dependence difference but "
diff --git a/llvm/test/Analysis/LoopAccessAnalysis/zero-distance-mixed-width.ll b/llvm/test/Analysis/LoopAccessAnalysis/zero-distance-mixed-width.ll
index 514f76fff2f4d..ad6634e580ae0 100644
--- a/llvm/test/Analysis/LoopAccessAnalysis/zero-distance-mixed-width.ll
+++ b/llvm/test/Analysis/LoopAccessAnalysis/zero-distance-mixed-width.ll
@@ -377,7 +377,7 @@ exit:
 
 ; Let the trip count be a runtime value; keep the common byte stride at 8.
 ; Independence does not require a constant trip count --> Forward.
-define void @load32_store64_dynamic_trip_count(ptr %base, i64 %n,
+define void @load32_store64_dynamic_trip_count(ptr %base, i64 %n, i32 %before, i64 %after) {
 ; CHECK-LABEL: 'load32_store64_dynamic_trip_count'
 ; CHECK-NEXT:    loop:
 ; CHECK-NEXT:      Memory dependences are safe
@@ -394,7 +394,6 @@ define void @load32_store64_dynamic_trip_count(ptr %base, i64 %n,
 ; CHECK-EMPTY:
 ; CHECK-NEXT:      Expressions re-written:
 ;
-                                              i32 %before, i64 %after) {
 entry:
   %empty = icmp eq i64 %n, 0
   br i1 %empty, label %exit, label %loop



More information about the llvm-commits mailing list