Skip to content

Commit 6dbe225

Browse files
committed
[NewPM][TSan] Reiterate the TSan port
Summary: Second iteration of D56433 which got reverted in rL350719. The problem in the previous version was that we dropped the thunk calling the tsan init function. The new version keeps the thunk which should appease dyld, but is not actually OK wrt. the current semantics of function passes. Hence, add a helper to insert the functions only on the first time. The helper allows hooking into the insertion to be able to append them to the global ctors list. Reviewers: chandlerc, vitalybuka, fedor.sergeev, leonardchan Subscribers: hiraditya, bollu, llvm-commits Differential Revision: https://reviews.llvm.org/D56538 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@351314 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent b95047e commit 6dbe225

File tree

11 files changed

+138
-41
lines changed

11 files changed

+138
-41
lines changed

bindings/go/llvm/InstrumentationBindings.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "llvm/IR/Module.h"
1818
#include "llvm/Transforms/Instrumentation.h"
1919
#include "llvm/Transforms/Instrumentation/MemorySanitizer.h"
20+
#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
2021

2122
using namespace llvm;
2223

@@ -29,7 +30,7 @@ void LLVMAddAddressSanitizerModulePass(LLVMPassManagerRef PM) {
2930
}
3031

3132
void LLVMAddThreadSanitizerPass(LLVMPassManagerRef PM) {
32-
unwrap(PM)->add(createThreadSanitizerPass());
33+
unwrap(PM)->add(createThreadSanitizerLegacyPassPass());
3334
}
3435

