[llvm] [AMDGPU] Fix LDS access via flat pointer argument in amdgpu-sw-lower-lds (PR #209842)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 21 03:51:12 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-amdgpu

Author: Chaitanya (skc7)

<details>
<summary>Changes</summary>

A non-kernel can receive lowered LDS storage as a flat (generic) pointer argument and cast it back to addrspace(3) before accessing it. After the amdgpu-sw-lower-lds pass relocates that storage into global memory, the addrspace(3) access reads dead LDS and silently produces wrong results.

Detect and redirect such argument-derived accesses so they go through the flat pointer, which points at the global backing buffer.

Changes:

- Add `getNonKernelsWithLDSFlatArguments` which performs a call-graph walk that records, per callee, which flat parameters carry LDS-derived pointers.

- During lowering, redirect memory operations that reach LDS through these arguments to the relocated global memory by reconstructing an equivalent flat pointer instead of accessing LDS. Whether a pointer derives from such an argument is decided by `flatPtrDerivesFromLDS`, which looks through `GEP/bitcast/phi/select` with a cycle guard so loop-carried pointers are handled.

- Add `amdgpu-sw-lower-lds-flat-ptr-arg-asan.ll` to test all the cases.

Assisted by: Claude-Opus-4.8

---

Patch is 28.96 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/209842.diff


2 Files Affected:

- (modified) llvm/lib/Target/AMDGPU/AMDGPUSwLowerLDS.cpp (+235-16) 
- (added) llvm/test/CodeGen/AMDGPU/amdgpu-sw-lower-lds-flat-ptr-arg-asan.ll (+256) 


``````````diff
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUSwLowerLDS.cpp b/llvm/lib/Target/AMDGPU/AMDGPUSwLowerLDS.cpp
index 2b78094837c68..25c82883bd419 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUSwLowerLDS.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUSwLowerLDS.cpp
@@ -81,6 +81,15 @@
 //    The Offset into the base "SW LDS" is obtained from
 //    corresponding element in offset table. With this information, replacement
 //    value is obtained.
+//
+// Replacement of LDS accessed via flat pointer arguments:
+//    An LDS pointer can reach a non-kernel as a flat (generic) pointer
+//    argument, e.g. after an addrspacecast of an LDS global to flat. A
+//    fixed-point call-graph walk identifies the functions and argument
+//    indices that carry such LDS-derived flat pointers. Memory operations
+//    that reach LDS through these arguments are redirected to the relocated
+//    global memory by reconstructing an equivalent flat pointer, instead of
+//    accessing the now-dead hardware LDS.
 //===----------------------------------------------------------------------===//
 
 #include "AMDGPU.h"
@@ -103,10 +112,13 @@
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/IntrinsicsAMDGPU.h"
 #include "llvm/IR/MDBuilder.h"
+#include "llvm/IR/Operator.h"
 #include "llvm/IR/ReplaceConstant.h"
+#include "llvm/IR/ValueHandle.h"
 #include "llvm/Pass.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Transforms/Instrumentation/AddressSanitizerCommon.h"
+#include "llvm/Transforms/Utils/Local.h"
 #include "llvm/Transforms/Utils/ModuleUtils.h"
 
 #include <algorithm>
@@ -169,6 +181,7 @@ struct FunctionsAndLDSAccess {
   SetVector<Function *> NonKernelsWithLDSArgument;
   SetVector<GlobalVariable *> AllNonKernelLDSAccess;
   FunctionVariableMap NonKernelToLDSAccessMap;
+  DenseMap<Function *, SmallPtrSet<Argument *, 4>> NonKernelsWithLDSFlatArg;
 };
 
 class AMDGPUSwLowerLDS {
@@ -178,6 +191,7 @@ class AMDGPUSwLowerLDS {
   bool run();
   void getUsesOfLDSByNonKernels();
   void getNonKernelsWithLDSArguments(const CallGraph &CG);
+  void getNonKernelsWithLDSFlatArguments();
   SetVector<Function *>
   getOrderedIndirectLDSAccessingKernels(SetVector<Function *> &Kernels);
   SetVector<GlobalVariable *>
@@ -204,6 +218,8 @@ class AMDGPUSwLowerLDS {
   void lowerNonKernelLDSAccesses(Function *Func,
                                  SetVector<GlobalVariable *> &LDSGlobals,
                                  NonKernelLDSParameters &NKLDSParams);
+  void lowerNonKernelLDSFlatArgAccesses(Function *Func);
+  Value *getFlatPtrForRoundTripLDSAccess(Function *Func, Value *LDSPtr);
   void
   updateMallocSizeForDynamicLDS(Function *Func, Value **CurrMallocSize,
                                 Value *HiddenDynLDSSize,
@@ -287,6 +303,99 @@ void AMDGPUSwLowerLDS::getNonKernelsWithLDSArguments(const CallGraph &CG) {
   }
 }
 
+// True if flat pointer V is derived from an LDS-carrying argument in LDSArgs,
+// looking through GEP/bitcast/phi/select; Visited breaks phi cycles. When
+// AcceptLocalCast is set, a flat<-local addrspacecast also counts as an origin.
+// Detection (getNonKernelsWithLDSFlatArguments) sets it, since that cast is how
+// LDS first enters a flat pointer; the rewrite
+// (getFlatPtrForRoundTripLDSAccess) leaves it clear so only argument origins
+// qualify.
+static bool flatPtrDerivesFromLDS(Value *V,
+                                  const SmallPtrSetImpl<Argument *> &LDSArgs,
+                                  SmallPtrSetImpl<Value *> &Visited,
+                                  bool AcceptLocalCast) {
+  if (!V->getType()->isPointerTy() || !Visited.insert(V).second)
+    return false;
+  if (auto *A = dyn_cast<Argument>(V))
+    return LDSArgs.contains(A);
+  auto *Op = dyn_cast<Operator>(V);
+  if (!Op)
+    return false;
+  switch (Op->getOpcode()) {
+  case Instruction::AddrSpaceCast: {
+    if (!AcceptLocalCast)
+      return false;
+    Value *Src = Op->getOperand(0);
+    if (Src->getType()->getPointerAddressSpace() == AMDGPUAS::LOCAL_ADDRESS)
+      return true;
+    return flatPtrDerivesFromLDS(Src, LDSArgs, Visited, AcceptLocalCast);
+  }
+  case Instruction::GetElementPtr:
+  case Instruction::BitCast:
+    return flatPtrDerivesFromLDS(Op->getOperand(0), LDSArgs, Visited,
+                                 AcceptLocalCast);
+  case Instruction::PHI:
+    for (Value *In : cast<PHINode>(Op)->incoming_values())
+      if (flatPtrDerivesFromLDS(In, LDSArgs, Visited, AcceptLocalCast))
+        return true;
+    return false;
+  case Instruction::Select: {
+    auto *SI = cast<SelectInst>(Op);
+    return flatPtrDerivesFromLDS(SI->getTrueValue(), LDSArgs, Visited,
+                                 AcceptLocalCast) ||
+           flatPtrDerivesFromLDS(SI->getFalseValue(), LDSArgs, Visited,
+                                 AcceptLocalCast);
+  }
+  default:
+    return false;
+  }
+}
+
+void AMDGPUSwLowerLDS::getNonKernelsWithLDSFlatArguments() {
+  // A kernel or non-kernel may pass lowered LDS storage to a non-kernel as a
+  // flat pointer instead of addrspace(3). Record, per callee, which flat
+  // parameters carry LDS. Must run before lowering rewrites the call-site
+  // casts.
+  auto &FlatArgMap = FuncLDSAccessInfo.NonKernelsWithLDSFlatArg;
+  bool Changed = true;
+  while (Changed) {
+    Changed = false;
+    for (Function &F : M) {
+      if (F.isDeclaration())
+        continue;
+      SmallPtrSet<Argument *, 4> CallerLDSArgs;
+      if (auto It = FlatArgMap.find(&F); It != FlatArgMap.end())
+        CallerLDSArgs = It->second;
+      for (BasicBlock &BB : F) {
+        for (Instruction &I : BB) {
+          auto *CB = dyn_cast<CallBase>(&I);
+          if (!CB)
+            continue;
+          Function *Callee = CB->getCalledFunction();
+          if (!Callee || Callee->isDeclaration() || AMDGPU::isKernel(*Callee))
+            continue;
+          unsigned NumArgs =
+              std::min(CB->arg_size(), (unsigned)Callee->arg_size());
+          for (unsigned ArgNo = 0; ArgNo < NumArgs; ++ArgNo) {
+            Argument *CalleeArg = Callee->getArg(ArgNo);
+            Type *ArgTy = CalleeArg->getType();
+            if (!ArgTy->isPointerTy() ||
+                ArgTy->getPointerAddressSpace() != AMDGPUAS::FLAT_ADDRESS)
+              continue;
+            Value *Actual = CB->getArgOperand(ArgNo);
+            SmallPtrSet<Value *, 8> Visited;
+            if (!flatPtrDerivesFromLDS(Actual, CallerLDSArgs, Visited,
+                                       /*AcceptLocalCast=*/true))
+              continue;
+            if (FlatArgMap[Callee].insert(CalleeArg).second)
+              Changed = true;
+          }
+        }
+      }
+    }
+  }
+}
+
 void AMDGPUSwLowerLDS::getUsesOfLDSByNonKernels() {
   for (GlobalVariable *GV : FuncLDSAccessInfo.AllNonKernelLDSAccess) {
     if (!AMDGPU::isLDSVariableToLower(*GV))
@@ -688,31 +797,51 @@ void AMDGPUSwLowerLDS::translateLDSMemoryOperationsToGlobalMemory(
     SetVector<Instruction *> &LDSInstructions) {
   LLVM_DEBUG(dbgs() << "Translating LDS memory operations to global memory : "
                     << Func->getName());
+  // Map an LDS pointer to its global-memory equivalent: a flat-argument access
+  // uses the flat source pointer, otherwise the base/offset table. nullptr
+  // means neither applies, so the operation is left unchanged.
+  auto TranslatePtr = [&](Value *LDSPtr) -> Value * {
+    if (Value *Flat = getFlatPtrForRoundTripLDSAccess(Func, LDSPtr))
+      return Flat;
+    if (!LoadMallocPtr)
+      return nullptr;
+    return getTranslatedGlobalMemoryPtrOfLDS(LoadMallocPtr, LDSPtr);
+  };
+  // Old LDS pointer operands to clean up. Flat-arg round-trips leave the cast
+  // and local GEPs dead; the base/offset path keeps them live via ptrtoint, so
+  // cleanup is a no-op there. WeakTrackingVH tolerates chains freed
+  // recursively.
+  SmallVector<WeakTrackingVH, 8> MaybeDeadPtrs;
   for (Instruction *Inst : LDSInstructions) {
     IRB.SetInsertPoint(Inst);
     if (LoadInst *LI = dyn_cast<LoadInst>(Inst)) {
       Value *LIOperand = LI->getPointerOperand();
-      Value *Replacement =
-          getTranslatedGlobalMemoryPtrOfLDS(LoadMallocPtr, LIOperand);
+      Value *Replacement = TranslatePtr(LIOperand);
+      if (!Replacement)
+        continue;
       LoadInst *NewLI =
           IRB.CreateLoad(LI->getType(), Replacement, LI->getProperties());
       AsanInfo.Instructions.insert(NewLI);
       LI->replaceAllUsesWith(NewLI);
       LI->eraseFromParent();
+      MaybeDeadPtrs.push_back(LIOperand);
     } else if (StoreInst *SI = dyn_cast<StoreInst>(Inst)) {
       Value *SIOperand = SI->getPointerOperand();
-      Value *Replacement =
-          getTranslatedGlobalMemoryPtrOfLDS(LoadMallocPtr, SIOperand);
+      Value *Replacement = TranslatePtr(SIOperand);
+      if (!Replacement)
+        continue;
       StoreInst *NewSI = IRB.CreateStore(SI->getValueOperand(), Replacement,
                                          SI->getProperties());
       AsanInfo.Instructions.insert(NewSI);
       SI->replaceAllUsesWith(NewSI);
       SI->eraseFromParent();
+      MaybeDeadPtrs.push_back(SIOperand);
     } else if (AtomicRMWInst *RMW = dyn_cast<AtomicRMWInst>(Inst)) {
       Value *RMWPtrOperand = RMW->getPointerOperand();
       Value *RMWValOperand = RMW->getValOperand();
-      Value *Replacement =
-          getTranslatedGlobalMemoryPtrOfLDS(LoadMallocPtr, RMWPtrOperand);
+      Value *Replacement = TranslatePtr(RMWPtrOperand);
+      if (!Replacement)
+        continue;
       AtomicRMWInst *NewRMW = IRB.CreateAtomicRMW(
           RMW->getOperation(), Replacement, RMWValOperand, RMW->getAlign(),
           RMW->getOrdering(), RMW->getSyncScopeID());
@@ -720,10 +849,12 @@ void AMDGPUSwLowerLDS::translateLDSMemoryOperationsToGlobalMemory(
       AsanInfo.Instructions.insert(NewRMW);
       RMW->replaceAllUsesWith(NewRMW);
       RMW->eraseFromParent();
+      MaybeDeadPtrs.push_back(RMWPtrOperand);
     } else if (AtomicCmpXchgInst *XCHG = dyn_cast<AtomicCmpXchgInst>(Inst)) {
       Value *XCHGPtrOperand = XCHG->getPointerOperand();
-      Value *Replacement =
-          getTranslatedGlobalMemoryPtrOfLDS(LoadMallocPtr, XCHGPtrOperand);
+      Value *Replacement = TranslatePtr(XCHGPtrOperand);
+      if (!Replacement)
+        continue;
       AtomicCmpXchgInst *NewXCHG = IRB.CreateAtomicCmpXchg(
           Replacement, XCHG->getCompareOperand(), XCHG->getNewValOperand(),
           XCHG->getAlign(), XCHG->getSuccessOrdering(),
@@ -732,10 +863,16 @@ void AMDGPUSwLowerLDS::translateLDSMemoryOperationsToGlobalMemory(
       AsanInfo.Instructions.insert(NewXCHG);
       XCHG->replaceAllUsesWith(NewXCHG);
       XCHG->eraseFromParent();
+      MaybeDeadPtrs.push_back(XCHGPtrOperand);
     } else if (AnyMemIntrinsic *MI = dyn_cast<AnyMemIntrinsic>(Inst)) {
-      Value *NewDest = MI->getRawDest();
-      if (MI->getDestAddressSpace() == AMDGPUAS::LOCAL_ADDRESS)
-        NewDest = getTranslatedGlobalMemoryPtrOfLDS(LoadMallocPtr, NewDest);
+      Value *OldRawDest = MI->getRawDest();
+      Value *NewDest = OldRawDest;
+      if (MI->getDestAddressSpace() == AMDGPUAS::LOCAL_ADDRESS) {
+        NewDest = TranslatePtr(NewDest);
+        if (!NewDest)
+          continue;
+        MaybeDeadPtrs.push_back(OldRawDest);
+      }
       CallInst *NewMI = nullptr;
       if (AnyMemSetInst *MSI = dyn_cast<AnyMemSetInst>(MI)) {
         if (MI->isAtomic()) {
@@ -748,9 +885,14 @@ void AMDGPUSwLowerLDS::translateLDSMemoryOperationsToGlobalMemory(
                                    cast<MemSetInst>(MI)->isVolatile());
         }
       } else if (AnyMemTransferInst *MTI = dyn_cast<AnyMemTransferInst>(MI)) {
-        Value *NewSrc = MTI->getRawSource();
-        if (MTI->getSourceAddressSpace() == AMDGPUAS::LOCAL_ADDRESS)
-          NewSrc = getTranslatedGlobalMemoryPtrOfLDS(LoadMallocPtr, NewSrc);
+        Value *OldRawSrc = MTI->getRawSource();
+        Value *NewSrc = OldRawSrc;
+        if (MTI->getSourceAddressSpace() == AMDGPUAS::LOCAL_ADDRESS) {
+          NewSrc = TranslatePtr(NewSrc);
+          if (!NewSrc)
+            continue;
+          MaybeDeadPtrs.push_back(OldRawSrc);
+        }
         if (MI->isAtomic()) {
           if (MI->getIntrinsicID() ==
               Intrinsic::memmove_element_unordered_atomic) {
@@ -777,17 +919,78 @@ void AMDGPUSwLowerLDS::translateLDSMemoryOperationsToGlobalMemory(
       MI->eraseFromParent();
     } else if (AddrSpaceCastInst *ASC = dyn_cast<AddrSpaceCastInst>(Inst)) {
       Value *AIOperand = ASC->getPointerOperand();
-      Value *Replacement =
-          getTranslatedGlobalMemoryPtrOfLDS(LoadMallocPtr, AIOperand);
+      Value *Replacement = TranslatePtr(AIOperand);
+      if (!Replacement)
+        continue;
       Value *NewAI = IRB.CreateAddrSpaceCast(Replacement, ASC->getType());
       // Note: No need to add the instruction to AsanInfo instructions to be
       // instrumented list. FLAT_ADDRESS ptr would have been already
       // instrumented by asan pass prior to this pass.
       ASC->replaceAllUsesWith(NewAI);
       ASC->eraseFromParent();
+      MaybeDeadPtrs.push_back(AIOperand);
     } else
       report_fatal_error("Unimplemented LDS lowering instruction");
   }
+
+  // Drop the now-dead flat->local casts and local GEPs.
+  RecursivelyDeleteTriviallyDeadInstructionsPermissive(MaybeDeadPtrs);
+}
+
+Value *AMDGPUSwLowerLDS::getFlatPtrForRoundTripLDSAccess(Function *Func,
+                                                         Value *LDSPtr) {
+  // If LDSPtr is lowered storage reached via a flat argument, i.e.
+  // addrspacecast(flat->local) optionally followed by local GEPs, return the
+  // equivalent flat pointer into the global backing buffer. Otherwise nullptr,
+  // so the caller falls back to the base/offset-table translation.
+  auto It = FuncLDSAccessInfo.NonKernelsWithLDSFlatArg.find(Func);
+  if (It == FuncLDSAccessInfo.NonKernelsWithLDSFlatArg.end())
+    return nullptr;
+  const SmallPtrSet<Argument *, 4> &LDSArgs = It->second;
+
+  // Peel off local GEPs sitting between the cast and the access.
+  SmallVector<GEPOperator *, 4> GEPs;
+  Value *Cur = LDSPtr;
+  while (auto *GEP = dyn_cast<GEPOperator>(Cur)) {
+    if (GEP->getPointerAddressSpace() != AMDGPUAS::LOCAL_ADDRESS)
+      return nullptr;
+    GEPs.push_back(GEP);
+    Cur = GEP->getPointerOperand();
+  }
+
+  auto *ASC = dyn_cast<Operator>(Cur);
+  if (!ASC || ASC->getOpcode() != Instruction::AddrSpaceCast)
+    return nullptr;
+  Value *FlatSrc = ASC->getOperand(0);
+  if (FlatSrc->getType()->getPointerAddressSpace() != AMDGPUAS::FLAT_ADDRESS)
+    return nullptr;
+
+  // The flat source must trace back to an LDS-carrying arg.
+  SmallPtrSet<Value *, 8> Visited;
+  if (!flatPtrDerivesFromLDS(FlatSrc, LDSArgs, Visited,
+                             /*AcceptLocalCast=*/false))
+    return nullptr;
+
+  // Re-apply the peeled GEPs in the flat address space.
+  Value *Flat = FlatSrc;
+  for (GEPOperator *GEP : reverse(GEPs)) {
+    SmallVector<Value *, 4> Indices(GEP->idx_begin(), GEP->idx_end());
+    Flat = IRB.CreateGEP(GEP->getSourceElementType(), Flat, Indices, "",
+                         GEP->getNoWrapFlags());
+  }
+  return Flat;
+}
+
+void AMDGPUSwLowerLDS::lowerNonKernelLDSFlatArgAccesses(Function *Func) {
+  // Lower a non-kernel that only reaches lowered LDS through a flat argument.
+  // No base/offset table is needed: the flat pointer already points at the
+  // global backing buffer, so the shared translation reuses the flat source.
+  SetVector<Instruction *> LDSInstructions;
+  getLDSMemoryInstructions(Func, LDSInstructions);
+  if (LDSInstructions.empty())
+    return;
+  translateLDSMemoryOperationsToGlobalMemory(Func, /*LoadMallocPtr=*/nullptr,
+                                             LDSInstructions);
 }
 
 void AMDGPUSwLowerLDS::poisonRedzones(Function *Func, Value *MallocPtr) {
@@ -1258,6 +1461,11 @@ bool AMDGPUSwLowerLDS::run() {
   // Get address sanitizer scale.
   initAsanInfo();
 
+  // Discover non-kernels that receive lowered LDS via a flat pointer argument.
+  // This must happen before lowering rewrites the call-site address-space
+  // casts.
+  getNonKernelsWithLDSFlatArguments();
+
   for (auto &K : FuncLDSAccessInfo.KernelToLDSParametersMap) {
     Function *Func = K.first;
     auto &LDSParams = FuncLDSAccessInfo.KernelToLDSParametersMap[Func];
@@ -1320,6 +1528,17 @@ bool AMDGPUSwLowerLDS::run() {
     Changed = true;
   }
 
+  // Lower non-kernels that reach lowered LDS only through a flat argument.
+  // Functions also handled above already had these accesses translated there.
+  for (auto &K : FuncLDSAccessInfo.NonKernelsWithLDSFlatArg) {
+    Function *Func = K.first;
+    if (FuncLDSAccessInfo.NonKernelToLDSAccessMap.contains(Func) ||
+        FuncLDSAccessInfo.NonKernelsWithLDSArgument.contains(Func))
+      continue;
+    lowerNonKernelLDSFlatArgAccesses(Func);
+    Changed = true;
+  }
+
   if (!Changed)
     return Changed;
 
diff --git a/llvm/test/CodeGen/AMDGPU/amdgpu-sw-lower-lds-flat-ptr-arg-asan.ll b/llvm/test/CodeGen/AMDGPU/amdgpu-sw-lower-lds-flat-ptr-arg-asan.ll
new file mode 100644
index 0000000000000..f69ebe54cb694
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/amdgpu-sw-lower-lds-flat-ptr-arg-asan.ll
@@ -0,0 +1,256 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=amdgpu-sw-lower-lds -amdgpu-asan-instrument-lds=false -S -mtriple=amdgcn-amd-amdhsa | FileCheck %s
+
+ at lds_var = internal addrspace(3) global [64 x i32] poison, align 4
+
+declare void @llvm.memset.p3.i64(ptr addrspace(3), i8, i64, i1)
+declare void @llvm.memcpy.p3.p1.i64(ptr addrspace(3), ptr addrspace(1), i64, i1)
+declare void @llvm.memcpy.p1.p3.i64(ptr addrspace(1), ptr addrspace(3), i64, i1)
+
+define void @store_case(ptr %storage) sanitize_address {
+; CHECK-LABEL: define void @store_case(
+; CHECK-SAME: ptr [[STORAGE:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT:    store i32 42, ptr [[STORAGE]], align 4
+; CHECK-NEXT:    ret void
+;
+  %cast = addrspacecast ptr %storage to ptr addrspace(3)
+  store i32 42, ptr addrspace(3) %cast, align 4
+  ret void
+}
+
+define i32 @load_case(ptr %storage) sanitize_address {
+; CHECK-LABEL: define i32 @load_case(
+; CHECK-SAME: ptr [[STORAGE:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[STORAGE]], align 4
+; CHECK-NEXT:    ret i32 [[TMP1]]
+;
+  %cast = addrspacecast ptr %storage to ptr addrspace(3)
+  %val = load i32, ptr addrspace(3) %cast, align 4
+  ret i32 %val
+}
+
+define i32 @atomicrmw_case(ptr %storage) sanitize_address {
+; CHECK-LABEL: define i32 @atomicrmw_case(
+; CHECK-SAME: ptr [[STORAGE:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    [[TMP1:%.*]] = atomicrmw add ptr [[STORAGE]], i32 1 seq_cst, align 4
+; CHECK-NEXT:    ret i32 [[TMP1]]
+;
+  %cast = addrspacecast ptr %storage to ptr addrspace(3)
+  %old = atomicrmw add ptr addrspace(3) %cast, i32 1 seq_cst, align 4
+  ret i32 %old
+}
+
+define i32 @cmpxchg_case(ptr %storage) sanitize_address {
+; CHECK-LABEL: define i32 @cmpxchg_case(
+; CHECK-SAME: ptr [[STORAGE:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    [[TMP1:%.*]] = cmpxchg ptr [[STORAGE]], i32 0, i32 1 seq_cst seq_cst, align 4
+; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i32, i1 } [[TMP1]], 0
+; CHECK-NEXT:    ret i32 [[VAL]]
+;
+  %cast = addrspacecast ptr %storage to ptr addrspace(3)
+  %res = cmpxchg ptr addrspace(3) %cast, i32 0, i32 1 seq_cst seq_cst, align 4
+  %val = extractvalue { i32, i1 } %res, 0
+  ret i32 %val
+}
+
+; GEP applied in the local address space after the cast must be rebuilt in flat.
+define void @gep_chain_case(ptr %storage) sanitize_address {
+; CHECK-LABEL: define void @gep_chain_case(
+; CHECK-SAME: ptr [[STORAGE:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i32, ptr [[STORAGE]], i64 5
+; CHECK-NEXT:    store i32 7, ptr [[TMP1]], align 4
+; CHECK-NEXT:    ret void
+;
+  %cast = addrspacecast ptr %storage to ptr addrspace(3)
+  %elem = getelementptr i32, ptr addrspace(3) %cast, i64 5
+  store i32 7, ptr addrspace(3) %elem, align 4
+  ret void
+}
+
+; A flat LDS argument forwarded through a non-kernel must be handled in the
+; callee too.
+define void @forwarded_callee(ptr %p) sanitize_address {
+; CHECK-LABEL: define void @forwarded_callee(
+; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    store i32 9, ptr [[P]], align 4
+; CHECK-NEXT:    ret void
+;
+  %cast = addrspacecast ptr %p...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/209842


More information about the llvm-commits mailing list