[llvm] [LAA] Don't check same-address accesses against each other on retry (PR #226428)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Sep 25 03:26:04 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-analysis
Author: Tim Besard (maleadt)
<details>
<summary>Changes</summary>
When LAA retries an unknown dependence with runtime checks, canCheckPtrAtRT removes the affected dependence classes, giving each pointer its own dependence set and checking group. This also separates safe dependences, including pointers accessing the same address each iteration, such as stores to A[i] in both branches of an if. Their identical bounds make the runtime check always fail, so the vector loop never runs.
Regroup accesses whose pointers have the same AddRec in the loop, whose loads and stores all have the same type, and whose stride is one element. They access the same bytes each iteration and disjoint bytes across iterations, so their only dependence has distance 0, preserved by vectorization. Consecutive accesses cannot join an interleave group that could reorder them, which matters because the retry discarded those dependences. Sharing a checking group also removes duplicate checks against other accesses.
Only the retry changes, after which recorded dependences are cleared. LoopLoadElimination and other passes relying on checks between classes while dependences are available are unaffected.
Motivation: Julia stores `Union{Float32, Nothing}` array values and type tags in one allocation, with tags at a runtime offset. Copy loops were vectorized with an always-failing check. In a reduced X86 test, memchecks drop from 7 comparisons (including `tags < tags_end` against itself) to 4, letting the vector loop run: 0.33 -> 0.07 ns/element on Zen 5 (n = 10000).
Assisted by: Codex 5.5, Astra 6
Part of a series of patches developed while migrating Julia to LLVM 23
---
Patch is 30.44 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/226428.diff
4 Files Affected:
- (modified) llvm/lib/Analysis/LoopAccessAnalysis.cpp (+36)
- (added) llvm/test/Analysis/LoopAccessAnalysis/retry-runtime-checks-same-address.ll (+338)
- (modified) llvm/test/Analysis/LoopAccessAnalysis/runtime-checks-max-dependences.ll (+122)
- (added) llvm/test/Transforms/LoopVectorize/X86/runtime-checks-same-address.ll (+120)
``````````diff
diff --git a/llvm/lib/Analysis/LoopAccessAnalysis.cpp b/llvm/lib/Analysis/LoopAccessAnalysis.cpp
index 409d9ceb5b812..08952a2a7e277 100644
--- a/llvm/lib/Analysis/LoopAccessAnalysis.cpp
+++ b/llvm/lib/Analysis/LoopAccessAnalysis.cpp
@@ -1526,6 +1526,42 @@ bool AccessAnalysis::canCheckPtrAtRT(RuntimePointerChecking &RtCheck,
continue;
}
+ // Removing dependence classes above also separates different pointers that
+ // access the same address in each iteration, like stores to A[i] in both
+ // branches of an if, and a runtime check between them would always fail.
+ // Put such accesses back into a common class if all loads and stores
+ // through them have the same type and the stride is one element: they then
+ // access the same bytes in each iteration, and bytes no other iteration
+ // accesses, so they only depend on each other within an iteration, which
+ // vectorization preserves. Being consecutive, they cannot be part of an
+ // interleave group, which could reorder them.
+ SmallDenseMap<std::pair<const SCEV *, Type *>, MemAccessInfo, 4>
+ SameAddressAccesses;
+ for (MemAccessInfo Access : AccessInfos) {
+ if (DepCands.contains(Access))
+ continue;
+ Value *Ptr = Access.getPointer();
+ SmallSetVector<Type *, 1> AccessTys;
+ for (bool IsWrite : {false, true})
+ if (auto It = Accesses.find(MemAccessInfo(Ptr, IsWrite));
+ It != Accesses.end())
+ AccessTys.insert_range(It->second);
+ if (AccessTys.size() != 1)
+ continue;
+ Type *AccessTy = AccessTys.front();
+ const auto *AR = dyn_cast<SCEVAddRecExpr>(PSE.getSCEV(Ptr));
+ if (!AR)
+ continue;
+ std::optional<int64_t> Stride =
+ getStrideFromAddRec(AR, TheLoop, AccessTy, Ptr, PSE);
+ if (Stride != 1 && Stride != -1)
+ continue;
+ auto [It, Inserted] =
+ SameAddressAccesses.try_emplace({AR, AccessTy}, Access);
+ if (!Inserted)
+ DepCands.unionSets(It->second, Access);
+ }
+
for (auto &Access : AccessInfos) {
for (const auto &AccessTy : Accesses[Access]) {
if (!createCheckForAccess(RtCheck, Access, AccessTy, StridesMap,
diff --git a/llvm/test/Analysis/LoopAccessAnalysis/retry-runtime-checks-same-address.ll b/llvm/test/Analysis/LoopAccessAnalysis/retry-runtime-checks-same-address.ll
new file mode 100644
index 0000000000000..5455cde87150d
--- /dev/null
+++ b/llvm/test/Analysis/LoopAccessAnalysis/retry-runtime-checks-same-address.ll
@@ -0,0 +1,338 @@
+; 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
+
+; The accesses to %A.sel and %A have a non-constant distance, so the
+; dependence analysis gives up and LAA retries with runtime checks between all
+; accesses to %A. Accesses through different pointers to the same address in
+; each iteration only depend on each other within an iteration. They must not
+; be checked against each other, as that check would always fail.
+
+define void @same_address_complementary_stores(ptr %A, i64 %off, ptr noalias %c, i64 %n) {
+; CHECK-LABEL: 'same_address_complementary_stores'
+; CHECK-NEXT: loop:
+; CHECK-NEXT: Memory dependences are safe with run-time checks
+; CHECK-NEXT: Dependences:
+; CHECK-NEXT: Run-time memory checks:
+; CHECK-NEXT: Check 0:
+; CHECK-NEXT: Comparing group GRP0:
+; CHECK-NEXT: %gep.sel.0 = getelementptr inbounds i8, ptr %A.sel, i64 %iv
+; CHECK-NEXT: %gep.sel.1 = getelementptr inbounds i8, ptr %A.sel, i64 %iv
+; CHECK-NEXT: Against group GRP1:
+; CHECK-NEXT: %gep.A = getelementptr inbounds i32, ptr %A, i64 %iv
+; CHECK-NEXT: Grouped accesses:
+; CHECK-NEXT: Group GRP0:
+; CHECK-NEXT: (Low: (%off + %A) High: (%off + %n + %A))
+; CHECK-NEXT: Member: {(%off + %A),+,1}<nw><%loop>
+; CHECK-NEXT: Member: {(%off + %A),+,1}<nw><%loop>
+; CHECK-NEXT: Group GRP1:
+; CHECK-NEXT: (Low: %A High: ((4 * %n) + %A))
+; CHECK-NEXT: Member: {%A,+,4}<%loop>
+; 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:
+ %A.sel = getelementptr i8, ptr %A, i64 %off
+ br label %loop
+
+loop:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %latch ]
+ %gep.c = getelementptr inbounds i8, ptr %c, i64 %iv
+ %c.val = load i8, ptr %gep.c, align 1
+ %cond = icmp eq i8 %c.val, 0
+ br i1 %cond, label %then, label %else
+
+then:
+ %gep.A = getelementptr inbounds i32, ptr %A, i64 %iv
+ store i32 0, ptr %gep.A, align 4
+ %gep.sel.1 = getelementptr inbounds i8, ptr %A.sel, i64 %iv
+ store i8 1, ptr %gep.sel.1, align 1
+ br label %latch
+
+else:
+ %gep.sel.0 = getelementptr inbounds i8, ptr %A.sel, i64 %iv
+ store i8 0, ptr %gep.sel.0, align 1
+ br label %latch
+
+latch:
+ %iv.next = add nuw nsw i64 %iv, 1
+ %ec = icmp eq i64 %iv.next, %n
+ br i1 %ec, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+define void @same_address_load_store(ptr %A, i64 %off, i64 %n) {
+; CHECK-LABEL: 'same_address_load_store'
+; CHECK-NEXT: loop:
+; CHECK-NEXT: Memory dependences are safe with run-time checks
+; CHECK-NEXT: Dependences:
+; CHECK-NEXT: Run-time memory checks:
+; CHECK-NEXT: Check 0:
+; CHECK-NEXT: Comparing group GRP0:
+; CHECK-NEXT: %gep.A = getelementptr inbounds i32, ptr %A, i64 %iv
+; CHECK-NEXT: Against group GRP1:
+; CHECK-NEXT: %gep.sel.1 = getelementptr inbounds i8, ptr %A.sel, i64 %iv
+; CHECK-NEXT: %gep.sel.0 = getelementptr inbounds i8, ptr %A.sel, i64 %iv
+; CHECK-NEXT: Grouped accesses:
+; CHECK-NEXT: Group GRP0:
+; CHECK-NEXT: (Low: %A High: ((4 * %n) + %A))
+; CHECK-NEXT: Member: {%A,+,4}<nuw><%loop>
+; CHECK-NEXT: Group GRP1:
+; CHECK-NEXT: (Low: (%off + %A) High: (%off + %n + %A))
+; CHECK-NEXT: Member: {(%off + %A),+,1}<nw><%loop>
+; CHECK-NEXT: Member: {(%off + %A),+,1}<nw><%loop>
+; 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:
+ %A.sel = getelementptr i8, ptr %A, i64 %off
+ br label %loop
+
+loop:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
+ %gep.A = getelementptr inbounds i32, ptr %A, i64 %iv
+ store i32 0, ptr %gep.A, align 4
+ %gep.sel.0 = getelementptr inbounds i8, ptr %A.sel, i64 %iv
+ %l = load i8, ptr %gep.sel.0, align 1
+ %add = add i8 %l, 1
+ %gep.sel.1 = getelementptr inbounds i8, ptr %A.sel, i64 %iv
+ store i8 %add, ptr %gep.sel.1, align 1
+ %iv.next = add nuw nsw i64 %iv, 1
+ %ec = icmp eq i64 %iv.next, %n
+ br i1 %ec, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+define void @same_address_negative_stride(ptr %A, i64 %off, i64 %n) {
+; CHECK-LABEL: 'same_address_negative_stride'
+; CHECK-NEXT: loop:
+; CHECK-NEXT: Memory dependences are safe with run-time checks
+; CHECK-NEXT: Dependences:
+; CHECK-NEXT: Run-time memory checks:
+; CHECK-NEXT: Check 0:
+; CHECK-NEXT: Comparing group GRP0:
+; CHECK-NEXT: %gep.A = getelementptr inbounds i32, ptr %A, i64 %iv.next
+; CHECK-NEXT: Against group GRP1:
+; CHECK-NEXT: %gep.sel.0 = getelementptr inbounds i8, ptr %A.sel, i64 %iv.next
+; CHECK-NEXT: %gep.sel.1 = getelementptr inbounds i8, ptr %A.sel, i64 %iv.next
+; CHECK-NEXT: Grouped accesses:
+; CHECK-NEXT: Group GRP0:
+; CHECK-NEXT: (Low: (-4 + (4 * (1 smin %n)) + %A) High: ((4 * %n) + %A))
+; CHECK-NEXT: Member: {(-4 + (4 * %n) + %A),+,-4}<%loop>
+; CHECK-NEXT: Group GRP1:
+; CHECK-NEXT: (Low: (-1 + (1 smin %n) + %off + %A) High: (%off + %n + %A))
+; CHECK-NEXT: Member: {(-1 + %off + %n + %A),+,-1}<nw><%loop>
+; CHECK-NEXT: Member: {(-1 + %off + %n + %A),+,-1}<nw><%loop>
+; 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:
+ %A.sel = getelementptr i8, ptr %A, i64 %off
+ br label %loop
+
+loop:
+ %iv = phi i64 [ %n, %entry ], [ %iv.next, %loop ]
+ %iv.next = add nsw i64 %iv, -1
+ %gep.A = getelementptr inbounds i32, ptr %A, i64 %iv.next
+ store i32 0, ptr %gep.A, align 4
+ %gep.sel.0 = getelementptr inbounds i8, ptr %A.sel, i64 %iv.next
+ store i8 0, ptr %gep.sel.0, align 1
+ %gep.sel.1 = getelementptr inbounds i8, ptr %A.sel, i64 %iv.next
+ store i8 1, ptr %gep.sel.1, align 1
+ %ec = icmp sgt i64 %iv, 1
+ br i1 %ec, label %loop, label %exit
+
+exit:
+ ret void
+}
+
+; The accesses have the same address in each iteration, but different sizes,
+; keep checking them.
+define void @same_address_different_sizes(ptr %A, i64 %off, i64 %n) {
+; CHECK-LABEL: 'same_address_different_sizes'
+; CHECK-NEXT: loop:
+; CHECK-NEXT: Memory dependences are safe with run-time checks
+; CHECK-NEXT: Dependences:
+; CHECK-NEXT: Run-time memory checks:
+; CHECK-NEXT: Check 0:
+; CHECK-NEXT: Comparing group GRP0:
+; CHECK-NEXT: %gep.A = getelementptr inbounds i32, ptr %A, i64 %iv
+; CHECK-NEXT: Against group GRP1:
+; CHECK-NEXT: %gep.sel.0 = getelementptr inbounds i16, ptr %A.sel, i64 %iv
+; CHECK-NEXT: Check 1:
+; CHECK-NEXT: Comparing group GRP0:
+; CHECK-NEXT: %gep.A = getelementptr inbounds i32, ptr %A, i64 %iv
+; CHECK-NEXT: Against group GRP2:
+; CHECK-NEXT: %gep.sel.1 = getelementptr inbounds i8, ptr %A.sel, i64 %iv.2
+; CHECK-NEXT: Check 2:
+; CHECK-NEXT: Comparing group GRP1:
+; CHECK-NEXT: %gep.sel.0 = getelementptr inbounds i16, ptr %A.sel, i64 %iv
+; CHECK-NEXT: Against group GRP2:
+; CHECK-NEXT: %gep.sel.1 = getelementptr inbounds i8, ptr %A.sel, i64 %iv.2
+; CHECK-NEXT: Grouped accesses:
+; CHECK-NEXT: Group GRP0:
+; CHECK-NEXT: (Low: %A High: ((4 * %n) + %A))
+; CHECK-NEXT: Member: {%A,+,4}<nuw><%loop>
+; CHECK-NEXT: Group GRP1:
+; CHECK-NEXT: (Low: (%off + %A) High: ((2 * %n) + %off + %A))
+; CHECK-NEXT: Member: {(%off + %A),+,2}<nw><%loop>
+; CHECK-NEXT: Group GRP2:
+; CHECK-NEXT: (Low: (%off + %A) High: (-1 + (2 * %n) + %off + %A))
+; CHECK-NEXT: Member: {(%off + %A),+,2}<nw><%loop>
+; 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:
+ %A.sel = getelementptr i8, ptr %A, i64 %off
+ br label %loop
+
+loop:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
+ %gep.A = getelementptr inbounds i32, ptr %A, i64 %iv
+ store i32 0, ptr %gep.A, align 4
+ %gep.sel.0 = getelementptr inbounds i16, ptr %A.sel, i64 %iv
+ store i16 0, ptr %gep.sel.0, align 2
+ %iv.2 = shl nuw nsw i64 %iv, 1
+ %gep.sel.1 = getelementptr inbounds i8, ptr %A.sel, i64 %iv.2
+ store i8 1, ptr %gep.sel.1, align 1
+ %iv.next = add nuw nsw i64 %iv, 1
+ %ec = icmp eq i64 %iv.next, %n
+ br i1 %ec, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; The accesses have the same address in each iteration, but are strided and
+; could be reordered as part of interleave groups, keep checking them.
+define void @same_address_strided(ptr %A, i64 %off, i64 %n) {
+; CHECK-LABEL: 'same_address_strided'
+; CHECK-NEXT: loop:
+; CHECK-NEXT: Memory dependences are safe with run-time checks
+; CHECK-NEXT: Dependences:
+; CHECK-NEXT: Run-time memory checks:
+; CHECK-NEXT: Check 0:
+; CHECK-NEXT: Comparing group GRP0:
+; CHECK-NEXT: %gep.A = getelementptr inbounds i32, ptr %A, i64 %iv.2
+; CHECK-NEXT: Against group GRP1:
+; CHECK-NEXT: %gep.sel.0 = getelementptr inbounds i8, ptr %A.sel, i64 %iv.2
+; CHECK-NEXT: Check 1:
+; CHECK-NEXT: Comparing group GRP0:
+; CHECK-NEXT: %gep.A = getelementptr inbounds i32, ptr %A, i64 %iv.2
+; CHECK-NEXT: Against group GRP2:
+; CHECK-NEXT: %gep.sel.1 = getelementptr inbounds i8, ptr %A.sel, i64 %iv.2
+; CHECK-NEXT: Check 2:
+; CHECK-NEXT: Comparing group GRP1:
+; CHECK-NEXT: %gep.sel.0 = getelementptr inbounds i8, ptr %A.sel, i64 %iv.2
+; CHECK-NEXT: Against group GRP2:
+; CHECK-NEXT: %gep.sel.1 = getelementptr inbounds i8, ptr %A.sel, i64 %iv.2
+; CHECK-NEXT: Grouped accesses:
+; CHECK-NEXT: Group GRP0:
+; CHECK-NEXT: (Low: %A High: (-4 + (8 * %n) + %A))
+; CHECK-NEXT: Member: {%A,+,8}<nuw><%loop>
+; CHECK-NEXT: Group GRP1:
+; CHECK-NEXT: (Low: (%off + %A) High: (-1 + (2 * %n) + %off + %A))
+; CHECK-NEXT: Member: {(%off + %A),+,2}<nw><%loop>
+; CHECK-NEXT: Group GRP2:
+; CHECK-NEXT: (Low: (%off + %A) High: (-1 + (2 * %n) + %off + %A))
+; CHECK-NEXT: Member: {(%off + %A),+,2}<nw><%loop>
+; 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:
+ %A.sel = getelementptr i8, ptr %A, i64 %off
+ br label %loop
+
+loop:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
+ %iv.2 = shl nuw nsw i64 %iv, 1
+ %gep.A = getelementptr inbounds i32, ptr %A, i64 %iv.2
+ store i32 0, ptr %gep.A, align 4
+ %gep.sel.0 = getelementptr inbounds i8, ptr %A.sel, i64 %iv.2
+ store i8 0, ptr %gep.sel.0, align 1
+ %gep.sel.1 = getelementptr inbounds i8, ptr %A.sel, i64 %iv.2
+ store i8 1, ptr %gep.sel.1, align 1
+ %iv.next = add nuw nsw i64 %iv, 1
+ %ec = icmp eq i64 %iv.next, %n
+ br i1 %ec, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; The accesses have the same address and size in each iteration, but different
+; types, keep checking them.
+define void @same_address_different_types(ptr %A, i64 %off, i64 %n) {
+; CHECK-LABEL: 'same_address_different_types'
+; CHECK-NEXT: loop:
+; CHECK-NEXT: Memory dependences are safe with run-time checks
+; CHECK-NEXT: Dependences:
+; CHECK-NEXT: Run-time memory checks:
+; CHECK-NEXT: Check 0:
+; CHECK-NEXT: Comparing group GRP0:
+; CHECK-NEXT: %gep.A = getelementptr inbounds i8, ptr %A, i64 %iv
+; CHECK-NEXT: Against group GRP1:
+; CHECK-NEXT: %gep.sel.0 = getelementptr inbounds i32, ptr %A.sel, i64 %iv
+; CHECK-NEXT: Check 1:
+; CHECK-NEXT: Comparing group GRP0:
+; CHECK-NEXT: %gep.A = getelementptr inbounds i8, ptr %A, i64 %iv
+; CHECK-NEXT: Against group GRP2:
+; CHECK-NEXT: %gep.sel.1 = getelementptr inbounds float, ptr %A.sel, i64 %iv
+; CHECK-NEXT: Check 2:
+; CHECK-NEXT: Comparing group GRP1:
+; CHECK-NEXT: %gep.sel.0 = getelementptr inbounds i32, ptr %A.sel, i64 %iv
+; CHECK-NEXT: Against group GRP2:
+; CHECK-NEXT: %gep.sel.1 = getelementptr inbounds float, ptr %A.sel, i64 %iv
+; CHECK-NEXT: Grouped accesses:
+; CHECK-NEXT: Group GRP0:
+; CHECK-NEXT: (Low: %A High: (%n + %A))
+; CHECK-NEXT: Member: {%A,+,1}<nuw><%loop>
+; CHECK-NEXT: Group GRP1:
+; CHECK-NEXT: (Low: (%off + %A) High: ((4 * %n) + %off + %A))
+; CHECK-NEXT: Member: {(%off + %A),+,4}<nw><%loop>
+; CHECK-NEXT: Group GRP2:
+; CHECK-NEXT: (Low: (%off + %A) High: ((4 * %n) + %off + %A))
+; CHECK-NEXT: Member: {(%off + %A),+,4}<nw><%loop>
+; 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:
+ %A.sel = getelementptr i8, ptr %A, i64 %off
+ br label %loop
+
+loop:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
+ %gep.A = getelementptr inbounds i8, ptr %A, i64 %iv
+ store i8 0, ptr %gep.A, align 1
+ %gep.sel.0 = getelementptr inbounds i32, ptr %A.sel, i64 %iv
+ store i32 0, ptr %gep.sel.0, align 4
+ %gep.sel.1 = getelementptr inbounds float, ptr %A.sel, i64 %iv
+ store float 1.0, ptr %gep.sel.1, align 4
+ %iv.next = add nuw nsw i64 %iv, 1
+ %ec = icmp eq i64 %iv.next, %n
+ br i1 %ec, label %exit, label %loop
+
+exit:
+ ret void
+}
diff --git a/llvm/test/Analysis/LoopAccessAnalysis/runtime-checks-max-dependences.ll b/llvm/test/Analysis/LoopAccessAnalysis/runtime-checks-max-dependences.ll
index 5b17920bde85e..0eecc5eb888e1 100644
--- a/llvm/test/Analysis/LoopAccessAnalysis/runtime-checks-max-dependences.ll
+++ b/llvm/test/Analysis/LoopAccessAnalysis/runtime-checks-max-dependences.ll
@@ -1,6 +1,7 @@
; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 5
; RUN: opt -passes='print<access-info>' -disable-output %s 2>&1 | FileCheck --check-prefix=DEFAULT %s
; RUN: opt -passes='print<access-info>' -disable-output -max-dependences=5 %s 2>&1 | FileCheck --check-prefix=MAXDEP5 %s
+; RUN: opt -passes='print<access-info>' -disable-output -max-dependences=1 %s 2>&1 | FileCheck --check-prefix=MAXDEP1 %s
define void @dependences_with_strides(ptr %dst, i64 %stride) {
; DEFAULT-LABEL: 'dependences_with_strides'
@@ -75,6 +76,17 @@ define void @dependences_with_strides(ptr %dst, i64 %stride) {
; MAXDEP5-EMPTY:
; MAXDEP5-NEXT: Expressions re-written:
;
+; MAXDEP1-LABEL: 'dependences_with_strides'
+; MAXDEP1-NEXT: loop:
+; MAXDEP1-NEXT: Too many dependences, not recorded
+; MAXDEP1-NEXT: Run-time memory checks:
+; MAXDEP1-NEXT: Grouped accesses:
+; MAXDEP1-EMPTY:
+; MAXDEP1-NEXT: Non vectorizable stores to invariant address were not found in loop.
+; MAXDEP1-NEXT: SCEV assumptions:
+; MAXDEP1-EMPTY:
+; MAXDEP1-NEXT: Expressions re-written:
+;
entry:
br label %loop
@@ -94,3 +106,113 @@ loop:
exit:
ret void
}
+
+; %p and %q have the same address in each iteration, but %p is also used by a
+; wider load, which overlaps the byte stored through %q in the next iteration.
+; The dependence between the load and the store through %q is not recorded with
+; -max-dependences=1, so %p and %q must still be checked against each other.
+define void @same_address_mixed_access_types(ptr %A, i64 %off, ptr noalias %out, i64 %n) {
+; DEFAULT-LABEL: 'same_address_mixed_access_types'
+; DEFAULT-NEXT: loop:
+; DEFAULT-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
+; DEFAULT-NEXT: Unknown data dependence.
+; DEFAULT-NEXT: Dependences:
+; DEFAULT-NEXT: Unknown:
+; DEFAULT-NEXT: store i8 2, ptr %q, align 1 ->
+; DEFAULT-NEXT: %l.r = load i8, ptr %r, align 1
+; DEFAULT-EMPTY:
+; DEFAULT-NEXT: Unknown:
+; DEFAULT-NEXT: store i8 1, ptr %p, align 1 ->
+; DEFAULT-NEXT: %l.r = load i8, ptr %r, align 1
+; DEFAULT-EMPTY:
+; DEFAULT-NEXT: IndirectUnsafe:
+; DEFAULT-NEXT: %l.p = load i16, ptr %p, align 1 ->
+; DEFAULT-NEXT: store i8 2, ptr %q, align 1
+; DEFAULT-EMPTY:
+; DEFAULT-NEXT: IndirectUnsafe:
+; DEFAULT-NEXT: store i8 1, ptr %p, align 1 ->
+; DEFAULT-NEXT: %l.p = load i16, ptr %p, align 1
+; DEFAULT-EMPTY:
+; DEFAULT-NEXT: Forward:
+; DEFAULT-NEXT: store i8 1, ptr %p, align 1 ->
+; DEFAULT-NEXT: store i8 2, ptr %q, align 1
+; DEFAULT-EMPTY:
+; DEFAULT-NEXT: Run-time memory checks:
+; DEFAULT-NEXT: Grouped accesses:
+; DEFAULT-EMPTY:
+; DEFAULT-NEXT: Non vectorizable stores to invariant address were not found in loop.
+; DEFAULT-NEXT: SCEV assumptions:
+; DEFAULT-EMPTY:
+; DEFAULT-NEXT: Expressions re-written:
+;
+; MAXDEP5-LABEL: 'same_address_mixed_access_types'
+; MAXDEP5-NEXT: loop:
+; MAXDEP5-NEXT: Too many dependences, not recorded
+; MAXDEP5-NEXT: Run-time memory checks:
+; MAXDEP5-NEXT: Grouped accesses:
+; MAXDEP5-EMPTY:
+; MA...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/226428
More information about the llvm-commits
mailing list