Skip to content

Commit 1e0b565

Browse files
committed
LibJS: Move ExecutionContext members with destructors to "rare data"
This makes ExecutionContext trivially destructible, which means less work to do on function exit.
1 parent 9ded35f commit 1e0b565

File tree

3 files changed

+73
-45
lines changed

3 files changed

+73
-45
lines changed

Libraries/LibJS/Bytecode/Interpreter.cpp

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,8 @@ NEVER_INLINE Interpreter::HandleExceptionResponse Interpreter::handle_exception(
311311
auto& handler = handlers->handler_offset;
312312
auto& finalizer = handlers->finalizer_offset;
313313

314-
VERIFY(!running_execution_context().unwind_contexts.is_empty());
315-
auto& unwind_context = running_execution_context().unwind_contexts.last();
314+
auto& unwind_contexts = running_execution_context().ensure_rare_data()->unwind_contexts;
315+
auto& unwind_context = unwind_contexts.last();
316316
VERIFY(unwind_context.executable == &current_executable());
317317

318318
if (handler.has_value()) {
@@ -485,8 +485,8 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
485485
do_return(saved_return_value());
486486
if (auto handlers = executable.exception_handlers_for_offset(program_counter); handlers.has_value()) {
487487
if (auto finalizer = handlers.value().finalizer_offset; finalizer.has_value()) {
488-
VERIFY(!running_execution_context.unwind_contexts.is_empty());
489-
auto& unwind_context = running_execution_context.unwind_contexts.last();
488+
auto& unwind_contexts = running_execution_context.ensure_rare_data()->unwind_contexts;
489+
auto& unwind_context = unwind_contexts.last();
490490
VERIFY(unwind_context.executable == &current_executable());
491491
reg(Register::saved_return_value()) = reg(Register::return_value());
492492
reg(Register::return_value()) = js_special_empty_value();
@@ -497,7 +497,7 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
497497
}
498498
return;
499499
}
500-
auto const old_scheduled_jump = running_execution_context.previously_scheduled_jumps.take_last();
500+
auto const old_scheduled_jump = running_execution_context.ensure_rare_data()->previously_scheduled_jumps.take_last();
501501
if (m_running_execution_context->scheduled_jump.has_value()) {
502502
program_counter = m_running_execution_context->scheduled_jump.value();
503503
m_running_execution_context->scheduled_jump = {};
@@ -762,23 +762,23 @@ ThrowCompletionOr<Value> Interpreter::run_executable(ExecutionContext& context,
762762

763763
void Interpreter::enter_unwind_context()
764764
{
765-
running_execution_context().unwind_contexts.empend(
765+
running_execution_context().ensure_rare_data()->unwind_contexts.empend(
766766
current_executable(),
767767
running_execution_context().lexical_environment);
768-
running_execution_context().previously_scheduled_jumps.append(m_running_execution_context->scheduled_jump);
768+
running_execution_context().rare_data()->previously_scheduled_jumps.append(m_running_execution_context->scheduled_jump);
769769
m_running_execution_context->scheduled_jump = {};
770770
}
771771

772772
void Interpreter::leave_unwind_context()
773773
{
774-
running_execution_context().unwind_contexts.take_last();
774+
running_execution_context().rare_data()->unwind_contexts.take_last();
775775
}
776776

777777
void Interpreter::catch_exception(Operand dst)
778778
{
779779
set(dst, reg(Register::exception()));
780780
reg(Register::exception()) = js_special_empty_value();
781-
auto& context = running_execution_context().unwind_contexts.last();
781+
auto& context = running_execution_context().rare_data()->unwind_contexts.last();
782782
VERIFY(!context.handler_called);
783783
VERIFY(context.executable == &current_executable());
784784
context.handler_called = true;
@@ -787,19 +787,19 @@ void Interpreter::catch_exception(Operand dst)
787787

788788
void Interpreter::restore_scheduled_jump()
789789
{
790-
m_running_execution_context->scheduled_jump = running_execution_context().previously_scheduled_jumps.take_last();
790+
m_running_execution_context->scheduled_jump = running_execution_context().rare_data()->previously_scheduled_jumps.take_last();
791791
}
792792

793793
void Interpreter::leave_finally()
794794
{
795795
reg(Register::exception()) = js_special_empty_value();
796-
m_running_execution_context->scheduled_jump = running_execution_context().previously_scheduled_jumps.take_last();
796+
m_running_execution_context->scheduled_jump = running_execution_context().rare_data()->previously_scheduled_jumps.take_last();
797797
}
798798

799799
void Interpreter::enter_object_environment(Object& object)
800800
{
801801
auto& old_environment = running_execution_context().lexical_environment;
802-
running_execution_context().saved_lexical_environments.append(old_environment);
802+
running_execution_context().ensure_rare_data()->saved_lexical_environments.append(old_environment);
803803
running_execution_context().lexical_environment = new_object_environment(object, true, old_environment);
804804
}
805805

@@ -1593,7 +1593,7 @@ inline ThrowCompletionOr<ECMAScriptFunctionObject*> new_class(VM& vm, Value supe
15931593

15941594
// NOTE: NewClass expects classEnv to be active lexical environment
15951595
auto* class_environment = vm.lexical_environment();
1596-
vm.running_execution_context().lexical_environment = vm.running_execution_context().saved_lexical_environments.take_last();
1596+
vm.running_execution_context().lexical_environment = vm.running_execution_context().rare_data()->saved_lexical_environments.take_last();
15971597

15981598
Optional<Utf16FlyString> binding_name;
15991599
Utf16FlyString class_name;
@@ -2416,7 +2416,7 @@ void CreateLexicalEnvironment::execute_impl(Bytecode::Interpreter& interpreter)
24162416
return environment;
24172417
};
24182418
auto& running_execution_context = interpreter.running_execution_context();
2419-
running_execution_context.saved_lexical_environments.append(make_and_swap_envs(running_execution_context.lexical_environment));
2419+
running_execution_context.ensure_rare_data()->saved_lexical_environments.append(make_and_swap_envs(running_execution_context.lexical_environment));
24202420
if (m_dst.has_value())
24212421
interpreter.set(*m_dst, running_execution_context.lexical_environment);
24222422
}
@@ -3161,7 +3161,7 @@ ThrowCompletionOr<void> ThrowIfTDZ::execute_impl(Bytecode::Interpreter& interpre
31613161
void LeaveLexicalEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const
31623162
{
31633163
auto& running_execution_context = interpreter.running_execution_context();
3164-
running_execution_context.lexical_environment = running_execution_context.saved_lexical_environments.take_last();
3164+
running_execution_context.lexical_environment = running_execution_context.rare_data()->saved_lexical_environments.take_last();
31653165
}
31663166

31673167
void LeavePrivateEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const

Libraries/LibJS/Runtime/ExecutionContext.cpp

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
namespace JS {
1616

1717
GC_DEFINE_ALLOCATOR(CachedSourceRange);
18+
GC_DEFINE_ALLOCATOR(ExecutionContextRareData);
1819

1920
class ExecutionContextAllocator {
2021
public:
@@ -110,10 +111,6 @@ ExecutionContext::ExecutionContext(u32 registers_and_constants_and_locals_count,
110111
arguments = { registers_and_constants_and_locals_and_arguments + registers_and_constants_and_locals_count, arguments_count };
111112
}
112113

113-
ExecutionContext::~ExecutionContext()
114-
{
115-
}
116-
117114
NonnullOwnPtr<ExecutionContext> ExecutionContext::copy() const
118115
{
119116
auto copy = create(registers_and_constants_and_locals_and_arguments_count, arguments.size());
@@ -127,9 +124,12 @@ NonnullOwnPtr<ExecutionContext> ExecutionContext::copy() const
127124
copy->this_value = this_value;
128125
copy->executable = executable;
129126
copy->passed_argument_count = passed_argument_count;
130-
copy->unwind_contexts = unwind_contexts;
131-
copy->saved_lexical_environments = saved_lexical_environments;
132-
copy->previously_scheduled_jumps = previously_scheduled_jumps;
127+
if (m_rare_data) {
128+
auto copy_rare_data = copy->ensure_rare_data();
129+
copy_rare_data->unwind_contexts = m_rare_data->unwind_contexts;
130+
copy_rare_data->saved_lexical_environments = m_rare_data->saved_lexical_environments;
131+
copy_rare_data->previously_scheduled_jumps = m_rare_data->previously_scheduled_jumps;
132+
}
133133
copy->registers_and_constants_and_locals_and_arguments_count = registers_and_constants_and_locals_and_arguments_count;
134134
for (size_t i = 0; i < registers_and_constants_and_locals_and_arguments_count; ++i)
135135
copy->registers_and_constants_and_locals_and_arguments()[i] = registers_and_constants_and_locals_and_arguments()[i];
@@ -146,19 +146,33 @@ void ExecutionContext::visit_edges(Cell::Visitor& visitor)
146146
visitor.visit(private_environment);
147147
visitor.visit(context_owner);
148148
visitor.visit(cached_source_range);
149+
visitor.visit(m_rare_data);
149150
if (this_value.has_value())
150151
visitor.visit(*this_value);
151152
visitor.visit(executable);
152153
visitor.visit(registers_and_constants_and_locals_and_arguments_span());
153-
for (auto& context : unwind_contexts) {
154-
visitor.visit(context.lexical_environment);
155-
}
156-
visitor.visit(saved_lexical_environments);
157154
script_or_module.visit(
158155
[](Empty) {},
159156
[&](auto& script_or_module) {
160157
visitor.visit(script_or_module);
161158
});
162159
}
163160

161+
void ExecutionContextRareData::visit_edges(Cell::Visitor& visitor)
162+
{
163+
Base::visit_edges(visitor);
164+
for (auto& context : unwind_contexts) {
165+
visitor.visit(context.lexical_environment);
166+
}
167+
visitor.visit(saved_lexical_environments);
168+
}
169+
170+
GC::Ref<ExecutionContextRareData> ExecutionContext::ensure_rare_data()
171+
{
172+
if (!m_rare_data) {
173+
m_rare_data = executable->heap().allocate<ExecutionContextRareData>();
174+
}
175+
return *m_rare_data;
176+
}
177+
164178
}

Libraries/LibJS/Runtime/ExecutionContext.h

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,25 @@ class CachedSourceRange final : public GC::Cell {
3636
Variant<UnrealizedSourceRange, SourceRange> source_range;
3737
};
3838

39+
class ExecutionContextRareData final : public GC::Cell {
40+
GC_CELL(ExecutionContextRareData, GC::Cell);
41+
GC_DECLARE_ALLOCATOR(ExecutionContextRareData);
42+
43+
public:
44+
Vector<Bytecode::UnwindInfo> unwind_contexts;
45+
Vector<Optional<size_t>> previously_scheduled_jumps;
46+
Vector<GC::Ptr<Environment>> saved_lexical_environments;
47+
48+
private:
49+
virtual void visit_edges(Cell::Visitor&) override;
50+
};
51+
3952
// 9.4 Execution Contexts, https://tc39.es/ecma262/#sec-execution-contexts
4053
struct JS_API ExecutionContext {
4154
static NonnullOwnPtr<ExecutionContext> create(u32 registers_and_constants_and_locals_count, u32 arguments_count);
4255
[[nodiscard]] NonnullOwnPtr<ExecutionContext> copy() const;
4356

44-
~ExecutionContext();
57+
~ExecutionContext() = default;
4558

4659
void visit_edges(Cell::Visitor&);
4760

@@ -51,6 +64,9 @@ struct JS_API ExecutionContext {
5164
public:
5265
ExecutionContext(u32 registers_and_constants_and_locals_count, u32 arguments_count);
5366

67+
GC::Ptr<ExecutionContextRareData> rare_data() const { return m_rare_data; }
68+
GC::Ref<ExecutionContextRareData> ensure_rare_data();
69+
5470
void operator delete(void* ptr);
5571

5672
GC::Ptr<FunctionObject> function; // [[Function]]
@@ -105,9 +121,9 @@ struct JS_API ExecutionContext {
105121

106122
Span<Value> arguments;
107123

108-
Vector<Bytecode::UnwindInfo> unwind_contexts;
109-
Vector<Optional<size_t>> previously_scheduled_jumps;
110-
Vector<GC::Ptr<Environment>> saved_lexical_environments;
124+
// NOTE: Rarely used data members go here to keep the size of ExecutionContext down,
125+
// and to avoid needing an ExecutionContext destructor in the common case.
126+
GC::Ptr<ExecutionContextRareData> m_rare_data;
111127

112128
u32 passed_argument_count { 0 };
113129

@@ -122,21 +138,19 @@ struct JS_API ExecutionContext {
122138
u32 registers_and_constants_and_locals_and_arguments_count { 0 };
123139
};
124140

125-
#define ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK_WITHOUT_CLEARING_ARGS(execution_context, \
126-
registers_and_constants_and_locals_count, \
127-
arguments_count) \
128-
auto execution_context_size = sizeof(JS::ExecutionContext) \
129-
+ (((registers_and_constants_and_locals_count) + (arguments_count)) \
130-
* sizeof(JS::Value)); \
131-
\
132-
void* execution_context_memory = alloca(execution_context_size); \
133-
\
134-
execution_context = new (execution_context_memory) \
135-
JS::ExecutionContext((registers_and_constants_and_locals_count), (arguments_count)); \
136-
\
137-
ScopeGuard run_execution_context_destructor([execution_context] { \
138-
execution_context->~ExecutionContext(); \
139-
})
141+
static_assert(IsTriviallyDestructible<ExecutionContext>);
142+
143+
#define ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK_WITHOUT_CLEARING_ARGS(execution_context, \
144+
registers_and_constants_and_locals_count, \
145+
arguments_count) \
146+
auto execution_context_size = sizeof(JS::ExecutionContext) \
147+
+ (((registers_and_constants_and_locals_count) + (arguments_count)) \
148+
* sizeof(JS::Value)); \
149+
\
150+
void* execution_context_memory = alloca(execution_context_size); \
151+
\
152+
execution_context = new (execution_context_memory) \
153+
JS::ExecutionContext((registers_and_constants_and_locals_count), (arguments_count));
140154

141155
#define ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK(execution_context, registers_and_constants_and_locals_count, \
142156
arguments_count) \

0 commit comments

Comments
 (0)