Skip to content

Commit 3c43737

Browse files
committedNov 11, 2016
[tsan][llvm] Implement the function attribute to disable TSan checking at run time
This implements a function annotation that disables TSan checking for the function at run time. The benefit over attribute((no_sanitize("thread"))) is that the accesses within the callees will also be suppressed. The motivation for this attribute is a guarantee given by the objective C language that the calls to the reference count decrement and object deallocation will be synchronized. To model this properly, we would need to intercept all ref count decrement calls (which are very common in ObjC due to use of ARC) and also every single message send. Instead, we propose to just ignore all accesses made from within dealloc at run time. The main downside is that this still does not introduce any synchronization, which means we might still report false positives if the code that relies on this synchronization is not executed from within dealloc. However, we have not seen this in practice so far and think these cases will be very rare. Differential Revision: https://reviews.llvm.org/D25858 llvm-svn: 286663
1 parent 9bfbf8b commit 3c43737

File tree

2 files changed

+58
-0
lines changed

2 files changed

+58
-0
lines changed
 

‎llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,15 @@ struct ThreadSanitizer : public FunctionPass {
9999
const DataLayout &DL);
100100
bool addrPointsToConstantData(Value *Addr);
101101
int getMemoryAccessFuncIndex(Value *Addr, const DataLayout &DL);
102+
void InsertRuntimeIgnores(Function &F, SmallVector<Instruction*, 8> &RetVec);
102103

103104
Type *IntptrTy;
104105
IntegerType *OrdTy;
105106
// Callbacks to run-time library are computed in doInitialization.
106107
Function *TsanFuncEntry;
107108
Function *TsanFuncExit;
109+
Function *TsanIgnoreBegin;
110+
Function *TsanIgnoreEnd;
108111
// Accesses sizes are powers of two: 1, 2, 4, 8, 16.
109112
static const size_t kNumberOfAccessSizes = 5;
110113
Function *TsanRead[kNumberOfAccessSizes];
@@ -152,6 +155,10 @@ void ThreadSanitizer::initializeCallbacks(Module &M) {
152155
"__tsan_func_entry", IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
153156
TsanFuncExit = checkSanitizerInterfaceFunction(
154157
M.getOrInsertFunction("__tsan_func_exit", IRB.getVoidTy(), nullptr));
158+
TsanIgnoreBegin = checkSanitizerInterfaceFunction(M.getOrInsertFunction(
159+
"__tsan_ignore_thread_begin", IRB.getVoidTy(), nullptr));
160+
TsanIgnoreEnd = checkSanitizerInterfaceFunction(M.getOrInsertFunction(
161+
"__tsan_ignore_thread_end", IRB.getVoidTy(), nullptr));
155162
OrdTy = IRB.getInt32Ty();
156163
for (size_t i = 0; i < kNumberOfAccessSizes; ++i) {
157164
const unsigned ByteSize = 1U << i;
@@ -376,6 +383,16 @@ static bool isAtomic(Instruction *I) {
376383
return false;
377384
}
378385

386+
void ThreadSanitizer::InsertRuntimeIgnores(Function &F,
387+
SmallVector<Instruction*, 8> &RetVec) {
388+
IRBuilder<> IRB(F.getEntryBlock().getFirstNonPHI());
389+
IRB.CreateCall(TsanIgnoreBegin);
390+
for (auto RetInst : RetVec) {
391+
IRBuilder<> IRB(RetInst);
392+
IRB.CreateCall(TsanIgnoreEnd);
393+
}
394+
}
395+
379396
bool ThreadSanitizer::runOnFunction(Function &F) {
380397
// This is required to prevent instrumenting call to __tsan_init from within
381398
// the module constructor.
@@ -438,6 +455,12 @@ bool ThreadSanitizer::runOnFunction(Function &F) {
438455
Res |= instrumentMemIntrinsic(Inst);
439456
}
440457

458+
if (F.hasFnAttribute("sanitize_thread_no_checking_at_run_time")) {
459+
assert(!F.hasFnAttribute(Attribute::SanitizeThread));
460+
if (HasCalls)
461+
InsertRuntimeIgnores(F, RetVec);
462+
}
463+
441464
// Instrument function entry/exit points if there were instrumented accesses.
442465
if ((Res || HasCalls) && ClInstrumentFuncEntryExit) {
443466
IRBuilder<> IRB(F.getEntryBlock().getFirstNonPHI());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
; RUN: opt < %s -tsan -S | FileCheck %s
2+
3+
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"
4+
target triple = "x86_64-unknown-linux-gnu"
5+
6+
define i32 @"\01-[NoCalls dealloc]"(i32* %a) "sanitize_thread_no_checking_at_run_time" {
7+
entry:
8+
%tmp1 = load i32, i32* %a, align 4
9+
ret i32 %tmp1
10+
}
11+
12+
; CHECK: define i32 @"\01-[NoCalls dealloc]"(i32* %a)
13+
; CHECK-NEXT: entry:
14+
; CHECK-NEXT: %tmp1 = load i32, i32* %a, align 4
15+
; CHECK-NEXT: ret i32 %tmp1
16+
17+
declare void @"foo"()
18+
19+
define i32 @"\01-[WithCalls dealloc]"(i32* %a) "sanitize_thread_no_checking_at_run_time" {
20+
entry:
21+
%tmp1 = load i32, i32* %a, align 4
22+
call void @foo()
23+
ret i32 %tmp1
24+
}
25+
26+
; CHECK: define i32 @"\01-[WithCalls dealloc]"(i32* %a)
27+
; CHECK-NEXT: entry:
28+
; CHECK-NEXT: %0 = call i8* @llvm.returnaddress(i32 0)
29+
; CHECK-NEXT: call void @__tsan_func_entry(i8* %0)
30+
; CHECK-NEXT: call void @__tsan_ignore_thread_begin()
31+
; CHECK-NEXT: %tmp1 = load i32, i32* %a, align 4
32+
; CHECK-NEXT: call void @foo()
33+
; CHECK-NEXT: call void @__tsan_ignore_thread_end()
34+
; CHECK-NEXT: call void @__tsan_func_exit()
35+
; CHECK-NEXT: ret i32 %tmp1

0 commit comments

Comments
 (0)
Please sign in to comment.