| 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, addr: (uptr)a, save_stack: false); |
| 235 | RWLock lock(&s->mtx, IsReleaseOrder(mo)); |
| 236 | if (IsAcqRelOrder(mo)) |
| 237 | thr->clock.ReleaseAcquire(dstp: &s->clock); |
| 238 | else if (IsReleaseOrder(mo)) |
| 239 | thr->clock.Release(dstp: &s->clock); |
| 240 | else if (IsAcquireOrder(mo)) |
| 241 | thr->clock.Acquire(src: 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(addr: (uptr)a); |
| 276 | if (s) { |
| 277 | SlotLocker locker(thr); |
| 278 | ReadLock lock(&s->mtx); |
| 279 | thr->clock.Acquire(src: 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, addr: (uptr)a, save_stack: false); |
| 319 | Lock lock(&s->mtx); |
| 320 | thr->clock.ReleaseStore(dstp: &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(v: a, cmp: old, xch: 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, addr: (uptr)a, save_stack: 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(dstp: &s->clock); |
| 467 | else if (success && IsReleaseOrder(mo)) |
| 468 | thr->clock.Release(dstp: &s->clock); |
| 469 | else if (IsAcquireOrder(mo)) |
| 470 | thr->clock.Acquire(src: 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>(mo: to_morder(mo), args: a); |
| 536 | } |
| 537 | |
| 538 | SANITIZER_INTERFACE_ATTRIBUTE |
| 539 | a16 __tsan_atomic16_load(const volatile a16 *a, int mo) { |
| 540 | return AtomicImpl<OpLoad>(mo: to_morder(mo), args: a); |
| 541 | } |
| 542 | |
| 543 | SANITIZER_INTERFACE_ATTRIBUTE |
| 544 | a32 __tsan_atomic32_load(const volatile a32 *a, int mo) { |
| 545 | return AtomicImpl<OpLoad>(mo: to_morder(mo), args: a); |
| 546 | } |
| 547 | |
| 548 | SANITIZER_INTERFACE_ATTRIBUTE |
| 549 | a64 __tsan_atomic64_load(const volatile a64 *a, int mo) { |
| 550 | return AtomicImpl<OpLoad>(mo: to_morder(mo), args: 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>(mo: to_morder(mo), args: 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>(mo: to_morder(mo), args: a, args: v); |
| 563 | } |
| 564 | |
| 565 | SANITIZER_INTERFACE_ATTRIBUTE |
| 566 | void __tsan_atomic16_store(volatile a16 *a, a16 v, int mo) { |
| 567 | return AtomicImpl<OpStore>(mo: to_morder(mo), args: a, args: v); |
| 568 | } |
| 569 | |
| 570 | SANITIZER_INTERFACE_ATTRIBUTE |
| 571 | void __tsan_atomic32_store(volatile a32 *a, a32 v, int mo) { |
| 572 | return AtomicImpl<OpStore>(mo: to_morder(mo), args: a, args: v); |
| 573 | } |
| 574 | |
| 575 | SANITIZER_INTERFACE_ATTRIBUTE |
| 576 | void __tsan_atomic64_store(volatile a64 *a, a64 v, int mo) { |
| 577 | return AtomicImpl<OpStore>(mo: to_morder(mo), args: a, args: 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>(mo: to_morder(mo), args: a, args: 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>(mo: to_morder(mo), args: a, args: v); |
| 590 | } |
| 591 | |
| 592 | SANITIZER_INTERFACE_ATTRIBUTE |
| 593 | a16 __tsan_atomic16_exchange(volatile a16 *a, a16 v, int mo) { |
| 594 | return AtomicImpl<OpExchange>(mo: to_morder(mo), args: a, args: v); |
| 595 | } |
| 596 | |
| 597 | SANITIZER_INTERFACE_ATTRIBUTE |
| 598 | a32 __tsan_atomic32_exchange(volatile a32 *a, a32 v, int mo) { |
| 599 | return AtomicImpl<OpExchange>(mo: to_morder(mo), args: a, args: v); |
| 600 | } |
| 601 | |
| 602 | SANITIZER_INTERFACE_ATTRIBUTE |
| 603 | a64 __tsan_atomic64_exchange(volatile a64 *a, a64 v, int mo) { |
| 604 | return AtomicImpl<OpExchange>(mo: to_morder(mo), args: a, args: 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>(mo: to_morder(mo), args: a, args: 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>(mo: to_morder(mo), args: a, args: v); |
| 617 | } |
| 618 | |
| 619 | SANITIZER_INTERFACE_ATTRIBUTE |
| 620 | a16 __tsan_atomic16_fetch_add(volatile a16 *a, a16 v, int mo) { |
| 621 | return AtomicImpl<OpFetchAdd>(mo: to_morder(mo), args: a, args: v); |
| 622 | } |
| 623 | |
| 624 | SANITIZER_INTERFACE_ATTRIBUTE |
| 625 | a32 __tsan_atomic32_fetch_add(volatile a32 *a, a32 v, int mo) { |
| 626 | return AtomicImpl<OpFetchAdd>(mo: to_morder(mo), args: a, args: v); |
| 627 | } |
| 628 | |
| 629 | SANITIZER_INTERFACE_ATTRIBUTE |
| 630 | a64 __tsan_atomic64_fetch_add(volatile a64 *a, a64 v, int mo) { |
| 631 | return AtomicImpl<OpFetchAdd>(mo: to_morder(mo), args: a, args: 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>(mo: to_morder(mo), args: a, args: 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>(mo: to_morder(mo), args: a, args: v); |
| 644 | } |
| 645 | |
| 646 | SANITIZER_INTERFACE_ATTRIBUTE |
| 647 | a16 __tsan_atomic16_fetch_sub(volatile a16 *a, a16 v, int mo) { |
| 648 | return AtomicImpl<OpFetchSub>(mo: to_morder(mo), args: a, args: v); |
| 649 | } |
| 650 | |
| 651 | SANITIZER_INTERFACE_ATTRIBUTE |
| 652 | a32 __tsan_atomic32_fetch_sub(volatile a32 *a, a32 v, int mo) { |
| 653 | return AtomicImpl<OpFetchSub>(mo: to_morder(mo), args: a, args: v); |
| 654 | } |
| 655 | |
| 656 | SANITIZER_INTERFACE_ATTRIBUTE |
| 657 | a64 __tsan_atomic64_fetch_sub(volatile a64 *a, a64 v, int mo) { |
| 658 | return AtomicImpl<OpFetchSub>(mo: to_morder(mo), args: a, args: 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>(mo: to_morder(mo), args: a, args: 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>(mo: to_morder(mo), args: a, args: v); |
| 671 | } |
| 672 | |
| 673 | SANITIZER_INTERFACE_ATTRIBUTE |
| 674 | a16 __tsan_atomic16_fetch_and(volatile a16 *a, a16 v, int mo) { |
| 675 | return AtomicImpl<OpFetchAnd>(mo: to_morder(mo), args: a, args: v); |
| 676 | } |
| 677 | |
| 678 | SANITIZER_INTERFACE_ATTRIBUTE |
| 679 | a32 __tsan_atomic32_fetch_and(volatile a32 *a, a32 v, int mo) { |
| 680 | return AtomicImpl<OpFetchAnd>(mo: to_morder(mo), args: a, args: v); |
| 681 | } |
| 682 | |
| 683 | SANITIZER_INTERFACE_ATTRIBUTE |
| 684 | a64 __tsan_atomic64_fetch_and(volatile a64 *a, a64 v, int mo) { |
| 685 | return AtomicImpl<OpFetchAnd>(mo: to_morder(mo), args: a, args: 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>(mo: to_morder(mo), args: a, args: 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>(mo: to_morder(mo), args: a, args: v); |
| 698 | } |
| 699 | |
| 700 | SANITIZER_INTERFACE_ATTRIBUTE |
| 701 | a16 __tsan_atomic16_fetch_or(volatile a16 *a, a16 v, int mo) { |
| 702 | return AtomicImpl<OpFetchOr>(mo: to_morder(mo), args: a, args: v); |
| 703 | } |
| 704 | |
| 705 | SANITIZER_INTERFACE_ATTRIBUTE |
| 706 | a32 __tsan_atomic32_fetch_or(volatile a32 *a, a32 v, int mo) { |
| 707 | return AtomicImpl<OpFetchOr>(mo: to_morder(mo), args: a, args: v); |
| 708 | } |
| 709 | |
| 710 | SANITIZER_INTERFACE_ATTRIBUTE |
| 711 | a64 __tsan_atomic64_fetch_or(volatile a64 *a, a64 v, int mo) { |
| 712 | return AtomicImpl<OpFetchOr>(mo: to_morder(mo), args: a, args: 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>(mo: to_morder(mo), args: a, args: 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>(mo: to_morder(mo), args: a, args: v); |
| 725 | } |
| 726 | |
| 727 | SANITIZER_INTERFACE_ATTRIBUTE |
| 728 | a16 __tsan_atomic16_fetch_xor(volatile a16 *a, a16 v, int mo) { |
| 729 | return AtomicImpl<OpFetchXor>(mo: to_morder(mo), args: a, args: v); |
| 730 | } |
| 731 | |
| 732 | SANITIZER_INTERFACE_ATTRIBUTE |
| 733 | a32 __tsan_atomic32_fetch_xor(volatile a32 *a, a32 v, int mo) { |
| 734 | return AtomicImpl<OpFetchXor>(mo: to_morder(mo), args: a, args: v); |
| 735 | } |
| 736 | |
| 737 | SANITIZER_INTERFACE_ATTRIBUTE |
| 738 | a64 __tsan_atomic64_fetch_xor(volatile a64 *a, a64 v, int mo) { |
| 739 | return AtomicImpl<OpFetchXor>(mo: to_morder(mo), args: a, args: 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>(mo: to_morder(mo), args: a, args: 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>(mo: to_morder(mo), args: a, args: v); |
| 752 | } |
| 753 | |
| 754 | SANITIZER_INTERFACE_ATTRIBUTE |
| 755 | a16 __tsan_atomic16_fetch_nand(volatile a16 *a, a16 v, int mo) { |
| 756 | return AtomicImpl<OpFetchNand>(mo: to_morder(mo), args: a, args: v); |
| 757 | } |
| 758 | |
| 759 | SANITIZER_INTERFACE_ATTRIBUTE |
| 760 | a32 __tsan_atomic32_fetch_nand(volatile a32 *a, a32 v, int mo) { |
| 761 | return AtomicImpl<OpFetchNand>(mo: to_morder(mo), args: a, args: v); |
| 762 | } |
| 763 | |
| 764 | SANITIZER_INTERFACE_ATTRIBUTE |
| 765 | a64 __tsan_atomic64_fetch_nand(volatile a64 *a, a64 v, int mo) { |
| 766 | return AtomicImpl<OpFetchNand>(mo: to_morder(mo), args: a, args: 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>(mo: to_morder(mo), args: a, args: 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>(mo: to_morder(mo), args: to_morder(mo: fmo), args: a, args: c, args: 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>(mo: to_morder(mo), args: to_morder(mo: fmo), args: a, args: c, args: 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>(mo: to_morder(mo), args: to_morder(mo: fmo), args: a, args: c, args: 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>(mo: to_morder(mo), args: to_morder(mo: fmo), args: a, args: c, args: 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>(mo: to_morder(mo), args: to_morder(mo: fmo), args: a, args: c, args: 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>(mo: to_morder(mo), args: to_morder(mo: fmo), args: a, args: c, args: 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>(mo: to_morder(mo), args: to_morder(mo: fmo), args: a, args: c, args: 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>(mo: to_morder(mo), args: to_morder(mo: fmo), args: a, args: c, args: 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>(mo: to_morder(mo), args: to_morder(mo: fmo), args: a, args: c, args: 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>(mo: to_morder(mo), args: to_morder(mo: fmo), args: a, args: c, args: 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>(mo: to_morder(mo), args: to_morder(mo: fmo), args: a, args: c, args: 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>(mo: to_morder(mo), args: to_morder(mo: fmo), args: a, args: c, args: 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>(mo: to_morder(mo), args: to_morder(mo: fmo), args: a, args: c, args: 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>(mo: to_morder(mo), args: to_morder(mo: fmo), args: a, args: c, args: 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>(mo: to_morder(mo), args: to_morder(mo: fmo), args: a, args: c, args: v); |
| 869 | } |
| 870 | # endif |
| 871 | |
| 872 | SANITIZER_INTERFACE_ATTRIBUTE |
| 873 | void __tsan_atomic_thread_fence(int mo) { |
| 874 | return AtomicImpl<OpFence>(mo: 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 | |