mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-26 09:45:06 +02:00
LibJS: Let invokers (callers) of [[Call]] allocate ExecutionContext
Instead of letting every [[Call]] implementation allocate an ExecutionContext, we now make that a responsibility of the caller. The main point of this exercise is to allow the Call instruction to write function arguments directly into the callee ExecutionContext instead of copying them later. This makes function calls significantly faster: - 10-20% faster on micro-benchmarks (depending on argument count) - 4% speedup on Kraken - 2% speedup on Octane - 5% speedup on JetStream
This commit is contained in:
committed by
Andreas Kling
parent
93788f8057
commit
a05be67e4a
Notes:
github-actions[bot]
2025-04-27 23:24:56 +00:00
Author: https://github.com/awesomekling Commit: https://github.com/LadybirdBrowser/ladybird/commit/a05be67e4ae Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4497 Reviewed-by: https://github.com/Hendiadyoin1
@@ -481,46 +481,39 @@ void ECMAScriptFunctionObject::initialize(Realm& realm)
|
||||
}
|
||||
}
|
||||
|
||||
// 10.2.1 [[Call]] ( thisArgument, argumentsList ), https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist
|
||||
ThrowCompletionOr<Value> ECMAScriptFunctionObject::internal_call(Value this_argument, ReadonlySpan<Value> arguments_list)
|
||||
ThrowCompletionOr<void> ECMAScriptFunctionObject::get_stack_frame_size(size_t& registers_and_constants_and_locals_count, size_t& argument_count)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 1. Let callerContext be the running execution context.
|
||||
// NOTE: No-op, kept by the VM in its execution context stack.
|
||||
|
||||
if (!m_bytecode_executable) {
|
||||
if (!ecmascript_code().bytecode_executable()) {
|
||||
if (is_module_wrapper()) {
|
||||
const_cast<Statement&>(ecmascript_code()).set_bytecode_executable(TRY(Bytecode::compile(vm, ecmascript_code(), kind(), name())));
|
||||
const_cast<Statement&>(ecmascript_code()).set_bytecode_executable(TRY(Bytecode::compile(vm(), ecmascript_code(), kind(), name())));
|
||||
} else {
|
||||
const_cast<Statement&>(ecmascript_code()).set_bytecode_executable(TRY(Bytecode::compile(vm, *this)));
|
||||
const_cast<Statement&>(ecmascript_code()).set_bytecode_executable(TRY(Bytecode::compile(vm(), *this)));
|
||||
}
|
||||
}
|
||||
m_bytecode_executable = ecmascript_code().bytecode_executable();
|
||||
}
|
||||
registers_and_constants_and_locals_count = m_bytecode_executable->number_of_registers + m_bytecode_executable->constants.size() + m_bytecode_executable->local_variable_names.size();
|
||||
argument_count = max(argument_count, formal_parameters().size());
|
||||
return {};
|
||||
}
|
||||
|
||||
u32 arguments_count = max(arguments_list.size(), formal_parameters().size());
|
||||
auto registers_and_constants_and_locals_count = m_bytecode_executable->number_of_registers + m_bytecode_executable->constants.size() + m_bytecode_executable->local_variable_names.size();
|
||||
ExecutionContext* callee_context = nullptr;
|
||||
ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK(callee_context, registers_and_constants_and_locals_count, arguments_count);
|
||||
// 10.2.1 [[Call]] ( thisArgument, argumentsList ), https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist
|
||||
ThrowCompletionOr<Value> ECMAScriptFunctionObject::internal_call(ExecutionContext& callee_context, Value this_argument)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
// Non-standard
|
||||
auto arguments = callee_context->arguments;
|
||||
if (!arguments_list.is_empty())
|
||||
arguments.overwrite(0, arguments_list.data(), arguments_list.size() * sizeof(Value));
|
||||
callee_context->passed_argument_count = arguments_list.size();
|
||||
if (arguments_list.size() < formal_parameters().size()) {
|
||||
for (size_t i = arguments_list.size(); i < formal_parameters().size(); ++i)
|
||||
arguments[i] = js_undefined();
|
||||
}
|
||||
VERIFY(m_bytecode_executable);
|
||||
|
||||
// 1. Let callerContext be the running execution context.
|
||||
// NOTE: No-op, kept by the VM in its execution context stack.
|
||||
|
||||
// 2. Let calleeContext be PrepareForOrdinaryCall(F, undefined).
|
||||
// NOTE: We throw if the end of the native stack is reached, so unlike in the spec this _does_ need an exception check.
|
||||
TRY(prepare_for_ordinary_call(*callee_context, nullptr));
|
||||
TRY(prepare_for_ordinary_call(callee_context, nullptr));
|
||||
|
||||
// 3. Assert: calleeContext is now the running execution context.
|
||||
VERIFY(&vm.running_execution_context() == callee_context);
|
||||
VERIFY(&vm.running_execution_context() == &callee_context);
|
||||
|
||||
// 4. If F.[[IsClassConstructor]] is true, then
|
||||
if (is_class_constructor()) {
|
||||
@@ -537,7 +530,7 @@ ThrowCompletionOr<Value> ECMAScriptFunctionObject::internal_call(Value this_argu
|
||||
|
||||
// 5. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument).
|
||||
if (uses_this())
|
||||
ordinary_call_bind_this(*callee_context, this_argument);
|
||||
ordinary_call_bind_this(callee_context, this_argument);
|
||||
|
||||
// 6. Let result be Completion(OrdinaryCallEvaluateBody(F, argumentsList)).
|
||||
auto result = ordinary_call_evaluate_body();
|
||||
|
||||
Reference in New Issue
Block a user