From 95fa2f93265ff2efddd05a52897247acacca34a2 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Thu, 12 Feb 2015 09:55:28 +0000 Subject: [PATCH] tsan: do not instrument not captured values I've built some tests in WebRTC with and without this change. With this change number of __tsan_read/write calls is reduced by 20-40%, binary size decreases by 5-10% and execution time drops by ~5%. For example: $ ls -l old/modules_unittests new/modules_unittests -rwxr-x--- 1 dvyukov 41708976 Jan 20 18:35 old/modules_unittests -rwxr-x--- 1 dvyukov 38294008 Jan 20 18:29 new/modules_unittests $ objdump -d old/modules_unittests | egrep "callq.*__tsan_(read|write|unaligned)" | wc -l 239871 $ objdump -d new/modules_unittests | egrep "callq.*__tsan_(read|write|unaligned)" | wc -l 148365 http://reviews.llvm.org/D7069 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@228917 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../Instrumentation/ThreadSanitizer.cpp | 15 +++ .../ThreadSanitizer/capture.ll | 91 +++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 test/Instrumentation/ThreadSanitizer/capture.ll diff --git a/lib/Transforms/Instrumentation/ThreadSanitizer.cpp b/lib/Transforms/Instrumentation/ThreadSanitizer.cpp index e9999c486fa..e4a49112144 100644 --- a/lib/Transforms/Instrumentation/ThreadSanitizer.cpp +++ b/lib/Transforms/Instrumentation/ThreadSanitizer.cpp @@ -19,6 +19,8 @@ // The rest is handled by the run-time library. //===----------------------------------------------------------------------===// +#include "llvm/Analysis/CaptureTracking.h" +#include "llvm/Analysis/ValueTracking.h" #include "llvm/Transforms/Instrumentation.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallString.h" @@ -68,6 +70,7 @@ STATISTIC(NumInstrumentedVtableReads, "Number of vtable ptr reads"); STATISTIC(NumOmittedReadsFromConstantGlobals, "Number of reads from constant globals"); STATISTIC(NumOmittedReadsFromVtable, "Number of vtable reads"); +STATISTIC(NumOmittedNonCaptured, "Number of accesses ignored due to capturing"); namespace { @@ -272,6 +275,7 @@ bool ThreadSanitizer::addrPointsToConstantData(Value *Addr) { // Instrumenting some of the accesses may be proven redundant. // Currently handled: // - read-before-write (within same BB, no calls between) +// - not captured variables // // We do not handle some of the patterns that should not survive // after the classic compiler optimizations. @@ -303,6 +307,17 @@ void ThreadSanitizer::chooseInstructionsToInstrument( continue; } } + Value *Addr = isa(*I) + ? cast(I)->getPointerOperand() + : cast(I)->getPointerOperand(); + if (isa(GetUnderlyingObject(Addr, nullptr)) && + !PointerMayBeCaptured(Addr, true, true)) { + // 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). + NumOmittedNonCaptured++; + continue; + } All.push_back(I); } Local.clear(); diff --git a/test/Instrumentation/ThreadSanitizer/capture.ll b/test/Instrumentation/ThreadSanitizer/capture.ll new file mode 100644 index 00000000000..d6c62f0b88b --- /dev/null +++ b/test/Instrumentation/ThreadSanitizer/capture.ll @@ -0,0 +1,91 @@ +; 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-S128" + +declare void @escape(i32*) + +@sink = global i32* null, align 4 + +define void @captured0() nounwind uwtable sanitize_thread { +entry: + %ptr = alloca i32, align 4 + ; escapes due to call + call void @escape(i32* %ptr) + store i32 42, i32* %ptr, align 4 + ret void +} +; CHECK-LABEL: define void @captured0 +; CHECK: __tsan_write +; CHECK: ret void + +define void @captured1() nounwind uwtable sanitize_thread { +entry: + %ptr = alloca i32, align 4 + ; escapes due to store into global + store i32* %ptr, i32** @sink, align 8 + store i32 42, i32* %ptr, align 4 + ret void +} +; CHECK-LABEL: define void @captured1 +; CHECK: __tsan_write +; CHECK: __tsan_write +; CHECK: ret void + +define void @captured2() nounwind uwtable sanitize_thread { +entry: + %ptr = alloca i32, align 4 + %tmp = alloca i32*, align 8 + ; transitive escape + store i32* %ptr, i32** %tmp, align 8 + %0 = load i32** %tmp, align 8 + store i32* %0, i32** @sink, align 8 + store i32 42, i32* %ptr, align 4 + ret void +} +; CHECK-LABEL: define void @captured2 +; CHECK: __tsan_write +; CHECK: __tsan_write +; CHECK: ret void + +define void @notcaptured0() nounwind uwtable sanitize_thread { +entry: + %ptr = alloca i32, align 4 + store i32 42, i32* %ptr, align 4 + ; escapes due to call + call void @escape(i32* %ptr) + ret void +} +; CHECK-LABEL: define void @notcaptured0 +; CHECK: __tsan_write +; CHECK: ret void + +define void @notcaptured1() nounwind uwtable sanitize_thread { +entry: + %ptr = alloca i32, align 4 + store i32 42, i32* %ptr, align 4 + ; escapes due to store into global + store i32* %ptr, i32** @sink, align 8 + ret void +} +; CHECK-LABEL: define void @notcaptured1 +; CHECK: __tsan_write +; CHECK: __tsan_write +; CHECK: ret void + +define void @notcaptured2() nounwind uwtable sanitize_thread { +entry: + %ptr = alloca i32, align 4 + %tmp = alloca i32*, align 8 + store i32 42, i32* %ptr, align 4 + ; transitive escape + store i32* %ptr, i32** %tmp, align 8 + %0 = load i32** %tmp, align 8 + store i32* %0, i32** @sink, align 8 + ret void +} +; CHECK-LABEL: define void @notcaptured2 +; CHECK: __tsan_write +; CHECK: __tsan_write +; CHECK: ret void + + -- 2.34.1