[llvm] [Instrumentation] Add TSan instrumentation for AMDGPU targets (PR #207713)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jul 6 05:22:13 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Joseph Huber (jhuber6)
<details>
<summary>Changes</summary>
Summary:
This PR adds TSan interfaces for AMDGPU targets. This uses the existing
names, except we need to add the optional atomic scope so they can be
recreated. Addotionally, we handle address spaces, only global / LDS
accesses have any meaningful threading participation. These all get put
through flat pointers. We have a per-kernel init function that will be
used to initialize LDS, as it is undef on init.
---
Patch is 23.82 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/207713.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp (+125-50)
- (added) llvm/test/Instrumentation/ThreadSanitizer/tsan_amdgpu.ll (+229)
``````````diff
diff --git a/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp
index f05efd863fb74..0f6b98c3c0997 100644
--- a/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp
@@ -24,6 +24,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSwitch.h"
#include "llvm/Analysis/CaptureTracking.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/ValueTracking.h"
@@ -38,9 +39,11 @@
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/ProfileData/InstrProf.h"
+#include "llvm/Support/AMDGPUAddrSpace.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
+#include "llvm/TargetParser/Triple.h"
#include "llvm/Transforms/Utils/EscapeEnumerator.h"
#include "llvm/Transforms/Utils/Instrumentation.h"
#include "llvm/Transforms/Utils/Local.h"
@@ -100,6 +103,30 @@ STATISTIC(NumOmittedNonCaptured, "Number of accesses ignored due to capturing");
const char kTsanModuleCtorName[] = "tsan.module_ctor";
const char kTsanInitName[] = "__tsan_init";
+// The scope values are hard-coded here just like the atomic ordering.
+static ConstantInt *createScope(IRBuilder<> *IRB, const Triple &T,
+ LLVMContext &Ctx, SyncScope::ID SSID) {
+ if (T.isAMDGPU()) {
+ auto Name = Ctx.getSyncScopeName(SSID);
+ if (!Name)
+ return IRB->getInt32(0);
+ uint32_t V = StringSwitch<uint32_t>(*Name)
+ .Case("agent", 1)
+ .Case("device", 1)
+ .Case("workgroup", 2)
+ .Case("wavefront", 3)
+ .Case("singlethread", 4)
+ .Case("cluster", 5)
+ .Default(0);
+ return IRB->getInt32(V);
+ }
+ llvm_unreachable("unsupported GPU target for TSAN scope mapping");
+}
+
+static bool isAMDGPUTarget(const Module &M) {
+ return M.getTargetTriple().isAMDGPU();
+}
+
namespace {
/// ThreadSanitizer: instrument the code in module to find races.
@@ -145,7 +172,9 @@ struct ThreadSanitizer {
int getMemoryAccessFuncIndex(Type *OrigTy, Value *Addr, const DataLayout &DL);
void InsertRuntimeIgnores(Function &F);
+ bool IsAMDGPU = false;
Type *IntptrTy;
+ FunctionCallee TsanKernelEntry;
FunctionCallee TsanFuncEntry;
FunctionCallee TsanFuncExit;
FunctionCallee TsanIgnoreBegin;
@@ -197,18 +226,40 @@ PreservedAnalyses ModuleThreadSanitizerPass::run(Module &M,
// Return early if nosanitize_thread module flag is present for the module.
if (checkIfAlreadyInstrumented(M, "nosanitize_thread"))
return PreservedAnalyses::all();
- insertModuleCtor(M);
+ if (!isAMDGPUTarget(M))
+ insertModuleCtor(M);
return PreservedAnalyses::none();
}
void ThreadSanitizer::initialize(Module &M, const TargetLibraryInfo &TLI) {
const DataLayout &DL = M.getDataLayout();
LLVMContext &Ctx = M.getContext();
IntptrTy = DL.getIntPtrType(Ctx);
+ IsAMDGPU = isAMDGPUTarget(M);
IRBuilder<> IRB(Ctx);
AttributeList Attr;
Attr = Attr.addFnAttribute(Ctx, Attribute::NoUnwind);
+
+ IntegerType *OrdTy = IRB.getInt32Ty();
+
+ // On GPU targets, atomic runtime functions take an extra i32 scope argument
+ // and we skip TLI-based parameter attributes (not needed for GPU ABIs).
+ auto getAtomicFnDecl = [&](StringRef Name, AttributeList HostAttr,
+ Type *RetTy,
+ ArrayRef<Type *> ArgTys) -> FunctionCallee {
+ SmallVector<Type *, 6> FinalArgs(ArgTys);
+ if (IsAMDGPU) {
+ FinalArgs.push_back(OrdTy);
+ return M.getOrInsertFunction(
+ Name, FunctionType::get(RetTy, FinalArgs, false), Attr);
+ }
+ return M.getOrInsertFunction(
+ Name, FunctionType::get(RetTy, FinalArgs, false), HostAttr);
+ };
+
// Initialize the callbacks.
+ TsanKernelEntry =
+ M.getOrInsertFunction("__tsan_kernel_entry", Attr, IRB.getVoidTy());
TsanFuncEntry = M.getOrInsertFunction("__tsan_func_entry", Attr,
IRB.getVoidTy(), IRB.getPtrTy());
TsanFuncExit =
@@ -217,7 +268,7 @@ void ThreadSanitizer::initialize(Module &M, const TargetLibraryInfo &TLI) {
IRB.getVoidTy());
TsanIgnoreEnd =
M.getOrInsertFunction("__tsan_ignore_thread_end", Attr, IRB.getVoidTy());
- IntegerType *OrdTy = IRB.getInt32Ty();
+
for (size_t i = 0; i < kNumberOfAccessSizes; ++i) {
const unsigned ByteSize = 1U << i;
const unsigned BitSize = ByteSize * 8;
@@ -270,20 +321,20 @@ void ThreadSanitizer::initialize(Module &M, const TargetLibraryInfo &TLI) {
Type *PtrTy = PointerType::get(Ctx, 0);
SmallString<32> AtomicLoadName("__tsan_atomic" + BitSizeStr + "_load");
TsanAtomicLoad[i] =
- M.getOrInsertFunction(AtomicLoadName,
- TLI.getAttrList(&Ctx, {1}, /*Signed=*/true,
- /*Ret=*/BitSize <= 32, Attr),
- Ty, PtrTy, OrdTy);
+ getAtomicFnDecl(AtomicLoadName,
+ TLI.getAttrList(&Ctx, {1}, /*Signed=*/true,
+ /*Ret=*/BitSize <= 32, Attr),
+ Ty, {PtrTy, OrdTy});
// Args of type Ty need extension only when BitSize is 32 or less.
using Idxs = std::vector<unsigned>;
- Idxs Idxs2Or12 ((BitSize <= 32) ? Idxs({1, 2}) : Idxs({2}));
+ Idxs Idxs2Or12((BitSize <= 32) ? Idxs({1, 2}) : Idxs({2}));
Idxs Idxs34Or1234((BitSize <= 32) ? Idxs({1, 2, 3, 4}) : Idxs({3, 4}));
SmallString<32> AtomicStoreName("__tsan_atomic" + BitSizeStr + "_store");
- TsanAtomicStore[i] = M.getOrInsertFunction(
+ TsanAtomicStore[i] = getAtomicFnDecl(
AtomicStoreName,
TLI.getAttrList(&Ctx, Idxs2Or12, /*Signed=*/true, /*Ret=*/false, Attr),
- IRB.getVoidTy(), PtrTy, Ty, OrdTy);
+ IRB.getVoidTy(), {PtrTy, Ty, OrdTy});
for (unsigned Op = AtomicRMWInst::FIRST_BINOP;
Op <= AtomicRMWInst::LAST_BINOP; ++Op) {
@@ -306,35 +357,35 @@ void ThreadSanitizer::initialize(Module &M, const TargetLibraryInfo &TLI) {
else
continue;
SmallString<32> RMWName("__tsan_atomic" + itostr(BitSize) + NamePart);
- TsanAtomicRMW[Op][i] = M.getOrInsertFunction(
- RMWName,
- TLI.getAttrList(&Ctx, Idxs2Or12, /*Signed=*/true,
- /*Ret=*/BitSize <= 32, Attr),
- Ty, PtrTy, Ty, OrdTy);
+ TsanAtomicRMW[Op][i] =
+ getAtomicFnDecl(RMWName,
+ TLI.getAttrList(&Ctx, Idxs2Or12, /*Signed=*/true,
+ /*Ret=*/BitSize <= 32, Attr),
+ Ty, {PtrTy, Ty, OrdTy});
}
SmallString<32> AtomicCASName("__tsan_atomic" + BitSizeStr +
"_compare_exchange_val");
- TsanAtomicCAS[i] = M.getOrInsertFunction(
- AtomicCASName,
- TLI.getAttrList(&Ctx, Idxs34Or1234, /*Signed=*/true,
- /*Ret=*/BitSize <= 32, Attr),
- Ty, PtrTy, Ty, Ty, OrdTy, OrdTy);
+ TsanAtomicCAS[i] =
+ getAtomicFnDecl(AtomicCASName,
+ TLI.getAttrList(&Ctx, Idxs34Or1234, /*Signed=*/true,
+ /*Ret=*/BitSize <= 32, Attr),
+ Ty, {PtrTy, Ty, Ty, OrdTy, OrdTy});
}
TsanVptrUpdate =
M.getOrInsertFunction("__tsan_vptr_update", Attr, IRB.getVoidTy(),
IRB.getPtrTy(), IRB.getPtrTy());
TsanVptrLoad = M.getOrInsertFunction("__tsan_vptr_read", Attr,
IRB.getVoidTy(), IRB.getPtrTy());
- TsanAtomicThreadFence = M.getOrInsertFunction(
+ TsanAtomicThreadFence = getAtomicFnDecl(
"__tsan_atomic_thread_fence",
TLI.getAttrList(&Ctx, {0}, /*Signed=*/true, /*Ret=*/false, Attr),
- IRB.getVoidTy(), OrdTy);
+ IRB.getVoidTy(), {OrdTy});
- TsanAtomicSignalFence = M.getOrInsertFunction(
+ TsanAtomicSignalFence = getAtomicFnDecl(
"__tsan_atomic_signal_fence",
TLI.getAttrList(&Ctx, {0}, /*Signed=*/true, /*Ret=*/false, Attr),
- IRB.getVoidTy(), OrdTy);
+ IRB.getVoidTy(), {OrdTy});
MemmoveFn =
M.getOrInsertFunction("__tsan_memmove", Attr, IRB.getPtrTy(),
@@ -374,10 +425,17 @@ static bool shouldInstrumentReadWriteFromAddress(const Module *M, Value *Addr) {
}
// Do not instrument accesses from different address spaces; we cannot deal
- // with them.
+ // with them. On GPU targets, we accept flat, global, and local (LDS).
Type *PtrTy = cast<PointerType>(Addr->getType()->getScalarType());
- if (PtrTy->getPointerAddressSpace() != 0)
- return false;
+ unsigned AS = PtrTy->getPointerAddressSpace();
+ if (isAMDGPUTarget(*M)) {
+ if (AS != AMDGPUAS::FLAT_ADDRESS && AS != AMDGPUAS::GLOBAL_ADDRESS &&
+ AS != AMDGPUAS::LOCAL_ADDRESS)
+ return false;
+ } else {
+ if (AS != 0)
+ return false;
+ }
return true;
}
@@ -577,6 +635,8 @@ bool ThreadSanitizer::sanitizeFunction(Function &F,
if ((Res || HasCalls) && ClInstrumentFuncEntryExit) {
InstrumentationIRBuilder IRB(&F.getEntryBlock(),
F.getEntryBlock().getFirstNonPHIIt());
+ if (IsAMDGPU && F.getCallingConv() == CallingConv::AMDGPU_KERNEL)
+ IRB.CreateCall(TsanKernelEntry, {});
auto ProgramAsPtrTy = PointerType::get(F.getParent()->getContext(),
DL.getProgramAddressSpace());
Value *ReturnAddress = IRB.CreateIntrinsic(
@@ -659,7 +719,7 @@ bool ThreadSanitizer::instrumentLoadOrStore(const InstructionInfo &II,
else
OnAccessFunc = IsWrite ? TsanUnalignedWrite[Idx] : TsanUnalignedRead[Idx];
}
- IRB.CreateCall(OnAccessFunc, Addr);
+ IRB.CreateCall(OnAccessFunc, IRB.CreateAddrSpaceCast(Addr, IRB.getPtrTy()));
if (IsCompoundRW || IsWrite)
NumInstrumentedWrites++;
if (IsCompoundRW || !IsWrite)
@@ -698,16 +758,14 @@ bool ThreadSanitizer::instrumentMemIntrinsic(Instruction *I) {
Value *Cast1 = IRB.CreateIntCast(M->getArgOperand(1), IRB.getInt32Ty(), false);
Value *Cast2 = IRB.CreateIntCast(M->getArgOperand(2), IntptrTy, false);
IRB.CreateCall(
- MemsetFn,
- {M->getArgOperand(0),
- Cast1,
- Cast2});
+ MemsetFn, {IRB.CreateAddrSpaceCast(M->getArgOperand(0), IRB.getPtrTy()),
+ Cast1, Cast2});
I->eraseFromParent();
} else if (MemTransferInst *M = dyn_cast<MemTransferInst>(I)) {
IRB.CreateCall(
isa<MemCpyInst>(M) ? MemcpyFn : MemmoveFn,
- {M->getArgOperand(0),
- M->getArgOperand(1),
+ {IRB.CreateAddrSpaceCast(M->getArgOperand(0), IRB.getPtrTy()),
+ IRB.CreateAddrSpaceCast(M->getArgOperand(1), IRB.getPtrTy()),
IRB.CreateIntCast(M->getArgOperand(2), IntptrTy, false)});
I->eraseFromParent();
}
@@ -724,20 +782,28 @@ bool ThreadSanitizer::instrumentMemIntrinsic(Instruction *I) {
bool ThreadSanitizer::instrumentAtomic(Instruction *I, const DataLayout &DL) {
InstrumentationIRBuilder IRB(I);
+ Module *M = I->getModule();
+ LLVMContext &Ctx = M->getContext();
+ const Triple &T = M->getTargetTriple();
+
if (LoadInst *LI = dyn_cast<LoadInst>(I)) {
- Value *Addr = LI->getPointerOperand();
+ Value *Addr =
+ IRB.CreateAddrSpaceCast(LI->getPointerOperand(), IRB.getPtrTy());
Type *OrigTy = LI->getType();
int Idx = getMemoryAccessFuncIndex(OrigTy, Addr, DL);
if (Idx < 0)
return false;
- Value *Args[] = {Addr,
- createOrdering(&IRB, LI->getOrdering())};
+ SmallVector<Value *, 4> Args = {Addr,
+ createOrdering(&IRB, LI->getOrdering())};
+ if (IsAMDGPU)
+ Args.push_back(createScope(&IRB, T, Ctx, LI->getSyncScopeID()));
Value *C = IRB.CreateCall(TsanAtomicLoad[Idx], Args);
Value *Cast = IRB.CreateBitOrPointerCast(C, OrigTy);
I->replaceAllUsesWith(Cast);
I->eraseFromParent();
} else if (StoreInst *SI = dyn_cast<StoreInst>(I)) {
- Value *Addr = SI->getPointerOperand();
+ Value *Addr =
+ IRB.CreateAddrSpaceCast(SI->getPointerOperand(), IRB.getPtrTy());
int Idx =
getMemoryAccessFuncIndex(SI->getValueOperand()->getType(), Addr, DL);
if (Idx < 0)
@@ -745,13 +811,16 @@ bool ThreadSanitizer::instrumentAtomic(Instruction *I, const DataLayout &DL) {
const unsigned ByteSize = 1U << Idx;
const unsigned BitSize = ByteSize * 8;
Type *Ty = Type::getIntNTy(IRB.getContext(), BitSize);
- Value *Args[] = {Addr,
- IRB.CreateBitOrPointerCast(SI->getValueOperand(), Ty),
- createOrdering(&IRB, SI->getOrdering())};
+ SmallVector<Value *, 4> Args = {
+ Addr, IRB.CreateBitOrPointerCast(SI->getValueOperand(), Ty),
+ createOrdering(&IRB, SI->getOrdering())};
+ if (IsAMDGPU)
+ Args.push_back(createScope(&IRB, T, Ctx, SI->getSyncScopeID()));
IRB.CreateCall(TsanAtomicStore[Idx], Args);
SI->eraseFromParent();
} else if (AtomicRMWInst *RMWI = dyn_cast<AtomicRMWInst>(I)) {
- Value *Addr = RMWI->getPointerOperand();
+ Value *Addr =
+ IRB.CreateAddrSpaceCast(RMWI->getPointerOperand(), IRB.getPtrTy());
int Idx =
getMemoryAccessFuncIndex(RMWI->getValOperand()->getType(), Addr, DL);
if (Idx < 0)
@@ -763,13 +832,16 @@ bool ThreadSanitizer::instrumentAtomic(Instruction *I, const DataLayout &DL) {
const unsigned BitSize = ByteSize * 8;
Type *Ty = Type::getIntNTy(IRB.getContext(), BitSize);
Value *Val = RMWI->getValOperand();
- Value *Args[] = {Addr, IRB.CreateBitOrPointerCast(Val, Ty),
- createOrdering(&IRB, RMWI->getOrdering())};
+ SmallVector<Value *, 4> Args = {Addr, IRB.CreateBitOrPointerCast(Val, Ty),
+ createOrdering(&IRB, RMWI->getOrdering())};
+ if (IsAMDGPU)
+ Args.push_back(createScope(&IRB, T, Ctx, RMWI->getSyncScopeID()));
Value *C = IRB.CreateCall(F, Args);
I->replaceAllUsesWith(IRB.CreateBitOrPointerCast(C, Val->getType()));
I->eraseFromParent();
} else if (AtomicCmpXchgInst *CASI = dyn_cast<AtomicCmpXchgInst>(I)) {
- Value *Addr = CASI->getPointerOperand();
+ Value *Addr =
+ IRB.CreateAddrSpaceCast(CASI->getPointerOperand(), IRB.getPtrTy());
Type *OrigOldValTy = CASI->getNewValOperand()->getType();
int Idx = getMemoryAccessFuncIndex(OrigOldValTy, Addr, DL);
if (Idx < 0)
@@ -781,11 +853,12 @@ bool ThreadSanitizer::instrumentAtomic(Instruction *I, const DataLayout &DL) {
IRB.CreateBitOrPointerCast(CASI->getCompareOperand(), Ty);
Value *NewOperand =
IRB.CreateBitOrPointerCast(CASI->getNewValOperand(), Ty);
- Value *Args[] = {Addr,
- CmpOperand,
- NewOperand,
- createOrdering(&IRB, CASI->getSuccessOrdering()),
- createOrdering(&IRB, CASI->getFailureOrdering())};
+ SmallVector<Value *, 6> Args = {
+ Addr, CmpOperand, NewOperand,
+ createOrdering(&IRB, CASI->getSuccessOrdering()),
+ createOrdering(&IRB, CASI->getFailureOrdering())};
+ if (IsAMDGPU)
+ Args.push_back(createScope(&IRB, T, Ctx, CASI->getSyncScopeID()));
CallInst *C = IRB.CreateCall(TsanAtomicCAS[Idx], Args);
Value *Success = IRB.CreateICmpEQ(C, CmpOperand);
Value *OldVal = C;
@@ -801,7 +874,9 @@ bool ThreadSanitizer::instrumentAtomic(Instruction *I, const DataLayout &DL) {
I->replaceAllUsesWith(Res);
I->eraseFromParent();
} else if (FenceInst *FI = dyn_cast<FenceInst>(I)) {
- Value *Args[] = {createOrdering(&IRB, FI->getOrdering())};
+ SmallVector<Value *, 2> Args = {createOrdering(&IRB, FI->getOrdering())};
+ if (IsAMDGPU)
+ Args.push_back(createScope(&IRB, T, Ctx, FI->getSyncScopeID()));
FunctionCallee F = FI->getSyncScopeID() == SyncScope::SingleThread
? TsanAtomicSignalFence
: TsanAtomicThreadFence;
diff --git a/llvm/test/Instrumentation/ThreadSanitizer/tsan_amdgpu.ll b/llvm/test/Instrumentation/ThreadSanitizer/tsan_amdgpu.ll
new file mode 100644
index 0000000000000..4d7bcf8036f6f
--- /dev/null
+++ b/llvm/test/Instrumentation/ThreadSanitizer/tsan_amdgpu.ll
@@ -0,0 +1,229 @@
+; RUN: opt < %s -passes='function(tsan),module(tsan-module)' -S | FileCheck %s
+; REQUIRES: amdgpu-registered-target
+
+target datalayout = "e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128-p9:192:256:256:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7:8:9"
+target triple = "amdgcn-amd-amdhsa"
+
+; CHECK-NOT: @llvm.global_ctors = {{.*}}@tsan.module_ctor
+
+; CHECK-LABEL: @entry_exit
+; CHECK: call void @__tsan_kernel_entry()
+; CHECK: call void @__tsan_func_entry(ptr %{{.*}})
+; CHECK: call void @__tsan_read4(ptr %p)
+; CHECK: call void @__tsan_func_exit()
+define amdgpu_kernel void @entry_exit(ptr %p) sanitize_thread {
+entry:
+ %0 = load i32, ptr %p, align 4
+ ret void
+}
+
+; CHECK-LABEL: @read_flat
+define amdgpu_kernel void @read_flat(ptr %p) sanitize_thread {
+entry:
+; CHECK: call void @__tsan_read4(ptr %p)
+ %0 = load i32, ptr %p, align 4
+ ret void
+}
+
+; CHECK-LABEL: @write_flat
+define amdgpu_kernel void @write_flat(ptr %p) sanitize_thread {
+entry:
+; CHECK: call void @__tsan_write4(ptr %p)
+ store i32 1, ptr %p, align 4
+ ret void
+}
+
+; CHECK-LABEL: @read_global
+define amdgpu_kernel void @read_global(ptr addrspace(1) %p) sanitize_thread {
+entry:
+; CHECK: addrspacecast ptr addrspace(1) %p to ptr
+; CHECK: call void @__tsan_read4(ptr %{{.*}})
+ %0 = load i32, ptr addrspace(1) %p, align 4
+ ret void
+}
+
+; CHECK-LABEL: @write_global
+define amdgpu_kernel void @write_global(ptr addrspace(1) %p) sanitize_thread {
+entry:
+; CHECK: addrspacecast ptr addrspace(1) %p to ptr
+; CHECK: call void @__tsan_write4(ptr %{{.*}})
+ store i32 1, ptr addrspace(1) %p, align 4
+ ret void
+}
+
+; CHECK-LABEL: @read_lds
+define amdgpu_kernel void @read_lds(ptr addrspace(3) %p) sanitize_thread {
+entry:
+; CHECK: addrspacecast ptr addrspace(3) %p to ptr
+; CHECK: call void @__tsan_read4(ptr %{{.*}})
+ %0 = load i32, ptr addrspace(3) %p, align 4
+ ret void
+}
+
+; CHECK-LABEL: @write_lds
+define amdgpu_kernel void @write_lds(ptr addrspace(3) %p) sanitize_thread {
+entry:
+; CHECK: addrspacecast ptr addrspace(3) %p to ptr
+; CHECK: call void @__tsan_write4(ptr %{{.*}})
+ store i32 1, ptr addrspace(3) %p, align 4
+ ret void
+}
+
+; CHECK-LABEL: @read_private
+define amdgpu_kernel void @read_private(ptr addrspace(5) %p) sanitize_thread {
+entry:
+; CHECK-NOT: call void @__tsan
+; CHECK: ret void
+ %0 = load i32, ptr addrspace(5) %p, align 4
+ ret void
+}
+
+; CHECK-LABEL: @read_constant
+define amdgpu_kernel void @read_constant(ptr addrspace(4) %p) sanitize_thread {
+entry:
+; CHECK-NOT: call void @__tsan
+; CHECK: ret void
+ %0 = load i32, ptr addrspace(4) %p, align 4
+ ret void
+}
+
+; CHECK-LABEL: @atomic_load_system
+define amdgpu_kernel void @atomic_load_system(ptr %p) sanitize_thread {
+entry:
+; CHECK: call i32 @__tsan_atomic32_load(ptr %p, i32 5, i32 0)
+ %0 = load atomic i32, ptr %p syncscope("") seq_cst, align 4
+ ret void
+}
+
+; CHECK-LABEL: @atomic_load_agent
+define amdgpu_kernel void @atomic_load_agent(ptr %p) sanitize_thread {
+entry:
+; CHECK: call i32 @__tsan_atomic32_load(ptr %p, i32 2, i32 1)
+ %0 = load atomic i32, ptr %p syncscope("agent") acquire, align 4
+ ret void
+}
+
+; CHECK-LABEL: @atomic_load_workgroup
+define amdgpu_kernel void @atomic_load_workgroup(ptr %p) sanitize_thread {
+entry:
+; CHECK: call i32 @__tsan_atomic32_load(ptr %p, i32 2, i32 2)
+ %0 = load atomic i32, ptr %p syncscope("workgroup") acquire, align 4
+ ret void
+}
+
+; CHECK-LABEL: @atomic_load_wavefront
+define amdgpu_kernel void @atomic_load_wavefront(ptr %p) sanitize_thread {
+entry:
+; CHECK: call i32 @__tsan_atomic32_load(ptr %p, i32 0, i32 3)
+ %0 = load atomic i32, ptr %p syncscope("wavefront") monotonic, align 4
+ ret void
+}
+
+; CHECK-LABEL: @atomic_store_agent
+define amdgpu_kernel void...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/207713
More information about the llvm-commits
mailing list