Skip to content

Commit ded1f3e

Browse files
authored
[TSan] Add option to ignore capturing behavior when instrumenting (#148156)
While not needed for most applications, some tools such as [MUST](https://www.i12.rwth-aachen.de/cms/i12/forschung/forschungsschwerpunkte/lehrstuhl-fuer-hochleistungsrechnen/~nrbe/must/) depend on the instrumentation being present. MUST uses the ThreadSanitizer annotation interface to detect data races in MPI programs, where the capture tracking is detrimental as it has no bearing on MPI data races, leading to missed races.
1 parent e80e7e7 commit ded1f3e

File tree

3 files changed

+100
-1
lines changed

3 files changed

+100
-1
lines changed

llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ static cl::opt<bool> ClCompoundReadBeforeWrite(
8080
"tsan-compound-read-before-write", cl::init(false),
8181
cl::desc("Emit special compound instrumentation for reads-before-writes"),
8282
cl::Hidden);
83+
static cl::opt<bool>
84+
ClOmitNonCaptured("tsan-omit-by-pointer-capturing", cl::init(true),
85+
cl::desc("Omit accesses due to pointer capturing"),
86+
cl::Hidden);
8387

8488
STATISTIC(NumInstrumentedReads, "Number of instrumented reads");
8589
STATISTIC(NumInstrumentedWrites, "Number of instrumented writes");
@@ -450,7 +454,8 @@ void ThreadSanitizer::chooseInstructionsToInstrument(
450454

451455
const AllocaInst *AI = findAllocaForValue(Addr);
452456
// Instead of Addr, we should check whether its base pointer is captured.
453-
if (AI && !PointerMayBeCaptured(AI, /*ReturnCaptures=*/true)) {
457+
if (AI && !PointerMayBeCaptured(AI, /*ReturnCaptures=*/true) &&
458+
ClOmitNonCaptured) {
454459
// The variable is addressable but not captured, so it cannot be
455460
// referenced from a different thread and participate in a data race
456461
// (see llvm/Analysis/CaptureTracking.h for details).
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
; RUN: opt < %s -passes=tsan -tsan-omit-by-pointer-capturing=0 -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-S128"
4+
5+
declare void @escape(ptr)
6+
7+
@sink = global ptr null, align 4
8+
9+
10+
define void @captured2() nounwind uwtable sanitize_thread {
11+
entry:
12+
%ptr = alloca i32, align 4
13+
%tmp = alloca ptr, align 8
14+
; transitive escape
15+
store ptr %ptr, ptr %tmp, align 8
16+
%0 = load ptr, ptr %tmp, align 8
17+
store ptr %0, ptr @sink, align 8
18+
store i32 42, ptr %ptr, align 4
19+
ret void
20+
}
21+
; CHECK-LABEL: define void @captured2
22+
; CHECK: __tsan_write
23+
; CHECK: __tsan_read
24+
; CHECK: __tsan_write
25+
; CHECK: __tsan_write
26+
; CHECK: ret void
27+
28+
define void @captured3() nounwind uwtable sanitize_thread {
29+
entry:
30+
%stkobj = alloca [2 x i32], align 8
31+
; escapes due to store into global
32+
store ptr %stkobj, ptr @sink, align 8
33+
; derived is captured as its base object is captured
34+
%derived = getelementptr inbounds i32, ptr %stkobj, i64 1
35+
store i32 42, ptr %derived, align 4
36+
ret void
37+
}
38+
; CHECK-LABEL: define void @captured3
39+
; CHECK: __tsan_write
40+
; CHECK: __tsan_write
41+
; CHECK: ret void
42+
43+
define void @notcaptured2() nounwind uwtable sanitize_thread {
44+
entry:
45+
%ptr = alloca i32, align 4
46+
%tmp = alloca ptr, align 8
47+
store i32 42, ptr %ptr, align 4
48+
; transitive escape
49+
store ptr %ptr, ptr %tmp, align 8
50+
%0 = load ptr, ptr %tmp, align 8
51+
store ptr %0, ptr @sink, align 8
52+
ret void
53+
}
54+
; CHECK-LABEL: define void @notcaptured2
55+
; CHECK: __tsan_write
56+
; CHECK: __tsan_write
57+
; CHECK: __tsan_read
58+
; CHECK: __tsan_write
59+
; CHECK: ret void
60+
61+
define void @notcaptured3(i1 %cond) nounwind uwtable sanitize_thread {
62+
entry:
63+
%stkobj = alloca [2 x i32], align 8
64+
%derived = getelementptr inbounds i32, ptr %stkobj, i64 1
65+
%ptr = select i1 %cond, ptr %derived, ptr %stkobj
66+
store i32 42, ptr %ptr, align 4
67+
ret void
68+
}
69+
; CHECK-LABEL: define void @notcaptured3
70+
; CHECK: __tsan_write
71+
; CHECK: ret void
72+
73+
define void @notcaptured4() nounwind uwtable sanitize_thread {
74+
entry:
75+
%stkobj = alloca [10 x i8], align 1
76+
br label %loop
77+
78+
exit:
79+
ret void
80+
81+
loop:
82+
%count = phi i32 [ 0, %entry ], [ %addone, %loop ]
83+
%derived = phi ptr [ %stkobj, %entry ], [ %ptraddone, %loop ]
84+
store i32 %count, ptr %derived, align 4
85+
%ptraddone = getelementptr inbounds i32, ptr %derived, i64 1
86+
%addone = add nuw nsw i32 %count, 1
87+
%eq10 = icmp eq i32 %addone, 10
88+
br i1 %eq10, label %exit, label %loop
89+
}
90+
; CHECK-LABEL: define void @notcaptured4
91+
; CHECK: ret void
92+
; CHECK: __tsan_write

llvm/test/Instrumentation/ThreadSanitizer/capture.ll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ entry:
4545
; CHECK-LABEL: define void @captured2
4646
; CHECK: __tsan_write
4747
; CHECK: __tsan_write
48+
; CHECK-NOT: __tsan_write
4849
; CHECK: ret void
4950

5051
define void @captured3() nounwind uwtable sanitize_thread {
@@ -101,6 +102,7 @@ entry:
101102
; CHECK-LABEL: define void @notcaptured2
102103
; CHECK: __tsan_write
103104
; CHECK: __tsan_write
105+
; CHECK-NOT: __tsan_write
104106
; CHECK: ret void
105107

106108
define void @notcaptured3(i1 %cond) nounwind uwtable sanitize_thread {

0 commit comments

Comments
 (0)