19
19
// ===----------------------------------------------------------------------===//
20
20
21
21
#include " llvm/Transforms/Instrumentation/ThreadSanitizer.h"
22
- #include " llvm/ADT/SmallPtrSet.h"
22
+ #include " llvm/ADT/DenseMap.h"
23
+ #include " llvm/ADT/Optional.h"
23
24
#include " llvm/ADT/SmallString.h"
24
25
#include " llvm/ADT/SmallVector.h"
25
26
#include " llvm/ADT/Statistic.h"
@@ -52,30 +53,36 @@ using namespace llvm;
52
53
53
54
#define DEBUG_TYPE " tsan"
54
55
55
- static cl::opt<bool > ClInstrumentMemoryAccesses (
56
+ static cl::opt<bool > ClInstrumentMemoryAccesses (
56
57
" tsan-instrument-memory-accesses" , cl::init(true ),
57
58
cl::desc(" Instrument memory accesses" ), cl::Hidden);
58
- static cl::opt<bool > ClInstrumentFuncEntryExit (
59
- " tsan-instrument-func-entry-exit" , cl::init(true ),
60
- cl::desc(" Instrument function entry and exit" ), cl::Hidden);
61
- static cl::opt<bool > ClHandleCxxExceptions (
59
+ static cl::opt<bool >
60
+ ClInstrumentFuncEntryExit (" tsan-instrument-func-entry-exit" , cl::init(true ),
61
+ cl::desc(" Instrument function entry and exit" ),
62
+ cl::Hidden);
63
+ static cl::opt<bool > ClHandleCxxExceptions (
62
64
" tsan-handle-cxx-exceptions" , cl::init(true ),
63
65
cl::desc(" Handle C++ exceptions (insert cleanup blocks for unwinding)" ),
64
66
cl::Hidden);
65
- static cl::opt<bool > ClInstrumentAtomics (
66
- " tsan-instrument-atomics" , cl::init(true ),
67
- cl::desc(" Instrument atomics" ), cl::Hidden);
68
- static cl::opt<bool > ClInstrumentMemIntrinsics (
67
+ static cl::opt<bool > ClInstrumentAtomics (" tsan-instrument-atomics" ,
68
+ cl::init (true ),
69
+ cl::desc(" Instrument atomics" ),
70
+ cl::Hidden);
71
+ static cl::opt<bool > ClInstrumentMemIntrinsics (
69
72
" tsan-instrument-memintrinsics" , cl::init(true ),
70
73
cl::desc(" Instrument memintrinsics (memset/memcpy/memmove)" ), cl::Hidden);
71
- static cl::opt<bool > ClDistinguishVolatile (
74
+ static cl::opt<bool > ClDistinguishVolatile (
72
75
" tsan-distinguish-volatile" , cl::init(false ),
73
76
cl::desc(" Emit special instrumentation for accesses to volatiles" ),
74
77
cl::Hidden);
75
- static cl::opt<bool > ClInstrumentReadBeforeWrite (
78
+ static cl::opt<bool > ClInstrumentReadBeforeWrite (
76
79
" tsan-instrument-read-before-write" , cl::init(false ),
77
80
cl::desc(" Do not eliminate read instrumentation for read-before-writes" ),
78
81
cl::Hidden);
82
+ static cl::opt<bool > ClCompoundReadBeforeWrite (
83
+ " tsan-compound-read-before-write" , cl::init(false ),
84
+ cl::desc(" Emit special compound instrumentation for reads-before-writes" ),
85
+ cl::Hidden);
79
86
80
87
STATISTIC (NumInstrumentedReads, " Number of instrumented reads" );
81
88
STATISTIC (NumInstrumentedWrites, " Number of instrumented writes" );
@@ -101,15 +108,37 @@ namespace {
101
108
// / ensures the __tsan_init function is in the list of global constructors for
102
109
// / the module.
103
110
struct ThreadSanitizer {
111
+ ThreadSanitizer () {
112
+ // Sanity check options and warn user.
113
+ if (ClInstrumentReadBeforeWrite && ClCompoundReadBeforeWrite) {
114
+ errs ()
115
+ << " warning: Option -tsan-compound-read-before-write has no effect "
116
+ " when -tsan-instrument-read-before-write is set.\n " ;
117
+ }
118
+ }
119
+
104
120
bool sanitizeFunction (Function &F, const TargetLibraryInfo &TLI);
105
121
106
122
private:
123
+ // Internal Instruction wrapper that contains more information about the
124
+ // Instruction from prior analysis.
125
+ struct InstructionInfo {
126
+ // Instrumentation emitted for this instruction is for a compounded set of
127
+ // read and write operations in the same basic block.
128
+ static constexpr unsigned kCompoundRW = (1U << 0 );
129
+
130
+ explicit InstructionInfo (Instruction *Inst) : Inst(Inst) {}
131
+
132
+ Instruction *Inst;
133
+ unsigned Flags = 0 ;
134
+ };
135
+
107
136
void initialize (Module &M);
108
- bool instrumentLoadOrStore (Instruction *I , const DataLayout &DL);
137
+ bool instrumentLoadOrStore (const InstructionInfo &II , const DataLayout &DL);
109
138
bool instrumentAtomic (Instruction *I, const DataLayout &DL);
110
139
bool instrumentMemIntrinsic (Instruction *I);
111
140
void chooseInstructionsToInstrument (SmallVectorImpl<Instruction *> &Local,
112
- SmallVectorImpl<Instruction * > &All,
141
+ SmallVectorImpl<InstructionInfo > &All,
113
142
const DataLayout &DL);
114
143
bool addrPointsToConstantData (Value *Addr);
115
144
int getMemoryAccessFuncIndex (Value *Addr, const DataLayout &DL);
@@ -130,6 +159,8 @@ struct ThreadSanitizer {
130
159
FunctionCallee TsanVolatileWrite[kNumberOfAccessSizes ];
131
160
FunctionCallee TsanUnalignedVolatileRead[kNumberOfAccessSizes ];
132
161
FunctionCallee TsanUnalignedVolatileWrite[kNumberOfAccessSizes ];
162
+ FunctionCallee TsanCompoundRW[kNumberOfAccessSizes ];
163
+ FunctionCallee TsanUnalignedCompoundRW[kNumberOfAccessSizes ];
133
164
FunctionCallee TsanAtomicLoad[kNumberOfAccessSizes ];
134
165
FunctionCallee TsanAtomicStore[kNumberOfAccessSizes ];
135
166
FunctionCallee TsanAtomicRMW[AtomicRMWInst::LAST_BINOP + 1 ]
@@ -268,6 +299,15 @@ void ThreadSanitizer::initialize(Module &M) {
268
299
TsanUnalignedVolatileWrite[i] = M.getOrInsertFunction (
269
300
UnalignedVolatileWriteName, Attr, IRB.getVoidTy (), IRB.getInt8PtrTy ());
270
301
302
+ SmallString<64 > CompoundRWName (" __tsan_read_write" + ByteSizeStr);
303
+ TsanCompoundRW[i] = M.getOrInsertFunction (
304
+ CompoundRWName, Attr, IRB.getVoidTy (), IRB.getInt8PtrTy ());
305
+
306
+ SmallString<64 > UnalignedCompoundRWName (" __tsan_unaligned_read_write" +
307
+ ByteSizeStr);
308
+ TsanUnalignedCompoundRW[i] = M.getOrInsertFunction (
309
+ UnalignedCompoundRWName, Attr, IRB.getVoidTy (), IRB.getInt8PtrTy ());
310
+
271
311
Type *Ty = Type::getIntNTy (M.getContext (), BitSize);
272
312
Type *PtrTy = Ty->getPointerTo ();
273
313
SmallString<32 > AtomicLoadName (" __tsan_atomic" + BitSizeStr + " _load" );
@@ -402,34 +442,42 @@ bool ThreadSanitizer::addrPointsToConstantData(Value *Addr) {
402
442
// 'Local' is a vector of insns within the same BB (no calls between).
403
443
// 'All' is a vector of insns that will be instrumented.
404
444
void ThreadSanitizer::chooseInstructionsToInstrument (
405
- SmallVectorImpl<Instruction *> &Local, SmallVectorImpl<Instruction *> &All,
406
- const DataLayout &DL) {
407
- SmallPtrSet <Value*, 8 > WriteTargets;
445
+ SmallVectorImpl<Instruction *> &Local,
446
+ SmallVectorImpl<InstructionInfo> &All, const DataLayout &DL) {
447
+ DenseMap <Value *, size_t > WriteTargets; // Map of addresses to index in All
408
448
// Iterate from the end.
409
449
for (Instruction *I : reverse (Local)) {
410
- if (StoreInst *Store = dyn_cast<StoreInst>(I)) {
411
- Value *Addr = Store->getPointerOperand ();
412
- if (!shouldInstrumentReadWriteFromAddress (I->getModule (), Addr))
413
- continue ;
414
- WriteTargets.insert (Addr);
415
- } else {
416
- LoadInst *Load = cast<LoadInst>(I);
417
- Value *Addr = Load->getPointerOperand ();
418
- if (!shouldInstrumentReadWriteFromAddress (I->getModule (), Addr))
419
- continue ;
420
- if (!ClInstrumentReadBeforeWrite && WriteTargets.count (Addr)) {
421
- // We will write to this temp, so no reason to analyze the read.
422
- NumOmittedReadsBeforeWrite++;
423
- continue ;
450
+ const bool IsWrite = isa<StoreInst>(*I);
451
+ Value *Addr = IsWrite ? cast<StoreInst>(I)->getPointerOperand ()
452
+ : cast<LoadInst>(I)->getPointerOperand ();
453
+
454
+ if (!shouldInstrumentReadWriteFromAddress (I->getModule (), Addr))
455
+ continue ;
456
+
457
+ if (!IsWrite) {
458
+ const auto WriteEntry = WriteTargets.find (Addr);
459
+ if (!ClInstrumentReadBeforeWrite && WriteEntry != WriteTargets.end ()) {
460
+ auto &WI = All[WriteEntry->second ];
461
+ // If we distinguish volatile accesses and if either the read or write
462
+ // is volatile, do not omit any instrumentation.
463
+ const bool AnyVolatile =
464
+ ClDistinguishVolatile && (cast<LoadInst>(I)->isVolatile () ||
465
+ cast<StoreInst>(WI.Inst )->isVolatile ());
466
+ if (!AnyVolatile) {
467
+ // We will write to this temp, so no reason to analyze the read.
468
+ // Mark the write instruction as compound.
469
+ WI.Flags |= InstructionInfo::kCompoundRW ;
470
+ NumOmittedReadsBeforeWrite++;
471
+ continue ;
472
+ }
424
473
}
474
+
425
475
if (addrPointsToConstantData (Addr)) {
426
476
// Addr points to some constant data -- it can not race with any writes.
427
477
continue ;
428
478
}
429
479
}
430
- Value *Addr = isa<StoreInst>(*I)
431
- ? cast<StoreInst>(I)->getPointerOperand ()
432
- : cast<LoadInst>(I)->getPointerOperand ();
480
+
433
481
if (isa<AllocaInst>(GetUnderlyingObject (Addr, DL)) &&
434
482
!PointerMayBeCaptured (Addr, true , true )) {
435
483
// The variable is addressable but not captured, so it cannot be
@@ -438,7 +486,14 @@ void ThreadSanitizer::chooseInstructionsToInstrument(
438
486
NumOmittedNonCaptured++;
439
487
continue ;
440
488
}
441
- All.push_back (I);
489
+
490
+ // Instrument this instruction.
491
+ All.emplace_back (I);
492
+ if (IsWrite) {
493
+ // For read-before-write and compound instrumentation we only need one
494
+ // write target, and we can override any previous entry if it exists.
495
+ WriteTargets[Addr] = All.size () - 1 ;
496
+ }
442
497
}
443
498
Local.clear ();
444
499
}
@@ -479,7 +534,7 @@ bool ThreadSanitizer::sanitizeFunction(Function &F,
479
534
if (F.hasFnAttribute (Attribute::Naked))
480
535
return false ;
481
536
initialize (*F.getParent ());
482
- SmallVector<Instruction* , 8 > AllLoadsAndStores;
537
+ SmallVector<InstructionInfo , 8 > AllLoadsAndStores;
483
538
SmallVector<Instruction*, 8 > LocalLoadsAndStores;
484
539
SmallVector<Instruction*, 8 > AtomicAccesses;
485
540
SmallVector<Instruction*, 8 > MemIntrinCalls;
@@ -514,8 +569,8 @@ bool ThreadSanitizer::sanitizeFunction(Function &F,
514
569
515
570
// Instrument memory accesses only if we want to report bugs in the function.
516
571
if (ClInstrumentMemoryAccesses && SanitizeFunction)
517
- for (auto Inst : AllLoadsAndStores) {
518
- Res |= instrumentLoadOrStore (Inst , DL);
572
+ for (const auto &II : AllLoadsAndStores) {
573
+ Res |= instrumentLoadOrStore (II , DL);
519
574
}
520
575
521
576
// Instrument atomic memory accesses in any case (they can be used to
@@ -553,13 +608,12 @@ bool ThreadSanitizer::sanitizeFunction(Function &F,
553
608
return Res;
554
609
}
555
610
556
- bool ThreadSanitizer::instrumentLoadOrStore (Instruction *I ,
611
+ bool ThreadSanitizer::instrumentLoadOrStore (const InstructionInfo &II ,
557
612
const DataLayout &DL) {
558
- IRBuilder<> IRB (I);
559
- bool IsWrite = isa<StoreInst>(*I);
560
- Value *Addr = IsWrite
561
- ? cast<StoreInst>(I)->getPointerOperand ()
562
- : cast<LoadInst>(I)->getPointerOperand ();
613
+ IRBuilder<> IRB (II.Inst );
614
+ const bool IsWrite = isa<StoreInst>(*II.Inst );
615
+ Value *Addr = IsWrite ? cast<StoreInst>(II.Inst )->getPointerOperand ()
616
+ : cast<LoadInst>(II.Inst )->getPointerOperand ();
563
617
564
618
// swifterror memory addresses are mem2reg promoted by instruction selection.
565
619
// As such they cannot have regular uses like an instrumentation function and
@@ -570,9 +624,9 @@ bool ThreadSanitizer::instrumentLoadOrStore(Instruction *I,
570
624
int Idx = getMemoryAccessFuncIndex (Addr, DL);
571
625
if (Idx < 0 )
572
626
return false ;
573
- if (IsWrite && isVtableAccess (I )) {
574
- LLVM_DEBUG (dbgs () << " VPTR : " << *I << " \n " );
575
- Value *StoredValue = cast<StoreInst>(I )->getValueOperand ();
627
+ if (IsWrite && isVtableAccess (II. Inst )) {
628
+ LLVM_DEBUG (dbgs () << " VPTR : " << *II. Inst << " \n " );
629
+ Value *StoredValue = cast<StoreInst>(II. Inst )->getValueOperand ();
576
630
// StoredValue may be a vector type if we are storing several vptrs at once.
577
631
// In this case, just take the first element of the vector since this is
578
632
// enough to find vptr races.
@@ -588,36 +642,46 @@ bool ThreadSanitizer::instrumentLoadOrStore(Instruction *I,
588
642
NumInstrumentedVtableWrites++;
589
643
return true ;
590
644
}
591
- if (!IsWrite && isVtableAccess (I )) {
645
+ if (!IsWrite && isVtableAccess (II. Inst )) {
592
646
IRB.CreateCall (TsanVptrLoad,
593
647
IRB.CreatePointerCast (Addr, IRB.getInt8PtrTy ()));
594
648
NumInstrumentedVtableReads++;
595
649
return true ;
596
650
}
597
- const unsigned Alignment = IsWrite
598
- ? cast<StoreInst>(I)->getAlignment ()
599
- : cast<LoadInst>(I)->getAlignment ();
600
- const bool IsVolatile =
601
- ClDistinguishVolatile && (IsWrite ? cast<StoreInst>(I)->isVolatile ()
602
- : cast<LoadInst>(I)->isVolatile ());
651
+
652
+ const unsigned Alignment = IsWrite ? cast<StoreInst>(II.Inst )->getAlignment ()
653
+ : cast<LoadInst>(II.Inst )->getAlignment ();
654
+ const bool IsCompoundRW =
655
+ ClCompoundReadBeforeWrite && (II.Flags & InstructionInfo::kCompoundRW );
656
+ const bool IsVolatile = ClDistinguishVolatile &&
657
+ (IsWrite ? cast<StoreInst>(II.Inst )->isVolatile ()
658
+ : cast<LoadInst>(II.Inst )->isVolatile ());
659
+ assert ((!IsVolatile || !IsCompoundRW) && " Compound volatile invalid!" );
660
+
603
661
Type *OrigTy = cast<PointerType>(Addr->getType ())->getElementType ();
604
662
const uint32_t TypeSize = DL.getTypeStoreSizeInBits (OrigTy);
605
663
FunctionCallee OnAccessFunc = nullptr ;
606
664
if (Alignment == 0 || Alignment >= 8 || (Alignment % (TypeSize / 8 )) == 0 ) {
607
- if (IsVolatile)
665
+ if (IsCompoundRW)
666
+ OnAccessFunc = TsanCompoundRW[Idx];
667
+ else if (IsVolatile)
608
668
OnAccessFunc = IsWrite ? TsanVolatileWrite[Idx] : TsanVolatileRead[Idx];
609
669
else
610
670
OnAccessFunc = IsWrite ? TsanWrite[Idx] : TsanRead[Idx];
611
671
} else {
612
- if (IsVolatile)
672
+ if (IsCompoundRW)
673
+ OnAccessFunc = TsanUnalignedCompoundRW[Idx];
674
+ else if (IsVolatile)
613
675
OnAccessFunc = IsWrite ? TsanUnalignedVolatileWrite[Idx]
614
676
: TsanUnalignedVolatileRead[Idx];
615
677
else
616
678
OnAccessFunc = IsWrite ? TsanUnalignedWrite[Idx] : TsanUnalignedRead[Idx];
617
679
}
618
680
IRB.CreateCall (OnAccessFunc, IRB.CreatePointerCast (Addr, IRB.getInt8PtrTy ()));
619
- if (IsWrite) NumInstrumentedWrites++;
620
- else NumInstrumentedReads++;
681
+ if (IsCompoundRW || IsWrite)
682
+ NumInstrumentedWrites++;
683
+ if (IsCompoundRW || !IsWrite)
684
+ NumInstrumentedReads++;
621
685
return true ;
622
686
}
623
687
0 commit comments