forked from mirrors/gecko-dev
Currently, MOZ_ALIGNED_DECL uses the order (_type, _align) for its parameters. However, this order makes the code less readable when _type is a larger object like a struct because the value for _align would be at the end of the struct definition. By swapping the order of _type and _align, the alignment value will always be next to the type name, regardless how far the definition of _type extends. Differential Revision: https://phabricator.services.mozilla.com/D77288
303 lines
13 KiB
C++
303 lines
13 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
|
*/
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "mozilla/Alignment.h"
|
|
#include "mozilla/Assertions.h"
|
|
|
|
#include "jit/AtomicOperations.h"
|
|
#include "jsapi-tests/tests.h"
|
|
#include "vm/ArrayBufferObject.h"
|
|
#include "vm/SharedMem.h"
|
|
#include "wasm/WasmJS.h"
|
|
|
|
using namespace js;
|
|
|
|
// Machinery to disguise pointer addresses to the C++ compiler -- quite possibly
|
|
// not thread-safe.
|
|
|
|
extern void setHiddenPointer(void* p);
|
|
extern void* getHiddenPointer();
|
|
|
|
void* hidePointerValue(void* p) {
|
|
setHiddenPointer(p);
|
|
return getHiddenPointer();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Lock-freedom predicates
|
|
|
|
BEGIN_TEST(testAtomicLockFree8) {
|
|
// isLockfree8() must not return true if there are no 8-byte atomics
|
|
|
|
CHECK(!jit::AtomicOperations::isLockfree8() ||
|
|
jit::AtomicOperations::hasAtomic8());
|
|
|
|
// We must have lock-free 8-byte atomics on every platform where we support
|
|
// wasm, but we don't care otherwise.
|
|
|
|
CHECK(!wasm::HasSupport(cx) || jit::AtomicOperations::isLockfree8());
|
|
return true;
|
|
}
|
|
END_TEST(testAtomicLockFree8)
|
|
|
|
// The JS spec requires specific behavior for all but 1 and 2.
|
|
|
|
BEGIN_TEST(testAtomicLockFreeJS) {
|
|
CHECK(jit::AtomicOperations::isLockfreeJS(1) ==
|
|
true); // false is allowed by spec but not in SpiderMonkey
|
|
CHECK(jit::AtomicOperations::isLockfreeJS(2) == true); // ditto
|
|
CHECK(jit::AtomicOperations::isLockfreeJS(3) == false); // required
|
|
CHECK(jit::AtomicOperations::isLockfreeJS(4) == true); // required
|
|
CHECK(jit::AtomicOperations::isLockfreeJS(5) == false); // required
|
|
CHECK(jit::AtomicOperations::isLockfreeJS(6) == false); // required
|
|
CHECK(jit::AtomicOperations::isLockfreeJS(7) == false); // required
|
|
CHECK(jit::AtomicOperations::isLockfreeJS(8) == false); // required
|
|
return true;
|
|
}
|
|
END_TEST(testAtomicLockFreeJS)
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Fence
|
|
|
|
// This only tests that fenceSeqCst is defined and that it doesn't crash if we
|
|
// call it, but it has no return value and its effect is not observable here.
|
|
|
|
BEGIN_TEST(testAtomicFence) {
|
|
jit::AtomicOperations::fenceSeqCst();
|
|
return true;
|
|
}
|
|
END_TEST(testAtomicFence)
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Memory access primitives
|
|
|
|
// These tests for the atomic load and store primitives ascertain that the
|
|
// primitives are defined and that they load and store the values they should,
|
|
// but not that the primitives are actually atomic wrt to the memory subsystem.
|
|
|
|
// Memory for testing atomics. This must be aligned to the natural alignment of
|
|
// the type we're testing; for now, use 8-byte alignment for all.
|
|
|
|
MOZ_ALIGNED_DECL(8, static uint8_t atomicMem[8]);
|
|
MOZ_ALIGNED_DECL(8, static uint8_t atomicMem2[8]);
|
|
|
|
// T is the primitive type we're testing, and A and B are references to constant
|
|
// bindings holding values of that type.
|
|
//
|
|
// No bytes of A and B should be 0 or FF. A+B and A-B must not overflow.
|
|
|
|
#define ATOMIC_TESTS(T, A, B) \
|
|
T* q = (T*)hidePointerValue((void*)atomicMem); \
|
|
*q = A; \
|
|
SharedMem<T*> p = \
|
|
SharedMem<T*>::shared((T*)hidePointerValue((T*)atomicMem)); \
|
|
CHECK(*q == A); \
|
|
CHECK(jit::AtomicOperations::loadSeqCst(p) == A); \
|
|
CHECK(*q == A); \
|
|
jit::AtomicOperations::storeSeqCst(p, B); \
|
|
CHECK(*q == B); \
|
|
CHECK(jit::AtomicOperations::exchangeSeqCst(p, A) == B); \
|
|
CHECK(*q == A); \
|
|
CHECK(jit::AtomicOperations::compareExchangeSeqCst(p, (T)0, (T)1) == \
|
|
A); /*failure*/ \
|
|
CHECK(*q == A); \
|
|
CHECK(jit::AtomicOperations::compareExchangeSeqCst(p, A, B) == \
|
|
A); /*success*/ \
|
|
CHECK(*q == B); \
|
|
*q = A; \
|
|
CHECK(jit::AtomicOperations::fetchAddSeqCst(p, B) == A); \
|
|
CHECK(*q == A + B); \
|
|
*q = A; \
|
|
CHECK(jit::AtomicOperations::fetchSubSeqCst(p, B) == A); \
|
|
CHECK(*q == A - B); \
|
|
*q = A; \
|
|
CHECK(jit::AtomicOperations::fetchAndSeqCst(p, B) == A); \
|
|
CHECK(*q == (A & B)); \
|
|
*q = A; \
|
|
CHECK(jit::AtomicOperations::fetchOrSeqCst(p, B) == A); \
|
|
CHECK(*q == (A | B)); \
|
|
*q = A; \
|
|
CHECK(jit::AtomicOperations::fetchXorSeqCst(p, B) == A); \
|
|
CHECK(*q == (A ^ B)); \
|
|
*q = A; \
|
|
CHECK(jit::AtomicOperations::loadSafeWhenRacy(p) == A); \
|
|
jit::AtomicOperations::storeSafeWhenRacy(p, B); \
|
|
CHECK(*q == B); \
|
|
T* q2 = (T*)hidePointerValue((void*)atomicMem2); \
|
|
SharedMem<T*> p2 = \
|
|
SharedMem<T*>::shared((T*)hidePointerValue((void*)atomicMem2)); \
|
|
*q = A; \
|
|
*q2 = B; \
|
|
jit::AtomicOperations::memcpySafeWhenRacy(p2, p, sizeof(T)); \
|
|
CHECK(*q2 == A); \
|
|
*q = A; \
|
|
*q2 = B; \
|
|
jit::AtomicOperations::memcpySafeWhenRacy(p2, p.unwrap(), sizeof(T)); \
|
|
CHECK(*q2 == A); \
|
|
*q = A; \
|
|
*q2 = B; \
|
|
jit::AtomicOperations::memcpySafeWhenRacy(p2.unwrap(), p, sizeof(T)); \
|
|
CHECK(*q2 == A); \
|
|
*q = A; \
|
|
*q2 = B; \
|
|
jit::AtomicOperations::memmoveSafeWhenRacy(p2, p, sizeof(T)); \
|
|
CHECK(*q2 == A); \
|
|
*q = A; \
|
|
*q2 = B; \
|
|
jit::AtomicOperations::podCopySafeWhenRacy(p2, p, 1); \
|
|
CHECK(*q2 == A); \
|
|
*q = A; \
|
|
*q2 = B; \
|
|
jit::AtomicOperations::podMoveSafeWhenRacy(p2, p, 1); \
|
|
CHECK(*q2 == A); \
|
|
return true
|
|
|
|
BEGIN_TEST(testAtomicOperationsU8) {
|
|
const uint8_t A = 0xab;
|
|
const uint8_t B = 0x37;
|
|
ATOMIC_TESTS(uint8_t, A, B);
|
|
}
|
|
END_TEST(testAtomicOperationsU8)
|
|
|
|
BEGIN_TEST(testAtomicOperationsI8) {
|
|
const int8_t A = 0x3b;
|
|
const int8_t B = 0x27;
|
|
ATOMIC_TESTS(int8_t, A, B);
|
|
}
|
|
END_TEST(testAtomicOperationsI8)
|
|
|
|
BEGIN_TEST(testAtomicOperationsU16) {
|
|
const uint16_t A = 0xabdc;
|
|
const uint16_t B = 0x3789;
|
|
ATOMIC_TESTS(uint16_t, A, B);
|
|
}
|
|
END_TEST(testAtomicOperationsU16)
|
|
|
|
BEGIN_TEST(testAtomicOperationsI16) {
|
|
const int16_t A = 0x3bdc;
|
|
const int16_t B = 0x2737;
|
|
ATOMIC_TESTS(int16_t, A, B);
|
|
}
|
|
END_TEST(testAtomicOperationsI16)
|
|
|
|
BEGIN_TEST(testAtomicOperationsU32) {
|
|
const uint32_t A = 0xabdc0588;
|
|
const uint32_t B = 0x37891942;
|
|
ATOMIC_TESTS(uint32_t, A, B);
|
|
}
|
|
END_TEST(testAtomicOperationsU32)
|
|
|
|
BEGIN_TEST(testAtomicOperationsI32) {
|
|
const int32_t A = 0x3bdc0588;
|
|
const int32_t B = 0x27371843;
|
|
ATOMIC_TESTS(int32_t, A, B);
|
|
}
|
|
END_TEST(testAtomicOperationsI32)
|
|
|
|
BEGIN_TEST(testAtomicOperationsU64) {
|
|
if (!jit::AtomicOperations::hasAtomic8()) {
|
|
return true;
|
|
}
|
|
|
|
const uint64_t A(0x9aadf00ddeadbeef);
|
|
const uint64_t B(0x4eedbead1337f001);
|
|
ATOMIC_TESTS(uint64_t, A, B);
|
|
}
|
|
END_TEST(testAtomicOperationsU64)
|
|
|
|
BEGIN_TEST(testAtomicOperationsI64) {
|
|
if (!jit::AtomicOperations::hasAtomic8()) {
|
|
return true;
|
|
}
|
|
|
|
const int64_t A(0x2aadf00ddeadbeef);
|
|
const int64_t B(0x4eedbead1337f001);
|
|
ATOMIC_TESTS(int64_t, A, B);
|
|
}
|
|
END_TEST(testAtomicOperationsI64)
|
|
|
|
// T is the primitive float type we're testing, and A and B are references to
|
|
// constant bindings holding values of that type.
|
|
//
|
|
// Stay away from 0, NaN, infinities, and denormals.
|
|
|
|
#define ATOMIC_FLOAT_TESTS(T, A, B) \
|
|
T* q = (T*)hidePointerValue((void*)atomicMem); \
|
|
*q = A; \
|
|
SharedMem<T*> p = \
|
|
SharedMem<T*>::shared((T*)hidePointerValue((T*)atomicMem)); \
|
|
CHECK(*q == A); \
|
|
CHECK(jit::AtomicOperations::loadSafeWhenRacy(p) == A); \
|
|
jit::AtomicOperations::storeSafeWhenRacy(p, B); \
|
|
CHECK(*q == B); \
|
|
T* q2 = (T*)hidePointerValue((void*)atomicMem2); \
|
|
SharedMem<T*> p2 = \
|
|
SharedMem<T*>::shared((T*)hidePointerValue((void*)atomicMem2)); \
|
|
*q = A; \
|
|
*q2 = B; \
|
|
jit::AtomicOperations::memcpySafeWhenRacy(p2, p, sizeof(T)); \
|
|
CHECK(*q2 == A); \
|
|
*q = A; \
|
|
*q2 = B; \
|
|
jit::AtomicOperations::memcpySafeWhenRacy(p2, p.unwrap(), sizeof(T)); \
|
|
CHECK(*q2 == A); \
|
|
*q = A; \
|
|
*q2 = B; \
|
|
jit::AtomicOperations::memcpySafeWhenRacy(p2.unwrap(), p, sizeof(T)); \
|
|
CHECK(*q2 == A); \
|
|
*q = A; \
|
|
*q2 = B; \
|
|
jit::AtomicOperations::memmoveSafeWhenRacy(p2, p, sizeof(T)); \
|
|
CHECK(*q2 == A); \
|
|
*q = A; \
|
|
*q2 = B; \
|
|
jit::AtomicOperations::podCopySafeWhenRacy(p2, p, 1); \
|
|
CHECK(*q2 == A); \
|
|
*q = A; \
|
|
*q2 = B; \
|
|
jit::AtomicOperations::podMoveSafeWhenRacy(p2, p, 1); \
|
|
CHECK(*q2 == A); \
|
|
return true
|
|
|
|
BEGIN_TEST(testAtomicOperationsF32) {
|
|
const float A(123.25);
|
|
const float B(-987.75);
|
|
ATOMIC_FLOAT_TESTS(float, A, B);
|
|
}
|
|
END_TEST(testAtomicOperationsF32)
|
|
|
|
BEGIN_TEST(testAtomicOperationsF64) {
|
|
const double A(123.25);
|
|
const double B(-987.75);
|
|
ATOMIC_FLOAT_TESTS(double, A, B);
|
|
}
|
|
END_TEST(testAtomicOperationsF64)
|
|
|
|
#define ATOMIC_CLAMPED_TESTS(T, A, B) \
|
|
T* q = (T*)hidePointerValue((void*)atomicMem); \
|
|
*q = A; \
|
|
SharedMem<T*> p = \
|
|
SharedMem<T*>::shared((T*)hidePointerValue((T*)atomicMem)); \
|
|
CHECK(*q == A); \
|
|
CHECK(jit::AtomicOperations::loadSafeWhenRacy(p) == A); \
|
|
jit::AtomicOperations::storeSafeWhenRacy(p, B); \
|
|
CHECK(*q == B); \
|
|
return true
|
|
|
|
BEGIN_TEST(testAtomicOperationsU8Clamped) {
|
|
const uint8_clamped A(0xab);
|
|
const uint8_clamped B(0x37);
|
|
ATOMIC_CLAMPED_TESTS(uint8_clamped, A, B);
|
|
}
|
|
END_TEST(testAtomicOperationsU8Clamped)
|
|
|
|
#undef ATOMIC_TESTS
|
|
#undef ATOMIC_FLOAT_TESTS
|
|
#undef ATOMIC_CLAMPED_TESTS
|