[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:17:38 PDT 2026


https://github.com/davidbolvansky created https://github.com/llvm/llvm-project/pull/226816

Consider this loop:

```c
void update(int *dst, const int *src) {
  for (int i = 0; i != 256; ++i)
    dst[i + 128] += src[i];
}
```

Runtime pointer checking currently falls back to a full-range overlap check because `dst` is both read and written and occurs multiple times in its checking group.

Allow a difference check when every member of each group refers to the same pointer and one pointer is read-modify-write while the other is read-only. Other read/write combinations keep the existing conservative fallback.

This is sound because the additional access is a read against another read, which cannot introduce a memory dependence. The difference check still covers the only potentially conflicting write/read pair.

>From e033eceb0f67a9198146dcf9d45a49b95821f379 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      | 33 ++++++----
 .../runtime-checks-difference.ll              | 60 +++++++++++++++++++
 2 files changed, 82 insertions(+), 11 deletions(-)

diff --git a/llvm/lib/Analysis/LoopAccessAnalysis.cpp b/llvm/lib/Analysis/LoopAccessAnalysis.cpp
index 804ffe5bb2edd..099f4a790d0af 100644
--- a/llvm/lib/Analysis/LoopAccessAnalysis.cpp
+++ b/llvm/lib/Analysis/LoopAccessAnalysis.cpp
@@ -511,19 +511,30 @@ 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) {
+    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