LibJS: Cache arguments span in ExecutionContext

Allows us to avoid doing math in ExecutionContext::argument()
This commit is contained in:
Aliaksandr Kalenik 2025-04-24 01:37:30 +02:00 committed by Andreas Kling
parent ff751173ac
commit e48645c83f
Notes: github-actions[bot] 2025-04-24 08:31:57 +00:00
13 changed files with 24 additions and 31 deletions

View file

@ -374,7 +374,7 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
}
auto& running_execution_context = this->running_execution_context();
auto* arguments = running_execution_context.arguments().data();
auto* arguments = running_execution_context.arguments.data();
auto& accumulator = this->accumulator();
auto& executable = current_executable();
auto const* bytecode = executable.bytecode.data();
@ -2331,7 +2331,7 @@ ThrowCompletionOr<void> CreateVariable::execute_impl(Bytecode::Interpreter& inte
void CreateRestParams::execute_impl(Bytecode::Interpreter& interpreter) const
{
auto const arguments = interpreter.running_execution_context().arguments();
auto const arguments = interpreter.running_execution_context().arguments;
auto arguments_count = interpreter.running_execution_context().passed_argument_count;
auto array = MUST(Array::create(interpreter.realm(), 0));
for (size_t rest_index = m_rest_index; rest_index < arguments_count; ++rest_index)
@ -2342,7 +2342,7 @@ void CreateRestParams::execute_impl(Bytecode::Interpreter& interpreter) const
void CreateArguments::execute_impl(Bytecode::Interpreter& interpreter) const
{
auto const& function = interpreter.running_execution_context().function;
auto const arguments = interpreter.running_execution_context().arguments();
auto const arguments = interpreter.running_execution_context().arguments;
auto const& environment = interpreter.running_execution_context().lexical_environment;
auto passed_arguments = ReadonlySpan<Value> { arguments.data(), interpreter.running_execution_context().passed_argument_count };

View file

@ -46,7 +46,7 @@ ThrowCompletionOr<GC::Ref<Object>> AsyncFunctionConstructor::construct(FunctionO
// 2. If bodyArg is not present, set bodyArg to the empty String.
// NOTE: This does that, as well as the string extraction done inside of CreateDynamicFunction
auto extracted = TRY(extract_parameter_arguments_and_body(vm, vm.running_execution_context().arguments()));
auto extracted = TRY(extract_parameter_arguments_and_body(vm, vm.running_execution_context().arguments));
// 3. Return ? CreateDynamicFunction(C, NewTarget, async, parameterArgs, bodyArg).
return TRY(FunctionConstructor::create_dynamic_function(vm, *constructor, &new_target, FunctionKind::Async, extracted.parameters, extracted.body));

View file

@ -47,7 +47,7 @@ ThrowCompletionOr<GC::Ref<Object>> AsyncGeneratorFunctionConstructor::construct(
// 2. If bodyArg is not present, set bodyArg to the empty String.
// NOTE: This does that, as well as the string extraction done inside of CreateDynamicFunction
auto extracted = TRY(extract_parameter_arguments_and_body(vm, vm.running_execution_context().arguments()));
auto extracted = TRY(extract_parameter_arguments_and_body(vm, vm.running_execution_context().arguments));
// 3. Return ? CreateDynamicFunction(C, NewTarget, async-generator, parameterArgs, bodyArg).
return TRY(FunctionConstructor::create_dynamic_function(vm, *constructor, &new_target, FunctionKind::AsyncGenerator, extracted.parameters, extracted.body));

View file

@ -506,7 +506,7 @@ ThrowCompletionOr<Value> ECMAScriptFunctionObject::internal_call(Value this_argu
ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK(callee_context, registers_and_constants_and_locals_count, arguments_count);
// Non-standard
auto arguments = callee_context->arguments();
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();
@ -578,7 +578,7 @@ ThrowCompletionOr<GC::Ref<Object>> ECMAScriptFunctionObject::internal_construct(
ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK(callee_context, registers_and_constants_and_locals_count, arguments_count);
// Non-standard
auto arguments = callee_context->arguments();
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();

View file

@ -106,6 +106,7 @@ ExecutionContext::ExecutionContext(u32 registers_and_constants_and_locals_count,
auto* registers_and_constants_and_locals_and_arguments = this->registers_and_constants_and_locals_and_arguments();
for (size_t i = 0; i < registers_and_constants_and_locals_count; ++i)
registers_and_constants_and_locals_and_arguments[i] = js_special_empty_value();
arguments = { registers_and_constants_and_locals_and_arguments + arguments_offset, registers_and_constants_and_locals_and_arguments_count - arguments_offset };
}
ExecutionContext::~ExecutionContext()
@ -114,7 +115,7 @@ ExecutionContext::~ExecutionContext()
NonnullOwnPtr<ExecutionContext> ExecutionContext::copy() const
{
auto copy = create(registers_and_constants_and_locals_and_arguments_count, arguments().size());
auto copy = create(registers_and_constants_and_locals_and_arguments_count, arguments.size());
copy->function = function;
copy->realm = realm;
copy->script_or_module = script_or_module;
@ -134,6 +135,7 @@ NonnullOwnPtr<ExecutionContext> ExecutionContext::copy() const
copy->registers_and_constants_and_locals_and_arguments_count = registers_and_constants_and_locals_and_arguments_count;
for (size_t i = 0; i < registers_and_constants_and_locals_and_arguments_count; ++i)
copy->registers_and_constants_and_locals_and_arguments()[i] = registers_and_constants_and_locals_and_arguments()[i];
copy->arguments = { copy->registers_and_constants_and_locals_and_arguments() + copy->arguments_offset, arguments.size() };
return copy;
}

View file

@ -83,10 +83,9 @@ public:
Value argument(size_t index) const
{
auto arguments_size = registers_and_constants_and_locals_and_arguments_count - arguments_offset;
if (index >= arguments_size) [[unlikely]]
if (index >= arguments.size()) [[unlikely]]
return js_undefined();
return registers_and_constants_and_locals_and_arguments()[arguments_offset + index];
return arguments[index];
}
Value& local(size_t index)
@ -98,15 +97,7 @@ public:
u32 passed_argument_count { 0 };
bool is_strict_mode { false };
Span<Value> arguments()
{
return { registers_and_constants_and_locals_and_arguments() + arguments_offset, registers_and_constants_and_locals_and_arguments_count - arguments_offset };
}
ReadonlySpan<Value> arguments() const
{
return { registers_and_constants_and_locals_and_arguments() + arguments_offset, registers_and_constants_and_locals_and_arguments_count - arguments_offset };
}
Span<Value> arguments;
Vector<Bytecode::UnwindInfo> unwind_contexts;
Vector<Optional<size_t>> previously_scheduled_jumps;

View file

@ -269,7 +269,7 @@ ThrowCompletionOr<GC::Ref<Object>> FunctionConstructor::construct(FunctionObject
// 2. If bodyArg is not present, set bodyArg to the empty String.
// NOTE: This does that, as well as the string extraction done inside of CreateDynamicFunction
auto extracted = TRY(extract_parameter_arguments_and_body(vm, vm.running_execution_context().arguments()));
auto extracted = TRY(extract_parameter_arguments_and_body(vm, vm.running_execution_context().arguments));
// 3. Return ? CreateDynamicFunction(C, NewTarget, normal, parameterArgs, bodyArg).
return TRY(create_dynamic_function(vm, *constructor, &new_target, FunctionKind::Normal, extracted.parameters, extracted.body));

View file

@ -98,7 +98,7 @@ JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::bind)
Vector<Value> arguments;
if (vm.argument_count() > 1) {
arguments.append(vm.running_execution_context().arguments().slice(1).data(), vm.argument_count() - 1);
arguments.append(vm.running_execution_context().arguments.slice(1).data(), vm.argument_count() - 1);
}
// 3. Let F be ? BoundFunctionCreate(Target, thisArg, args).
@ -129,7 +129,7 @@ JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::call)
// FIXME: 3. Perform PrepareForTailCall().
auto this_arg = vm.argument(0);
auto args = vm.argument_count() > 1 ? vm.running_execution_context().arguments().slice(1) : ReadonlySpan<Value> {};
auto args = vm.argument_count() > 1 ? vm.running_execution_context().arguments.slice(1) : ReadonlySpan<Value> {};
// 4. Return ? Call(func, thisArg, args).
return TRY(JS::call(vm, function, this_arg, args));

View file

@ -45,7 +45,7 @@ ThrowCompletionOr<GC::Ref<Object>> GeneratorFunctionConstructor::construct(Funct
// 2. If bodyArg is not present, set bodyArg to the empty String.
// NOTE: This does that, as well as the string extraction done inside of CreateDynamicFunction
auto extracted = TRY(extract_parameter_arguments_and_body(vm, vm.running_execution_context().arguments()));
auto extracted = TRY(extract_parameter_arguments_and_body(vm, vm.running_execution_context().arguments));
// 3. Return ? CreateDynamicFunction(C, NewTarget, generator, parameterArgs, bodyArg).
return TRY(FunctionConstructor::create_dynamic_function(vm, *constructor, &new_target, FunctionKind::Generator, extracted.parameters, extracted.body));

View file

@ -146,7 +146,7 @@ ThrowCompletionOr<Value> NativeFunction::internal_call(Value this_argument, Read
// 8. Perform any necessary implementation-defined initialization of calleeContext.
callee_context->this_value = this_argument;
if (!arguments_list.is_empty())
callee_context->arguments().overwrite(0, arguments_list.data(), arguments_list.size() * sizeof(Value));
callee_context->arguments.overwrite(0, arguments_list.data(), arguments_list.size() * sizeof(Value));
callee_context->lexical_environment = caller_context.lexical_environment;
callee_context->variable_environment = caller_context.variable_environment;
@ -208,7 +208,7 @@ ThrowCompletionOr<GC::Ref<Object>> NativeFunction::internal_construct(ReadonlySp
// 8. Perform any necessary implementation-defined initialization of calleeContext.
if (!arguments_list.is_empty())
callee_context->arguments().overwrite(0, arguments_list.data(), arguments_list.size() * sizeof(Value));
callee_context->arguments.overwrite(0, arguments_list.data(), arguments_list.size() * sizeof(Value));
callee_context->lexical_environment = caller_context.lexical_environment;
callee_context->variable_environment = caller_context.variable_environment;

View file

@ -465,7 +465,7 @@ JS_DEFINE_NATIVE_FUNCTION(PromiseConstructor::try_)
auto callback = vm.argument(0);
Span<Value> args;
if (vm.argument_count() > 1) {
args = vm.running_execution_context().arguments().slice(1, vm.argument_count() - 1);
args = vm.running_execution_context().arguments.slice(1, vm.argument_count() - 1);
}
// 1. Let C be the this value.

View file

@ -160,7 +160,7 @@ public:
size_t argument_count() const
{
return running_execution_context().arguments().size();
return running_execution_context().arguments.size();
}
Value argument(size_t index) const

View file

@ -137,7 +137,7 @@ Optional<JS::PropertyDescriptor> cross_origin_get_own_property_helper(Variant<HT
if (value->is_function()) {
value = JS::NativeFunction::create(
realm, [function = GC::make_root(*value)](auto& vm) {
return JS::call(vm, function.value(), JS::js_undefined(), vm.running_execution_context().arguments());
return JS::call(vm, function.value(), JS::js_undefined(), vm.running_execution_context().arguments);
},
0);
}
@ -154,7 +154,7 @@ Optional<JS::PropertyDescriptor> cross_origin_get_own_property_helper(Variant<HT
if (*entry.needs_get) {
cross_origin_get = JS::NativeFunction::create(
realm, [object_ptr, getter = GC::make_root(*original_descriptor->get)](auto& vm) {
return JS::call(vm, getter.cell(), object_ptr, vm.running_execution_context().arguments());
return JS::call(vm, getter.cell(), object_ptr, vm.running_execution_context().arguments);
},
0);
}
@ -166,7 +166,7 @@ Optional<JS::PropertyDescriptor> cross_origin_get_own_property_helper(Variant<HT
if (*entry.needs_set) {
cross_origin_set = JS::NativeFunction::create(
realm, [object_ptr, setter = GC::make_root(*original_descriptor->set)](auto& vm) {
return JS::call(vm, setter.cell(), object_ptr, vm.running_execution_context().arguments());
return JS::call(vm, setter.cell(), object_ptr, vm.running_execution_context().arguments);
},
0);
}