[llvm] [AA] Teach getModRefInfo(FenceInst) to consult the AA chain (PR #192043)

via llvm-commits llvm-commits at lists.llvm.org
Tue Apr 14 05:20:56 PDT 2026


https://github.com/michaelselehov created https://github.com/llvm/llvm-project/pull/192043

Extend the AA chain (Concept / Model / AAResultBase) with a getModRefInfo(FenceInst) overload, mirroring the existing CallBase path.  Implement it in ScopedNoAliasAAResult so that !noalias metadata on fences is respected.  Previously, AAResults::getModRefInfo(FenceInst) only checked getModRefInfoMask and ignored individual AA passes.

Assisted-by: Claude Opus

>From 8cf688927069c58a7fb6b8114157fbf3cc2ecfea Mon Sep 17 00:00:00 2001
From: mselehov <mselehov at amd.com>
Date: Tue, 14 Apr 2026 07:13:41 -0500
Subject: [PATCH] [AA] Teach getModRefInfo(FenceInst) to consult the AA chain

Extend the AA chain (Concept / Model / AAResultBase) with a
getModRefInfo(FenceInst) overload, mirroring the existing CallBase
path.  Implement it in ScopedNoAliasAAResult so that !noalias metadata
on fences is respected.  Previously, AAResults::getModRefInfo(FenceInst)
only checked getModRefInfoMask and ignored individual AA passes.

Assisted-by: Claude Opus
---
 llvm/include/llvm/Analysis/AliasAnalysis.h    | 16 ++++
 .../llvm/Analysis/BasicAliasAnalysis.h        |  1 +
 llvm/include/llvm/Analysis/ScopedNoAliasAA.h  |  4 +
 .../llvm/Analysis/TypeBasedAliasAnalysis.h    |  1 +
 llvm/lib/Analysis/AliasAnalysis.cpp           | 23 ++++--
 llvm/lib/Analysis/ScopedNoAliasAA.cpp         | 18 +++++
 .../AMDGPU/fence-noalias-metadata.ll          | 77 +++++++++++++++++++
 7 files changed, 135 insertions(+), 5 deletions(-)
 create mode 100644 llvm/test/Transforms/LoadStoreVectorizer/AMDGPU/fence-noalias-metadata.ll

diff --git a/llvm/include/llvm/Analysis/AliasAnalysis.h b/llvm/include/llvm/Analysis/AliasAnalysis.h
index 1bd6fc48da289..4c199bb6e97c5 100644
--- a/llvm/include/llvm/Analysis/AliasAnalysis.h
+++ b/llvm/include/llvm/Analysis/AliasAnalysis.h
@@ -810,6 +810,12 @@ class LLVM_ABI AAResults::Concept {
   virtual ModRefInfo getModRefInfo(const CallBase *Call1, const CallBase *Call2,
                                    AAQueryInfo &AAQI) = 0;
 
+  /// getModRefInfo (for fences) - Return information about whether
+  /// a particular fence modifies or reads the specified memory location.
+  virtual ModRefInfo getModRefInfo(const FenceInst *S,
+                                   const MemoryLocation &Loc,
+                                   AAQueryInfo &AAQI) = 0;
+
   /// @}
 };
 
@@ -862,6 +868,11 @@ template <typename AAResultT> class AAResults::Model final : public Concept {
                            AAQueryInfo &AAQI) override {
     return Result.getModRefInfo(Call1, Call2, AAQI);
   }
+
+  ModRefInfo getModRefInfo(const FenceInst *S, const MemoryLocation &Loc,
+                           AAQueryInfo &AAQI) override {
+    return Result.getModRefInfo(S, Loc, AAQI);
+  }
 };
 
 /// A base class to help implement the function alias analysis results concept.
@@ -920,6 +931,11 @@ class AAResultBase {
                            AAQueryInfo &AAQI) {
     return ModRefInfo::ModRef;
   }
