/* ThreadSanitizer * Copyright (c) 2011, Google Inc. All rights reserved. * Author: Dmitry Vyukov (dvyukov) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "ts_util.h" #include "ts_atomic_int.h" char const* tsan_atomic_to_str(tsan_memory_order mo) { switch (mo) { case tsan_memory_order_invalid: return "invalid"; case tsan_memory_order_natomic: return "natomic"; case tsan_memory_order_relaxed: return "relaxed"; case tsan_memory_order_consume: return "consume"; case tsan_memory_order_acquire: return "acquire"; case tsan_memory_order_release: return "release"; case tsan_memory_order_acq_rel: return "acq_rel"; case tsan_memory_order_seq_cst: return "seq_cst"; default: return "-------"; } } char const* tsan_atomic_to_str(tsan_atomic_op op) { switch (op) { case tsan_atomic_op_invalid: return "invalid"; case tsan_atomic_op_fence: return "fence"; case tsan_atomic_op_load: return "load"; case tsan_atomic_op_store: return "store"; case tsan_atomic_op_exchange: return "exchange"; case tsan_atomic_op_fetch_add: return "fetch_add"; case tsan_atomic_op_fetch_sub: return "fetch_sub"; case tsan_atomic_op_fetch_and: return "fetch_and"; case tsan_atomic_op_fetch_xor: return "fetch_xor"; case tsan_atomic_op_fetch_or: return "fetch_or"; case tsan_atomic_op_compare_exchange_weak: return "compare_exchange_weak"; case tsan_atomic_op_compare_exchange_strong: return "compare_exchange_strong"; default: return "---"; } } bool tsan_atomic_is_acquire(tsan_memory_order mo) { return !!(mo & (tsan_memory_order_consume | tsan_memory_order_acquire | tsan_memory_order_acq_rel | tsan_memory_order_seq_cst)); } bool tsan_atomic_is_release(tsan_memory_order mo) { return !!(mo & (tsan_memory_order_release | tsan_memory_order_acq_rel | tsan_memory_order_seq_cst)); } bool tsan_atomic_is_rmw(tsan_atomic_op op) { return !!(op & (tsan_atomic_op_exchange | tsan_atomic_op_fetch_add | tsan_atomic_op_fetch_sub | tsan_atomic_op_fetch_and | tsan_atomic_op_fetch_xor | tsan_atomic_op_fetch_or | tsan_atomic_op_compare_exchange_weak | tsan_atomic_op_compare_exchange_strong)); } void tsan_atomic_verify(tsan_atomic_op op, tsan_memory_order mo, tsan_memory_order fail_mo, size_t size, void volatile* a) { CHECK(size == 1 || size == 2 || size == 4 || size == 8); CHECK((((uintptr_t)a) % size) == 0); if (op == tsan_atomic_op_load) { CHECK(mo & (tsan_memory_order_natomic | tsan_memory_order_relaxed | tsan_memory_order_consume | tsan_memory_order_acquire | tsan_memory_order_seq_cst)); } else if (op == tsan_atomic_op_store) { CHECK(mo & (tsan_memory_order_natomic | tsan_memory_order_relaxed | tsan_memory_order_release | tsan_memory_order_seq_cst)); } else if (op == tsan_atomic_op_fence) { CHECK(mo & (tsan_memory_order_consume | tsan_memory_order_acquire | tsan_memory_order_release | tsan_memory_order_acq_rel | tsan_memory_order_seq_cst)); } else if (op & (tsan_atomic_op_exchange | tsan_atomic_op_fetch_add | tsan_atomic_op_fetch_sub | tsan_atomic_op_fetch_and | tsan_atomic_op_fetch_xor | tsan_atomic_op_fetch_or | tsan_atomic_op_compare_exchange_weak | tsan_atomic_op_compare_exchange_strong)) { CHECK(mo & (tsan_memory_order_relaxed | tsan_memory_order_consume | tsan_memory_order_acquire | tsan_memory_order_release | tsan_memory_order_acq_rel | tsan_memory_order_seq_cst)); } else { CHECK("unknown tsan_atomic_op" == 0); } } #if defined(__i386__) # define __x86__ #elif defined(__x86_64__) # define __x86__ #endif #if defined(__GNUC__) && defined(__x86_64__) uint64_t tsan_atomic_do_op(tsan_atomic_op op, tsan_memory_order mo, tsan_memory_order fail_mo, size_t size, void volatile* a, uint64_t v, uint64_t cmp, uint64_t* newv, uint64_t* prev) { *newv = v; if (op != tsan_atomic_op_fence) { if (size == 1) { *prev = *(uint8_t volatile*)a; } else if (size == 2) { *prev = *(uint16_t volatile*)a; } else if (size == 4) { *prev = *(uint32_t volatile*)a; } else if (size == 8) { *prev = *(uint64_t volatile*)a; } } if (op == tsan_atomic_op_load) { return *prev; } else if (op == tsan_atomic_op_store) { if (mo == tsan_memory_order_seq_cst) { if (size == 1) { uint8_t vv = (uint8_t)v; __asm__ __volatile__ ("xchgb %1, %0" : "=r" (vv) : "m" (*(uint8_t volatile*)a), "0" (vv)); *prev = vv; } else if (size == 2) { uint16_t vv = (uint16_t)v; __asm__ __volatile__ ("xchgw %1, %0" : "=r" (vv) : "m" (*(uint16_t volatile*)a), "0" (vv)); *prev = vv; } else if (size == 4) { uint32_t vv = (uint32_t)v; __asm__ __volatile__ ("xchgl %1, %0" : "=r" (vv) : "m" (*(uint32_t volatile*)a), "0" (vv)); *prev = vv; } else if (size == 8) { #ifdef __x86_64__ uint64_t vv = (uint64_t)v; __asm__ __volatile__ ("xchgq %1, %0" : "=r" (vv) : "m" (*(uint64_t volatile*)a), "0" (vv)); *prev = vv; #else #error "IMPLEMENT ME, PLZ" //uint64_t cmp = *a; //!!!while (!tsan_atomic64_compare_exchange_strong(a, &cmp, v, mo, mo)) //!!! {} #endif } } else { if (size == 1) { *(uint8_t volatile*)a = v; } else if (size == 2) { *(uint16_t volatile*)a = v; } else if (size == 4) { *(uint32_t volatile*)a = v; } else if (size == 8) { *(uint64_t volatile*)a = v; } } return 0; } else if (op == tsan_atomic_op_exchange) { if (size == 1) { uint8_t vv = (uint8_t)v; __asm__ __volatile__ ("xchgb %1, %0" : "=r" (vv) : "m" (*(uint8_t volatile*)a), "0" (vv)); *prev = vv; return vv; } else if (size == 2) { uint16_t vv = (uint16_t)v; __asm__ __volatile__ ("xchgw %1, %0" : "=r" (vv) : "m" (*(uint16_t volatile*)a), "0" (vv)); *prev = vv; return vv; } else if (size == 4) { uint32_t vv = (uint32_t)v; __asm__ __volatile__ ("xchgl %1, %0" : "=r" (vv) : "m" (*(uint32_t volatile*)a), "0" (vv)); *prev = vv; return vv; } else if (size == 8) { # ifdef __x86_64__ uint64_t vv = (uint64_t)v; __asm__ __volatile__ ("xchgq %1, %0" : "=r" (vv) : "m" (*(uint64_t volatile*)a), "0" (vv)); *prev = vv; return vv; #else #error "IMPLEMENT ME, PLZ" //uint64_t cmp = *a; //while (!tsan_atomic64_compare_exchange_strong(a, &cmp, v, mo, mo)) // {} //return cmp; #endif } } else if (op == tsan_atomic_op_fetch_add) { if (size == 1) { uint8_t prevv = __sync_fetch_and_add((uint8_t volatile*)a, (uint8_t)v); *prev = prevv; *newv = prevv + (uint8_t)v; return prevv; } else if (size == 2) { uint16_t prevv = __sync_fetch_and_add( (uint16_t volatile*)a, (uint16_t)v); *prev = prevv; *newv = prevv + (uint16_t)v; return prevv; } else if (size == 4) { uint32_t prevv = __sync_fetch_and_add( (uint32_t volatile*)a, (uint32_t)v); *prev = prevv; *newv = prevv + (uint32_t)v; return prevv; } else if (size == 8) { uint64_t prevv = __sync_fetch_and_add( (uint64_t volatile*)a, (uint64_t)v); *prev = prevv; *newv = prevv + v; return prevv; } } else if (op == tsan_atomic_op_fetch_sub) { if (size == 1) { uint8_t prevv = __sync_fetch_and_sub( (uint8_t volatile*)a, (uint8_t)v); *prev = prevv; *newv = prevv - (uint8_t)v; return prevv; } else if (size == 2) { uint16_t prevv = __sync_fetch_and_sub( (uint16_t volatile*)a, (uint16_t)v); *prev = prevv; *newv = prevv - (uint16_t)v; return prevv; } else if (size == 4) { uint32_t prevv = __sync_fetch_and_sub( (uint32_t volatile*)a, (uint32_t)v); *prev = prevv; *newv = prevv - (uint32_t)v; return prevv; } else if (size == 8) { uint64_t prevv = __sync_fetch_and_sub( (uint64_t volatile*)a, (uint64_t)v); *prev = prevv; *newv = prevv - v; return prevv; } } else if (op == tsan_atomic_op_fetch_and) { if (size == 1) { uint8_t prevv = __sync_fetch_and_and( (uint8_t volatile*)a, (uint8_t)v); *prev = prevv; *newv = prevv & (uint8_t)v; return prevv; } else if (size == 2) { uint16_t prevv = __sync_fetch_and_and( (uint16_t volatile*)a, (uint16_t)v); *prev = prevv; *newv = prevv & (uint16_t)v; return prevv; } else if (size == 4) { uint32_t prevv = __sync_fetch_and_and( (uint32_t volatile*)a, (uint32_t)v); *prev = prevv; *newv = prevv & (uint32_t)v; return prevv; } else if (size == 8) { uint64_t prevv = __sync_fetch_and_and( (uint64_t volatile*)a, (uint64_t)v); *prev = prevv; *newv = prevv & v; return prevv; } } else if (op == tsan_atomic_op_fetch_xor) { if (size == 1) { uint8_t prevv = __sync_fetch_and_xor( (uint8_t volatile*)a, (uint8_t)v); *prev = prevv; *newv = prevv ^ (uint8_t)v; return prevv; } else if (size == 2) { uint16_t prevv = __sync_fetch_and_xor( (uint16_t volatile*)a, (uint16_t)v); *prev = prevv; *newv = prevv ^ (uint16_t)v; return prevv; } else if (size == 4) { uint32_t prevv = __sync_fetch_and_xor( (uint32_t volatile*)a, (uint32_t)v); *prev = prevv; *newv = prevv ^ (uint32_t)v; return prevv; } else if (size == 8) { uint64_t prevv = __sync_fetch_and_xor( (uint64_t volatile*)a, (uint64_t)v); *prev = prevv; *newv = prevv ^ v; return prevv; } } else if (op == tsan_atomic_op_fetch_or) { if (size == 1) { uint8_t prevv = __sync_fetch_and_or( (uint8_t volatile*)a, (uint8_t)v); *prev = prevv; *newv = prevv | (uint8_t)v; return prevv; } else if (size == 2) { uint16_t prevv = __sync_fetch_and_or( (uint16_t volatile*)a, (uint16_t)v); *prev = prevv; *newv = prevv | (uint16_t)v; return prevv; } else if (size == 4) { uint32_t prevv = __sync_fetch_and_or( (uint32_t volatile*)a, (uint32_t)v); *prev = prevv; *newv = prevv | (uint32_t)v; return prevv; } else if (size == 8) { uint64_t prevv = __sync_fetch_and_or( (uint64_t volatile*)a, (uint64_t)v); *prev = prevv; *newv = prevv | v; return prevv; } } else if (op == tsan_atomic_op_compare_exchange_strong || op == tsan_atomic_op_compare_exchange_weak) { uint64_t prevv = 0; if (size == 1) { prevv = __sync_val_compare_and_swap((uint8_t volatile*)a, cmp, v); } else if (size == 2) { prevv = __sync_val_compare_and_swap((uint16_t volatile*)a, cmp, v); } else if (size == 4) { prevv = __sync_val_compare_and_swap((uint32_t volatile*)a, cmp, v); } else if (size == 8) { prevv = __sync_val_compare_and_swap((uint64_t volatile*)a, cmp, v); } *prev = prevv; return prevv; } else if (op == tsan_atomic_op_fence) { if (mo == tsan_memory_order_seq_cst) __sync_synchronize(); return 0; } CHECK("unknown atomic operation" == 0); return 0; } #else uint64_t tsan_atomic_do_op(tsan_atomic_op op, tsan_memory_order mo, tsan_memory_order fail_mo, size_t size, void volatile* a, uint64_t v, uint64_t cmp, uint64_t* newv, uint64_t* prev) { CHECK(!"IMPLEMENTED" == 0); return 0; } #endif