Index: compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp =================================================================== --- compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp +++ compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp @@ -286,6 +286,16 @@ ThreadIgnoreEnd(thr); } +void INTERFACE_ATTRIBUTE AnnotateAllAtomicBegin(char *f, int l) { + SCOPED_ANNOTATION(AnnotateAllAtomicBegin); + ThreadAllAtomicBegin(thr, pc); +} + +void INTERFACE_ATTRIBUTE AnnotateAllAtomicEnd(char *f, int l) { + SCOPED_ANNOTATION(AnnotateAllAtomicEnd); + ThreadAllAtomicEnd(thr); +} + void INTERFACE_ATTRIBUTE AnnotateIgnoreSyncBegin(char *f, int l) { SCOPED_ANNOTATION(AnnotateIgnoreSyncBegin); ThreadIgnoreSyncBegin(thr, pc); Index: compiler-rt/lib/tsan/rtl/tsan_rtl.h =================================================================== --- compiler-rt/lib/tsan/rtl/tsan_rtl.h +++ compiler-rt/lib/tsan/rtl/tsan_rtl.h @@ -94,6 +94,7 @@ // FastState (from most significant bit): // ignore : 1 // tid : kTidBits +// allAtomics : 1 // unused : - // history_size : 3 // epoch : kClkBits @@ -141,6 +142,10 @@ void ClearIgnoreBit() { x_ &= ~kIgnoreBit; } bool GetIgnoreBit() const { return (s64)x_ < 0; } + void SetAllAtomicBit() { x_ |= kAllAtomicBit; } + void ClearAllAtomicBit() { x_ &= ~kAllAtomicBit; } + bool GetAllAtomicBit() const { return x_ & kAllAtomicBit; } + void SetHistorySize(int hs) { CHECK_GE(hs, 0); CHECK_LE(hs, 7); @@ -166,6 +171,8 @@ private: friend class Shadow; + static const u64 kAllAtomicShift = 6 + kClkBits; + static const u64 kAllAtomicBit = 1ull << kAllAtomicShift; static const int kTidShift = 64 - kTidBits - 1; static const u64 kIgnoreBit = 1ull << 63; static const u64 kFreedBit = 1ull << 63; @@ -216,6 +223,11 @@ DCHECK_EQ(IsAtomic(), kIsAtomic); } + void UpgradeAtomic(bool kIsAtomic) { + if (kIsAtomic) + x_ |= kAtomicBit; + } + bool IsAtomic() const { return x_ & kAtomicBit; } @@ -385,6 +397,7 @@ // We do not distinguish beteween ignoring reads and writes // for better performance. int ignore_reads_and_writes; + int all_atomic; atomic_sint32_t pending_signals; int ignore_sync; int suppress_reports; @@ -752,6 +765,8 @@ void ThreadIgnoreBegin(ThreadState *thr, uptr pc); void ThreadIgnoreEnd(ThreadState *thr); +void ThreadAllAtomicBegin(ThreadState *thr, uptr pc); +void ThreadAllAtomicEnd(ThreadState *thr); void ThreadIgnoreSyncBegin(ThreadState *thr, uptr pc); void ThreadIgnoreSyncEnd(ThreadState *thr); Index: compiler-rt/lib/tsan/rtl/tsan_rtl.cpp =================================================================== --- compiler-rt/lib/tsan/rtl/tsan_rtl.cpp +++ compiler-rt/lib/tsan/rtl/tsan_rtl.cpp @@ -844,7 +844,7 @@ Shadow cur(fast_state); cur.SetAddr0AndSizeLog(addr & 7, kAccessSizeLog); cur.SetWrite(kAccessIsWrite); - cur.SetAtomic(kIsAtomic); + cur.UpgradeAtomic(kIsAtomic); if (LIKELY(ContainsSameAccess(shadow_mem, cur.raw(), thr->fast_synch_epoch, kAccessIsWrite))) { @@ -858,8 +858,8 @@ cur.IncrementEpoch(); } - MemoryAccessImpl1(thr, addr, kAccessSizeLog, kAccessIsWrite, kIsAtomic, - shadow_mem, cur); + MemoryAccessImpl1(thr, addr, kAccessSizeLog, kAccessIsWrite, cur.IsAtomic(), + shadow_mem, cur); } // Called by MemoryAccessRange in tsan_rtl_thread.cpp @@ -1041,6 +1041,29 @@ } } +void ThreadAllAtomicBegin(ThreadState *thr, uptr pc) { + DPrintf("#%d: ThreadAllAtomicBegin\n", thr->tid); + thr->all_atomic++; + CHECK_GT(thr->all_atomic, 0); + thr->fast_state.SetAllAtomicBit(); + /*#if !SANITIZER_GO + if (pc && !ctx->after_multithreaded_fork) + thr->mop_ignore_set.Add(CurrentStackId(thr, pc)); + #endif*/ +} + +void ThreadAllAtomicEnd(ThreadState *thr) { + DPrintf("#%d: ThreadAllAtomicEnd\n", thr->tid); + CHECK_GT(thr->all_atomic, 0); + thr->all_atomic--; + if (thr->all_atomic == 0) { + thr->fast_state.ClearAllAtomicBit(); + /*#if !SANITIZER_GO + thr->mop_ignore_set.Reset(); + #endif*/ + } +} + #if !SANITIZER_GO extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr __tsan_testonly_shadow_stack_current_size() { Index: openmp/tools/archer/ompt-tsan.cpp =================================================================== --- openmp/tools/archer/ompt-tsan.cpp +++ openmp/tools/archer/ompt-tsan.cpp @@ -173,6 +173,12 @@ AnnotateIgnoreWritesBegin(const char *file, int line) {} void __attribute__((weak)) AnnotateIgnoreWritesEnd(const char *file, int line) { } +void __attribute__((weak)) AnnotateAllAtomicBegin(const char *file, int line) { + AnnotateIgnoreWritesBegin(file, line); +} +void __attribute__((weak)) AnnotateAllAtomicEnd(const char *file, int line) { + AnnotateIgnoreWritesEnd(file, line); +} void __attribute__((weak)) AnnotateNewMemory(const char *file, int line, const volatile void *cv, size_t size) {} @@ -199,6 +205,13 @@ // Resume checking for racy writes. #define TsanIgnoreWritesEnd() AnnotateIgnoreWritesEnd(__FILE__, __LINE__) +// Promote all memory accesses to be atomic between here and the next +// TsanAllAtomicEnd. +#define TsanAllAtomicBegin() AnnotateAllAtomicBegin(__FILE__, __LINE__) + +// Resume checking for racy writes. +#define TsanAllAtomicEnd() AnnotateAllAtomicEnd(__FILE__, __LINE__) + // We don't really delete the clock for now #define TsanDeleteClock(cv) @@ -728,7 +741,7 @@ // 2. execution of another task. // For the latter case we will re-enable tracking in task_switch. Data->InBarrier = true; - TsanIgnoreWritesBegin(); + TsanAllAtomicBegin(); } break; @@ -761,7 +774,7 @@ if (hasReductionCallback < ompt_set_always) { // We want to track writes after the barrier again. Data->InBarrier = false; - TsanIgnoreWritesEnd(); + TsanAllAtomicEnd(); } char BarrierIndex = Data->BarrierIndex; @@ -816,7 +829,7 @@ case ompt_scope_begin: switch (kind) { case ompt_sync_region_reduction: - TsanIgnoreWritesBegin(); + TsanAllAtomicBegin(); break; default: break; @@ -825,7 +838,7 @@ case ompt_scope_end: switch (kind) { case ompt_sync_region_reduction: - TsanIgnoreWritesEnd(); + TsanAllAtomicEnd(); break; default: break; @@ -927,7 +940,7 @@ if (hasReductionCallback < ompt_set_always && FromTask->InBarrier) { // We want to ignore writes in the runtime code during barriers, // but not when executing tasks with user code! - TsanIgnoreWritesEnd(); + TsanAllAtomicEnd(); } // The late fulfill happens after the detached task finished execution @@ -972,7 +985,7 @@ // Legacy handling for missing reduction callback if (hasReductionCallback < ompt_set_always && ToTask->InBarrier) { // We re-enter runtime code which currently performs a barrier. - TsanIgnoreWritesBegin(); + TsanAllAtomicBegin(); } // task suspended @@ -1101,6 +1114,8 @@ (void (*)(const char *, int, const volatile void *))); findTsanFunction(AnnotateIgnoreWritesBegin, (void (*)(const char *, int))); findTsanFunction(AnnotateIgnoreWritesEnd, (void (*)(const char *, int))); + findTsanFunction(AnnotateAllAtomicBegin, (void (*)(const char *, int))); + findTsanFunction(AnnotateAllAtomicEnd, (void (*)(const char *, int))); findTsanFunction( AnnotateNewMemory, (void (*)(const char *, int, const volatile void *, size_t))); Index: openmp/tools/archer/tests/races/reduction-race.c =================================================================== --- /dev/null +++ openmp/tools/archer/tests/races/reduction-race.c @@ -0,0 +1,42 @@ +/* + * reduction-race.c -- Archer testcase + */ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// +// See tools/archer/LICENSE.txt for details. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// RUN: %libarcher-compile-and-run-race | FileCheck %s +// RUN: %libarcher-compile-and-run-race-noserial | FileCheck %s +// REQUIRES: tsan +#include +#include + +int main(int argc, char *argv[]) { + int var = 0; + +#pragma omp parallel num_threads(8) shared(var) + { +#pragma omp master + var++; +#pragma omp for reduction(+ : var) + for (int i = 0; i < 100; i++) + var++; + } + + int error = (var != 101); + fprintf(stderr, "DONE\n"); + return error; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NEXT: {{(Write|Read)}} of size 4 +// CHECK-NEXT: #0 {{.*}}parallel-simple.c:23 +// CHECK: Previous write of size 4 +// CHECK-NEXT: #0 {{.*}}parallel-simple.c:23 +// CHECK: DONE +// CHECK: ThreadSanitizer: reported 1 warnings