tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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