[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.
This commit is contained in:
Yussur Mustafa Oraji 2025-08-06 15:47:33 +02:00 committed by GitHub
parent e80e7e717e
commit ded1f3ec96
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 100 additions and 1 deletions

View File

@ -80,6 +80,10 @@ static cl::opt<bool> ClCompoundReadBeforeWrite(
"tsan-compound-read-before-write", cl::init(false),
cl::desc("Emit special compound instrumentation for reads-before-writes"),
cl::Hidden);
static cl::opt<bool>
ClOmitNonCaptured("tsan-omit-by-pointer-capturing", cl::init(true),
cl::desc("Omit accesses due to pointer capturing"),
cl::Hidden);
STATISTIC(NumInstrumentedReads, "Number of instrumented reads");
STATISTIC(NumInstrumentedWrites, "Number of instrumented writes");
@ -450,7 +454,8 @@ void ThreadSanitizer::chooseInstructionsToInstrument(
const AllocaInst *AI = findAllocaForValue(Addr);
// Instead of Addr, we should check whether its base pointer is captured.
if (AI && !PointerMayBeCaptured(AI, /*ReturnCaptures=*/true)) {
if (AI && !PointerMayBeCaptured(AI, /*ReturnCaptures=*/true) &&
ClOmitNonCaptured) {
// The variable is addressable but not captured, so it cannot be
// referenced from a different thread and participate in a data race
// (see llvm/Analysis/CaptureTracking.h for details).

View File

@ -0,0 +1,92 @@
; RUN: opt < %s -passes=tsan -tsan-omit-by-pointer-capturing=0 -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-S128"
declare void @escape(ptr)
@sink = global ptr null, align 4
define void @captured2() nounwind uwtable sanitize_thread {
entry:
%ptr = alloca i32, align 4
%tmp = alloca ptr, align 8
; transitive escape
store ptr %ptr, ptr %tmp, align 8
%0 = load ptr, ptr %tmp, align 8
store ptr %0, ptr @sink, align 8
store i32 42, ptr %ptr, align 4
ret void
}
; CHECK-LABEL: define void @captured2
; CHECK: __tsan_write
; CHECK: __tsan_read
; CHECK: __tsan_write
; CHECK: __tsan_write
; CHECK: ret void
define void @captured3() nounwind uwtable sanitize_thread {
entry:
%stkobj = alloca [2 x i32], align 8
; escapes due to store into global
store ptr %stkobj, ptr @sink, align 8
; derived is captured as its base object is captured
%derived = getelementptr inbounds i32, ptr %stkobj, i64 1
store i32 42, ptr %derived, align 4
ret void
}
; CHECK-LABEL: define void @captured3
; CHECK: __tsan_write
; CHECK: __tsan_write
; CHECK: ret void
define void @notcaptured2() nounwind uwtable sanitize_thread {
entry:
%ptr = alloca i32, align 4
%tmp = alloca ptr, align 8
store i32 42, ptr %ptr, align 4
; transitive escape
store ptr %ptr, ptr %tmp, align 8
%0 = load ptr, ptr %tmp, align 8
store ptr %0, ptr @sink, align 8
ret void
}
; CHECK-LABEL: define void @notcaptured2
; CHECK: __tsan_write
; CHECK: __tsan_write
; CHECK: __tsan_read
; CHECK: __tsan_write
; CHECK: ret void
define void @notcaptured3(i1 %cond) nounwind uwtable sanitize_thread {
entry:
%stkobj = alloca [2 x i32], align 8
%derived = getelementptr inbounds i32, ptr %stkobj, i64 1
%ptr = select i1 %cond, ptr %derived, ptr %stkobj
store i32 42, ptr %ptr, align 4
ret void
}
; CHECK-LABEL: define void @notcaptured3
; CHECK: __tsan_write
; CHECK: ret void
define void @notcaptured4() nounwind uwtable sanitize_thread {
entry:
%stkobj = alloca [10 x i8], align 1
br label %loop
exit:
ret void
loop:
%count = phi i32 [ 0, %entry ], [ %addone, %loop ]
%derived = phi ptr [ %stkobj, %entry ], [ %ptraddone, %loop ]
store i32 %count, ptr %derived, align 4
%ptraddone = getelementptr inbounds i32, ptr %derived, i64 1
%addone = add nuw nsw i32 %count, 1
%eq10 = icmp eq i32 %addone, 10
br i1 %eq10, label %exit, label %loop
}
; CHECK-LABEL: define void @notcaptured4
; CHECK: ret void
; CHECK: __tsan_write

View File

@ -45,6 +45,7 @@ entry:
; CHECK-LABEL: define void @captured2
; CHECK: __tsan_write
; CHECK: __tsan_write
; CHECK-NOT: __tsan_write
; CHECK: ret void
define void @captured3() nounwind uwtable sanitize_thread {
@ -101,6 +102,7 @@ entry:
; CHECK-LABEL: define void @notcaptured2
; CHECK: __tsan_write
; CHECK: __tsan_write
; CHECK-NOT: __tsan_write
; CHECK: ret void
define void @notcaptured3(i1 %cond) nounwind uwtable sanitize_thread {