xref: /llvm-project/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp (revision 9254b81990a403f0a5d4a96a4e9efc69e19870bc)
1 //===-- tsan_interface_atomic.cpp -----------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file is a part of ThreadSanitizer (TSan), a race detector.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 // ThreadSanitizer atomic operations are based on C++11/C1x standards.
14 // For background see C++11 standard.  A slightly older, publicly
15 // available draft of the standard (not entirely up-to-date, but close enough
16 // for casual browsing) is available here:
17 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf
18 // The following page contains more background information:
19 // http://www.hpl.hp.com/personal/Hans_Boehm/c++mm/
20 
21 #include "sanitizer_common/sanitizer_mutex.h"
22 #include "sanitizer_common/sanitizer_placement_new.h"
23 #include "sanitizer_common/sanitizer_stacktrace.h"
24 #include "tsan_flags.h"
25 #include "tsan_interface.h"
26 #include "tsan_rtl.h"
27 
28 using namespace __tsan;
29 
30 #if !SANITIZER_GO && __TSAN_HAS_INT128
31 // Protects emulation of 128-bit atomic operations.
32 static StaticSpinMutex mutex128;
33 #endif
34 
35 #if SANITIZER_DEBUG
36 static bool IsLoadOrder(morder mo) {
37   return mo == mo_relaxed || mo == mo_consume || mo == mo_acquire ||
38          mo == mo_seq_cst;
39 }
40 
41 static bool IsStoreOrder(morder mo) {
42   return mo == mo_relaxed || mo == mo_release || mo == mo_seq_cst;
43 }
44 #endif
45 
46 static bool IsReleaseOrder(morder mo) {
47   return mo == mo_release || mo == mo_acq_rel || mo == mo_seq_cst;
48 }
49 
50 static bool IsAcquireOrder(morder mo) {
51   return mo == mo_consume || mo == mo_acquire || mo == mo_acq_rel ||
52          mo == mo_seq_cst;
53 }
54 
55 static bool IsAcqRelOrder(morder mo) {
56   return mo == mo_acq_rel || mo == mo_seq_cst;
57 }
58 
59 template <typename T>
60 T func_xchg(volatile T *v, T op) {
61   T res = __sync_lock_test_and_set(v, op);
62   // __sync_lock_test_and_set does not contain full barrier.
63   __sync_synchronize();
64   return res;
65 }
66 
67 template <typename T>
68 T func_add(volatile T *v, T op) {
69   return __sync_fetch_and_add(v, op);
70 }
71 
72 template <typename T>
73 T func_sub(volatile T *v, T op) {
74   return __sync_fetch_and_sub(v, op);
75 }
76 
77 template <typename T>
78 T func_and(volatile T *v, T op) {
79   return __sync_fetch_and_and(v, op);
80 }
81 
82 template <typename T>
83 T func_or(volatile T *v, T op) {
84   return __sync_fetch_and_or(v, op);
85 }
86 
87 template <typename T>
88 T func_xor(volatile T *v, T op) {
89   return __sync_fetch_and_xor(v, op);
90 }
91 
92 template <typename T>
93 T func_nand(volatile T *v, T op) {
94   // clang does not support __sync_fetch_and_nand.
95   T cmp = *v;
96   for (;;) {
97     T newv = ~(cmp & op);
98     T cur = __sync_val_compare_and_swap(v, cmp, newv);
99     if (cmp == cur)
100       return cmp;
101     cmp = cur;
102   }
103 }
104 
105 template <typename T>
106 T func_cas(volatile T *v, T cmp, T xch) {
107   return __sync_val_compare_and_swap(v, cmp, xch);
108 }
109 
110 // clang does not support 128-bit atomic ops.
111 // Atomic ops are executed under tsan internal mutex,
112 // here we assume that the atomic variables are not accessed
113 // from non-instrumented code.
114 #if !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) && !SANITIZER_GO && \
115     __TSAN_HAS_INT128
116 a128 func_xchg(volatile a128 *v, a128 op) {
117   SpinMutexLock lock(&mutex128);
118   a128 cmp = *v;
119   *v = op;
120   return cmp;
121 }
122 
123 a128 func_add(volatile a128 *v, a128 op) {
124   SpinMutexLock lock(&mutex128);
125   a128 cmp = *v;
126   *v = cmp + op;
127   return cmp;
128 }
129 
130 a128 func_sub(volatile a128 *v, a128 op) {
131   SpinMutexLock lock(&mutex128);
132   a128 cmp = *v;
133   *v = cmp - op;
134   return cmp;
135 }
136 
137 a128 func_and(volatile a128 *v, a128 op) {
138   SpinMutexLock lock(&mutex128);
139   a128 cmp = *v;
140   *v = cmp & op;
141   return cmp;
142 }
143 
144 a128 func_or(volatile a128 *v, a128 op) {
145   SpinMutexLock lock(&mutex128);
146   a128 cmp = *v;
147   *v = cmp | op;
148   return cmp;
149 }
150 
151 a128 func_xor(volatile a128 *v, a128 op) {
152   SpinMutexLock lock(&mutex128);
153   a128 cmp = *v;
154   *v = cmp ^ op;
155   return cmp;
156 }
157 
158 a128 func_nand(volatile a128 *v, a128 op) {
159   SpinMutexLock lock(&mutex128);
160   a128 cmp = *v;
161   *v = ~(cmp & op);
162   return cmp;
163 }
164 
165 a128 func_cas(volatile a128 *v, a128 cmp, a128 xch) {
166   SpinMutexLock lock(&mutex128);
167   a128 cur = *v;
168   if (cur == cmp)
169     *v = xch;
170   return cur;
171 }
172 #endif
173 
174 template <typename T>
175 static int AccessSize() {
176   if (sizeof(T) <= 1)
177     return 1;
178   else if (sizeof(T) <= 2)
179     return 2;
180   else if (sizeof(T) <= 4)
181     return 4;
182   else
183     return 8;
184   // For 16-byte atomics we also use 8-byte memory access,
185   // this leads to false negatives only in very obscure cases.
186 }
187 
188 #if !SANITIZER_GO
189 static atomic_uint8_t *to_atomic(const volatile a8 *a) {
190   return reinterpret_cast<atomic_uint8_t *>(const_cast<a8 *>(a));
191 }
192 
193 static atomic_uint16_t *to_atomic(const volatile a16 *a) {
194   return reinterpret_cast<atomic_uint16_t *>(const_cast<a16 *>(a));
195 }
196 #endif
197 
198 static atomic_uint32_t *to_atomic(const volatile a32 *a) {
199   return reinterpret_cast<atomic_uint32_t *>(const_cast<a32 *>(a));
200 }
201 
202 static atomic_uint64_t *to_atomic(const volatile a64 *a) {
203   return reinterpret_cast<atomic_uint64_t *>(const_cast<a64 *>(a));
204 }
205 
206 static memory_order to_mo(morder mo) {
207   switch (mo) {
208     case mo_relaxed:
209       return memory_order_relaxed;
210     case mo_consume:
211       return memory_order_consume;
212     case mo_acquire:
213       return memory_order_acquire;
214     case mo_release:
215       return memory_order_release;
216     case mo_acq_rel:
217       return memory_order_acq_rel;
218     case mo_seq_cst:
219       return memory_order_seq_cst;
220   }
221   DCHECK(0);
222   return memory_order_seq_cst;
223 }
224 
225 namespace {
226 
227 template <typename T, T (*F)(volatile T *v, T op)>
228 static T AtomicRMW(ThreadState *thr, uptr pc, volatile T *a, T v, morder mo) {
229   MemoryAccess(thr, pc, (uptr)a, AccessSize<T>(), kAccessWrite | kAccessAtomic);
230   if (LIKELY(mo == mo_relaxed))
231     return F(a, v);
232   SlotLocker locker(thr);
233   {
234     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);
242     v = F(a, v);
243   }
244   if (IsReleaseOrder(mo))
245     IncrementEpoch(thr);
246   return v;
247 }
248 
249 struct OpLoad {
250   template <typename T>
251   static T NoTsanAtomic(morder mo, const volatile T *a) {
252     return atomic_load(to_atomic(a), to_mo(mo));
253   }
254 
255 #if __TSAN_HAS_INT128 && !SANITIZER_GO
256   static a128 NoTsanAtomic(morder mo, const volatile a128 *a) {
257     SpinMutexLock lock(&mutex128);
258     return *a;
259   }
260 #endif
261 
262   template <typename T>
263   static T Atomic(ThreadState *thr, uptr pc, morder mo, const volatile T *a) {
264     DCHECK(IsLoadOrder(mo));
265     // This fast-path is critical for performance.
266     // Assume the access is atomic.
267     if (!IsAcquireOrder(mo)) {
268       MemoryAccess(thr, pc, (uptr)a, AccessSize<T>(),
269                    kAccessRead | kAccessAtomic);
270       return NoTsanAtomic(mo, a);
271     }
272     // Don't create sync object if it does not exist yet. For example, an atomic
273     // pointer is initialized to nullptr and then periodically acquire-loaded.
274     T v = NoTsanAtomic(mo, a);
275     SyncVar *s = ctx->metamap.GetSyncIfExists((uptr)a);
276     if (s) {
277       SlotLocker locker(thr);
278       ReadLock lock(&s->mtx);
279       thr->clock.Acquire(s->clock);
280       // Re-read under sync mutex because we need a consistent snapshot
281       // of the value and the clock we acquire.
282       v = NoTsanAtomic(mo, a);
283     }
284     MemoryAccess(thr, pc, (uptr)a, AccessSize<T>(),
285                  kAccessRead | kAccessAtomic);
286     return v;
287   }
288 };
289 
290 struct OpStore {
291   template <typename T>
292   static void NoTsanAtomic(morder mo, volatile T *a, T v) {
293     atomic_store(to_atomic(a), v, to_mo(mo));
294   }
295 
296 #if __TSAN_HAS_INT128 && !SANITIZER_GO
297   static void NoTsanAtomic(morder mo, volatile a128 *a, a128 v) {
298     SpinMutexLock lock(&mutex128);
299     *a = v;
300   }
301 #endif
302 
303   template <typename T>
304   static void Atomic(ThreadState *thr, uptr pc, morder mo, volatile T *a, T v) {
305     DCHECK(IsStoreOrder(mo));
306     MemoryAccess(thr, pc, (uptr)a, AccessSize<T>(),
307                  kAccessWrite | kAccessAtomic);
308     // This fast-path is critical for performance.
309     // Assume the access is atomic.
310     // Strictly saying even relaxed store cuts off release sequence,
311     // so must reset the clock.
312     if (!IsReleaseOrder(mo)) {
313       NoTsanAtomic(mo, a, v);
314       return;
315     }
316     SlotLocker locker(thr);
317     {
318       auto s = ctx->metamap.GetSyncOrCreate(thr, pc, (uptr)a, false);
319       Lock lock(&s->mtx);
320       thr->clock.ReleaseStore(&s->clock);
321       NoTsanAtomic(mo, a, v);
322     }
323     IncrementEpoch(thr);
324   }
325 };
326 
327 struct OpExchange {
328   template <typename T>
329   static T NoTsanAtomic(morder mo, volatile T *a, T v) {
330     return func_xchg(a, v);
331   }
332   template <typename T>
333   static T Atomic(ThreadState *thr, uptr pc, morder mo, volatile T *a, T v) {
334     return AtomicRMW<T, func_xchg>(thr, pc, a, v, mo);
335   }
336 };
337 
338 struct OpFetchAdd {
339   template <typename T>
340   static T NoTsanAtomic(morder mo, volatile T *a, T v) {
341     return func_add(a, v);
342   }
343 
344   template <typename T>
345   static T Atomic(ThreadState *thr, uptr pc, morder mo, volatile T *a, T v) {
346     return AtomicRMW<T, func_add>(thr, pc, a, v, mo);
347   }
348 };
349 
350 struct OpFetchSub {
351   template <typename T>
352   static T NoTsanAtomic(morder mo, volatile T *a, T v) {
353     return func_sub(a, v);
354   }
355 
356   template <typename T>
357   static T Atomic(ThreadState *thr, uptr pc, morder mo, volatile T *a, T v) {
358     return AtomicRMW<T, func_sub>(thr, pc, a, v, mo);
359   }
360 };
361 
362 struct OpFetchAnd {
363   template <typename T>
364   static T NoTsanAtomic(morder mo, volatile T *a, T v) {
365     return func_and(a, v);
366   }
367 
368   template <typename T>
369   static T Atomic(ThreadState *thr, uptr pc, morder mo, volatile T *a, T v) {
370     return AtomicRMW<T, func_and>(thr, pc, a, v, mo);
371   }
372 };
373 
374 struct OpFetchOr {
375   template <typename T>
376   static T NoTsanAtomic(morder mo, volatile T *a, T v) {
377     return func_or(a, v);
378   }
379 
380   template <typename T>
381   static T Atomic(ThreadState *thr, uptr pc, morder mo, volatile T *a, T v) {
382     return AtomicRMW<T, func_or>(thr, pc, a, v, mo);
383   }
384 };
385 
386 struct OpFetchXor {
387   template <typename T>
388   static T NoTsanAtomic(morder mo, volatile T *a, T v) {
389     return func_xor(a, v);
390   }
391 
392   template <typename T>
393   static T Atomic(ThreadState *thr, uptr pc, morder mo, volatile T *a, T v) {
394     return AtomicRMW<T, func_xor>(thr, pc, a, v, mo);
395   }
396 };
397 
398 struct OpFetchNand {
399   template <typename T>
400   static T NoTsanAtomic(morder mo, volatile T *a, T v) {
401     return func_nand(a, v);
402   }
403 
404   template <typename T>
405   static T Atomic(ThreadState *thr, uptr pc, morder mo, volatile T *a, T v) {
406     return AtomicRMW<T, func_nand>(thr, pc, a, v, mo);
407   }
408 };
409 
410 struct OpCAS {
411   template <typename T>
412   static bool NoTsanAtomic(morder mo, morder fmo, volatile T *a, T *c, T v) {
413     return atomic_compare_exchange_strong(to_atomic(a), c, v, to_mo(mo));
414   }
415 
416 #if __TSAN_HAS_INT128
417   static bool NoTsanAtomic(morder mo, morder fmo, volatile a128 *a, a128 *c,
418                            a128 v) {
419     a128 old = *c;
420     a128 cur = func_cas(a, old, v);
421     if (cur == old)
422       return true;
423     *c = cur;
424     return false;
425   }
426 #endif
427 
428   template <typename T>
429   static T NoTsanAtomic(morder mo, morder fmo, volatile T *a, T c, T v) {
430     NoTsanAtomic(mo, fmo, a, &c, v);
431     return c;
432   }
433 
434   template <typename T>
435   static bool Atomic(ThreadState *thr, uptr pc, morder mo, morder fmo,
436                      volatile T *a, T *c, T v) {
437     // 31.7.2.18: "The failure argument shall not be memory_order_release
438     // nor memory_order_acq_rel". LLVM (2021-05) fallbacks to Monotonic
439     // (mo_relaxed) when those are used.
440     DCHECK(IsLoadOrder(fmo));
441 
442     MemoryAccess(thr, pc, (uptr)a, AccessSize<T>(),
443                  kAccessWrite | kAccessAtomic);
444     if (LIKELY(mo == mo_relaxed && fmo == mo_relaxed)) {
445       T cc = *c;
446       T pr = func_cas(a, cc, v);
447       if (pr == cc)
448         return true;
449       *c = pr;
450       return false;
451     }
452     SlotLocker locker(thr);
453     bool release = IsReleaseOrder(mo);
454     bool success;
455     {
456       auto s = ctx->metamap.GetSyncOrCreate(thr, pc, (uptr)a, false);
457       RWLock lock(&s->mtx, release);
458       T cc = *c;
459       T pr = func_cas(a, cc, v);
460       success = pr == cc;
461       if (!success) {
462         *c = pr;
463         mo = fmo;
464       }
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);
471     }
472     if (success && release)
473       IncrementEpoch(thr);
474     return success;
475   }
476 
477   template <typename T>
478   static T Atomic(ThreadState *thr, uptr pc, morder mo, morder fmo,
479                   volatile T *a, T c, T v) {
480     Atomic(thr, pc, mo, fmo, a, &c, v);
481     return c;
482   }
483 };
484 
485 #if !SANITIZER_GO
486 struct OpFence {
487   static void NoTsanAtomic(morder mo) { __sync_synchronize(); }
488 
489   static void Atomic(ThreadState *thr, uptr pc, morder mo) {
490     // FIXME(dvyukov): not implemented.
491     __sync_synchronize();
492   }
493 };
494 #endif
495 
496 }  // namespace
497 
498 // Interface functions follow.
499 #if !SANITIZER_GO
500 
501 // C/C++
502 
503 static morder convert_morder(morder mo) {
504   return flags()->force_seq_cst_atomics ? mo_seq_cst : mo;
505 }
506 
507 static morder to_morder(int mo) {
508   // Filter out additional memory order flags:
509   // MEMMODEL_SYNC        = 1 << 15
510   // __ATOMIC_HLE_ACQUIRE = 1 << 16
511   // __ATOMIC_HLE_RELEASE = 1 << 17
512   //
513   // HLE is an optimization, and we pretend that elision always fails.
514   // MEMMODEL_SYNC is used when lowering __sync_ atomics,
515   // since we use __sync_ atomics for actual atomic operations,
516   // we can safely ignore it as well. It also subtly affects semantics,
517   // but we don't model the difference.
518   morder res = static_cast<morder>(static_cast<u8>(mo));
519   DCHECK_LE(res, mo_seq_cst);
520   return res;
521 }
522 
523 template <class Op, class... Types>
524 ALWAYS_INLINE auto AtomicImpl(morder mo, Types... args) {
525   ThreadState *const thr = cur_thread();
526   ProcessPendingSignals(thr);
527   if (UNLIKELY(thr->ignore_sync || thr->ignore_interceptors))
528     return Op::NoTsanAtomic(mo, args...);
529   return Op::Atomic(thr, GET_CALLER_PC(), convert_morder(mo), args...);
530 }
531 
532 extern "C" {
533 SANITIZER_INTERFACE_ATTRIBUTE
534 a8 __tsan_atomic8_load(const volatile a8 *a, int mo) {
535   return AtomicImpl<OpLoad>(to_morder(mo), a);
536 }
537 
538 SANITIZER_INTERFACE_ATTRIBUTE
539 a16 __tsan_atomic16_load(const volatile a16 *a, int mo) {
540   return AtomicImpl<OpLoad>(to_morder(mo), a);
541 }
542 
543 SANITIZER_INTERFACE_ATTRIBUTE
544 a32 __tsan_atomic32_load(const volatile a32 *a, int mo) {
545   return AtomicImpl<OpLoad>(to_morder(mo), a);
546 }
547 
548 SANITIZER_INTERFACE_ATTRIBUTE
549 a64 __tsan_atomic64_load(const volatile a64 *a, int mo) {
550   return AtomicImpl<OpLoad>(to_morder(mo), a);
551 }
552 
553 #  if __TSAN_HAS_INT128
554 SANITIZER_INTERFACE_ATTRIBUTE
555 a128 __tsan_atomic128_load(const volatile a128 *a, int mo) {
556   return AtomicImpl<OpLoad>(to_morder(mo), a);
557 }
558 #  endif
559 
560 SANITIZER_INTERFACE_ATTRIBUTE
561 void __tsan_atomic8_store(volatile a8 *a, a8 v, int mo) {
562   return AtomicImpl<OpStore>(to_morder(mo), a, v);
563 }
564 
565 SANITIZER_INTERFACE_ATTRIBUTE
566 void __tsan_atomic16_store(volatile a16 *a, a16 v, int mo) {
567   return AtomicImpl<OpStore>(to_morder(mo), a, v);
568 }
569 
570 SANITIZER_INTERFACE_ATTRIBUTE
571 void __tsan_atomic32_store(volatile a32 *a, a32 v, int mo) {
572   return AtomicImpl<OpStore>(to_morder(mo), a, v);
573 }
574 
575 SANITIZER_INTERFACE_ATTRIBUTE
576 void __tsan_atomic64_store(volatile a64 *a, a64 v, int mo) {
577   return AtomicImpl<OpStore>(to_morder(mo), a, v);
578 }
579 
580 #  if __TSAN_HAS_INT128
581 SANITIZER_INTERFACE_ATTRIBUTE
582 void __tsan_atomic128_store(volatile a128 *a, a128 v, int mo) {
583   return AtomicImpl<OpStore>(to_morder(mo), a, v);
584 }
585 #  endif
586 
587 SANITIZER_INTERFACE_ATTRIBUTE
588 a8 __tsan_atomic8_exchange(volatile a8 *a, a8 v, int mo) {
589   return AtomicImpl<OpExchange>(to_morder(mo), a, v);
590 }
591 
592 SANITIZER_INTERFACE_ATTRIBUTE
593 a16 __tsan_atomic16_exchange(volatile a16 *a, a16 v, int mo) {
594   return AtomicImpl<OpExchange>(to_morder(mo), a, v);
595 }
596 
597 SANITIZER_INTERFACE_ATTRIBUTE
598 a32 __tsan_atomic32_exchange(volatile a32 *a, a32 v, int mo) {
599   return AtomicImpl<OpExchange>(to_morder(mo), a, v);
600 }
601 
602 SANITIZER_INTERFACE_ATTRIBUTE
603 a64 __tsan_atomic64_exchange(volatile a64 *a, a64 v, int mo) {
604   return AtomicImpl<OpExchange>(to_morder(mo), a, v);
605 }
606 
607 #  if __TSAN_HAS_INT128
608 SANITIZER_INTERFACE_ATTRIBUTE
609 a128 __tsan_atomic128_exchange(volatile a128 *a, a128 v, int mo) {
610   return AtomicImpl<OpExchange>(to_morder(mo), a, v);
611 }
612 #  endif
613 
614 SANITIZER_INTERFACE_ATTRIBUTE
615 a8 __tsan_atomic8_fetch_add(volatile a8 *a, a8 v, int mo) {
616   return AtomicImpl<OpFetchAdd>(to_morder(mo), a, v);
617 }
618 
619 SANITIZER_INTERFACE_ATTRIBUTE
620 a16 __tsan_atomic16_fetch_add(volatile a16 *a, a16 v, int mo) {
621   return AtomicImpl<OpFetchAdd>(to_morder(mo), a, v);
622 }
623 
624 SANITIZER_INTERFACE_ATTRIBUTE
625 a32 __tsan_atomic32_fetch_add(volatile a32 *a, a32 v, int mo) {
626   return AtomicImpl<OpFetchAdd>(to_morder(mo), a, v);
627 }
628 
629 SANITIZER_INTERFACE_ATTRIBUTE
630 a64 __tsan_atomic64_fetch_add(volatile a64 *a, a64 v, int mo) {
631   return AtomicImpl<OpFetchAdd>(to_morder(mo), a, v);
632 }
633 
634 #  if __TSAN_HAS_INT128
635 SANITIZER_INTERFACE_ATTRIBUTE
636 a128 __tsan_atomic128_fetch_add(volatile a128 *a, a128 v, int mo) {
637   return AtomicImpl<OpFetchAdd>(to_morder(mo), a, v);
638 }
639 #  endif
640 
641 SANITIZER_INTERFACE_ATTRIBUTE
642 a8 __tsan_atomic8_fetch_sub(volatile a8 *a, a8 v, int mo) {
643   return AtomicImpl<OpFetchSub>(to_morder(mo), a, v);
644 }
645 
646 SANITIZER_INTERFACE_ATTRIBUTE
647 a16 __tsan_atomic16_fetch_sub(volatile a16 *a, a16 v, int mo) {
648   return AtomicImpl<OpFetchSub>(to_morder(mo), a, v);
649 }
650 
651 SANITIZER_INTERFACE_ATTRIBUTE
652 a32 __tsan_atomic32_fetch_sub(volatile a32 *a, a32 v, int mo) {
653   return AtomicImpl<OpFetchSub>(to_morder(mo), a, v);
654 }
655 
656 SANITIZER_INTERFACE_ATTRIBUTE
657 a64 __tsan_atomic64_fetch_sub(volatile a64 *a, a64 v, int mo) {
658   return AtomicImpl<OpFetchSub>(to_morder(mo), a, v);
659 }
660 
661 #  if __TSAN_HAS_INT128
662 SANITIZER_INTERFACE_ATTRIBUTE
663 a128 __tsan_atomic128_fetch_sub(volatile a128 *a, a128 v, int mo) {
664   return AtomicImpl<OpFetchSub>(to_morder(mo), a, v);
665 }
666 #  endif
667 
668 SANITIZER_INTERFACE_ATTRIBUTE
669 a8 __tsan_atomic8_fetch_and(volatile a8 *a, a8 v, int mo) {
670   return AtomicImpl<OpFetchAnd>(to_morder(mo), a, v);
671 }
672 
673 SANITIZER_INTERFACE_ATTRIBUTE
674 a16 __tsan_atomic16_fetch_and(volatile a16 *a, a16 v, int mo) {
675   return AtomicImpl<OpFetchAnd>(to_morder(mo), a, v);
676 }
677 
678 SANITIZER_INTERFACE_ATTRIBUTE
679 a32 __tsan_atomic32_fetch_and(volatile a32 *a, a32 v, int mo) {
680   return AtomicImpl<OpFetchAnd>(to_morder(mo), a, v);
681 }
682 
683 SANITIZER_INTERFACE_ATTRIBUTE
684 a64 __tsan_atomic64_fetch_and(volatile a64 *a, a64 v, int mo) {
685   return AtomicImpl<OpFetchAnd>(to_morder(mo), a, v);
686 }
687 
688 #  if __TSAN_HAS_INT128
689 SANITIZER_INTERFACE_ATTRIBUTE
690 a128 __tsan_atomic128_fetch_and(volatile a128 *a, a128 v, int mo) {
691   return AtomicImpl<OpFetchAnd>(to_morder(mo), a, v);
692 }
693 #  endif
694 
695 SANITIZER_INTERFACE_ATTRIBUTE
696 a8 __tsan_atomic8_fetch_or(volatile a8 *a, a8 v, int mo) {
697   return AtomicImpl<OpFetchOr>(to_morder(mo), a, v);
698 }
699 
700 SANITIZER_INTERFACE_ATTRIBUTE
701 a16 __tsan_atomic16_fetch_or(volatile a16 *a, a16 v, int mo) {
702   return AtomicImpl<OpFetchOr>(to_morder(mo), a, v);
703 }
704 
705 SANITIZER_INTERFACE_ATTRIBUTE
706 a32 __tsan_atomic32_fetch_or(volatile a32 *a, a32 v, int mo) {
707   return AtomicImpl<OpFetchOr>(to_morder(mo), a, v);
708 }
709 
710 SANITIZER_INTERFACE_ATTRIBUTE
711 a64 __tsan_atomic64_fetch_or(volatile a64 *a, a64 v, int mo) {
712   return AtomicImpl<OpFetchOr>(to_morder(mo), a, v);
713 }
714 
715 #  if __TSAN_HAS_INT128
716 SANITIZER_INTERFACE_ATTRIBUTE
717 a128 __tsan_atomic128_fetch_or(volatile a128 *a, a128 v, int mo) {
718   return AtomicImpl<OpFetchOr>(to_morder(mo), a, v);
719 }
720 #  endif
721 
722 SANITIZER_INTERFACE_ATTRIBUTE
723 a8 __tsan_atomic8_fetch_xor(volatile a8 *a, a8 v, int mo) {
724   return AtomicImpl<OpFetchXor>(to_morder(mo), a, v);
725 }
726 
727 SANITIZER_INTERFACE_ATTRIBUTE
728 a16 __tsan_atomic16_fetch_xor(volatile a16 *a, a16 v, int mo) {
729   return AtomicImpl<OpFetchXor>(to_morder(mo), a, v);
730 }
731 
732 SANITIZER_INTERFACE_ATTRIBUTE
733 a32 __tsan_atomic32_fetch_xor(volatile a32 *a, a32 v, int mo) {
734   return AtomicImpl<OpFetchXor>(to_morder(mo), a, v);
735 }
736 
737 SANITIZER_INTERFACE_ATTRIBUTE
738 a64 __tsan_atomic64_fetch_xor(volatile a64 *a, a64 v, int mo) {
739   return AtomicImpl<OpFetchXor>(to_morder(mo), a, v);
740 }
741 
742 #  if __TSAN_HAS_INT128
743 SANITIZER_INTERFACE_ATTRIBUTE
744 a128 __tsan_atomic128_fetch_xor(volatile a128 *a, a128 v, int mo) {
745   return AtomicImpl<OpFetchXor>(to_morder(mo), a, v);
746 }
747 #  endif
748 
749 SANITIZER_INTERFACE_ATTRIBUTE
750 a8 __tsan_atomic8_fetch_nand(volatile a8 *a, a8 v, int mo) {
751   return AtomicImpl<OpFetchNand>(to_morder(mo), a, v);
752 }
753 
754 SANITIZER_INTERFACE_ATTRIBUTE
755 a16 __tsan_atomic16_fetch_nand(volatile a16 *a, a16 v, int mo) {
756   return AtomicImpl<OpFetchNand>(to_morder(mo), a, v);
757 }
758 
759 SANITIZER_INTERFACE_ATTRIBUTE
760 a32 __tsan_atomic32_fetch_nand(volatile a32 *a, a32 v, int mo) {
761   return AtomicImpl<OpFetchNand>(to_morder(mo), a, v);
762 }
763 
764 SANITIZER_INTERFACE_ATTRIBUTE
765 a64 __tsan_atomic64_fetch_nand(volatile a64 *a, a64 v, int mo) {
766   return AtomicImpl<OpFetchNand>(to_morder(mo), a, v);
767 }
768 
769 #  if __TSAN_HAS_INT128
770 SANITIZER_INTERFACE_ATTRIBUTE
771 a128 __tsan_atomic128_fetch_nand(volatile a128 *a, a128 v, int mo) {
772   return AtomicImpl<OpFetchNand>(to_morder(mo), a, v);
773 }
774 #  endif
775 
776 SANITIZER_INTERFACE_ATTRIBUTE
777 int __tsan_atomic8_compare_exchange_strong(volatile a8 *a, a8 *c, a8 v, int mo,
778                                            int fmo) {
779   return AtomicImpl<OpCAS>(to_morder(mo), to_morder(fmo), a, c, v);
780 }
781 
782 SANITIZER_INTERFACE_ATTRIBUTE
783 int __tsan_atomic16_compare_exchange_strong(volatile a16 *a, a16 *c, a16 v,
784                                             int mo, int fmo) {
785   return AtomicImpl<OpCAS>(to_morder(mo), to_morder(fmo), a, c, v);
786 }
787 
788 SANITIZER_INTERFACE_ATTRIBUTE
789 int __tsan_atomic32_compare_exchange_strong(volatile a32 *a, a32 *c, a32 v,
790                                             int mo, int fmo) {
791   return AtomicImpl<OpCAS>(to_morder(mo), to_morder(fmo), a, c, v);
792 }
793 
794 SANITIZER_INTERFACE_ATTRIBUTE
795 int __tsan_atomic64_compare_exchange_strong(volatile a64 *a, a64 *c, a64 v,
796                                             int mo, int fmo) {
797   return AtomicImpl<OpCAS>(to_morder(mo), to_morder(fmo), a, c, v);
798 }
799 
800 #  if __TSAN_HAS_INT128
801 SANITIZER_INTERFACE_ATTRIBUTE
802 int __tsan_atomic128_compare_exchange_strong(volatile a128 *a, a128 *c, a128 v,
803                                              int mo, int fmo) {
804   return AtomicImpl<OpCAS>(to_morder(mo), to_morder(fmo), a, c, v);
805 }
806 #  endif
807 
808 SANITIZER_INTERFACE_ATTRIBUTE
809 int __tsan_atomic8_compare_exchange_weak(volatile a8 *a, a8 *c, a8 v, int mo,
810                                          int fmo) {
811   return AtomicImpl<OpCAS>(to_morder(mo), to_morder(fmo), a, c, v);
812 }
813 
814 SANITIZER_INTERFACE_ATTRIBUTE
815 int __tsan_atomic16_compare_exchange_weak(volatile a16 *a, a16 *c, a16 v,
816                                           int mo, int fmo) {
817   return AtomicImpl<OpCAS>(to_morder(mo), to_morder(fmo), a, c, v);
818 }
819 
820 SANITIZER_INTERFACE_ATTRIBUTE
821 int __tsan_atomic32_compare_exchange_weak(volatile a32 *a, a32 *c, a32 v,
822                                           int mo, int fmo) {
823   return AtomicImpl<OpCAS>(to_morder(mo), to_morder(fmo), a, c, v);
824 }
825 
826 SANITIZER_INTERFACE_ATTRIBUTE
827 int __tsan_atomic64_compare_exchange_weak(volatile a64 *a, a64 *c, a64 v,
828                                           int mo, int fmo) {
829   return AtomicImpl<OpCAS>(to_morder(mo), to_morder(fmo), a, c, v);
830 }
831 
832 #  if __TSAN_HAS_INT128
833 SANITIZER_INTERFACE_ATTRIBUTE
834 int __tsan_atomic128_compare_exchange_weak(volatile a128 *a, a128 *c, a128 v,
835                                            int mo, int fmo) {
836   return AtomicImpl<OpCAS>(to_morder(mo), to_morder(fmo), a, c, v);
837 }
838 #  endif
839 
840 SANITIZER_INTERFACE_ATTRIBUTE
841 a8 __tsan_atomic8_compare_exchange_val(volatile a8 *a, a8 c, a8 v, int mo,
842                                        int fmo) {
843   return AtomicImpl<OpCAS>(to_morder(mo), to_morder(fmo), a, c, v);
844 }
845 
846 SANITIZER_INTERFACE_ATTRIBUTE
847 a16 __tsan_atomic16_compare_exchange_val(volatile a16 *a, a16 c, a16 v, int mo,
848                                          int fmo) {
849   return AtomicImpl<OpCAS>(to_morder(mo), to_morder(fmo), a, c, v);
850 }
851 
852 SANITIZER_INTERFACE_ATTRIBUTE
853 a32 __tsan_atomic32_compare_exchange_val(volatile a32 *a, a32 c, a32 v, int mo,
854                                          int fmo) {
855   return AtomicImpl<OpCAS>(to_morder(mo), to_morder(fmo), a, c, v);
856 }
857 
858 SANITIZER_INTERFACE_ATTRIBUTE
859 a64 __tsan_atomic64_compare_exchange_val(volatile a64 *a, a64 c, a64 v, int mo,
860                                          int fmo) {
861   return AtomicImpl<OpCAS>(to_morder(mo), to_morder(fmo), a, c, v);
862 }
863 
864 #  if __TSAN_HAS_INT128
865 SANITIZER_INTERFACE_ATTRIBUTE
866 a128 __tsan_atomic128_compare_exchange_val(volatile a128 *a, a128 c, a128 v,
867                                            int mo, int fmo) {
868   return AtomicImpl<OpCAS>(to_morder(mo), to_morder(fmo), a, c, v);
869 }
870 #  endif
871 
872 SANITIZER_INTERFACE_ATTRIBUTE
873 void __tsan_atomic_thread_fence(int mo) {
874   return AtomicImpl<OpFence>(to_morder(mo));
875 }
876 
877 SANITIZER_INTERFACE_ATTRIBUTE
878 void __tsan_atomic_signal_fence(int mo) {}
879 }  // extern "C"
880 
881 #else  // #if !SANITIZER_GO
882 
883 // Go
884 
885 template <class Op, class... Types>
886 void AtomicGo(ThreadState *thr, uptr cpc, uptr pc, Types... args) {
887   if (thr->ignore_sync) {
888     (void)Op::NoTsanAtomic(args...);
889   } else {
890     FuncEntry(thr, cpc);
891     (void)Op::Atomic(thr, pc, args...);
892     FuncExit(thr);
893   }
894 }
895 
896 template <class Op, class... Types>
897 auto AtomicGoRet(ThreadState *thr, uptr cpc, uptr pc, Types... args) {
898   if (thr->ignore_sync) {
899     return Op::NoTsanAtomic(args...);
900   } else {
901     FuncEntry(thr, cpc);
902     auto ret = Op::Atomic(thr, pc, args...);
903     FuncExit(thr);
904     return ret;
905   }
906 }
907 
908 extern "C" {
909 SANITIZER_INTERFACE_ATTRIBUTE
910 void __tsan_go_atomic32_load(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
911   *(a32 *)(a + 8) = AtomicGoRet<OpLoad>(thr, cpc, pc, mo_acquire, *(a32 **)a);
912 }
913 
914 SANITIZER_INTERFACE_ATTRIBUTE
915 void __tsan_go_atomic64_load(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
916   *(a64 *)(a + 8) = AtomicGoRet<OpLoad>(thr, cpc, pc, mo_acquire, *(a64 **)a);
917 }
918 
919 SANITIZER_INTERFACE_ATTRIBUTE
920 void __tsan_go_atomic32_store(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
921   AtomicGo<OpStore>(thr, cpc, pc, mo_release, *(a32 **)a, *(a32 *)(a + 8));
922 }
923 
924 SANITIZER_INTERFACE_ATTRIBUTE
925 void __tsan_go_atomic64_store(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
926   AtomicGo<OpStore>(thr, cpc, pc, mo_release, *(a64 **)a, *(a64 *)(a + 8));
927 }
928 
929 SANITIZER_INTERFACE_ATTRIBUTE
930 void __tsan_go_atomic32_fetch_add(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
931   *(a32 *)(a + 16) = AtomicGoRet<OpFetchAdd>(thr, cpc, pc, mo_acq_rel,
932                                              *(a32 **)a, *(a32 *)(a + 8));
933 }
934 
935 SANITIZER_INTERFACE_ATTRIBUTE
936 void __tsan_go_atomic64_fetch_add(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
937   *(a64 *)(a + 16) = AtomicGoRet<OpFetchAdd>(thr, cpc, pc, mo_acq_rel,
938                                              *(a64 **)a, *(a64 *)(a + 8));
939 }
940 
941 SANITIZER_INTERFACE_ATTRIBUTE
942 void __tsan_go_atomic32_fetch_and(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
943   *(a32 *)(a + 16) = AtomicGoRet<OpFetchAnd>(thr, cpc, pc, mo_acq_rel,
944                                              *(a32 **)a, *(a32 *)(a + 8));
945 }
946 
947 SANITIZER_INTERFACE_ATTRIBUTE
948 void __tsan_go_atomic64_fetch_and(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
949   *(a64 *)(a + 16) = AtomicGoRet<OpFetchAnd>(thr, cpc, pc, mo_acq_rel,
950                                              *(a64 **)a, *(a64 *)(a + 8));
951 }
952 
953 SANITIZER_INTERFACE_ATTRIBUTE
954 void __tsan_go_atomic32_fetch_or(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
955   *(a32 *)(a + 16) = AtomicGoRet<OpFetchOr>(thr, cpc, pc, mo_acq_rel,
956                                             *(a32 **)a, *(a32 *)(a + 8));
957 }
958 
959 SANITIZER_INTERFACE_ATTRIBUTE
960 void __tsan_go_atomic64_fetch_or(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
961   *(a64 *)(a + 16) = AtomicGoRet<OpFetchOr>(thr, cpc, pc, mo_acq_rel,
962                                             *(a64 **)a, *(a64 *)(a + 8));
963 }
964 
965 SANITIZER_INTERFACE_ATTRIBUTE
966 void __tsan_go_atomic32_exchange(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
967   *(a32 *)(a + 16) = AtomicGoRet<OpExchange>(thr, cpc, pc, mo_acq_rel,
968                                              *(a32 **)a, *(a32 *)(a + 8));
969 }
970 
971 SANITIZER_INTERFACE_ATTRIBUTE
972 void __tsan_go_atomic64_exchange(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
973   *(a64 *)(a + 16) = AtomicGoRet<OpExchange>(thr, cpc, pc, mo_acq_rel,
974                                              *(a64 **)a, *(a64 *)(a + 8));
975 }
976 
977 SANITIZER_INTERFACE_ATTRIBUTE
978 void __tsan_go_atomic32_compare_exchange(ThreadState *thr, uptr cpc, uptr pc,
979                                          u8 *a) {
980   a32 cmp = *(a32 *)(a + 8);
981   a32 cur = AtomicGoRet<OpCAS>(thr, cpc, pc, mo_acq_rel, mo_acquire, *(a32 **)a,
982                                cmp, *(a32 *)(a + 12));
983   *(bool *)(a + 16) = (cur == cmp);
984 }
985 
986 SANITIZER_INTERFACE_ATTRIBUTE
987 void __tsan_go_atomic64_compare_exchange(ThreadState *thr, uptr cpc, uptr pc,
988                                          u8 *a) {
989   a64 cmp = *(a64 *)(a + 8);
990   a64 cur = AtomicGoRet<OpCAS>(thr, cpc, pc, mo_acq_rel, mo_acquire, *(a64 **)a,
991                                cmp, *(a64 *)(a + 16));
992   *(bool *)(a + 24) = (cur == cmp);
993 }
994 }  // extern "C"
995 #endif  // #if !SANITIZER_GO
996