[llvm] [AMDGPU] Fix noalias metadata for calls that capture a pointer earlier (PR #219887)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 30 23:00:49 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-amdgpu
Author: Arseniy Obolenskiy (aobolensk)
<details>
<summary>Changes</summary>
A call could get `!noalias` against a kernel noalias argument even when that argument was captured into a global earlier and the call can reach it that way
Only calls that touch just their own argument pointees are actually safe to mark this way
---
Patch is 30.07 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/219887.diff
2 Files Affected:
- (modified) llvm/lib/Target/AMDGPU/AMDGPULowerKernelArguments.cpp (+51-48)
- (modified) llvm/test/CodeGen/AMDGPU/lower-noalias-kernargs.ll (+234-28)
``````````diff
diff --git a/llvm/lib/Target/AMDGPU/AMDGPULowerKernelArguments.cpp b/llvm/lib/Target/AMDGPU/AMDGPULowerKernelArguments.cpp
index d4f6532bcec67..4911b2dc7d3bf 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPULowerKernelArguments.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPULowerKernelArguments.cpp
@@ -100,13 +100,21 @@ static void addAliasScopeMetadata(Function &F, const DataLayout &DL,
// If instruction accesses memory, collect its pointer arguments.
Instruction *I = &(*Inst);
SmallVector<const Value *, 2u> PtrArgs;
+ // May reach a noalias argument via a copy captured before the call.
+ bool IsUnrestrictedCall = false;
if (std::optional<MemoryLocation> MO = MemoryLocation::getOrNone(I))
PtrArgs.push_back(MO->Ptr);
else if (const CallBase *Call = dyn_cast<CallBase>(I)) {
- if (Call->doesNotAccessMemory())
+ MemoryEffects ME = Call->getMemoryEffects();
+ if (ME.doesNotAccessMemory())
continue;
+ // Inaccessible memory cannot alias any IR-visible pointer.
+ if (ME.onlyAccessesInaccessibleMem())
+ continue;
+ IsUnrestrictedCall = !ME.onlyAccessesArgPointees();
+
for (Value *Arg : Call->args()) {
if (!Arg->getType()->isPointerTy())
continue;
@@ -123,65 +131,60 @@ static void addAliasScopeMetadata(Function &F, const DataLayout &DL,
SmallPtrSet<const Value *, 4u> ObjSet;
SmallVector<Metadata *, 4u> NoAliases;
- if (!PtrArgs.empty()) {
- // Trace pointer arguments back to underlying objects and decide which
- // noalias scopes apply based on provenance and capture analysis.
- for (const Value *Val : PtrArgs) {
- SmallVector<const Value *, 4u> Objects;
- getUnderlyingObjects(Val, Objects);
- ObjSet.insert_range(Objects);
- }
+ for (const Value *Val : PtrArgs) {
+ SmallVector<const Value *, 4u> Objects;
+ getUnderlyingObjects(Val, Objects);
+ ObjSet.insert_range(Objects);
+ }
- bool RequiresNoCaptureBefore = false;
- bool UsesUnknownObject = false;
- bool UsesAliasingPtr = false;
+ bool RequiresNoCaptureBefore = false;
+ bool UsesUnknownObject = false;
+ bool UsesAliasingPtr = false;
- for (const Value *Val : ObjSet) {
- if (isa<ConstantData>(Val))
- continue;
+ for (const Value *Val : ObjSet) {
+ if (isa<ConstantData>(Val))
+ continue;
- if (const Argument *Arg = dyn_cast<Argument>(Val)) {
- if (!Arg->hasAttribute(Attribute::NoAlias))
- UsesAliasingPtr = true;
- } else
+ if (const Argument *Arg = dyn_cast<Argument>(Val)) {
+ if (!Arg->hasAttribute(Attribute::NoAlias))
UsesAliasingPtr = true;
-
- if (isEscapeSource(Val))
- RequiresNoCaptureBefore = true;
- else if (!isa<Argument>(Val) && isIdentifiedObject(Val))
- UsesUnknownObject = true;
+ } else
+ UsesAliasingPtr = true;
+
+ if (isEscapeSource(Val)) {
+ // Can only alias a noalias argument if captured beforehand.
+ RequiresNoCaptureBefore = true;
+ } else if (!isa<Argument>(Val) && !isIdentifiedObject(Val)) {
+ // Unknown provenance: assume nothing.
+ UsesUnknownObject = true;
}
+ }
- if (UsesUnknownObject)
- continue;
+ if (UsesUnknownObject)
+ continue;
- // Collect noalias scopes for instruction.
- for (const Argument *Arg : NoAliasArgs) {
- if (ObjSet.contains(Arg))
- continue;
+ if (IsUnrestrictedCall)
+ RequiresNoCaptureBefore = true;
- if (!RequiresNoCaptureBefore ||
- !capturesAnything(PointerMayBeCapturedBefore(
- Arg, false, I, &DT, false, CaptureComponents::Provenance)))
- NoAliases.push_back(NewScopes[Arg]);
- }
+ // Collect noalias scopes for instruction.
+ for (const Argument *Arg : NoAliasArgs) {
+ if (ObjSet.contains(Arg))
+ continue;
- // Collect scopes for alias.scope metadata.
- if (!UsesAliasingPtr)
- for (const Argument *Arg : NoAliasArgs) {
- if (ObjSet.count(Arg))
- Scopes.push_back(NewScopes[Arg]);
- }
- } else {
- // The instruction accesses memory but has no pointer arguments.
- // Since none of its operands derive from any noalias kernel argument,
- // it cannot possibly alias them. Mark it as !noalias w.r.t. every
- // noalias scope so that ScopedNoAliasAA can prove non-aliasing when
- // other instructions reference those scopes via !alias.scope.
- for (const Argument *Arg : NoAliasArgs)
+ if (!RequiresNoCaptureBefore ||
+ !capturesAnything(PointerMayBeCapturedBefore(
+ Arg, false, I, &DT, false, CaptureComponents::Provenance)))
NoAliases.push_back(NewScopes[Arg]);
}
+ // Collect scopes for alias.scope metadata. Skip unrestricted calls: they
+ // may touch memory beyond their pointer arguments' pointees.
+ if (!UsesAliasingPtr && !IsUnrestrictedCall)
+ for (const Argument *Arg : NoAliasArgs) {
+ if (ObjSet.count(Arg))
+ Scopes.push_back(NewScopes[Arg]);
+ }
+
// Add noalias metadata to instruction.
if (!NoAliases.empty()) {
MDNode *NewMD =
diff --git a/llvm/test/CodeGen/AMDGPU/lower-noalias-kernargs.ll b/llvm/test/CodeGen/AMDGPU/lower-noalias-kernargs.ll
index 722eb1c073fb8..19a2fecd7a470 100644
--- a/llvm/test/CodeGen/AMDGPU/lower-noalias-kernargs.ll
+++ b/llvm/test/CodeGen/AMDGPU/lower-noalias-kernargs.ll
@@ -13,7 +13,7 @@ define amdgpu_kernel void @aliasinfo_2i32(ptr addrspace(1) %out, ptr addrspace(1
; CHECK-NEXT: [[TID:%.*]] = call i32 @llvm.amdgcn.workitem.id.x()
; CHECK-NEXT: [[IN_GEP:%.*]] = getelementptr i32, ptr addrspace(1) [[IN_LOAD]], i32 [[TID]]
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr addrspace(1) [[IN_GEP]], align 4
-; CHECK-NEXT: [[CTLZ:%.*]] = call i32 @llvm.ctlz.i32(i32 [[VAL]], i1 false) #[[ATTR3:[0-9]+]]
+; CHECK-NEXT: [[CTLZ:%.*]] = call i32 @llvm.ctlz.i32(i32 [[VAL]], i1 false) #[[ATTR5:[0-9]+]]
; CHECK-NEXT: store i32 [[CTLZ]], ptr addrspace(1) [[OUT_LOAD]], align 4
; CHECK-NEXT: ret void
;
@@ -38,7 +38,7 @@ define amdgpu_kernel void @aliasinfo_2i32_NA(ptr addrspace(1) noalias %out, ptr
; CHECK-NEXT: [[TID:%.*]] = call i32 @llvm.amdgcn.workitem.id.x()
; CHECK-NEXT: [[IN_GEP:%.*]] = getelementptr i32, ptr addrspace(1) [[IN_LOAD]], i32 [[TID]]
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr addrspace(1) [[IN_GEP]], align 4, !alias.scope [[META1:![0-9]+]], !noalias [[META4:![0-9]+]]
-; CHECK-NEXT: [[CTLZ:%.*]] = call i32 @llvm.ctlz.i32(i32 [[VAL]], i1 false) #[[ATTR3]]
+; CHECK-NEXT: [[CTLZ:%.*]] = call i32 @llvm.ctlz.i32(i32 [[VAL]], i1 false) #[[ATTR5]]
; CHECK-NEXT: store i32 [[CTLZ]], ptr addrspace(1) [[OUT_LOAD]], align 4, !alias.scope [[META4]], !noalias [[META1]]
; CHECK-NEXT: ret void
;
@@ -63,7 +63,7 @@ define amdgpu_kernel void @aliasinfo_2i32_AS(ptr addrspace(1) %out, ptr addrspac
; CHECK-NEXT: [[TID:%.*]] = call i32 @llvm.amdgcn.workitem.id.x()
; CHECK-NEXT: [[IN_GEP:%.*]] = getelementptr i32, ptr addrspace(1) [[IN_LOAD]], i32 [[TID]]
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr addrspace(1) [[IN_GEP]], align 4, !alias.scope [[META6:![0-9]+]], !noalias [[META9:![0-9]+]]
-; CHECK-NEXT: [[CTLZ:%.*]] = call i32 @llvm.ctlz.i32(i32 [[VAL]], i1 false) #[[ATTR3]]
+; CHECK-NEXT: [[CTLZ:%.*]] = call i32 @llvm.ctlz.i32(i32 [[VAL]], i1 false) #[[ATTR5]]
; CHECK-NEXT: store i32 [[CTLZ]], ptr addrspace(1) [[OUT_LOAD]], align 4, !alias.scope [[META9]], !noalias [[META6]]
; CHECK-NEXT: ret void
;
@@ -88,7 +88,7 @@ define amdgpu_kernel void @aliasinfo_2i32_NA_AS(ptr addrspace(1) noalias %out, p
; CHECK-NEXT: [[TID:%.*]] = call i32 @llvm.amdgcn.workitem.id.x()
; CHECK-NEXT: [[IN_GEP:%.*]] = getelementptr i32, ptr addrspace(1) [[IN_LOAD]], i32 [[TID]]
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr addrspace(1) [[IN_GEP]], align 4, !alias.scope [[META11:![0-9]+]], !noalias [[META14:![0-9]+]]
-; CHECK-NEXT: [[CTLZ:%.*]] = call i32 @llvm.ctlz.i32(i32 [[VAL]], i1 false) #[[ATTR3]]
+; CHECK-NEXT: [[CTLZ:%.*]] = call i32 @llvm.ctlz.i32(i32 [[VAL]], i1 false) #[[ATTR5]]
; CHECK-NEXT: store i32 [[CTLZ]], ptr addrspace(1) [[OUT_LOAD]], align 4, !alias.scope [[META14]], !noalias [[META11]]
; CHECK-NEXT: ret void
;
@@ -287,11 +287,11 @@ define amdgpu_kernel void @aliasinfo_mixed_intrinsics_NA(ptr addrspace(1) noalia
; CHECK-NEXT: [[INOUT_LOAD:%.*]] = load ptr addrspace(1), ptr addrspace(4) [[INOUT_KERNARG_OFFSET]], align 4, !invariant.load [[META0]]
; CHECK-NEXT: [[OUT_KERNARG_OFFSET:%.*]] = getelementptr inbounds i8, ptr addrspace(4) [[ALIASINFO_MIXED_INTRINSICS_NA_KERNARG_SEGMENT]], i64 52
; CHECK-NEXT: [[OUT_LOAD:%.*]] = load ptr addrspace(1), ptr addrspace(4) [[OUT_KERNARG_OFFSET]], align 4, !invariant.load [[META0]]
-; CHECK-NEXT: [[VAL1:%.*]] = call <4 x float> @llvm.amdgcn.global.load.tr.b256.v4f32.p1(ptr addrspace(1) [[IN_LOAD]]), !alias.scope [[META42:![0-9]+]], !noalias [[META45:![0-9]+]]
-; CHECK-NEXT: [[VAL2:%.*]] = call <4 x float> @llvm.amdgcn.global.load.tr.b256.v4f32.p1(ptr addrspace(1) [[INOUT_LOAD]]), !alias.scope [[META48:![0-9]+]], !noalias [[META49:![0-9]+]]
-; CHECK-NEXT: call void @llvm.memcpy.p1.p1.i64(ptr addrspace(1) [[OUT_LOAD]], ptr addrspace(1) [[INOUT_LOAD]], i64 16, i1 false), !alias.scope [[META45]], !noalias [[META42]]
+; CHECK-NEXT: [[VAL1:%.*]] = call <4 x float> @llvm.amdgcn.global.load.tr.b256.v4f32.p1(ptr addrspace(1) [[IN_LOAD]]), !noalias [[META42:![0-9]+]]
+; CHECK-NEXT: [[VAL2:%.*]] = call <4 x float> @llvm.amdgcn.global.load.tr.b256.v4f32.p1(ptr addrspace(1) [[INOUT_LOAD]]), !noalias [[META46:![0-9]+]]
+; CHECK-NEXT: call void @llvm.memcpy.p1.p1.i64(ptr addrspace(1) [[OUT_LOAD]], ptr addrspace(1) [[INOUT_LOAD]], i64 16, i1 false), !alias.scope [[META42]], !noalias [[META47:![0-9]+]]
; CHECK-NEXT: [[VAL3:%.*]] = fmul <4 x float> [[VAL1]], [[VAL2]]
-; CHECK-NEXT: store <4 x float> [[VAL3]], ptr addrspace(1) [[INOUT_LOAD]], align 16, !alias.scope [[META48]], !noalias [[META49]]
+; CHECK-NEXT: store <4 x float> [[VAL3]], ptr addrspace(1) [[INOUT_LOAD]], align 16, !alias.scope [[META49:![0-9]+]], !noalias [[META50:![0-9]+]]
; CHECK-NEXT: ret void
;
entry:
@@ -341,9 +341,9 @@ define amdgpu_kernel void @aliasinfo_mixed_intrinsics_NA_AS(ptr addrspace(1) noa
; CHECK-NEXT: [[INOUT_LOAD:%.*]] = load ptr addrspace(1), ptr addrspace(4) [[INOUT_KERNARG_OFFSET]], align 4, !invariant.load [[META0]]
; CHECK-NEXT: [[OUT_KERNARG_OFFSET:%.*]] = getelementptr inbounds i8, ptr addrspace(4) [[ALIASINFO_MIXED_INTRINSICS_NA_AS_KERNARG_SEGMENT]], i64 52
; CHECK-NEXT: [[OUT_LOAD:%.*]] = load ptr addrspace(1), ptr addrspace(4) [[OUT_KERNARG_OFFSET]], align 4, !invariant.load [[META0]]
-; CHECK-NEXT: [[VAL1:%.*]] = call <4 x float> @llvm.amdgcn.global.load.tr.b256.v4f32.p1(ptr addrspace(1) [[IN_LOAD]]), !alias.scope [[META50:![0-9]+]], !noalias [[META53:![0-9]+]]
-; CHECK-NEXT: [[VAL2:%.*]] = call <4 x float> @llvm.amdgcn.global.load.tr.b256.v4f32.p1(ptr addrspace(1) [[INOUT_LOAD]]), !alias.scope [[META56:![0-9]+]], !noalias [[META57:![0-9]+]]
-; CHECK-NEXT: call void @llvm.memcpy.p1.p1.i64(ptr addrspace(1) [[OUT_LOAD]], ptr addrspace(1) [[INOUT_LOAD]], i64 16, i1 false), !alias.scope [[META53]], !noalias [[META50]]
+; CHECK-NEXT: [[VAL1:%.*]] = call <4 x float> @llvm.amdgcn.global.load.tr.b256.v4f32.p1(ptr addrspace(1) [[IN_LOAD]]), !alias.scope [[META6]], !noalias [[META51:![0-9]+]]
+; CHECK-NEXT: [[VAL2:%.*]] = call <4 x float> @llvm.amdgcn.global.load.tr.b256.v4f32.p1(ptr addrspace(1) [[INOUT_LOAD]]), !alias.scope [[META6]], !noalias [[META55:![0-9]+]]
+; CHECK-NEXT: call void @llvm.memcpy.p1.p1.i64(ptr addrspace(1) [[OUT_LOAD]], ptr addrspace(1) [[INOUT_LOAD]], i64 16, i1 false), !alias.scope [[META51]], !noalias [[META56:![0-9]+]]
; CHECK-NEXT: [[VAL3:%.*]] = fmul <4 x float> [[VAL1]], [[VAL2]]
; CHECK-NEXT: store <4 x float> [[VAL3]], ptr addrspace(1) [[INOUT_LOAD]], align 16, !alias.scope [[META58:![0-9]+]], !noalias [[META59:![0-9]+]]
; CHECK-NEXT: ret void
@@ -357,13 +357,195 @@ entry:
ret void
}
+ at cap = addrspace(3) global ptr addrspace(1) poison
+
+; No pointer args, but reaches *%p via the copy captured in @cap.
+define amdgpu_kernel void @aliasinfo_captured_before_call(ptr addrspace(1) noalias %p, ptr addrspace(1) %out) nounwind {
+; CHECK-LABEL: define amdgpu_kernel void @aliasinfo_captured_before_call(
+; CHECK-SAME: ptr addrspace(1) noalias [[P:%.*]], ptr addrspace(1) [[OUT:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[ALIASINFO_CAPTURED_BEFORE_CALL_KERNARG_SEGMENT:%.*]] = call nonnull align 16 dereferenceable(272) ptr addrspace(4) @llvm.amdgcn.kernarg.segment.ptr()
+; CHECK-NEXT: [[P_KERNARG_OFFSET:%.*]] = getelementptr inbounds i8, ptr addrspace(4) [[ALIASINFO_CAPTURED_BEFORE_CALL_KERNARG_SEGMENT]], i64 36
+; CHECK-NEXT: [[P_LOAD:%.*]] = load ptr addrspace(1), ptr addrspace(4) [[P_KERNARG_OFFSET]], align 4, !invariant.load [[META0]]
+; CHECK-NEXT: [[OUT_KERNARG_OFFSET:%.*]] = getelementptr inbounds i8, ptr addrspace(4) [[ALIASINFO_CAPTURED_BEFORE_CALL_KERNARG_SEGMENT]], i64 44
+; CHECK-NEXT: [[OUT_LOAD:%.*]] = load ptr addrspace(1), ptr addrspace(4) [[OUT_KERNARG_OFFSET]], align 4, !invariant.load [[META0]]
+; CHECK-NEXT: store ptr addrspace(1) [[P_LOAD]], ptr addrspace(3) @cap, align 8, !noalias [[META60:![0-9]+]]
+; CHECK-NEXT: call void @readwrite_no_ptr_args()
+; CHECK-NEXT: [[V:%.*]] = load i32, ptr addrspace(1) [[P_LOAD]], align 4, !alias.scope [[META60]]
+; CHECK-NEXT: store i32 [[V]], ptr addrspace(1) [[OUT_LOAD]], align 4, !noalias [[META60]]
+; CHECK-NEXT: ret void
+;
+ store ptr addrspace(1) %p, ptr addrspace(3) @cap
+ call void @readwrite_no_ptr_args()
+ %v = load i32, ptr addrspace(1) %p
+ store i32 %v, ptr addrspace(1) %out
+ ret void
+}
+
+; Same, with a pointer argument that does not derive from %p.
+define amdgpu_kernel void @aliasinfo_captured_before_call_ptr_arg(ptr addrspace(1) noalias %p, ptr addrspace(1) %out) nounwind {
+; CHECK-LABEL: define amdgpu_kernel void @aliasinfo_captured_before_call_ptr_arg(
+; CHECK-SAME: ptr addrspace(1) noalias [[P:%.*]], ptr addrspace(1) [[OUT:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[ALIASINFO_CAPTURED_BEFORE_CALL_PTR_ARG_KERNARG_SEGMENT:%.*]] = call nonnull align 16 dereferenceable(272) ptr addrspace(4) @llvm.amdgcn.kernarg.segment.ptr()
+; CHECK-NEXT: [[P_KERNARG_OFFSET:%.*]] = getelementptr inbounds i8, ptr addrspace(4) [[ALIASINFO_CAPTURED_BEFORE_CALL_PTR_ARG_KERNARG_SEGMENT]], i64 36
+; CHECK-NEXT: [[P_LOAD:%.*]] = load ptr addrspace(1), ptr addrspace(4) [[P_KERNARG_OFFSET]], align 4, !invariant.load [[META0]]
+; CHECK-NEXT: [[OUT_KERNARG_OFFSET:%.*]] = getelementptr inbounds i8, ptr addrspace(4) [[ALIASINFO_CAPTURED_BEFORE_CALL_PTR_ARG_KERNARG_SEGMENT]], i64 44
+; CHECK-NEXT: [[OUT_LOAD:%.*]] = load ptr addrspace(1), ptr addrspace(4) [[OUT_KERNARG_OFFSET]], align 4, !invariant.load [[META0]]
+; CHECK-NEXT: store ptr addrspace(1) [[P_LOAD]], ptr addrspace(3) @cap, align 8, !noalias [[META63:![0-9]+]]
+; CHECK-NEXT: call void @readwrite_with_ptr_arg(ptr addrspace(1) [[OUT_LOAD]])
+; CHECK-NEXT: [[V:%.*]] = load i32, ptr addrspace(1) [[P_LOAD]], align 4, !alias.scope [[META63]]
+; CHECK-NEXT: store i32 [[V]], ptr addrspace(1) [[OUT_LOAD]], align 4, !noalias [[META63]]
+; CHECK-NEXT: ret void
+;
+ store ptr addrspace(1) %p, ptr addrspace(3) @cap
+ call void @readwrite_with_ptr_arg(ptr addrspace(1) %out)
+ %v = load i32, ptr addrspace(1) %p
+ store i32 %v, ptr addrspace(1) %out
+ ret void
+}
+
+; A capture after the call is not observable by the callee, so the scope stays.
+define amdgpu_kernel void @aliasinfo_captured_after_call(ptr addrspace(1) noalias %p, ptr addrspace(1) %out) nounwind {
+; CHECK-LABEL: define amdgpu_kernel void @aliasinfo_captured_after_call(
+; CHECK-SAME: ptr addrspace(1) noalias [[P:%.*]], ptr addrspace(1) [[OUT:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[ALIASINFO_CAPTURED_AFTER_CALL_KERNARG_SEGMENT:%.*]] = call nonnull align 16 dereferenceable(272) ptr addrspace(4) @llvm.amdgcn.kernarg.segment.ptr()
+; CHECK-NEXT: [[P_KERNARG_OFFSET:%.*]] = getelementptr inbounds i8, ptr addrspace(4) [[ALIASINFO_CAPTURED_AFTER_CALL_KERNARG_SEGMENT]], i64 36
+; CHECK-NEXT: [[P_LOAD:%.*]] = load ptr addrspace(1), ptr addrspace(4) [[P_KERNARG_OFFSET]], align 4, !invariant.load [[META0]]
+; CHECK-NEXT: [[OUT_KERNARG_OFFSET:%.*]] = getelementptr inbounds i8, ptr addrspace(4) [[ALIASINFO_CAPTURED_AFTER_CALL_KERNARG_SEGMENT]], i64 44
+; CHECK-NEXT: [[OUT_LOAD:%.*]] = load ptr addrspace(1), ptr addrspace(4) [[OUT_KERNARG_OFFSET]], align 4, !invariant.load [[META0]]
+; CHECK-NEXT: [[V:%.*]] = load i32, ptr addrspace(1) [[P_LOAD]], align 4, !alias.scope [[META66:![0-9]+]]
+; CHECK-NEXT: call void @readwrite_no_ptr_args(), !noalias [[META66]]
+; CHECK-NEXT: store ptr addrspace(1) [[P_LOAD]], ptr addrspace(3) @cap, align 8, !noalias [[META66]]
+; CHECK-NEXT: store i32 [[V]], ptr addrspace(1) [[OUT_LOAD]], align 4, !noalias [[META66]]
+; CHECK-NEXT: ret void
+;
+ %v = load i32, ptr addrspace(1) %p
+ call void @readwrite_no_ptr_args()
+ store ptr addrspace(1) %p, ptr addrspace(3) @cap
+ store i32 %v, ptr addrspace(1) %out
+ ret void
+}
+
+; An argmem-only callee cannot use the captured copy, so the scope stays.
+define amdgpu_kernel void @aliasinfo_captured_before_argmemonly_call(ptr addrspace(1) noalias %p, ptr addrspace(1) %out) nounwind {
+; CHECK-LABEL: define amdgpu_kernel void @aliasinfo_captured_before_argmemonly_call(
+; CHECK-SAME: ptr addrspace(1) noalias [[P:%.*]], ptr addrspace(1) [[OUT:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[ALIASINFO_CAPTURED_BEFORE_ARGMEMONLY_CALL_KERNARG_SEGMENT:%.*]] = call nonnull align 16 dereferenceable(272) ptr addrspace(4) @llvm.amdgcn.kernarg.segment.ptr()
+; CHECK-NEXT: [[P_KERNARG_OFFSET:%.*]] = getelementptr inbounds i8, ptr addrspace(4) [[ALIASINFO_CAPTURED_BEFORE_ARGMEMONLY_CALL_KERNARG_SEGMENT]], i64 36
+; CHECK-NEXT: [[P_LOAD:%.*]] = load ptr addrspace(1), ptr addrspace(4) [[P_KERNARG_OFFSET]], align 4, !invariant.load [[META0]]
+; CHECK-NEXT: [[OUT_KERNARG_OFFSET:%.*]] = getelementptr inbounds i8, ptr addrspace(4) [[ALIASINFO_CAPTURED_BEFORE_ARGMEMONLY_CALL_KERNARG_SEGMENT]], i64 44
+; CHECK-NEXT: [[OUT_LOAD:%.*]] = load ptr addrspace(1), ptr addrspace(4) [[OUT_KERNARG_OFFSET]], align 4, !invariant.load [[META0]]
+; CHECK-NEXT: store ptr addrspace(1) [[P_LOAD]], ptr addrspace(3) @cap, align 8, !noalias [[META69:![0-9]+]]
+; CHECK-NEXT: call void @argmemonly_rw_with_ptr_arg(ptr addrspace(1) [[OUT_LOAD]]), !noalias [[META69]]
+; CHECK-NEXT: [[V:%.*]] = load i32, ptr addrspace(1) [[P_LOAD]], align 4, !alias.scope [[META69]]
+; CHECK-NEXT: store i32 [[V]], ptr addrspace(1) [[OUT_LOAD]], align 4, !noalias [[META69]]
+; CHECK-NEXT: ret void
+;
+ store ptr addrspace(1) %p, ptr addrspace(3) @cap
+ call void @argmemonly_rw_with_ptr_arg(ptr addrspace(1) %out)
+ %v = load i32, ptr addrspace(1) %p
+ store i32 %v, ptr addrspace(1) %out
+ ret void
+}
+
+; Non-argmem-only call gets no !alias.scope; keeps !noalias for %q, unreachable by it.
+define amdgpu_kernel void @aliasinfo_no_scope_on_nonargmem_call(ptr addrspace(1) noalias %p, ptr addrspace(1) noalias %q, ptr addrspace(1) %out) nounwind {
+; CHECK-LABEL: define amdgpu_kernel void @aliasinfo_no_scope_on_nonargmem_call(
+; CHECK-SAME: ptr addrspace(1) noalias [[P:%.*]], ptr addrspace(1) noalias [[Q:%.*]], ptr addrspace(1) [[OUT:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[ALIASINFO_NO_SCOPE_ON_NONARGMEM_CALL_KERNARG_SEGMENT:%.*]] = call nonnull align 16 dereferenceable(280) ptr addrspace(4) @llvm.amdgcn.kernarg.segment.ptr()
+; CHECK-NEXT: [[P_KERNARG_OFFSET:%.*]] = getelementptr inbounds i8, ptr addrspace(4) [[ALIASINFO_NO_SCOPE_ON_NONARGMEM_CALL_KERNARG_SEGMENT]], i64 36
+; CHECK-NEXT: [[P_LOAD:%.*]] = load ptr addrspace(1), ptr addrspace(4) [[P_KERNARG_OFFSET]], align 4, !invariant.load [[META0]]
+; CHECK-NEXT: [[Q_KERNARG_OFFSET:%.*]] = getelementptr inbounds i8, ptr addrspace(4) [[ALIASINFO_NO_SCOPE_ON_NONARGMEM_CALL_KERNARG_SEGMENT]], i64 44
+; CHECK-NEXT: [[Q_LOAD:%.*]] = load ptr addrspace(1), ptr addrspace(4) [[Q_KERNARG_OFFSET]], align 4, !inv...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/219887
More information about the llvm-commits
mailing list