[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