LibJS: Move ExecutionContext members with destructors to "rare data"

This makes ExecutionContext trivially destructible, which means less
work to do on function exit.
This commit is contained in:
Andreas Kling 2025-10-31 14:56:25 +01:00 committed by Andreas Kling
parent 9ded35f98f
commit 1e0b56586b
3 changed files with 73 additions and 45 deletions

View File

@ -311,8 +311,8 @@ NEVER_INLINE Interpreter::HandleExceptionResponse Interpreter::handle_exception(
auto& handler = handlers->handler_offset; auto& handler = handlers->handler_offset;
auto& finalizer = handlers->finalizer_offset; auto& finalizer = handlers->finalizer_offset;
VERIFY(!running_execution_context().unwind_contexts.is_empty()); auto& unwind_contexts = running_execution_context().ensure_rare_data()->unwind_contexts;
auto& unwind_context = running_execution_context().unwind_contexts.last(); auto& unwind_context = unwind_contexts.last();
VERIFY(unwind_context.executable == &current_executable()); VERIFY(unwind_context.executable == &current_executable());
if (handler.has_value()) { if (handler.has_value()) {
@ -485,8 +485,8 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
do_return(saved_return_value()); do_return(saved_return_value());
if (auto handlers = executable.exception_handlers_for_offset(program_counter); handlers.has_value()) { if (auto handlers = executable.exception_handlers_for_offset(program_counter); handlers.has_value()) {
if (auto finalizer = handlers.value().finalizer_offset; finalizer.has_value()) { if (auto finalizer = handlers.value().finalizer_offset; finalizer.has_value()) {
VERIFY(!running_execution_context.unwind_contexts.is_empty()); auto& unwind_contexts = running_execution_context.ensure_rare_data()->unwind_contexts;
auto& unwind_context = running_execution_context.unwind_contexts.last(); auto& unwind_context = unwind_contexts.last();
VERIFY(unwind_context.executable == &current_executable()); VERIFY(unwind_context.executable == &current_executable());
reg(Register::saved_return_value()) = reg(Register::return_value()); reg(Register::saved_return_value()) = reg(Register::return_value());
reg(Register::return_value()) = js_special_empty_value(); reg(Register::return_value()) = js_special_empty_value();
@ -497,7 +497,7 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
} }
return; return;
} }
auto const old_scheduled_jump = running_execution_context.previously_scheduled_jumps.take_last(); auto const old_scheduled_jump = running_execution_context.ensure_rare_data()->previously_scheduled_jumps.take_last();
if (m_running_execution_context->scheduled_jump.has_value()) { if (m_running_execution_context->scheduled_jump.has_value()) {
program_counter = m_running_execution_context->scheduled_jump.value(); program_counter = m_running_execution_context->scheduled_jump.value();
m_running_execution_context->scheduled_jump = {}; m_running_execution_context->scheduled_jump = {};
@ -762,23 +762,23 @@ ThrowCompletionOr<Value> Interpreter::run_executable(ExecutionContext& context,
void Interpreter::enter_unwind_context() void Interpreter::enter_unwind_context()
{ {
running_execution_context().unwind_contexts.empend( running_execution_context().ensure_rare_data()->unwind_contexts.empend(
current_executable(), current_executable(),
running_execution_context().lexical_environment); running_execution_context().lexical_environment);
running_execution_context().previously_scheduled_jumps.append(m_running_execution_context->scheduled_jump); running_execution_context().rare_data()->previously_scheduled_jumps.append(m_running_execution_context->scheduled_jump);
m_running_execution_context->scheduled_jump = {}; m_running_execution_context->scheduled_jump = {};
} }
void Interpreter::leave_unwind_context() void Interpreter::leave_unwind_context()
{ {
running_execution_context().unwind_contexts.take_last(); running_execution_context().rare_data()->unwind_contexts.take_last();
} }
void Interpreter::catch_exception(Operand dst) void Interpreter::catch_exception(Operand dst)
{ {
set(dst, reg(Register::exception())); set(dst, reg(Register::exception()));
reg(Register::exception()) = js_special_empty_value(); reg(Register::exception()) = js_special_empty_value();
auto& context = running_execution_context().unwind_contexts.last(); auto& context = running_execution_context().rare_data()->unwind_contexts.last();
VERIFY(!context.handler_called); VERIFY(!context.handler_called);
VERIFY(context.executable == &current_executable()); VERIFY(context.executable == &current_executable());
context.handler_called = true; context.handler_called = true;
@ -787,19 +787,19 @@ void Interpreter::catch_exception(Operand dst)
void Interpreter::restore_scheduled_jump() void Interpreter::restore_scheduled_jump()
{ {
m_running_execution_context->scheduled_jump = running_execution_context().previously_scheduled_jumps.take_last(); m_running_execution_context->scheduled_jump = running_execution_context().rare_data()->previously_scheduled_jumps.take_last();
} }
void Interpreter::leave_finally() void Interpreter::leave_finally()
{ {
reg(Register::exception()) = js_special_empty_value(); reg(Register::exception()) = js_special_empty_value();
m_running_execution_context->scheduled_jump = running_execution_context().previously_scheduled_jumps.take_last(); m_running_execution_context->scheduled_jump = running_execution_context().rare_data()->previously_scheduled_jumps.take_last();
} }
void Interpreter::enter_object_environment(Object& object) void Interpreter::enter_object_environment(Object& object)
{ {
auto& old_environment = running_execution_context().lexical_environment; auto& old_environment = running_execution_context().lexical_environment;
running_execution_context().saved_lexical_environments.append(old_environment); running_execution_context().ensure_rare_data()->saved_lexical_environments.append(old_environment);
running_execution_context().lexical_environment = new_object_environment(object, true, old_environment); running_execution_context().lexical_environment = new_object_environment(object, true, old_environment);
} }
@ -1593,7 +1593,7 @@ inline ThrowCompletionOr<ECMAScriptFunctionObject*> new_class(VM& vm, Value supe
// NOTE: NewClass expects classEnv to be active lexical environment // NOTE: NewClass expects classEnv to be active lexical environment
auto* class_environment = vm.lexical_environment(); auto* class_environment = vm.lexical_environment();
vm.running_execution_context().lexical_environment = vm.running_execution_context().saved_lexical_environments.take_last(); vm.running_execution_context().lexical_environment = vm.running_execution_context().rare_data()->saved_lexical_environments.take_last();
Optional<Utf16FlyString> binding_name; Optional<Utf16FlyString> binding_name;
Utf16FlyString class_name; Utf16FlyString class_name;
@ -2416,7 +2416,7 @@ void CreateLexicalEnvironment::execute_impl(Bytecode::Interpreter& interpreter)
return environment; return environment;
}; };
auto& running_execution_context = interpreter.running_execution_context(); auto& running_execution_context = interpreter.running_execution_context();
running_execution_context.saved_lexical_environments.append(make_and_swap_envs(running_execution_context.lexical_environment)); running_execution_context.ensure_rare_data()->saved_lexical_environments.append(make_and_swap_envs(running_execution_context.lexical_environment));
if (m_dst.has_value()) if (m_dst.has_value())
interpreter.set(*m_dst, running_execution_context.lexical_environment); interpreter.set(*m_dst, running_execution_context.lexical_environment);
} }
@ -3161,7 +3161,7 @@ ThrowCompletionOr<void> ThrowIfTDZ::execute_impl(Bytecode::Interpreter& interpre
void LeaveLexicalEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const void LeaveLexicalEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const
{ {
auto& running_execution_context = interpreter.running_execution_context(); auto& running_execution_context = interpreter.running_execution_context();
running_execution_context.lexical_environment = running_execution_context.saved_lexical_environments.take_last(); running_execution_context.lexical_environment = running_execution_context.rare_data()->saved_lexical_environments.take_last();
} }
void LeavePrivateEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const void LeavePrivateEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const

View File

@ -15,6 +15,7 @@
namespace JS { namespace JS {
GC_DEFINE_ALLOCATOR(CachedSourceRange); GC_DEFINE_ALLOCATOR(CachedSourceRange);
GC_DEFINE_ALLOCATOR(ExecutionContextRareData);
class ExecutionContextAllocator { class ExecutionContextAllocator {
public: public:
@ -110,10 +111,6 @@ ExecutionContext::ExecutionContext(u32 registers_and_constants_and_locals_count,
arguments = { registers_and_constants_and_locals_and_arguments + registers_and_constants_and_locals_count, arguments_count }; arguments = { registers_and_constants_and_locals_and_arguments + registers_and_constants_and_locals_count, arguments_count };
} }
ExecutionContext::~ExecutionContext()
{
}
NonnullOwnPtr<ExecutionContext> ExecutionContext::copy() const 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());
@ -127,9 +124,12 @@ NonnullOwnPtr<ExecutionContext> ExecutionContext::copy() const
copy->this_value = this_value; copy->this_value = this_value;
copy->executable = executable; copy->executable = executable;
copy->passed_argument_count = passed_argument_count; copy->passed_argument_count = passed_argument_count;
copy->unwind_contexts = unwind_contexts; if (m_rare_data) {
copy->saved_lexical_environments = saved_lexical_environments; auto copy_rare_data = copy->ensure_rare_data();
copy->previously_scheduled_jumps = previously_scheduled_jumps; copy_rare_data->unwind_contexts = m_rare_data->unwind_contexts;
copy_rare_data->saved_lexical_environments = m_rare_data->saved_lexical_environments;
copy_rare_data->previously_scheduled_jumps = m_rare_data->previously_scheduled_jumps;
}
copy->registers_and_constants_and_locals_and_arguments_count = registers_and_constants_and_locals_and_arguments_count; 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) 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->registers_and_constants_and_locals_and_arguments()[i] = registers_and_constants_and_locals_and_arguments()[i];
@ -146,14 +146,11 @@ void ExecutionContext::visit_edges(Cell::Visitor& visitor)
visitor.visit(private_environment); visitor.visit(private_environment);
visitor.visit(context_owner); visitor.visit(context_owner);
visitor.visit(cached_source_range); visitor.visit(cached_source_range);
visitor.visit(m_rare_data);
if (this_value.has_value()) if (this_value.has_value())
visitor.visit(*this_value); visitor.visit(*this_value);
visitor.visit(executable); visitor.visit(executable);
visitor.visit(registers_and_constants_and_locals_and_arguments_span()); visitor.visit(registers_and_constants_and_locals_and_arguments_span());
for (auto& context : unwind_contexts) {
visitor.visit(context.lexical_environment);
}
visitor.visit(saved_lexical_environments);
script_or_module.visit( script_or_module.visit(
[](Empty) {}, [](Empty) {},
[&](auto& script_or_module) { [&](auto& script_or_module) {
@ -161,4 +158,21 @@ void ExecutionContext::visit_edges(Cell::Visitor& visitor)
}); });
} }
void ExecutionContextRareData::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
for (auto& context : unwind_contexts) {
visitor.visit(context.lexical_environment);
}
visitor.visit(saved_lexical_environments);
}
GC::Ref<ExecutionContextRareData> ExecutionContext::ensure_rare_data()
{
if (!m_rare_data) {
m_rare_data = executable->heap().allocate<ExecutionContextRareData>();
}
return *m_rare_data;
}
} }

View File

@ -36,12 +36,25 @@ public:
Variant<UnrealizedSourceRange, SourceRange> source_range; Variant<UnrealizedSourceRange, SourceRange> source_range;
}; };
class ExecutionContextRareData final : public GC::Cell {
GC_CELL(ExecutionContextRareData, GC::Cell);
GC_DECLARE_ALLOCATOR(ExecutionContextRareData);
public:
Vector<Bytecode::UnwindInfo> unwind_contexts;
Vector<Optional<size_t>> previously_scheduled_jumps;
Vector<GC::Ptr<Environment>> saved_lexical_environments;
private:
virtual void visit_edges(Cell::Visitor&) override;
};
// 9.4 Execution Contexts, https://tc39.es/ecma262/#sec-execution-contexts // 9.4 Execution Contexts, https://tc39.es/ecma262/#sec-execution-contexts
struct JS_API ExecutionContext { struct JS_API ExecutionContext {
static NonnullOwnPtr<ExecutionContext> create(u32 registers_and_constants_and_locals_count, u32 arguments_count); static NonnullOwnPtr<ExecutionContext> create(u32 registers_and_constants_and_locals_count, u32 arguments_count);
[[nodiscard]] NonnullOwnPtr<ExecutionContext> copy() const; [[nodiscard]] NonnullOwnPtr<ExecutionContext> copy() const;
~ExecutionContext(); ~ExecutionContext() = default;
void visit_edges(Cell::Visitor&); void visit_edges(Cell::Visitor&);
@ -51,6 +64,9 @@ private:
public: public:
ExecutionContext(u32 registers_and_constants_and_locals_count, u32 arguments_count); ExecutionContext(u32 registers_and_constants_and_locals_count, u32 arguments_count);
GC::Ptr<ExecutionContextRareData> rare_data() const { return m_rare_data; }
GC::Ref<ExecutionContextRareData> ensure_rare_data();
void operator delete(void* ptr); void operator delete(void* ptr);
GC::Ptr<FunctionObject> function; // [[Function]] GC::Ptr<FunctionObject> function; // [[Function]]
@ -105,9 +121,9 @@ public:
Span<Value> arguments; Span<Value> arguments;
Vector<Bytecode::UnwindInfo> unwind_contexts; // NOTE: Rarely used data members go here to keep the size of ExecutionContext down,
Vector<Optional<size_t>> previously_scheduled_jumps; // and to avoid needing an ExecutionContext destructor in the common case.
Vector<GC::Ptr<Environment>> saved_lexical_environments; GC::Ptr<ExecutionContextRareData> m_rare_data;
u32 passed_argument_count { 0 }; u32 passed_argument_count { 0 };
@ -122,21 +138,19 @@ private:
u32 registers_and_constants_and_locals_and_arguments_count { 0 }; u32 registers_and_constants_and_locals_and_arguments_count { 0 };
}; };
#define ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK_WITHOUT_CLEARING_ARGS(execution_context, \ static_assert(IsTriviallyDestructible<ExecutionContext>);
registers_and_constants_and_locals_count, \
arguments_count) \ #define ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK_WITHOUT_CLEARING_ARGS(execution_context, \
auto execution_context_size = sizeof(JS::ExecutionContext) \ registers_and_constants_and_locals_count, \
+ (((registers_and_constants_and_locals_count) + (arguments_count)) \ arguments_count) \
* sizeof(JS::Value)); \ auto execution_context_size = sizeof(JS::ExecutionContext) \
\ + (((registers_and_constants_and_locals_count) + (arguments_count)) \
void* execution_context_memory = alloca(execution_context_size); \ * sizeof(JS::Value)); \
\ \
execution_context = new (execution_context_memory) \ void* execution_context_memory = alloca(execution_context_size); \
JS::ExecutionContext((registers_and_constants_and_locals_count), (arguments_count)); \ \
\ execution_context = new (execution_context_memory) \
ScopeGuard run_execution_context_destructor([execution_context] { \ JS::ExecutionContext((registers_and_constants_and_locals_count), (arguments_count));
execution_context->~ExecutionContext(); \
})
#define ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK(execution_context, registers_and_constants_and_locals_count, \ #define ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK(execution_context, registers_and_constants_and_locals_count, \
arguments_count) \ arguments_count) \