testAtomicOperations.cpp (13739B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 */ 4 /* This Source Code Form is subject to the terms of the Mozilla Public 5 * License, v. 2.0. If a copy of the MPL was not distributed with this 6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 7 8 #include "jit/AtomicOperations.h" 9 #include "jsapi-tests/tests.h" 10 #include "vm/ArrayBufferObject.h" 11 #include "vm/SharedMem.h" 12 #include "vm/Uint8Clamped.h" 13 #include "wasm/WasmFeatures.h" 14 15 using namespace js; 16 17 // Machinery to disguise pointer addresses to the C++ compiler -- quite possibly 18 // not thread-safe. 19 20 extern void setHiddenPointer(void* p); 21 extern void* getHiddenPointer(); 22 23 void* hidePointerValue(void* p) { 24 setHiddenPointer(p); 25 return getHiddenPointer(); 26 } 27 28 ////////////////////////////////////////////////////////////////////// 29 // 30 // Lock-freedom predicates 31 32 BEGIN_REUSABLE_TEST(testAtomicLockFree8) { 33 // isLockfree8() must not return true if there are no 8-byte atomics 34 35 CHECK(!jit::AtomicOperations::isLockfree8() || 36 jit::AtomicOperations::hasAtomic8()); 37 38 // We must have lock-free 8-byte atomics on every platform where we support 39 // wasm, but we don't care otherwise. 40 41 CHECK(!wasm::HasSupport(cx) || jit::AtomicOperations::isLockfree8()); 42 return true; 43 } 44 END_TEST(testAtomicLockFree8) 45 46 // The JS spec requires specific behavior for all but 1 and 2. 47 48 BEGIN_REUSABLE_TEST(testAtomicLockFreeJS) { 49 static_assert(jit::AtomicOperations::isLockfreeJS(1) == 50 true); // false is allowed by spec but not in SpiderMonkey 51 static_assert(jit::AtomicOperations::isLockfreeJS(2) == true); // ditto 52 static_assert(jit::AtomicOperations::isLockfreeJS(8) == true); // ditto 53 static_assert(jit::AtomicOperations::isLockfreeJS(3) == false); // required 54 static_assert(jit::AtomicOperations::isLockfreeJS(4) == true); // required 55 static_assert(jit::AtomicOperations::isLockfreeJS(5) == false); // required 56 static_assert(jit::AtomicOperations::isLockfreeJS(6) == false); // required 57 static_assert(jit::AtomicOperations::isLockfreeJS(7) == false); // required 58 return true; 59 } 60 END_TEST(testAtomicLockFreeJS) 61 62 ////////////////////////////////////////////////////////////////////// 63 // 64 // Fence 65 66 // This only tests that fenceSeqCst is defined and that it doesn't crash if we 67 // call it, but it has no return value and its effect is not observable here. 68 69 BEGIN_REUSABLE_TEST(testAtomicFence) { 70 jit::AtomicOperations::fenceSeqCst(); 71 return true; 72 } 73 END_TEST(testAtomicFence) 74 75 ////////////////////////////////////////////////////////////////////// 76 // 77 // Memory access primitives 78 79 // These tests for the atomic load and store primitives ascertain that the 80 // primitives are defined and that they load and store the values they should, 81 // but not that the primitives are actually atomic wrt to the memory subsystem. 82 83 // Memory for testing atomics. This must be aligned to the natural alignment of 84 // the type we're testing; for now, use 8-byte alignment for all. 85 86 alignas(8) static uint8_t atomicMem[8]; 87 alignas(8) static uint8_t atomicMem2[8]; 88 89 // T is the primitive type we're testing, and A and B are references to constant 90 // bindings holding values of that type. 91 // 92 // No bytes of A and B should be 0 or FF. A+B and A-B must not overflow. 93 94 #define ATOMIC_TESTS(T, A, B) \ 95 T* q = (T*)hidePointerValue((void*)atomicMem); \ 96 *q = A; \ 97 SharedMem<T*> p = \ 98 SharedMem<T*>::shared((T*)hidePointerValue((T*)atomicMem)); \ 99 CHECK(*q == A); \ 100 CHECK(jit::AtomicOperations::loadSeqCst(p) == A); \ 101 CHECK(*q == A); \ 102 jit::AtomicOperations::storeSeqCst(p, B); \ 103 CHECK(*q == B); \ 104 CHECK(jit::AtomicOperations::exchangeSeqCst(p, A) == B); \ 105 CHECK(*q == A); \ 106 CHECK(jit::AtomicOperations::compareExchangeSeqCst(p, (T)0, (T)1) == \ 107 A); /*failure*/ \ 108 CHECK(*q == A); \ 109 CHECK(jit::AtomicOperations::compareExchangeSeqCst(p, A, B) == \ 110 A); /*success*/ \ 111 CHECK(*q == B); \ 112 *q = A; \ 113 CHECK(jit::AtomicOperations::fetchAddSeqCst(p, B) == A); \ 114 CHECK(*q == A + B); \ 115 *q = A; \ 116 CHECK(jit::AtomicOperations::fetchSubSeqCst(p, B) == A); \ 117 CHECK(*q == A - B); \ 118 *q = A; \ 119 CHECK(jit::AtomicOperations::fetchAndSeqCst(p, B) == A); \ 120 CHECK(*q == (A & B)); \ 121 *q = A; \ 122 CHECK(jit::AtomicOperations::fetchOrSeqCst(p, B) == A); \ 123 CHECK(*q == (A | B)); \ 124 *q = A; \ 125 CHECK(jit::AtomicOperations::fetchXorSeqCst(p, B) == A); \ 126 CHECK(*q == (A ^ B)); \ 127 *q = A; \ 128 CHECK(jit::AtomicOperations::loadSafeWhenRacy(p) == A); \ 129 jit::AtomicOperations::storeSafeWhenRacy(p, B); \ 130 CHECK(*q == B); \ 131 T* q2 = (T*)hidePointerValue((void*)atomicMem2); \ 132 SharedMem<T*> p2 = \ 133 SharedMem<T*>::shared((T*)hidePointerValue((void*)atomicMem2)); \ 134 *q = A; \ 135 *q2 = B; \ 136 jit::AtomicOperations::memcpySafeWhenRacy(p2, p, sizeof(T)); \ 137 CHECK(*q2 == A); \ 138 *q = A; \ 139 *q2 = B; \ 140 jit::AtomicOperations::memcpySafeWhenRacy(p2, p.unwrap(), sizeof(T)); \ 141 CHECK(*q2 == A); \ 142 *q = A; \ 143 *q2 = B; \ 144 jit::AtomicOperations::memcpySafeWhenRacy(p2.unwrap(), p, sizeof(T)); \ 145 CHECK(*q2 == A); \ 146 *q = A; \ 147 *q2 = B; \ 148 jit::AtomicOperations::memmoveSafeWhenRacy(p2, p, sizeof(T)); \ 149 CHECK(*q2 == A); \ 150 *q = A; \ 151 *q2 = B; \ 152 jit::AtomicOperations::podCopySafeWhenRacy(p2, p, 1); \ 153 CHECK(*q2 == A); \ 154 *q = A; \ 155 *q2 = B; \ 156 jit::AtomicOperations::podMoveSafeWhenRacy(p2, p, 1); \ 157 CHECK(*q2 == A); \ 158 return true 159 160 BEGIN_REUSABLE_TEST(testAtomicOperationsU8) { 161 const uint8_t A = 0xab; 162 const uint8_t B = 0x37; 163 ATOMIC_TESTS(uint8_t, A, B); 164 } 165 END_TEST(testAtomicOperationsU8) 166 167 BEGIN_REUSABLE_TEST(testAtomicOperationsI8) { 168 const int8_t A = 0x3b; 169 const int8_t B = 0x27; 170 ATOMIC_TESTS(int8_t, A, B); 171 } 172 END_TEST(testAtomicOperationsI8) 173 174 BEGIN_REUSABLE_TEST(testAtomicOperationsU16) { 175 const uint16_t A = 0xabdc; 176 const uint16_t B = 0x3789; 177 ATOMIC_TESTS(uint16_t, A, B); 178 } 179 END_TEST(testAtomicOperationsU16) 180 181 BEGIN_REUSABLE_TEST(testAtomicOperationsI16) { 182 const int16_t A = 0x3bdc; 183 const int16_t B = 0x2737; 184 ATOMIC_TESTS(int16_t, A, B); 185 } 186 END_TEST(testAtomicOperationsI16) 187 188 BEGIN_REUSABLE_TEST(testAtomicOperationsU32) { 189 const uint32_t A = 0xabdc0588; 190 const uint32_t B = 0x37891942; 191 ATOMIC_TESTS(uint32_t, A, B); 192 } 193 END_TEST(testAtomicOperationsU32) 194 195 BEGIN_REUSABLE_TEST(testAtomicOperationsI32) { 196 const int32_t A = 0x3bdc0588; 197 const int32_t B = 0x27371843; 198 ATOMIC_TESTS(int32_t, A, B); 199 } 200 END_TEST(testAtomicOperationsI32) 201 202 BEGIN_REUSABLE_TEST(testAtomicOperationsU64) { 203 if (!jit::AtomicOperations::hasAtomic8()) { 204 return true; 205 } 206 207 const uint64_t A(0x9aadf00ddeadbeef); 208 const uint64_t B(0x4eedbead1337f001); 209 ATOMIC_TESTS(uint64_t, A, B); 210 } 211 END_TEST(testAtomicOperationsU64) 212 213 BEGIN_REUSABLE_TEST(testAtomicOperationsI64) { 214 if (!jit::AtomicOperations::hasAtomic8()) { 215 return true; 216 } 217 218 const int64_t A(0x2aadf00ddeadbeef); 219 const int64_t B(0x4eedbead1337f001); 220 ATOMIC_TESTS(int64_t, A, B); 221 } 222 END_TEST(testAtomicOperationsI64) 223 224 // T is the primitive float type we're testing, and A and B are references to 225 // constant bindings holding values of that type. 226 // 227 // Stay away from 0, NaN, infinities, and denormals. 228 229 #define ATOMIC_FLOAT_TESTS(T, A, B) \ 230 T* q = (T*)hidePointerValue((void*)atomicMem); \ 231 *q = A; \ 232 SharedMem<T*> p = \ 233 SharedMem<T*>::shared((T*)hidePointerValue((T*)atomicMem)); \ 234 CHECK(*q == A); \ 235 CHECK(jit::AtomicOperations::loadSafeWhenRacy(p) == A); \ 236 jit::AtomicOperations::storeSafeWhenRacy(p, B); \ 237 CHECK(*q == B); \ 238 T* q2 = (T*)hidePointerValue((void*)atomicMem2); \ 239 SharedMem<T*> p2 = \ 240 SharedMem<T*>::shared((T*)hidePointerValue((void*)atomicMem2)); \ 241 *q = A; \ 242 *q2 = B; \ 243 jit::AtomicOperations::memcpySafeWhenRacy(p2, p, sizeof(T)); \ 244 CHECK(*q2 == A); \ 245 *q = A; \ 246 *q2 = B; \ 247 jit::AtomicOperations::memcpySafeWhenRacy(p2, p.unwrap(), sizeof(T)); \ 248 CHECK(*q2 == A); \ 249 *q = A; \ 250 *q2 = B; \ 251 jit::AtomicOperations::memcpySafeWhenRacy(p2.unwrap(), p, sizeof(T)); \ 252 CHECK(*q2 == A); \ 253 *q = A; \ 254 *q2 = B; \ 255 jit::AtomicOperations::memmoveSafeWhenRacy(p2, p, sizeof(T)); \ 256 CHECK(*q2 == A); \ 257 *q = A; \ 258 *q2 = B; \ 259 jit::AtomicOperations::podCopySafeWhenRacy(p2, p, 1); \ 260 CHECK(*q2 == A); \ 261 *q = A; \ 262 *q2 = B; \ 263 jit::AtomicOperations::podMoveSafeWhenRacy(p2, p, 1); \ 264 CHECK(*q2 == A); \ 265 return true 266 267 BEGIN_REUSABLE_TEST(testAtomicOperationsF32) { 268 const float A(123.25); 269 const float B(-987.75); 270 ATOMIC_FLOAT_TESTS(float, A, B); 271 } 272 END_TEST(testAtomicOperationsF32) 273 274 BEGIN_REUSABLE_TEST(testAtomicOperationsF64) { 275 const double A(123.25); 276 const double B(-987.75); 277 ATOMIC_FLOAT_TESTS(double, A, B); 278 } 279 END_TEST(testAtomicOperationsF64) 280 281 #define ATOMIC_CLAMPED_TESTS(T, A, B) \ 282 T* q = (T*)hidePointerValue((void*)atomicMem); \ 283 *q = A; \ 284 SharedMem<T*> p = \ 285 SharedMem<T*>::shared((T*)hidePointerValue((T*)atomicMem)); \ 286 CHECK(*q == A); \ 287 CHECK(jit::AtomicOperations::loadSafeWhenRacy(p) == A); \ 288 jit::AtomicOperations::storeSafeWhenRacy(p, B); \ 289 CHECK(*q == B); \ 290 return true 291 292 BEGIN_REUSABLE_TEST(testAtomicOperationsU8Clamped) { 293 const uint8_clamped A(0xab); 294 const uint8_clamped B(0x37); 295 ATOMIC_CLAMPED_TESTS(uint8_clamped, A, B); 296 } 297 END_TEST(testAtomicOperationsU8Clamped) 298 299 #undef ATOMIC_TESTS 300 #undef ATOMIC_FLOAT_TESTS 301 #undef ATOMIC_CLAMPED_TESTS