Index: lib/Transforms/Instrumentation/ThreadSanitizer.cpp =================================================================== --- lib/Transforms/Instrumentation/ThreadSanitizer.cpp +++ lib/Transforms/Instrumentation/ThreadSanitizer.cpp @@ -99,12 +99,16 @@ const DataLayout &DL); bool addrPointsToConstantData(Value *Addr); int getMemoryAccessFuncIndex(Value *Addr, const DataLayout &DL); + void InsertRuntimeIgnores(Function &F, SmallVector &RetVec, + SmallVector &NoRetCalls); Type *IntptrTy; IntegerType *OrdTy; // Callbacks to run-time library are computed in doInitialization. Function *TsanFuncEntry; Function *TsanFuncExit; + Function *TsanIgnoreBegin; + Function *TsanIgnoreEnd; // Accesses sizes are powers of two: 1, 2, 4, 8, 16. static const size_t kNumberOfAccessSizes = 5; Function *TsanRead[kNumberOfAccessSizes]; @@ -152,6 +156,10 @@ "__tsan_func_entry", IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr)); TsanFuncExit = checkSanitizerInterfaceFunction( M.getOrInsertFunction("__tsan_func_exit", IRB.getVoidTy(), nullptr)); + TsanIgnoreBegin = checkSanitizerInterfaceFunction(M.getOrInsertFunction( + "__tsan_ignore_thread_begin", IRB.getVoidTy(), nullptr)); + TsanIgnoreEnd = checkSanitizerInterfaceFunction(M.getOrInsertFunction( + "__tsan_ignore_thread_end", IRB.getVoidTy(), nullptr)); OrdTy = IRB.getInt32Ty(); for (size_t i = 0; i < kNumberOfAccessSizes; ++i) { const unsigned ByteSize = 1U << i; @@ -376,6 +384,37 @@ return false; } +static bool isNoCheckingAtRunTime(Function &F) { + if (F.hasFnAttribute("sanitize_thread_no_checking_at_run_time")) + return true; + return false; +} + +static bool shouldSanitize(Function &F) { + if (!F.hasFnAttribute(Attribute::SanitizeThread)) + return false; + if (isNoCheckingAtRunTime(F)) + return false; + return true; +} + +void ThreadSanitizer::InsertRuntimeIgnores(Function &F, + SmallVector &RetVec, + SmallVector &NoRetCalls) { + // Ignore all acesses while we are executing ObjC dealloc. TSan does not see + // the synchronization between retain count release and dealloc. + IRBuilder<> IRB(F.getEntryBlock().getFirstNonPHI()); + IRB.CreateCall(TsanIgnoreBegin); + for (auto RetInst : RetVec) { + IRBuilder<> IRB(RetInst); + IRB.CreateCall(TsanIgnoreEnd); + } + for (auto NoRetCallInst : NoRetCalls) { + IRBuilder<> IRB(NoRetCallInst); + IRB.CreateCall(TsanIgnoreEnd); + } +} + bool ThreadSanitizer::runOnFunction(Function &F) { // This is required to prevent instrumenting call to __tsan_init from within // the module constructor. @@ -387,9 +426,10 @@ SmallVector LocalLoadsAndStores; SmallVector AtomicAccesses; SmallVector MemIntrinCalls; + SmallVector NoReturnCalls; bool Res = false; bool HasCalls = false; - bool SanitizeFunction = F.hasFnAttribute(Attribute::SanitizeThread); + bool SanitizeFunction = shouldSanitize(F); const DataLayout &DL = F.getParent()->getDataLayout(); const TargetLibraryInfo *TLI = &getAnalysis().getTLI(); @@ -408,6 +448,11 @@ maybeMarkSanitizerLibraryCallNoBuiltin(CI, TLI); if (isa(Inst)) MemIntrinCalls.push_back(&Inst); + CallInst *CI = dyn_cast(&Inst); + InvokeInst *II = dyn_cast(&Inst); + if ((CI && CI->doesNotReturn()) || (II && II->doesNotReturn())) { + NoReturnCalls.push_back(&Inst); + } HasCalls = true; chooseInstructionsToInstrument(LocalLoadsAndStores, AllLoadsAndStores, DL); @@ -438,6 +483,10 @@ Res |= instrumentMemIntrinsic(Inst); } + // Ignore all accesses happening while dealloc is running. + if (isNoCheckingAtRunTime(F)) + InsertRuntimeIgnores(F, RetVec, NoReturnCalls); + // Instrument function entry/exit points if there were instrumented accesses. if ((Res || HasCalls) && ClInstrumentFuncEntryExit) { IRBuilder<> IRB(F.getEntryBlock().getFirstNonPHI()); Index: test/Instrumentation/ThreadSanitizer/sanitize-thread-no-checking.ll =================================================================== --- /dev/null +++ test/Instrumentation/ThreadSanitizer/sanitize-thread-no-checking.ll @@ -0,0 +1,36 @@ +; RUN: opt < %s -tsan -S | FileCheck %s + +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" +target triple = "x86_64-unknown-linux-gnu" + +define i32 @"\01-[MyObject1 dealloc]"(i32* %a) "sanitize_thread_no_checking_at_run_time" { +entry: + %tmp1 = load i32, i32* %a, align 4 + ret i32 %tmp1 +} + +; CHECK: define i32 @"\01-[MyObject1 dealloc]"(i32* %a) +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @__tsan_ignore_thread_begin() +; CHECK-NEXT: %tmp1 = load i32, i32* %a, align 4 +; CHECK-NEXT: call void @__tsan_ignore_thread_end() +; CHECK-NEXT: ret i32 %tmp1 + +; no sanitize_thread attribute here +define i32 @"\01-[MyObject2 dealloc]"(i32* %a) "sanitize_thread_no_checking_at_run_time" { +entry: + %tmp1 = load i32, i32* %a, align 4 + call void @no_ret_foo() + ret i32 %tmp1 +} + +; CHECK: define i32 @"\01-[MyObject2 dealloc]"(i32* %a) +; CHECK-NEXT: entry: +; CHECK-NEXT: %0 = call i8* @llvm.returnaddress(i32 0) +; CHECK-NEXT: call void @__tsan_func_entry(i8* %0) +; CHECK-NEXT: call void @__tsan_ignore_thread_begin() +; CHECK-NEXT: %tmp1 = load i32, i32* %a, align 4 +; CHECK-NEXT: call void @__tsan_ignore_thread_end() +; CHECK-NEXT: call void @no_ret_foo() + +declare void @no_ret_foo() noreturn