3536
void LLVMAddMemorySanitizerLegacyPassPass(LLVMPassManagerRef PM) {

include/llvm/InitializePasses.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ void initializeTailDuplicatePass(PassRegistry&);
392392
void initializeTargetLibraryInfoWrapperPassPass(PassRegistry&);
393393
void initializeTargetPassConfigPass(PassRegistry&);
394394
void initializeTargetTransformInfoWrapperPassPass(PassRegistry&);
395-
void initializeThreadSanitizerPass(PassRegistry&);
395+
void initializeThreadSanitizerLegacyPassPass(PassRegistry&);
396396
void initializeTwoAddressInstructionPassPass(PassRegistry&);
397397
void initializeTypeBasedAAWrapperPassPass(PassRegistry&);
398398
void initializeUnifyFunctionExitNodesPass(PassRegistry&);

include/llvm/Transforms/Instrumentation.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,6 @@ ModulePass *createAddressSanitizerModulePass(bool CompileKernel = false,
155155
FunctionPass *createHWAddressSanitizerPass(bool CompileKernel = false,
156156
bool Recover = false);
157157

158-
// Insert ThreadSanitizer (race detection) instrumentation
159-
FunctionPass *createThreadSanitizerPass();
160-
161158
// Insert DataFlowSanitizer (dynamic data flow analysis) instrumentation
162159
ModulePass *createDataFlowSanitizerPass(
163160
const std::vector<std::string> &ABIListFiles = std::vector<std::string>(),
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//===- Transforms/Instrumentation/MemorySanitizer.h - TSan Pass -----------===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
//
10+
// This file defines the thread sanitizer pass.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef LLVM_TRANSFORMS_INSTRUMENTATION_THREADSANITIZER_H
15+
#define LLVM_TRANSFORMS_INSTRUMENTATION_THREADSANITIZER_H
16+
17+
#include "llvm/IR/PassManager.h"
18+
#include "llvm/Pass.h"
19+
20+
namespace llvm {
21+
// Insert ThreadSanitizer (race detection) instrumentation
22+
FunctionPass *createThreadSanitizerLegacyPassPass();
23+
24+
/// A function pass for tsan instrumentation.
25+
///
26+
/// Instruments functions to detect race conditions reads. This function pass
27+
/// inserts calls to runtime library functions. If the functions aren't declared
28+
/// yet, the pass inserts the declarations. Otherwise the existing globals are
29+
struct ThreadSanitizerPass : public PassInfoMixin<ThreadSanitizerPass> {
30+
PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM);
31+
};
32+
} // namespace llvm
33+
#endif /* LLVM_TRANSFORMS_INSTRUMENTATION_THREADSANITIZER_H */

include/llvm/Transforms/Utils/ModuleUtils.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,19 @@ std::pair<Function *, Function *> createSanitizerCtorAndInitFunctions(
5858
ArrayRef<Type *> InitArgTypes, ArrayRef<Value *> InitArgs,
5959
StringRef VersionCheckName = StringRef());
6060

61+
/// Creates sanitizer constructor function lazily. If a constructor and init
62+
/// function already exist, this function returns it. Otherwise it calls \c
63+
/// createSanitizerCtorAndInitFunctions. The FunctionsCreatedCallback is invoked
64+
/// in that case, passing the new Ctor and Init function.
65+
///
66+
/// \return Returns pair of pointers to constructor, and init functions
67+
/// respectively.
68+
std::pair<Function *, Function *> getOrCreateSanitizerCtorAndInitFunctions(
69+
Module &M, StringRef CtorName, StringRef InitName,
70+
ArrayRef<Type *> InitArgTypes, ArrayRef<Value *> InitArgs,
71+
function_ref<void(Function *, Function *)> FunctionsCreatedCallback,
72+
StringRef VersionCheckName = StringRef());
73+
6174
// Creates and returns a sanitizer init function without argument if it doesn't
6275
// exist, and adds it to the global constructors list. Otherwise it returns the
6376
// existing function.

lib/Passes/PassBuilder.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
#include "llvm/Transforms/Instrumentation/GCOVProfiler.h"
9696
#include "llvm/Transforms/Instrumentation/InstrProfiling.h"
9797
#include "llvm/Transforms/Instrumentation/MemorySanitizer.h"
98+
#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
9899
#include "llvm/Transforms/Instrumentation/PGOInstrumentation.h"
99100
#include "llvm/Transforms/Scalar/ADCE.h"
100101
#include "llvm/Transforms/Scalar/AlignmentFromAssumptions.h"

lib/Passes/PassRegistry.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ FUNCTION_PASS("view-cfg", CFGViewerPass())
232232
FUNCTION_PASS("view-cfg-only", CFGOnlyViewerPass())
233233
FUNCTION_PASS("transform-warning", WarnMissedTransformationsPass())
234234
FUNCTION_PASS("msan", MemorySanitizerPass())
235+
FUNCTION_PASS("tsan", ThreadSanitizerPass())
235236
#undef FUNCTION_PASS
236237

237238
#ifndef FUNCTION_PASS_WITH_PARAMS

lib/Transforms/Instrumentation/Instrumentation.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ void llvm::initializeInstrumentation(PassRegistry &Registry) {
113113
initializeInstrProfilingLegacyPassPass(Registry);
114114
initializeMemorySanitizerLegacyPassPass(Registry);
115115
initializeHWAddressSanitizerPass(Registry);
116-
initializeThreadSanitizerPass(Registry);
116+
initializeThreadSanitizerLegacyPassPass(Registry);
117117
initializeSanitizerCoverageModulePass(Registry);
118118
initializeDataFlowSanitizerPass(Registry);
119119
initializeEfficiencySanitizerPass(Registry);

lib/Transforms/Instrumentation/ThreadSanitizer.cpp

Lines changed: 63 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
// The rest is handled by the run-time library.
2020
//===----------------------------------------------------------------------===//
2121

22+
#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
2223
#include "llvm/ADT/SmallPtrSet.h"
2324
#include "llvm/ADT/SmallString.h"
2425
#include "llvm/ADT/SmallVector.h"
@@ -86,15 +87,16 @@ static const char *const kTsanInitName = "__tsan_init";
8687
namespace {
8788

8889
/// ThreadSanitizer: instrument the code in module to find races.
89-
struct ThreadSanitizer : public FunctionPass {
90-
ThreadSanitizer() : FunctionPass(ID) {}
91-
StringRef getPassName() const override;
92-
void getAnalysisUsage(AnalysisUsage &AU) const override;
93-
bool runOnFunction(Function &F) override;
94-
bool doInitialization(Module &M) override;
95-
static char ID; // Pass identification, replacement for typeid.
96-
97-
private:
90+
///
91+
/// Instantiating ThreadSanitizer inserts the tsan runtime library API function
92+
/// declarations into the module if they don't exist already. Instantiating
93+
/// ensures the __tsan_init function is in the list of global constructors for
94+
/// the module.
95+
struct ThreadSanitizer {
96+
ThreadSanitizer(Module &M);
97+
bool sanitizeFunction(Function &F, const TargetLibraryInfo &TLI);
98+
99+
private:
98100
void initializeCallbacks(Module &M);
99101
bool instrumentLoadOrStore(Instruction *I, const DataLayout &DL);
100102
bool instrumentAtomic(Instruction *I, const DataLayout &DL);
@@ -130,27 +132,55 @@ struct ThreadSanitizer : public FunctionPass {
130132
Function *MemmoveFn, *MemcpyFn, *MemsetFn;
131133
Function *TsanCtorFunction;
132134
};
135+
136+
struct ThreadSanitizerLegacyPass : FunctionPass {
137+
ThreadSanitizerLegacyPass() : FunctionPass(ID) {}
138+
StringRef getPassName() const override;
139+
void getAnalysisUsage(AnalysisUsage &AU) const override;
140+
bool runOnFunction(Function &F) override;
141+
bool doInitialization(Module &M) override;
142+
static char ID; // Pass identification, replacement for typeid.
143+
private:
144+
Optional<ThreadSanitizer> TSan;
145+
};
133146
} // namespace
134147

135-
char ThreadSanitizer::ID = 0;
136-
INITIALIZE_PASS_BEGIN(
137-
ThreadSanitizer, "tsan",
138-
"ThreadSanitizer: detects data races.",
139-
false, false)
148+
PreservedAnalyses ThreadSanitizerPass::run(Function &F,
149+
FunctionAnalysisManager &FAM) {
150+
ThreadSanitizer TSan(*F.getParent());
151+
if (TSan.sanitizeFunction(F, FAM.getResult<TargetLibraryAnalysis>(F)))
152+
return PreservedAnalyses::none();
153+
return PreservedAnalyses::all();
154+
}
155+
156+
char ThreadSanitizerLegacyPass::ID = 0;
157+
INITIALIZE_PASS_BEGIN(ThreadSanitizerLegacyPass, "tsan",
158+
"ThreadSanitizer: detects data races.", false, false)
140159
INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)
141-
INITIALIZE_PASS_END(
142-
ThreadSanitizer, "tsan",
143-
"ThreadSanitizer: detects data races.",
144-
false, false)
160+
INITIALIZE_PASS_END(ThreadSanitizerLegacyPass, "tsan",
161+
"ThreadSanitizer: detects data races.", false, false)
145162

146-
StringRef ThreadSanitizer::getPassName() const { return "ThreadSanitizer"; }
163+
StringRef ThreadSanitizerLegacyPass::getPassName() const {
164+
return "ThreadSanitizerLegacyPass";
165+
}
147166

148-
void ThreadSanitizer::getAnalysisUsage(AnalysisUsage &AU) const {
167+
void ThreadSanitizerLegacyPass::getAnalysisUsage(AnalysisUsage &AU) const {
149168
AU.addRequired<TargetLibraryInfoWrapperPass>();
150169
}
151170

152-
FunctionPass *llvm::createThreadSanitizerPass() {
153-
return new ThreadSanitizer();
171+
bool ThreadSanitizerLegacyPass::doInitialization(Module &M) {
172+
TSan.emplace(M);
173+
return true;
174+
}
175+
176+
bool ThreadSanitizerLegacyPass::runOnFunction(Function &F) {
177+
auto &TLI = getAnalysis<TargetLibraryInfoWrapperPass>().getTLI();
178+
TSan->sanitizeFunction(F, TLI);
179+
return true;
180+
}
181+
182+
FunctionPass *llvm::createThreadSanitizerLegacyPassPass() {
183+
return new ThreadSanitizerLegacyPass();
154184
}
155185

156186
void ThreadSanitizer::initializeCallbacks(Module &M) {
@@ -252,16 +282,16 @@ void ThreadSanitizer::initializeCallbacks(Module &M) {
252282
IRB.getInt32Ty(), IntptrTy));
253283
}
254284

255-
bool ThreadSanitizer::doInitialization(Module &M) {
285+
ThreadSanitizer::ThreadSanitizer(Module &M) {
256286
const DataLayout &DL = M.getDataLayout();
257287
IntptrTy = DL.getIntPtrType(M.getContext());
258-
std::tie(TsanCtorFunction, std::ignore) = createSanitizerCtorAndInitFunctions(
259-
M, kTsanModuleCtorName, kTsanInitName, /*InitArgTypes=*/{},
260-
/*InitArgs=*/{});
261-
262-
appendToGlobalCtors(M, TsanCtorFunction, 0);
263-
264-
return true;
288+
std::tie(TsanCtorFunction, std::ignore) =
289+
getOrCreateSanitizerCtorAndInitFunctions(
290+
M, kTsanModuleCtorName, kTsanInitName, /*InitArgTypes=*/{},
291+
/*InitArgs=*/{},
292+
// This callback is invoked when the functions are created the first
293+
// time. Hook them into the global ctors list in that case:
294+
[&](Function *Ctor, Function *) { appendToGlobalCtors(M, Ctor, 0); });
265295
}
266296

267297
static bool isVtableAccess(Instruction *I) {
@@ -402,7 +432,8 @@ void ThreadSanitizer::InsertRuntimeIgnores(Function &F) {
402432
}
403433
}
404434

405-
bool ThreadSanitizer::runOnFunction(Function &F) {
435+
bool ThreadSanitizer::sanitizeFunction(Function &F,
436+
const TargetLibraryInfo &TLI) {
406437
// This is required to prevent instrumenting call to __tsan_init from within
407438
// the module constructor.
408439
if (&F == TsanCtorFunction)
@@ -416,8 +447,6 @@ bool ThreadSanitizer::runOnFunction(Function &F) {
416447
bool HasCalls = false;
417448
bool SanitizeFunction = F.hasFnAttribute(Attribute::SanitizeThread);
418449
const DataLayout &DL = F.getParent()->getDataLayout();
419-
const TargetLibraryInfo *TLI =
420-
&getAnalysis<TargetLibraryInfoWrapperPass>().getTLI();
421450

422451
// Traverse all instructions, collect loads/stores/returns, check for calls.
423452
for (auto &BB : F) {
@@ -428,7 +457,7 @@ bool ThreadSanitizer::runOnFunction(Function &F) {
428457
LocalLoadsAndStores.push_back(&Inst);
429458
else if (isa<CallInst>(Inst) || isa<InvokeInst>(Inst)) {
430459
if (CallInst *CI = dyn_cast<CallInst>(&Inst))
431-
maybeMarkSanitizerLibraryCallNoBuiltin(CI, TLI);
460+
maybeMarkSanitizerLibraryCallNoBuiltin(CI, &TLI);
432461
if (isa<MemIntrinsic>(Inst))
433462
MemIntrinCalls.push_back(&Inst);
434463
HasCalls = true;

lib/Transforms/Utils/ModuleUtils.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,28 @@ std::pair<Function *, Function *> llvm::createSanitizerCtorAndInitFunctions(
174174
return std::make_pair(Ctor, InitFunction);
175175
}
176176

177+
std::pair<Function *, Function *>
178+
llvm::getOrCreateSanitizerCtorAndInitFunctions(
179+
Module &M, StringRef CtorName, StringRef InitName,
180+
ArrayRef<Type *> InitArgTypes, ArrayRef<Value *> InitArgs,
181+
function_ref<void(Function *, Function *)> FunctionsCreatedCallback,
182+
StringRef VersionCheckName) {
183+
assert(!CtorName.empty() && "Expected ctor function name");
184+
185+
if (Function *Ctor = M.getFunction(CtorName))
186+
// FIXME: Sink this logic into the module, similar to the handling of
187+
// globals. This will make moving to a concurrent model much easier.
188+
if (Ctor->arg_size() == 0 ||
189+
Ctor->getReturnType() == Type::getVoidTy(M.getContext()))
190+
return {Ctor, declareSanitizerInitFunction(M, InitName, InitArgTypes)};
191+
192+
Function *Ctor, *InitFunction;
193+
std::tie(Ctor, InitFunction) = llvm::createSanitizerCtorAndInitFunctions(
194+
M, CtorName, InitName, InitArgTypes, InitArgs, VersionCheckName);
195+
FunctionsCreatedCallback(Ctor, InitFunction);
196+
return std::make_pair(Ctor, InitFunction);
197+
}
198+
177199
Function *llvm::getOrCreateInitFunction(Module &M, StringRef Name) {
178200
assert(!Name.empty() && "Expected init function name");
179201
if (Function *F = M.getFunction(Name)) {

0 commit comments

Comments
 (0)