mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-21 23:20:20 +00:00
LibJS: Implement tc39/proposal-atomics-microwait
(Atomics.pause)
Some checks are pending
CI / Lagom (false, FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, macos-14, macOS, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (true, NO_FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Push notes / build (push) Waiting to run
Some checks are pending
CI / Lagom (false, FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, macos-14, macOS, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (true, NO_FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Push notes / build (push) Waiting to run
Implements the https://github.com/tc39/proposal-atomics-microwait proposal which has recently hit Stage 3. This commit passes all relevant tests in `test262`.
This commit is contained in:
parent
01c2ecf355
commit
755b83c01a
Notes:
github-actions[bot]
2024-11-03 13:23:18 +00:00
Author: https://github.com/yyny Commit: https://github.com/LadybirdBrowser/ladybird/commit/755b83c01ae Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2112 Reviewed-by: https://github.com/awesomekling Reviewed-by: https://github.com/trflynn89
7 changed files with 84 additions and 0 deletions
|
@ -112,6 +112,15 @@ static inline V* atomic_load(T volatile** var, MemoryOrder order = memory_order_
|
||||||
return __atomic_load_n(const_cast<V**>(var), order);
|
return __atomic_load_n(const_cast<V**>(var), order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void atomic_pause()
|
||||||
|
{
|
||||||
|
#if __has_builtin(__builtin_ia32_pause)
|
||||||
|
__builtin_ia32_pause();
|
||||||
|
#elif __has_builtin(__builtin_arm_yield)
|
||||||
|
__builtin_arm_yield();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static inline void atomic_store(T volatile* var, T desired, MemoryOrder order = memory_order_seq_cst) noexcept
|
static inline void atomic_store(T volatile* var, T desired, MemoryOrder order = memory_order_seq_cst) noexcept
|
||||||
{
|
{
|
||||||
|
|
|
@ -239,6 +239,7 @@ void AtomicsObject::initialize(Realm& realm)
|
||||||
define_native_function(realm, vm.names.isLockFree, is_lock_free, 1, attr);
|
define_native_function(realm, vm.names.isLockFree, is_lock_free, 1, attr);
|
||||||
define_native_function(realm, vm.names.load, load, 2, attr);
|
define_native_function(realm, vm.names.load, load, 2, attr);
|
||||||
define_native_function(realm, vm.names.or_, or_, 3, attr);
|
define_native_function(realm, vm.names.or_, or_, 3, attr);
|
||||||
|
define_native_function(realm, vm.names.pause, pause, 0, attr);
|
||||||
define_native_function(realm, vm.names.store, store, 3, attr);
|
define_native_function(realm, vm.names.store, store, 3, attr);
|
||||||
define_native_function(realm, vm.names.sub, sub, 3, attr);
|
define_native_function(realm, vm.names.sub, sub, 3, attr);
|
||||||
define_native_function(realm, vm.names.wait, wait, 4, attr);
|
define_native_function(realm, vm.names.wait, wait, 4, attr);
|
||||||
|
@ -440,6 +441,40 @@ JS_DEFINE_NATIVE_FUNCTION(AtomicsObject::or_)
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1 Atomics.pause ( [ N ] ), http://tc39.es/proposal-atomics-microwait/
|
||||||
|
JS_DEFINE_NATIVE_FUNCTION(AtomicsObject::pause)
|
||||||
|
{
|
||||||
|
// NOTE: This value is arbitrary, but intends to put an upper bound on the spin loop of between ~10-100ns on most systems.
|
||||||
|
constexpr i32 MAXIMUM_ITERATIONS = 1000;
|
||||||
|
// NOTE: This value is arbitrary, but intends to account for function call overhead.
|
||||||
|
constexpr i32 DEFAULT_ITERATIONS = 100;
|
||||||
|
|
||||||
|
// 1. If N is neither undefined nor an integral Number, throw a TypeError exception.
|
||||||
|
auto pause = vm.argument(0);
|
||||||
|
|
||||||
|
if (!pause.is_undefined() && !pause.is_integral_number())
|
||||||
|
return vm.throw_completion<TypeError>(ErrorType::NotAnIntegerOrUndefined, "pause time");
|
||||||
|
|
||||||
|
// 2. If the execution environment of the ECMAScript implementation supports signaling to the operating system or CPU that the current executing code is in a spin-wait loop, such as executing a pause CPU instruction, send that signal.
|
||||||
|
// When N is not undefined, it determines the number of times that signal is sent.
|
||||||
|
u32 N = DEFAULT_ITERATIONS;
|
||||||
|
if (!pause.is_undefined()) {
|
||||||
|
auto integral = pause.as_i32_clamped_integral_number();
|
||||||
|
if (integral < 0)
|
||||||
|
N = MAXIMUM_ITERATIONS + max(integral, -MAXIMUM_ITERATIONS) + 1;
|
||||||
|
else
|
||||||
|
// Implementation note: `N` is not required to be the _number of times_ that the signal is sent, but it seems like reasonable behaviour regardless.
|
||||||
|
N = min(integral, MAXIMUM_ITERATIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The number of times the signal is sent for an integral Number N is less than or equal to the number times it is sent for N + 1 if both N and N + 1 have the same sign.
|
||||||
|
for (; N != 0; N--)
|
||||||
|
AK::atomic_pause();
|
||||||
|
|
||||||
|
// 3. Return undefined.
|
||||||
|
return js_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
// 25.4.11 Atomics.store ( typedArray, index, value ), https://tc39.es/ecma262/#sec-atomics.store
|
// 25.4.11 Atomics.store ( typedArray, index, value ), https://tc39.es/ecma262/#sec-atomics.store
|
||||||
JS_DEFINE_NATIVE_FUNCTION(AtomicsObject::store)
|
JS_DEFINE_NATIVE_FUNCTION(AtomicsObject::store)
|
||||||
{
|
{
|
||||||
|
|
|
@ -28,6 +28,7 @@ private:
|
||||||
JS_DECLARE_NATIVE_FUNCTION(is_lock_free);
|
JS_DECLARE_NATIVE_FUNCTION(is_lock_free);
|
||||||
JS_DECLARE_NATIVE_FUNCTION(load);
|
JS_DECLARE_NATIVE_FUNCTION(load);
|
||||||
JS_DECLARE_NATIVE_FUNCTION(or_);
|
JS_DECLARE_NATIVE_FUNCTION(or_);
|
||||||
|
JS_DECLARE_NATIVE_FUNCTION(pause);
|
||||||
JS_DECLARE_NATIVE_FUNCTION(store);
|
JS_DECLARE_NATIVE_FUNCTION(store);
|
||||||
JS_DECLARE_NATIVE_FUNCTION(sub);
|
JS_DECLARE_NATIVE_FUNCTION(sub);
|
||||||
JS_DECLARE_NATIVE_FUNCTION(wait);
|
JS_DECLARE_NATIVE_FUNCTION(wait);
|
||||||
|
|
|
@ -411,6 +411,7 @@ namespace JS {
|
||||||
P(parse) \
|
P(parse) \
|
||||||
P(parseFloat) \
|
P(parseFloat) \
|
||||||
P(parseInt) \
|
P(parseInt) \
|
||||||
|
P(pause) \
|
||||||
P(plainDate) \
|
P(plainDate) \
|
||||||
P(plainDateISO) \
|
P(plainDateISO) \
|
||||||
P(plainDateTime) \
|
P(plainDateTime) \
|
||||||
|
|
|
@ -96,6 +96,7 @@
|
||||||
M(NotAConstructor, "{} is not a constructor") \
|
M(NotAConstructor, "{} is not a constructor") \
|
||||||
M(NotAFunction, "{} is not a function") \
|
M(NotAFunction, "{} is not a function") \
|
||||||
M(NotAFunctionNoParam, "Not a function") \
|
M(NotAFunctionNoParam, "Not a function") \
|
||||||
|
M(NotAnIntegerOrUndefined, "{} is neither an integer nor undefined") \
|
||||||
M(NotAnObject, "{} is not an object") \
|
M(NotAnObject, "{} is not an object") \
|
||||||
M(NotAnObjectOfType, "Not an object of type {}") \
|
M(NotAnObjectOfType, "Not an object of type {}") \
|
||||||
M(NotAnObjectOrNull, "{} is neither an object nor null") \
|
M(NotAnObjectOrNull, "{} is neither an object nor null") \
|
||||||
|
|
|
@ -458,6 +458,19 @@ public:
|
||||||
return static_cast<i32>(m_value.encoded & 0xFFFFFFFF);
|
return static_cast<i32>(m_value.encoded & 0xFFFFFFFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i32 as_i32_clamped_integral_number() const
|
||||||
|
{
|
||||||
|
VERIFY(is_int32() || is_finite_number());
|
||||||
|
if (is_int32())
|
||||||
|
return as_i32();
|
||||||
|
double value = trunc(as_double());
|
||||||
|
if (value > INT32_MAX)
|
||||||
|
return INT32_MAX;
|
||||||
|
if (value < INT32_MIN)
|
||||||
|
return INT32_MIN;
|
||||||
|
return static_cast<i32>(value);
|
||||||
|
}
|
||||||
|
|
||||||
bool to_boolean_slow_case() const;
|
bool to_boolean_slow_case() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
test("invariants", () => {
|
||||||
|
expect(Atomics.pause).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("error cases", () => {
|
||||||
|
expect(() => {
|
||||||
|
Atomics.pause({});
|
||||||
|
}).toThrow(TypeError);
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
Atomics.pause("not an integer");
|
||||||
|
}).toThrow(TypeError);
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
Atomics.pause("0");
|
||||||
|
}).toThrow(TypeError);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("basic functionality", () => {
|
||||||
|
expect(Atomics.pause()).toBeUndefined();
|
||||||
|
expect(Atomics.pause(0)).toBeUndefined();
|
||||||
|
expect(Atomics.pause(1)).toBeUndefined();
|
||||||
|
expect(Atomics.pause(-1)).toBeUndefined();
|
||||||
|
});
|
Loading…
Reference in a new issue