1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 /* For overall documentation, see jit/AtomicOperations.h */
8 
9 #ifndef jit_shared_AtomicOperations_x86_shared_gcc_h
10 #define jit_shared_AtomicOperations_x86_shared_gcc_h
11 
12 #include "mozilla/Assertions.h"
13 #include "mozilla/Types.h"
14 
15 #include "vm/ArrayBufferObject.h"
16 
17 #if !defined(__clang__) && !defined(__GNUC__)
18 #error "This file only for gcc-compatible compilers"
19 #endif
20 
21 // Lock-freedom and access-atomicity on x86 and x64.
22 //
23 // In general, aligned accesses are access-atomic up to 8 bytes ever since the
24 // Pentium; Firefox requires SSE2, which was introduced with the Pentium 4, so
25 // we may assume access-atomicity.
26 //
27 // Four-byte accesses and smaller are simple:
28 //  - Use MOV{B,W,L} to load and store.  Stores require a post-fence
29 //    for sequential consistency as defined by the JS spec.  The fence
30 //    can be MFENCE, or the store can be implemented using XCHG.
31 //  - For compareExchange use LOCK; CMPXCGH{B,W,L}
32 //  - For exchange, use XCHG{B,W,L}
33 //  - For add, etc use LOCK; ADD{B,W,L} etc
34 //
35 // Eight-byte accesses are easy on x64:
36 //  - Use MOVQ to load and store (again with a fence for the store)
37 //  - For compareExchange, we use CMPXCHGQ
38 //  - For exchange, we use XCHGQ
39 //  - For add, etc use LOCK; ADDQ etc
40 //
41 // Eight-byte accesses are harder on x86:
42 //  - For load, use a sequence of MOVL + CMPXCHG8B
43 //  - For store, use a sequence of MOVL + a CMPXCGH8B in a loop,
44 //    no additional fence required
45 //  - For exchange, do as for store
46 //  - For add, etc do as for store
47 
48 // Firefox requires gcc > 4.8, so we will always have the __atomic intrinsics
49 // added for use in C++11 <atomic>.
50 //
51 // Note that using these intrinsics for most operations is not correct: the code
52 // has undefined behavior.  The gcc documentation states that the compiler
53 // assumes the code is race free.  This supposedly means C++ will allow some
54 // instruction reorderings (effectively those allowed by TSO) even for seq_cst
55 // ordered operations, but these reorderings are not allowed by JS.  To do
56 // better we will end up with inline assembler or JIT-generated code.
57 
58 // For now, we require that the C++ compiler's atomics are lock free, even for
59 // 64-bit accesses.
60 
61 // When compiling with Clang on 32-bit linux it will be necessary to link with
62 // -latomic to get the proper 64-bit intrinsics.
63 
hasAtomic8()64 inline bool js::jit::AtomicOperations::hasAtomic8() { return true; }
65 
isLockfree8()66 inline bool js::jit::AtomicOperations::isLockfree8() {
67   MOZ_ASSERT(__atomic_always_lock_free(sizeof(int8_t), 0));
68   MOZ_ASSERT(__atomic_always_lock_free(sizeof(int16_t), 0));
69   MOZ_ASSERT(__atomic_always_lock_free(sizeof(int32_t), 0));
70   MOZ_ASSERT(__atomic_always_lock_free(sizeof(int64_t), 0));
71   return true;
72 }
73 
fenceSeqCst()74 inline void js::jit::AtomicOperations::fenceSeqCst() {
75   __atomic_thread_fence(__ATOMIC_SEQ_CST);
76 }
77 
78 template <typename T>
loadSeqCst(T * addr)79 inline T js::jit::AtomicOperations::loadSeqCst(T* addr) {
80   MOZ_ASSERT(tier1Constraints(addr));
81   T v;
82   __atomic_load(addr, &v, __ATOMIC_SEQ_CST);
83   return v;
84 }
85 
86 template <typename T>
storeSeqCst(T * addr,T val)87 inline void js::jit::AtomicOperations::storeSeqCst(T* addr, T val) {
88   MOZ_ASSERT(tier1Constraints(addr));
89   __atomic_store(addr, &val, __ATOMIC_SEQ_CST);
90 }
91 
92 template <typename T>
exchangeSeqCst(T * addr,T val)93 inline T js::jit::AtomicOperations::exchangeSeqCst(T* addr, T val) {
94   MOZ_ASSERT(tier1Constraints(addr));
95   T v;
96   __atomic_exchange(addr, &val, &v, __ATOMIC_SEQ_CST);
97   return v;
98 }
99 
100 template <typename T>
compareExchangeSeqCst(T * addr,T oldval,T newval)101 inline T js::jit::AtomicOperations::compareExchangeSeqCst(T* addr, T oldval,
102                                                           T newval) {
103   MOZ_ASSERT(tier1Constraints(addr));
104   __atomic_compare_exchange(addr, &oldval, &newval, false, __ATOMIC_SEQ_CST,
105                             __ATOMIC_SEQ_CST);
106   return oldval;
107 }
108 
109 template <typename T>
fetchAddSeqCst(T * addr,T val)110 inline T js::jit::AtomicOperations::fetchAddSeqCst(T* addr, T val) {
111   MOZ_ASSERT(tier1Constraints(addr));
112   return __atomic_fetch_add(addr, val, __ATOMIC_SEQ_CST);
113 }
114 
115 template <typename T>
fetchSubSeqCst(T * addr,T val)116 inline T js::jit::AtomicOperations::fetchSubSeqCst(T* addr, T val) {
117   MOZ_ASSERT(tier1Constraints(addr));
118   return __atomic_fetch_sub(addr, val, __ATOMIC_SEQ_CST);
119 }
120 
121 template <typename T>
fetchAndSeqCst(T * addr,T val)122 inline T js::jit::AtomicOperations::fetchAndSeqCst(T* addr, T val) {
123   MOZ_ASSERT(tier1Constraints(addr));
124   return __atomic_fetch_and(addr, val, __ATOMIC_SEQ_CST);
125 }
126 
127 template <typename T>
fetchOrSeqCst(T * addr,T val)128 inline T js::jit::AtomicOperations::fetchOrSeqCst(T* addr, T val) {
129   MOZ_ASSERT(tier1Constraints(addr));
130   return __atomic_fetch_or(addr, val, __ATOMIC_SEQ_CST);
131 }
132 
133 template <typename T>
fetchXorSeqCst(T * addr,T val)134 inline T js::jit::AtomicOperations::fetchXorSeqCst(T* addr, T val) {
135   MOZ_ASSERT(tier1Constraints(addr));
136   return __atomic_fetch_xor(addr, val, __ATOMIC_SEQ_CST);
137 }
138 
139 template <typename T>
loadSafeWhenRacy(T * addr)140 inline T js::jit::AtomicOperations::loadSafeWhenRacy(T* addr) {
141   MOZ_ASSERT(tier1Constraints(addr));
142   T v;
143   __atomic_load(addr, &v, __ATOMIC_RELAXED);
144   return v;
145 }
146 
147 namespace js {
148 namespace jit {
149 
150 #define GCC_RACYLOADOP(T)                                         \
151   template <>                                                     \
152   inline T js::jit::AtomicOperations::loadSafeWhenRacy(T* addr) { \
153     return *addr;                                                 \
154   }
155 
156 // On 32-bit platforms, loadSafeWhenRacy need not be access-atomic for 64-bit
157 // data, so just use regular accesses instead of the expensive __atomic_load
158 // solution which must use CMPXCHG8B.
159 #ifndef JS_64BIT
160 GCC_RACYLOADOP(int64_t)
GCC_RACYLOADOP(uint64_t)161 GCC_RACYLOADOP(uint64_t)
162 #endif
163 
164 // Float and double accesses are not access-atomic.
165 GCC_RACYLOADOP(float)
166 GCC_RACYLOADOP(double)
167 
168 // Clang requires a specialization for uint8_clamped.
169 template <>
170 inline uint8_clamped js::jit::AtomicOperations::loadSafeWhenRacy(
171     uint8_clamped* addr) {
172   uint8_t v;
173   __atomic_load(&addr->val, &v, __ATOMIC_RELAXED);
174   return uint8_clamped(v);
175 }
176 
177 #undef GCC_RACYLOADOP
178 }
179 }  // namespace js
180 
181 template <typename T>
storeSafeWhenRacy(T * addr,T val)182 inline void js::jit::AtomicOperations::storeSafeWhenRacy(T* addr, T val) {
183   MOZ_ASSERT(tier1Constraints(addr));
184   __atomic_store(addr, &val, __ATOMIC_RELAXED);
185 }
186 
187 namespace js {
188 namespace jit {
189 
190 #define GCC_RACYSTOREOP(T)                                                   \
191   template <>                                                                \
192   inline void js::jit::AtomicOperations::storeSafeWhenRacy(T* addr, T val) { \
193     *addr = val;                                                             \
194   }
195 
196 // On 32-bit platforms, storeSafeWhenRacy need not be access-atomic for 64-bit
197 // data, so just use regular accesses instead of the expensive __atomic_store
198 // solution which must use CMPXCHG8B.
199 #ifndef JS_64BIT
200 GCC_RACYSTOREOP(int64_t)
GCC_RACYSTOREOP(uint64_t)201 GCC_RACYSTOREOP(uint64_t)
202 #endif
203 
204 // Float and double accesses are not access-atomic.
205 GCC_RACYSTOREOP(float)
206 GCC_RACYSTOREOP(double)
207 
208 // Clang requires a specialization for uint8_clamped.
209 template <>
210 inline void js::jit::AtomicOperations::storeSafeWhenRacy(uint8_clamped* addr,
211                                                          uint8_clamped val) {
212   __atomic_store(&addr->val, &val.val, __ATOMIC_RELAXED);
213 }
214 
215 #undef GCC_RACYSTOREOP
216 }
217 }  // namespace js
218 
memcpySafeWhenRacy(void * dest,const void * src,size_t nbytes)219 inline void js::jit::AtomicOperations::memcpySafeWhenRacy(void* dest,
220                                                           const void* src,
221                                                           size_t nbytes) {
222   MOZ_ASSERT(!((char*)dest <= (char*)src && (char*)src < (char*)dest + nbytes));
223   MOZ_ASSERT(!((char*)src <= (char*)dest && (char*)dest < (char*)src + nbytes));
224   ::memcpy(dest, src, nbytes);
225 }
226 
memmoveSafeWhenRacy(void * dest,const void * src,size_t nbytes)227 inline void js::jit::AtomicOperations::memmoveSafeWhenRacy(void* dest,
228                                                            const void* src,
229                                                            size_t nbytes) {
230   ::memmove(dest, src, nbytes);
231 }
232 
233 #endif  // jit_shared_AtomicOperations_x86_shared_gcc_h
234