summaryrefslogtreecommitdiff
path: root/lib/sanitizer_common/sanitizer_atomic_clang_other.h
blob: d2acc311bf7da5d531631df766c94b23f8de3641 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//===-- sanitizer_atomic_clang_other.h --------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
// Not intended for direct inclusion. Include sanitizer_atomic.h.
//
//===----------------------------------------------------------------------===//

#ifndef SANITIZER_ATOMIC_CLANG_OTHER_H
#define SANITIZER_ATOMIC_CLANG_OTHER_H

namespace __sanitizer {

// MIPS32 does not support atomic > 4 bytes. To address this lack of
// functionality, the sanitizer library provides helper methods which use an
// internal spin lock mechanism to emulate atomic oprations when the size is
// 8 bytes.
#if defined(_MIPS_SIM) && _MIPS_SIM == _ABIO32
static void __spin_lock(volatile int *lock) {
  while (__sync_lock_test_and_set(lock, 1))
    while (*lock) {
    }
}

static void __spin_unlock(volatile int *lock) { __sync_lock_release(lock); }


// Make sure the lock is on its own cache line to prevent false sharing.
// Put it inside a struct that is aligned and padded to the typical MIPS
// cacheline which is 32 bytes.
static struct {
  int lock;
  char pad[32 - sizeof(int)];
} __attribute__((aligned(32))) lock = {0};

template <class T>
T __mips_sync_fetch_and_add(volatile T *ptr, T val) {
  T ret;

  __spin_lock(&lock.lock);

  ret = *ptr;
  *ptr = ret + val;

  __spin_unlock(&lock.lock);

  return ret;
}

template <class T>
T __mips_sync_val_compare_and_swap(volatile T *ptr, T oldval, T newval) {
  T ret;
  __spin_lock(&lock.lock);

  ret = *ptr;
  if (ret == oldval) *ptr = newval;

  __spin_unlock(&lock.lock);

  return ret;
}
#endif

INLINE void proc_yield(int cnt) {
  __asm__ __volatile__("" ::: "memory");
}

template<typename T>
INLINE typename T::Type atomic_load(
    const volatile T *a, memory_order mo) {
  DCHECK(mo & (memory_order_relaxed | memory_order_consume
      | memory_order_acquire | memory_order_seq_cst));
  DCHECK(!((uptr)a % sizeof(*a)));
  typename T::Type v;

  if (sizeof(*a) < 8 || sizeof(void*) == 8) {
    // Assume that aligned loads are atomic.
    if (mo == memory_order_relaxed) {
      v = a->val_dont_use;
    } else if (mo == memory_order_consume) {
      // Assume that processor respects data dependencies
      // (and that compiler won't break them).
      __asm__ __volatile__("" ::: "memory");
      v = a->val_dont_use;
      __asm__ __volatile__("" ::: "memory");
    } else if (mo == memory_order_acquire) {
      __asm__ __volatile__("" ::: "memory");
      v = a->val_dont_use;
      __sync_synchronize();
    } else {  // seq_cst
      // E.g. on POWER we need a hw fence even before the store.
      __sync_synchronize();
      v = a->val_dont_use;
      __sync_synchronize();
    }
  } else {
    // 64-bit load on 32-bit platform.
    // Gross, but simple and reliable.
    // Assume that it is not in read-only memory.
#if defined(_MIPS_SIM) && _MIPS_SIM == _ABIO32
    typename T::Type volatile *val_ptr =
        const_cast<typename T::Type volatile *>(&a->val_dont_use);
    v = __mips_sync_fetch_and_add<u64>(
        reinterpret_cast<u64 volatile *>(val_ptr), 0);
#else
    v = __sync_fetch_and_add(
        const_cast<typename T::Type volatile *>(&a->val_dont_use), 0);
#endif
  }
  return v;
}

template<typename T>
INLINE void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
  DCHECK(mo & (memory_order_relaxed | memory_order_release
      | memory_order_seq_cst));
  DCHECK(!((uptr)a % sizeof(*a)));

  if (sizeof(*a) < 8 || sizeof(void*) == 8) {
    // Assume that aligned loads are atomic.
    if (mo == memory_order_relaxed) {
      a->val_dont_use = v;
    } else if (mo == memory_order_release) {
      __sync_synchronize();
      a->val_dont_use = v;
      __asm__ __volatile__("" ::: "memory");
    } else {  // seq_cst
      __sync_synchronize();
      a->val_dont_use = v;
      __sync_synchronize();
    }
  } else {
    // 64-bit store on 32-bit platform.
    // Gross, but simple and reliable.
    typename T::Type cmp = a->val_dont_use;
    typename T::Type cur;
    for (;;) {
#if defined(_MIPS_SIM) && _MIPS_SIM == _ABIO32
      typename T::Type volatile *val_ptr =
          const_cast<typename T::Type volatile *>(&a->val_dont_use);
      cur = __mips_sync_val_compare_and_swap<u64>(
          reinterpret_cast<u64 volatile *>(val_ptr), (u64)cmp, (u64)v);
#else
      cur = __sync_val_compare_and_swap(&a->val_dont_use, cmp, v);
#endif
      if (cmp == v)
        break;
      cmp = cur;
    }
  }
}

}  // namespace __sanitizer

#endif  // #ifndef SANITIZER_ATOMIC_CLANG_OTHER_H