[llvm] [LAA] Use diff checks for read-modify-write pointers (PR #226816)
Dávid Bolvanský via llvm-commits
llvm-commits at lists.llvm.org
Sun Sep 27 11:27:51 PDT 2026
https://github.com/davidbolvansky updated https://github.com/llvm/llvm-project/pull/226816
>From 1a6c9be9cf3a4d3334164ff9afc3dbae9ddfe7f8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1vid=20Bolvansk=C3=BD?= <david.bolvansky at gmail.com>
Date: Sun, 27 Sep 2026 20:16:40 +0200
Subject: [PATCH] [LAA] Use diff checks for read-modify-write pointers
---
llvm/lib/Analysis/LoopAccessAnalysis.cpp | 34 +++++++----
.../runtime-checks-difference.ll | 60 +++++++++++++++++++
2 files changed, 83 insertions(+), 11 deletions(-)
diff --git a/llvm/lib/Analysis/LoopAccessAnalysis.cpp b/llvm/lib/Analysis/LoopAccessAnalysis.cpp
index 804ffe5bb2edd..d2dad611905f6 100644
--- a/llvm/lib/Analysis/LoopAccessAnalysis.cpp
+++ b/llvm/lib/Analysis/LoopAccessAnalysis.cpp
@@ -511,19 +511,31 @@ bool RuntimePointerChecking::insert(Loop *Lp, Value *Ptr, const SCEV *PtrExpr,
bool RuntimePointerChecking::tryToCreateDiffCheck(
const RuntimeCheckingPtrGroup &CGI, const RuntimeCheckingPtrGroup &CGJ) {
- // If either group contains multiple different pointers, bail out.
- // TODO: Support multiple pointers by using the minimum or maximum pointer,
- // depending on src & sink.
- if (CGI.Members.size() != 1 || CGJ.Members.size() != 1)
+ // A read-modify-write pointer may occur multiple times in a group. Accept
+ // such groups as long as all members refer to the same pointer info.
+ auto GetSinglePointer = [&](const RuntimeCheckingPtrGroup &CG)
+ -> const PointerInfo * {
+ unsigned Index = CG.Members.front();
+ if (!all_of(CG.Members, [Index](unsigned I) { return I == Index; }))
+ return static_cast<const PointerInfo *>(nullptr);
+ return &Pointers[Index];
+ };
+ const PointerInfo *Src = GetSinglePointer(CGI);
+ const PointerInfo *Sink = GetSinglePointer(CGJ);
+ if (!Src || !Sink)
return false;
- const PointerInfo *Src = &Pointers[CGI.Members[0]];
- const PointerInfo *Sink = &Pointers[CGJ.Members[0]];
-
- // If either pointer is read and written, multiple checks may be needed. Bail
- // out.
- if (!DC.getOrderForAccess(Src->PointerValue, !Src->IsWritePtr).empty() ||
- !DC.getOrderForAccess(Sink->PointerValue, !Sink->IsWritePtr).empty())
+ bool SrcHasOppositeAccess =
+ !DC.getOrderForAccess(Src->PointerValue, !Src->IsWritePtr).empty();
+ bool SinkHasOppositeAccess =
+ !DC.getOrderForAccess(Sink->PointerValue, !Sink->IsWritePtr).empty();
+ // A read-modify-write pointer against a read-only pointer only needs the
+ // write/read check. Other combinations may need multiple checks.
+ if (SrcHasOppositeAccess &&
+ !(Src->IsWritePtr && !Sink->IsWritePtr && !SinkHasOppositeAccess))
+ return false;
+ if (SinkHasOppositeAccess &&
+ !(Sink->IsWritePtr && !Src->IsWritePtr && !SrcHasOppositeAccess))
return false;
ArrayRef<unsigned> AccSrc =
diff --git a/llvm/test/Transforms/LoopVectorize/runtime-checks-difference.ll b/llvm/test/Transforms/LoopVectorize/runtime-checks-difference.ll
index e816b559bffe6..576546eaa59f9 100644
--- a/llvm/test/Transforms/LoopVectorize/runtime-checks-difference.ll
+++ b/llvm/test/Transforms/LoopVectorize/runtime-checks-difference.ll
@@ -907,3 +907,63 @@ loop:
exit:
ret void
}
+
+; The first pointer is read and written, while the second is read-only. The
+; read/read pair does not need checking, so use a difference check for the
+; write/read dependence instead of a full-range overlap check.
+define void @read_modify_write_and_read_only(ptr %dst, ptr %src) {
+; CHECK-LABEL: define void @read_modify_write_and_read_only(
+; CHECK: vector.memcheck:
+; CHECK-NOT: bound
+; CHECK: [[DIFF_CHECK:%.*]] = icmp ult i64 {{.*}}, 15
+; CHECK-NEXT: br i1 [[DIFF_CHECK]], label %{{.*}}, label %{{.*}}
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
+ %src.gep = getelementptr inbounds i32, ptr %src, i64 %iv
+ %src.load = load i32, ptr %src.gep
+ %dst.base = getelementptr inbounds i32, ptr %dst, i64 128
+ %dst.gep = getelementptr inbounds i32, ptr %dst.base, i64 %iv
+ %dst.load = load i32, ptr %dst.gep
+ %add = add i32 %dst.load, %src.load
+ store i32 %add, ptr %dst.gep
+ %iv.next = add nuw nsw i64 %iv, 1
+ %exitcond = icmp eq i64 %iv.next, 256
+ br i1 %exitcond, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; A read-modify-write pointer against a write pointer may need multiple checks.
+; Keep the full-range overlap check in this case.
+define void @read_modify_write_and_write_only(ptr %dst, ptr %other) {
+; CHECK-LABEL: define void @read_modify_write_and_write_only(
+; CHECK: vector.memcheck:
+; CHECK: [[BOUND0:%.*]] = icmp ult ptr {{.*}}, {{.*}}
+; CHECK-NEXT: [[BOUND1:%.*]] = icmp ult ptr {{.*}}, {{.*}}
+; CHECK-NEXT: [[FOUND_CONFLICT:%.*]] = and i1 [[BOUND0]], [[BOUND1]]
+; CHECK-NEXT: br i1 [[FOUND_CONFLICT]], label %{{.*}}, label %{{.*}}
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
+ %dst.base = getelementptr inbounds i32, ptr %dst, i64 128
+ %dst.gep = getelementptr inbounds i32, ptr %dst.base, i64 %iv
+ %dst.load = load i32, ptr %dst.gep
+ %add = add i32 %dst.load, 1
+ store i32 %add, ptr %dst.gep
+ %other.gep = getelementptr inbounds i32, ptr %other, i64 %iv
+ store i32 %dst.load, ptr %other.gep
+ %iv.next = add nuw nsw i64 %iv, 1
+ %exitcond = icmp eq i64 %iv.next, 256
+ br i1 %exitcond, label %exit, label %loop
+
+exit:
+ ret void
+}
More information about the llvm-commits
mailing list