llvm.org GIT mirror llvm / 2076af0
[tsan] compile-time instrumentation: do not instrument a read if a write to the same temp follows in the same BB. Also add stats printing. On Spec CPU2006 this optimization saves roughly 4% of instrumented reads (which is 3% of all instrumented accesses): Writes : 161216 Reads : 446458 Reads-before-write: 18295 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@154418 91177308-0d34-0410-b5e6-96231b3b80d8 Kostya Serebryany 7 years ago
2 changed file(s) with 115 addition(s) and 6 deletion(s). Raw diff Collapse all Expand all
2121 #define DEBUG_TYPE "tsan"
2222
2323 #include "FunctionBlackList.h"
24 #include "llvm/ADT/SmallSet.h"
2425 #include "llvm/ADT/SmallString.h"
2526 #include "llvm/ADT/SmallVector.h"
2627 #include "llvm/ADT/StringExtras.h"
4445 static cl::opt ClBlackListFile("tsan-blacklist",
4546 cl::desc("Blacklist file"), cl::Hidden);
4647
48 static cl::opt ClPrintStats("tsan-print-stats",
49 cl::desc("Print ThreadSanitizer instrumentation stats"), cl::Hidden);
50
4751 namespace {
52
53 // Stats counters for ThreadSanitizer instrumentation.
54 struct ThreadSanitizerStats {
55 size_t NumInstrumentedReads;
56 size_t NumInstrumentedWrites;
57 size_t NumOmittedReadsBeforeWrite;
58 size_t NumAccessesWithBadSize;
59 size_t NumInstrumentedVtableWrites;
60 };
61
4862 /// ThreadSanitizer: instrument the code in module to find races.
4963 struct ThreadSanitizer : public FunctionPass {
5064 ThreadSanitizer();
5165 bool runOnFunction(Function &F);
5266 bool doInitialization(Module &M);
67 bool doFinalization(Module &M);
5368 bool instrumentLoadOrStore(Instruction *I);
5469 static char ID; // Pass identification, replacement for typeid.
5570
5671 private:
72 void choseInstructionsToInstrument(SmallVectorImpl &Local,
73 SmallVectorImpl &All);
74
5775 TargetData *TD;
5876 OwningPtr BL;
5977 // Callbacks to run-time library are computed in doInitialization.
6482 Value *TsanRead[kNumberOfAccessSizes];
6583 Value *TsanWrite[kNumberOfAccessSizes];
6684 Value *TsanVptrUpdate;
85
86 // Stats are modified w/o synchronization.
87 ThreadSanitizerStats stats;
6788 };
6889 } // namespace
6990
86107 if (!TD)
87108 return false;
88109 BL.reset(new FunctionBlackList(ClBlackListFile));
110 memset(&stats, 0, sizeof(stats));
89111
90112 // Always insert a call to __tsan_init into the module's CTORs.
91113 IRBuilder<> IRB(M.getContext());
114136 return true;
115137 }
116138
139 bool ThreadSanitizer::doFinalization(Module &M) {
140 if (ClPrintStats) {
141 errs() << "ThreadSanitizerStats " << M.getModuleIdentifier()
142 << ": wr " << stats.NumInstrumentedWrites
143 << "; rd " << stats.NumInstrumentedReads
144 << "; vt " << stats.NumInstrumentedVtableWrites
145 << "; bs " << stats.NumAccessesWithBadSize
146 << "; rbw " << stats.NumOmittedReadsBeforeWrite
147 << "\n";
148 }
149 return true;
150 }
151
152 // Instrumenting some of the accesses may be proven redundant.
153 // Currently handled:
154 // - read-before-write (within same BB, no calls between)
155 //
156 // We do not handle some of the patterns that should not survive
157 // after the classic compiler optimizations.
158 // E.g. two reads from the same temp should be eliminated by CSE,
159 // two writes should be eliminated by DSE, etc.
160 //
161 // 'Local' is a vector of insns within the same BB (no calls between).
162 // 'All' is a vector of insns that will be instrumented.
163 void ThreadSanitizer::choseInstructionsToInstrument(
164 SmallVectorImpl &Local,
165 SmallVectorImpl &All) {
166 SmallSet WriteTargets;
167 // Iterate from the end.
168 for (SmallVectorImpl::reverse_iterator It = Local.rbegin(),
169 E = Local.rend(); It != E; ++It) {
170 Instruction *I = *It;
171 if (StoreInst *Store = dyn_cast(I)) {
172 WriteTargets.insert(Store->getPointerOperand());
173 } else {
174 LoadInst *Load = cast(I);
175 if (WriteTargets.count(Load->getPointerOperand())) {
176 // We will write to this temp, so no reason to analyze the read.
177 stats.NumOmittedReadsBeforeWrite++;
178 continue;
179 }
180 }
181 All.push_back(I);
182 }
183 Local.clear();
184 }
185
117186 bool ThreadSanitizer::runOnFunction(Function &F) {
118187 if (!TD) return false;
119188 if (BL->isIn(F)) return false;
120189 SmallVector RetVec;
121 SmallVector LoadsAndStores;
190 SmallVector AllLoadsAndStores;
191 SmallVector LocalLoadsAndStores;
122192 bool Res = false;
123193 bool HasCalls = false;
124194
129199 for (BasicBlock::iterator BI = BB.begin(), BE = BB.end();
130200 BI != BE; ++BI) {
131201 if (isa(BI) || isa(BI))
132 LoadsAndStores.push_back(BI);
202 LocalLoadsAndStores.push_back(BI);
133203 else if (isa(BI))
134204 RetVec.push_back(BI);
135 else if (isa(BI) || isa(BI))
205 else if (isa(BI) || isa(BI)) {
136206 HasCalls = true;
137 }
207 choseInstructionsToInstrument(LocalLoadsAndStores, AllLoadsAndStores);
208 }
209 }
210 choseInstructionsToInstrument(LocalLoadsAndStores, AllLoadsAndStores);
138211 }
139212
140213 // We have collected all loads and stores.
142215 // (e.g. variables that do not escape, etc).
143216
144217 // Instrument memory accesses.
145 for (size_t i = 0, n = LoadsAndStores.size(); i < n; ++i) {
146 Res |= instrumentLoadOrStore(LoadsAndStores[i]);
218 for (size_t i = 0, n = AllLoadsAndStores.size(); i < n; ++i) {
219 Res |= instrumentLoadOrStore(AllLoadsAndStores[i]);
147220 }
148221
149222 // Instrument function entry/exit points if there were instrumented accesses.
184257 uint32_t TypeSize = TD->getTypeStoreSizeInBits(OrigTy);
185258 if (TypeSize != 8 && TypeSize != 16 &&
186259 TypeSize != 32 && TypeSize != 64 && TypeSize != 128) {
260 stats.NumAccessesWithBadSize++;
187261 // Ignore all unusual sizes.
188262 return false;
189263 }
192266 IRB.CreateCall2(TsanVptrUpdate,
193267 IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()),
194268 IRB.CreatePointerCast(StoredValue, IRB.getInt8PtrTy()));
269 stats.NumInstrumentedVtableWrites++;
195270 return true;
196271 }
197272 size_t Idx = CountTrailingZeros_32(TypeSize / 8);
198273 assert(Idx < kNumberOfAccessSizes);
199274 Value *OnAccessFunc = IsWrite ? TsanWrite[Idx] : TsanRead[Idx];
200275 IRB.CreateCall(OnAccessFunc, IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()));
276 if (IsWrite) stats.NumInstrumentedWrites++;
277 else stats.NumInstrumentedReads++;
201278 return true;
202279 }
0 ; RUN: opt < %s -tsan -S | FileCheck %s
1
2 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"
3
4 define void @IncrementMe(i32* nocapture %ptr) nounwind uwtable {
5 entry:
6 %0 = load i32* %ptr, align 4
7 %inc = add nsw i32 %0, 1
8 store i32 %inc, i32* %ptr, align 4
9 ret void
10 }
11 ; CHECK: define void @IncrementMe
12 ; CHECK-NOT: __tsan_read
13 ; CHECK: __tsan_write
14 ; CHECK: ret void
15
16 define void @IncrementMeWithCallInBetween(i32* nocapture %ptr) nounwind uwtable {
17 entry:
18 %0 = load i32* %ptr, align 4
19 %inc = add nsw i32 %0, 1
20 call void @foo()
21 store i32 %inc, i32* %ptr, align 4
22 ret void
23 }
24
25 ; CHECK: define void @IncrementMeWithCallInBetween
26 ; CHECK: __tsan_read
27 ; CHECK: __tsan_write
28 ; CHECK: ret void
29
30 declare void @foo()
31