[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