[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