@@ -120,6 +120,9 @@ struct ThreadSanitizerOnSpirv {
120120
121121 void instrumentModule ();
122122
123+ bool instrumentAllocInst (Function *F,
124+ SmallVectorImpl<Instruction *> &AllocaInsts);
125+
123126 void appendDebugInfoToArgs (Instruction *I, SmallVectorImpl<Value *> &Args);
124127
125128private:
@@ -144,6 +147,7 @@ struct ThreadSanitizerOnSpirv {
144147
145148 // Accesses sizes are powers of two: 1, 2, 4, 8, 16.
146149 static const size_t kNumberOfAccessSizes = 5 ;
150+ FunctionCallee TsanCleanupPrivate;
147151 FunctionCallee TsanRead[kNumberOfAccessSizes ];
148152 FunctionCallee TsanWrite[kNumberOfAccessSizes ];
149153
@@ -261,6 +265,10 @@ void ThreadSanitizerOnSpirv::initialize() {
261265 Attr = Attr.addFnAttribute (C, Attribute::NoUnwind);
262266 Type *Int8PtrTy = IRB.getInt8PtrTy (kSpirOffloadConstantAS );
263267
268+ TsanCleanupPrivate =
269+ M.getOrInsertFunction (" __tsan_cleanup_private" , Attr, IRB.getVoidTy (),
270+ IntptrTy, IRB.getInt32Ty ());
271+
264272 for (size_t i = 0 ; i < kNumberOfAccessSizes ; ++i) {
265273 const unsigned ByteSize = 1U << i;
266274 std::string ByteSizeStr = utostr (ByteSize);
@@ -282,6 +290,28 @@ void ThreadSanitizerOnSpirv::initialize() {
282290 }
283291}
284292
293+ bool ThreadSanitizerOnSpirv::instrumentAllocInst (
294+ Function *F, SmallVectorImpl<Instruction *> &AllocaInsts) {
295+ bool Changed = false ;
296+
297+ EscapeEnumerator EE (*F, " tsan_cleanup" , false );
298+ while (IRBuilder<> *AtExit = EE.Next ()) {
299+ InstrumentationIRBuilder::ensureDebugInfo (*AtExit, *F);
300+ for (auto *Inst : AllocaInsts) {
301+ AllocaInst *AI = cast<AllocaInst>(Inst);
302+ if (auto AllocSize = AI->getAllocationSize (DL)) {
303+ AtExit->CreateCall (
304+ TsanCleanupPrivate,
305+ {AtExit->CreatePtrToInt (AI, IntptrTy),
306+ ConstantInt::get (AtExit->getInt32Ty (), *AllocSize)});
307+ Changed |= true ;
308+ }
309+ }
310+ }
311+
312+ return Changed;
313+ }
314+
285315void ThreadSanitizerOnSpirv::appendDebugInfoToArgs (
286316 Instruction *I, SmallVectorImpl<Value *> &Args) {
287317 auto &Loc = I->getDebugLoc ();
@@ -793,6 +823,7 @@ bool ThreadSanitizer::sanitizeFunction(Function &F,
793823 SmallVector<Instruction*, 8 > LocalLoadsAndStores;
794824 SmallVector<Instruction*, 8 > AtomicAccesses;
795825 SmallVector<Instruction*, 8 > MemIntrinCalls;
826+ SmallVector<Instruction *, 8 > Allocas;
796827 bool Res = false ;
797828 bool HasCalls = false ;
798829 bool SanitizeFunction = F.hasFnAttribute (Attribute::SanitizeThread);
@@ -808,6 +839,9 @@ bool ThreadSanitizer::sanitizeFunction(Function &F,
808839 AtomicAccesses.push_back (&Inst);
809840 else if (isa<LoadInst>(Inst) || isa<StoreInst>(Inst))
810841 LocalLoadsAndStores.push_back (&Inst);
842+ else if (Spirv && isa<AllocaInst>(Inst) &&
843+ cast<AllocaInst>(Inst).getAllocatedType ()->isSized ())
844+ Allocas.push_back (&Inst);
811845 else if ((isa<CallInst>(Inst) && !isa<DbgInfoIntrinsic>(Inst)) ||
812846 isa<InvokeInst>(Inst)) {
813847 if (CallInst *CI = dyn_cast<CallInst>(&Inst))
@@ -850,6 +884,14 @@ bool ThreadSanitizer::sanitizeFunction(Function &F,
850884 InsertRuntimeIgnores (F);
851885 }
852886
887+ // FIXME: We need to skip the check for private memory, otherwise OpenCL CPU
888+ // device may generate false positive reports due to stack re-use in different
889+ // threads. However, SPIR-V builts 'ToPrivate' doesn't work as expected on
890+ // OpenCL CPU device. So we need to manually cleanup private shadow before
891+ // each function exit point.
892+ if (Spirv && !Allocas.empty ())
893+ Res |= Spirv->instrumentAllocInst (&F, Allocas);
894+
853895 // Instrument function entry/exit points if there were instrumented accesses.
854896 if ((Res || HasCalls) && ClInstrumentFuncEntryExit) {
855897 InstrumentationIRBuilder IRB (&F.getEntryBlock (),
0 commit comments