Skip to content

Commit 30e2550

Browse files
committed
TSan: Support relaxed accesses and fences
1 parent 6565e07 commit 30e2550

File tree

3 files changed

+86
-23
lines changed

3 files changed

+86
-23
lines changed

compiler-rt/lib/tsan/rtl/tsan_flags.inc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,6 @@ TSAN_FLAG(bool, shared_ptr_interceptor, true,
8080
TSAN_FLAG(bool, print_full_thread_history, false,
8181
"If set, prints thread creation stacks for the threads involved in "
8282
"the report and their ancestors up to the main thread.")
83+
TSAN_FLAG(bool, correct_race_detection, false,
84+
"If set, remove optimizations and execute correct race detection "
85+
"supporting fences and release sequence.")

compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp

Lines changed: 81 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727

2828
using namespace __tsan;
2929

30+
static bool correctRaceDetection(){
31+
return flags()->correct_race_detection;
32+
}
33+
3034
#if !SANITIZER_GO && __TSAN_HAS_INT128
3135
// Protects emulation of 128-bit atomic operations.
3236
static StaticSpinMutex mutex128;
@@ -227,18 +231,36 @@ namespace {
227231
template <typename T, T (*F)(volatile T *v, T op)>
228232
static T AtomicRMW(ThreadState *thr, uptr pc, volatile T *a, T v, morder mo) {
229233
MemoryAccess(thr, pc, (uptr)a, AccessSize<T>(), kAccessWrite | kAccessAtomic);
230-
if (LIKELY(mo == mo_relaxed))
231-
return F(a, v);
234+
if (!correctRaceDetection()){
235+
if (LIKELY(mo == mo_relaxed))
236+
return F(a, v);
237+
}
232238
SlotLocker locker(thr);
233239
{
234240
auto s = ctx->metamap.GetSyncOrCreate(thr, pc, (uptr)a, false);
235-
RWLock lock(&s->mtx, IsReleaseOrder(mo));
236-
if (IsAcqRelOrder(mo))
237-
thr->clock.ReleaseAcquire(&s->clock);
238-
else if (IsReleaseOrder(mo))
239-
thr->clock.Release(&s->clock);
240-
else if (IsAcquireOrder(mo))
241-
thr->clock.Acquire(s->clock);
241+
bool fullLock = correctRaceDetection() || IsReleaseOrder(mo);
242+
RWLock lock(&s->mtx, fullLock);
243+
if (!correctRaceDetection()){
244+
if (IsAcqRelOrder(mo))
245+
thr->clock.ReleaseAcquire(&s->clock);
246+
else if (IsReleaseOrder(mo))
247+
thr->clock.Release(&s->clock);
248+
else if (IsAcquireOrder(mo))
249+
thr->clock.Acquire(s->clock);
250+
} else {
251+
if (mo == mo_relaxed){
252+
thr->clockA.Acquire(s->clock);
253+
thr->clockR.Release(&s->clock);
254+
} else if (IsAcqRelOrder(mo)) {
255+
thr->clock.ReleaseAcquire(&s->clock);
256+
} else if (IsReleaseOrder(mo)) {
257+
thr->clockA.Acquire(s->clock);
258+
thr->clock.Release(&s->clock);
259+
} else if (IsAcquireOrder(mo)) {
260+
thr->clock.Acquire(s->clock);
261+
thr->clockR.Release(&s->clock);
262+
}
263+
}
242264
v = F(a, v);
243265
}
244266
if (IsReleaseOrder(mo))
@@ -264,7 +286,7 @@ struct OpLoad {
264286
DCHECK(IsLoadOrder(mo));
265287
// This fast-path is critical for performance.
266288
// Assume the access is atomic.
267-
if (!IsAcquireOrder(mo)) {
289+
if (!correctRaceDetection() && !IsAcquireOrder(mo)) {
268290
MemoryAccess(thr, pc, (uptr)a, AccessSize<T>(),
269291
kAccessRead | kAccessAtomic);
270292
return NoTsanAtomic(mo, a);
@@ -276,7 +298,11 @@ struct OpLoad {
276298
if (s) {
277299
SlotLocker locker(thr);
278300
ReadLock lock(&s->mtx);
279-
thr->clock.Acquire(s->clock);
301+
if (IsAcquireOrder(mo)) {
302+
thr->clock.Acquire(s->clock);
303+
} else if (correctRaceDetection()) {
304+
thr->clockA.Acquire(s->clock);
305+
}
280306
// Re-read under sync mutex because we need a consistent snapshot
281307
// of the value and the clock we acquire.
282308
v = NoTsanAtomic(mo, a);
@@ -309,18 +335,22 @@ struct OpStore {
309335
// Assume the access is atomic.
310336
// Strictly saying even relaxed store cuts off release sequence,
311337
// so must reset the clock.
312-
if (!IsReleaseOrder(mo)) {
338+
if (!correctRaceDetection() && !IsReleaseOrder(mo)) {
313339
NoTsanAtomic(mo, a, v);
314340
return;
315341
}
316342
SlotLocker locker(thr);
317343
{
318344
auto s = ctx->metamap.GetSyncOrCreate(thr, pc, (uptr)a, false);
319345
Lock lock(&s->mtx);
320-
thr->clock.ReleaseStore(&s->clock);
346+
if (IsReleaseOrder(mo))
347+
thr->clock.ReleaseStore(&s->clock);
348+
else if (correctRaceDetection())
349+
thr->clockR.ReleaseStore(&s->clock);
321350
NoTsanAtomic(mo, a, v);
322351
}
323-
IncrementEpoch(thr);
352+
if (IsReleaseOrder(mo))
353+
IncrementEpoch(thr);
324354
}
325355
};
326356

@@ -441,7 +471,7 @@ struct OpCAS {
441471

442472
MemoryAccess(thr, pc, (uptr)a, AccessSize<T>(),
443473
kAccessWrite | kAccessAtomic);
444-
if (LIKELY(mo == mo_relaxed && fmo == mo_relaxed)) {
474+
if (LIKELY(!correctRaceDetection() && mo == mo_relaxed && fmo == mo_relaxed)) {
445475
T cc = *c;
446476
T pr = func_cas(a, cc, v);
447477
if (pr == cc)
@@ -454,20 +484,36 @@ struct OpCAS {
454484
bool success;
455485
{
456486
auto s = ctx->metamap.GetSyncOrCreate(thr, pc, (uptr)a, false);
457-
RWLock lock(&s->mtx, release);
487+
bool fullLock = correctRaceDetection() || release;
488+
RWLock lock(&s->mtx, fullLock);
458489
T cc = *c;
459490
T pr = func_cas(a, cc, v);
460491
success = pr == cc;
461492
if (!success) {
462493
*c = pr;
463494
mo = fmo;
464495
}
465-
if (success && IsAcqRelOrder(mo))
466-
thr->clock.ReleaseAcquire(&s->clock);
467-
else if (success && IsReleaseOrder(mo))
468-
thr->clock.Release(&s->clock);
469-
else if (IsAcquireOrder(mo))
470-
thr->clock.Acquire(s->clock);
496+
if (!correctRaceDetection()){
497+
if (success && IsAcqRelOrder(mo))
498+
thr->clock.ReleaseAcquire(&s->clock);
499+
else if (success && IsReleaseOrder(mo))
500+
thr->clock.Release(&s->clock);
501+
else if (IsAcquireOrder(mo))
502+
thr->clock.Acquire(s->clock);
503+
} else {
504+
if (!IsAcquireOrder(mo)){
505+
thr->clockA.Acquire(s->clock);
506+
} else {
507+
thr->clock.Acquire(s->clock);
508+
}
509+
if (success){
510+
if (!IsReleaseOrder(mo)){
511+
thr->clockR.Release(&s->clock);
512+
} else {
513+
thr->clock.Release(&s->clock);
514+
}
515+
}
516+
}
471517
}
472518
if (success && release)
473519
IncrementEpoch(thr);
@@ -487,7 +533,19 @@ struct OpFence {
487533
static void NoTsanAtomic(morder mo) { __sync_synchronize(); }
488534

489535
static void Atomic(ThreadState *thr, uptr pc, morder mo) {
490-
// FIXME(dvyukov): not implemented.
536+
if (correctRaceDetection()){
537+
SlotLocker locker(thr);
538+
if (IsAcquireOrder(mo))
539+
thr->clock.Acquire(&thr->clockA);
540+
if (mo == mo_seq_cst){
541+
auto s = ctx->metamap.GetSyncOrCreate(thr, pc, 0, false);
542+
thr->clock.ReleaseAcquire(&s->clock);
543+
}
544+
if (IsReleaseOrder(mo)){
545+
thr->clockR.Acquire(&thr->clock);
546+
IncrementEpoch(thr);
547+
}
548+
}
491549
__sync_synchronize();
492550
}
493551
};

compiler-rt/lib/tsan/rtl/tsan_rtl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ struct alignas(SANITIZER_CACHE_LINE_SIZE) ThreadState {
177177
atomic_sint32_t pending_signals;
178178

179179
VectorClock clock;
180+
VectorClock clockR;
181+
VectorClock clockA;
180182

181183
// This is a slow path flag. On fast path, fast_state.GetIgnoreBit() is read.
182184
// We do not distinguish beteween ignoring reads and writes

0 commit comments

Comments
 (0)