@@ -120,6 +120,9 @@ struct ThreadSanitizerOnSpirv {
120
120
121
121
void instrumentModule ();
122
122
123
+ bool instrumentAllocInst (Function *F,
124
+ SmallVectorImpl<Instruction *> &AllocaInsts);
125
+
123
126
void appendDebugInfoToArgs (Instruction *I, SmallVectorImpl<Value *> &Args);
124
127
125
128
private:
@@ -144,6 +147,7 @@ struct ThreadSanitizerOnSpirv {
144
147
145
148
// Accesses sizes are powers of two: 1, 2, 4, 8, 16.
146
149
static const size_t kNumberOfAccessSizes = 5 ;
150
+ FunctionCallee TsanCleanupPrivate;
147
151
FunctionCallee TsanRead[kNumberOfAccessSizes ];
148
152
FunctionCallee TsanWrite[kNumberOfAccessSizes ];
149
153
@@ -261,6 +265,10 @@ void ThreadSanitizerOnSpirv::initialize() {
261
265
Attr = Attr.addFnAttribute (C, Attribute::NoUnwind);
262
266
Type *Int8PtrTy = IRB.getInt8PtrTy (kSpirOffloadConstantAS );
263
267
268
+ TsanCleanupPrivate =
269
+ M.getOrInsertFunction (" __tsan_cleanup_private" , Attr, IRB.getVoidTy (),
270
+ IntptrTy, IRB.getInt32Ty ());
271
+
264
272
for (size_t i = 0 ; i < kNumberOfAccessSizes ; ++i) {
265
273
const unsigned ByteSize = 1U << i;
266
274
std::string ByteSizeStr = utostr (ByteSize);
@@ -282,6 +290,28 @@ void ThreadSanitizerOnSpirv::initialize() {
282
290
}
283
291
}
284
292
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
+
285
315
void ThreadSanitizerOnSpirv::appendDebugInfoToArgs (
286
316
Instruction *I, SmallVectorImpl<Value *> &Args) {
287
317
auto &Loc = I->getDebugLoc ();
@@ -793,6 +823,7 @@ bool ThreadSanitizer::sanitizeFunction(Function &F,
793
823
SmallVector<Instruction*, 8 > LocalLoadsAndStores;
794
824
SmallVector<Instruction*, 8 > AtomicAccesses;
795
825
SmallVector<Instruction*, 8 > MemIntrinCalls;
826
+ SmallVector<Instruction *, 8 > Allocas;
796
827
bool Res = false ;
797
828
bool HasCalls = false ;
798
829
bool SanitizeFunction = F.hasFnAttribute (Attribute::SanitizeThread);
@@ -808,6 +839,9 @@ bool ThreadSanitizer::sanitizeFunction(Function &F,
808
839
AtomicAccesses.push_back (&Inst);
809
840
else if (isa<LoadInst>(Inst) || isa<StoreInst>(Inst))
810
841
LocalLoadsAndStores.push_back (&Inst);
842
+ else if (Spirv && isa<AllocaInst>(Inst) &&
843
+ cast<AllocaInst>(Inst).getAllocatedType ()->isSized ())
844
+ Allocas.push_back (&Inst);
811
845
else if ((isa<CallInst>(Inst) && !isa<DbgInfoIntrinsic>(Inst)) ||
812
846
isa<InvokeInst>(Inst)) {
813
847
if (CallInst *CI = dyn_cast<CallInst>(&Inst))
@@ -850,6 +884,14 @@ bool ThreadSanitizer::sanitizeFunction(Function &F,
850
884
InsertRuntimeIgnores (F);
851
885
}
852
886
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
+
853
895
// Instrument function entry/exit points if there were instrumented accesses.
854
896
if ((Res || HasCalls) && ClInstrumentFuncEntryExit) {
855
897
InstrumentationIRBuilder IRB (&F.getEntryBlock (),
0 commit comments