+
+  ModRefInfo getModRefInfo(const FenceInst *S, const MemoryLocation &Loc,
+                           AAQueryInfo &AAQI) {
+    return ModRefInfo::ModRef;
+  }
 };
 
 /// Return true if this pointer is returned by a noalias function.
diff --git a/llvm/include/llvm/Analysis/BasicAliasAnalysis.h b/llvm/include/llvm/Analysis/BasicAliasAnalysis.h
index 31875e59207b3..66e6292c7785e 100644
--- a/llvm/include/llvm/Analysis/BasicAliasAnalysis.h
+++ b/llvm/include/llvm/Analysis/BasicAliasAnalysis.h
@@ -75,6 +75,7 @@ class BasicAAResult : public AAResultBase {
 
   LLVM_ABI AliasResult aliasErrno(const MemoryLocation &Loc, const Module *M);
 
+  using AAResultBase::getModRefInfo;
   LLVM_ABI ModRefInfo getModRefInfo(const CallBase *Call,
                                     const MemoryLocation &Loc,
                                     AAQueryInfo &AAQI);
diff --git a/llvm/include/llvm/Analysis/ScopedNoAliasAA.h b/llvm/include/llvm/Analysis/ScopedNoAliasAA.h
index 9515a0e64ba39..04458e4dfe968 100644
--- a/llvm/include/llvm/Analysis/ScopedNoAliasAA.h
+++ b/llvm/include/llvm/Analysis/ScopedNoAliasAA.h
@@ -22,6 +22,7 @@
 
 namespace llvm {
 
+class FenceInst;
 class Function;
 class MDNode;
 class MemoryLocation;
@@ -47,6 +48,9 @@ class ScopedNoAliasAAResult : public AAResultBase {
                                     AAQueryInfo &AAQI);
   LLVM_ABI ModRefInfo getModRefInfo(const CallBase *Call1,
                                     const CallBase *Call2, AAQueryInfo &AAQI);
+  LLVM_ABI ModRefInfo getModRefInfo(const FenceInst *S,
+                                    const MemoryLocation &Loc,
+                                    AAQueryInfo &AAQI);
 
   LLVM_ABI static void
   collectScopedDomains(const MDNode *NoAlias,
diff --git a/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h b/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h
index 38f9fc718824f..515aabd5cceab 100644
--- a/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h
+++ b/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h
@@ -57,6 +57,7 @@ class TypeBasedAAResult : public AAResultBase {
   LLVM_ABI MemoryEffects getMemoryEffects(const CallBase *Call,
                                           AAQueryInfo &AAQI);
   LLVM_ABI MemoryEffects getMemoryEffects(const Function *F);
+  using AAResultBase::getModRefInfo;
   LLVM_ABI ModRefInfo getModRefInfo(const CallBase *Call,
                                     const MemoryLocation &Loc,
                                     AAQueryInfo &AAQI);
diff --git a/llvm/lib/Analysis/AliasAnalysis.cpp b/llvm/lib/Analysis/AliasAnalysis.cpp
index 102f0c070d517..eb672bedf9f37 100644
--- a/llvm/lib/Analysis/AliasAnalysis.cpp
+++ b/llvm/lib/Analysis/AliasAnalysis.cpp
@@ -505,11 +505,24 @@ ModRefInfo AAResults::getModRefInfo(const StoreInst *S,
 ModRefInfo AAResults::getModRefInfo(const FenceInst *S,
                                     const MemoryLocation &Loc,
                                     AAQueryInfo &AAQI) {
-  // All we know about a fence instruction is what we get from the ModRef
-  // mask: if Loc is a constant memory location, the fence definitely could
-  // not modify it.
-  if (Loc.Ptr)
-    return getModRefInfoMask(Loc);
+  if (Loc.Ptr) {
+    ModRefInfo Result = ModRefInfo::ModRef;
+
+    for (const auto &AA : AAs) {
+      Result &= AA->getModRefInfo(S, Loc, AAQI);
+
+      if (isNoModRef(Result))
+        return ModRefInfo::NoModRef;
+    }
+
+    // Apply the ModRef mask. This ensures that if Loc is a constant memory
+    // location, we take into account the fact that the fence definitely could
+    // not modify the memory location.
+    if (!isNoModRef(Result))
+      Result &= getModRefInfoMask(Loc);
+
+    return Result;
+  }
   return ModRefInfo::ModRef;
 }
 
diff --git a/llvm/lib/Analysis/ScopedNoAliasAA.cpp b/llvm/lib/Analysis/ScopedNoAliasAA.cpp
index a76388281edb0..0ae7a080e094d 100644
--- a/llvm/lib/Analysis/ScopedNoAliasAA.cpp
+++ b/llvm/lib/Analysis/ScopedNoAliasAA.cpp
@@ -36,6 +36,7 @@
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/Analysis/MemoryLocation.h"
 #include "llvm/IR/InstrTypes.h"
+#include "llvm/IR/Instructions.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Metadata.h"
 #include "llvm/InitializePasses.h"
@@ -93,6 +94,23 @@ ModRefInfo ScopedNoAliasAAResult::getModRefInfo(const CallBase *Call,
   return ModRefInfo::ModRef;
 }
 
+ModRefInfo ScopedNoAliasAAResult::getModRefInfo(const FenceInst *S,
+                                                const MemoryLocation &Loc,
+                                                AAQueryInfo &AAQI) {
+  if (!EnableScopedNoAlias)
+    return ModRefInfo::ModRef;
+
+  if (!mayAliasInScopes(Loc.AATags.Scope,
+                        S->getMetadata(LLVMContext::MD_noalias)))
+    return ModRefInfo::NoModRef;
+
+  if (!mayAliasInScopes(S->getMetadata(LLVMContext::MD_alias_scope),
+                        Loc.AATags.NoAlias))
+    return ModRefInfo::NoModRef;
+
+  return ModRefInfo::ModRef;
+}
+
 ModRefInfo ScopedNoAliasAAResult::getModRefInfo(const CallBase *Call1,
                                                 const CallBase *Call2,
                                                 AAQueryInfo &AAQI) {
diff --git a/llvm/test/Transforms/LoadStoreVectorizer/AMDGPU/fence-noalias-metadata.ll b/llvm/test/Transforms/LoadStoreVectorizer/AMDGPU/fence-noalias-metadata.ll
new file mode 100644
index 0000000000000..0f5560dde2222
--- /dev/null
+++ b/llvm/test/Transforms/LoadStoreVectorizer/AMDGPU/fence-noalias-metadata.ll
@@ -0,0 +1,77 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -mtriple=amdgcn-amd-amdhsa -aa-pipeline=basic-aa,scoped-noalias-aa -passes=load-store-vectorizer -S -o - %s | FileCheck %s
+
+; Test that getModRefInfo(FenceInst, Loc) uses scoped noalias metadata on the
+; fence to prove it cannot affect a given memory location. Without this, the
+; load-store vectorizer conservatively treats fences as potential clobbers,
+; preventing vectorization of adjacent loads across fences.
+;
+; This models what happens after AMDGPULowerKernelArguments: noalias kernel
+; pointer arguments are replaced with loads from the kernarg segment, losing
+; the readonly attribute. The !alias.scope/!noalias metadata partially
+; compensates, but getModRefInfo(FenceInst) did not check it.
+
+define void @vectorize_loads_across_fence_with_noalias(ptr addrspace(1) %ptr) {
+; CHECK-LABEL: define void @vectorize_loads_across_fence_with_noalias(
+; CHECK-SAME: ptr addrspace(1) [[PTR:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = load <2 x i32>, ptr addrspace(1) [[PTR]], align 8, !alias.scope [[META0:![0-9]+]], !noalias [[META3:![0-9]+]]
+; CHECK-NEXT:    [[LOAD01:%.*]] = extractelement <2 x i32> [[TMP1]], i32 0
+; CHECK-NEXT:    [[LOAD12:%.*]] = extractelement <2 x i32> [[TMP1]], i32 1
+; CHECK-NEXT:    fence syncscope("workgroup") release, !noalias [[META5:![0-9]+]]
+; CHECK-NEXT:    fence syncscope("workgroup") acquire, !noalias [[META5]]
+; CHECK-NEXT:    [[SUM:%.*]] = add i32 [[LOAD01]], [[LOAD12]]
+; CHECK-NEXT:    call void @use(i32 [[SUM]])
+; CHECK-NEXT:    ret void
+;
+  %gep1 = getelementptr i32, ptr addrspace(1) %ptr, i64 1
+  %load0 = load i32, ptr addrspace(1) %ptr, align 8, !alias.scope !0, !noalias !3
+  fence syncscope("workgroup") release, !noalias !5
+  fence syncscope("workgroup") acquire, !noalias !5
+  %load1 = load i32, ptr addrspace(1) %gep1, align 4, !alias.scope !0, !noalias !3
+  %sum = add i32 %load0, %load1
+  call void @use(i32 %sum)
+  ret void
+}
+
+; Negative test: without !noalias on the fence, vectorization is blocked.
+define void @no_vectorize_loads_across_fence_without_noalias(ptr addrspace(1) %ptr) {
+; CHECK-LABEL: define void @no_vectorize_loads_across_fence_without_noalias(
+; CHECK-SAME: ptr addrspace(1) [[PTR:%.*]]) {
+; CHECK-NEXT:    [[GEP1:%.*]] = getelementptr i32, ptr addrspace(1) [[PTR]], i64 1
+; CHECK-NEXT:    [[LOAD0:%.*]] = load i32, ptr addrspace(1) [[PTR]], align 8, !alias.scope [[META0]], !noalias [[META3]]
+; CHECK-NEXT:    fence syncscope("workgroup") release
+; CHECK-NEXT:    fence syncscope("workgroup") acquire
+; CHECK-NEXT:    [[LOAD1:%.*]] = load i32, ptr addrspace(1) [[GEP1]], align 4, !alias.scope [[META0]], !noalias [[META3]]
+; CHECK-NEXT:    [[SUM:%.*]] = add i32 [[LOAD0]], [[LOAD1]]
+; CHECK-NEXT:    call void @use(i32 [[SUM]])
+; CHECK-NEXT:    ret void
+;
+  %gep1 = getelementptr i32, ptr addrspace(1) %ptr, i64 1
+  %load0 = load i32, ptr addrspace(1) %ptr, align 8, !alias.scope !0, !noalias !3
+  fence syncscope("workgroup") release
+  fence syncscope("workgroup") acquire
+  %load1 = load i32, ptr addrspace(1) %gep1, align 4, !alias.scope !0, !noalias !3
+  %sum = add i32 %load0, %load1
+  call void @use(i32 %sum)
+  ret void
+}
+
+declare void @use(i32)
+
+; Metadata: two noalias scopes in the same domain.
+; Loads access memory in "arg_scope". Fences declare !noalias for both scopes,
+; meaning they do not concern memory in either scope.
+!0 = !{!1}
+!1 = distinct !{!1, !2, !"arg_scope"}
+!2 = distinct !{!2, !"kernel_domain"}
+!3 = !{!4}
+!4 = distinct !{!4, !2, !"other_arg_scope"}
+!5 = !{!1, !4}
+;.
+; CHECK: [[META0]] = !{[[META1:![0-9]+]]}
+; CHECK: [[META1]] = distinct !{[[META1]], [[META2:![0-9]+]], !"arg_scope"}
+; CHECK: [[META2]] = distinct !{[[META2]], !"kernel_domain"}
+; CHECK: [[META3]] = !{[[META4:![0-9]+]]}
+; CHECK: [[META4]] = distinct !{[[META4]], [[META2]], !"other_arg_scope"}
+; CHECK: [[META5]] = !{[[META1]], [[META4]]}
+;.



More information about the llvm-commits mailing list