[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