1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 08:47:44 +00:00

LibJS: Put Bytecode::CallFrame + register slots in a single allocation

The number of registers in a call frame never changes, so we can
allocate it at the end of the CallFrame object and save ourselves the
cost of allocating separate Vector storage for every call frame.
This commit is contained in:
Andreas Kling 2023-11-27 17:27:51 +01:00
parent 3dc5f467a8
commit 3fc0333ee6
11 changed files with 57 additions and 36 deletions

View file

@ -691,7 +691,7 @@ ThrowCompletionOr<Value> perform_eval(VM& vm, Value x, CallerMode strict_caller,
if (result_or_error.value.is_error())
return result_or_error.value.release_error();
auto& result = result_or_error.frame->registers[0];
auto& result = result_or_error.frame->registers()[0];
if (!result.is_empty())
eval_result = result;

View file

@ -16,7 +16,7 @@ namespace JS {
JS_DEFINE_ALLOCATOR(AsyncGenerator);
ThrowCompletionOr<NonnullGCPtr<AsyncGenerator>> AsyncGenerator::create(Realm& realm, Value initial_value, ECMAScriptFunctionObject* generating_function, NonnullOwnPtr<ExecutionContext> execution_context, Bytecode::CallFrame frame)
ThrowCompletionOr<NonnullGCPtr<AsyncGenerator>> AsyncGenerator::create(Realm& realm, Value initial_value, ECMAScriptFunctionObject* generating_function, NonnullOwnPtr<ExecutionContext> execution_context, NonnullOwnPtr<Bytecode::CallFrame> frame)
{
auto& vm = realm.vm();
// This is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png)
@ -45,7 +45,7 @@ void AsyncGenerator::visit_edges(Cell::Visitor& visitor)
}
visitor.visit(m_generating_function);
visitor.visit(m_previous_value);
if (m_frame.has_value())
if (m_frame)
m_frame->visit_edges(visitor);
visitor.visit(m_current_promise);
}
@ -192,18 +192,18 @@ void AsyncGenerator::execute(VM& vm, Completion completion)
VERIFY(!m_generating_function->bytecode_executable()->basic_blocks.find_if([next_block](auto& block) { return block == next_block; }).is_end());
Bytecode::CallFrame* frame = nullptr;
if (m_frame.has_value())
frame = &m_frame.value();
if (m_frame)
frame = m_frame.ptr();
if (frame)
frame->registers[0] = completion_object;
frame->registers()[0] = completion_object;
else
bytecode_interpreter.accumulator() = completion_object;
auto next_result = bytecode_interpreter.run_and_return_frame(*m_generating_function->bytecode_executable(), next_block, frame);
if (!m_frame.has_value())
m_frame = move(*next_result.frame);
if (!m_frame)
m_frame = move(next_result.frame);
auto result_value = move(next_result.value);
if (!result_value.is_throw_completion()) {

View file

@ -28,7 +28,7 @@ public:
Completed,
};
static ThrowCompletionOr<NonnullGCPtr<AsyncGenerator>> create(Realm&, Value, ECMAScriptFunctionObject*, NonnullOwnPtr<ExecutionContext>, Bytecode::CallFrame);
static ThrowCompletionOr<NonnullGCPtr<AsyncGenerator>> create(Realm&, Value, ECMAScriptFunctionObject*, NonnullOwnPtr<ExecutionContext>, NonnullOwnPtr<Bytecode::CallFrame>);
virtual ~AsyncGenerator() override = default;
@ -60,7 +60,7 @@ private:
GCPtr<ECMAScriptFunctionObject> m_generating_function;
Value m_previous_value;
Optional<Bytecode::CallFrame> m_frame;
OwnPtr<Bytecode::CallFrame> m_frame;
GCPtr<Promise> m_current_promise;
};

View file

@ -704,7 +704,7 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
if (value_and_frame.value.is_error())
return value_and_frame.value.release_error();
// Resulting value is in the accumulator.
argument_value = value_and_frame.frame->registers.at(0);
argument_value = value_and_frame.frame->registers()[0];
} else {
argument_value = js_undefined();
}
@ -1225,11 +1225,11 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
return { Completion::Type::Return, result.value_or(js_undefined()), {} };
if (m_kind == FunctionKind::AsyncGenerator) {
auto async_generator_object = TRY(AsyncGenerator::create(realm, result, this, vm.running_execution_context().copy(), move(*result_and_frame.frame)));
auto async_generator_object = TRY(AsyncGenerator::create(realm, result, this, vm.running_execution_context().copy(), result_and_frame.frame.release_nonnull()));
return { Completion::Type::Return, async_generator_object, {} };
}
auto generator_object = TRY(GeneratorObject::create(realm, result, this, vm.running_execution_context().copy(), move(*result_and_frame.frame)));
auto generator_object = TRY(GeneratorObject::create(realm, result, this, vm.running_execution_context().copy(), result_and_frame.frame.release_nonnull()));
// NOTE: Async functions are entirely transformed to generator functions, and wrapped in a custom driver that returns a promise
// See AwaitExpression::generate_bytecode() for the transformation.

View file

@ -16,7 +16,7 @@ namespace JS {
JS_DEFINE_ALLOCATOR(GeneratorObject);
ThrowCompletionOr<NonnullGCPtr<GeneratorObject>> GeneratorObject::create(Realm& realm, Value initial_value, ECMAScriptFunctionObject* generating_function, NonnullOwnPtr<ExecutionContext> execution_context, Bytecode::CallFrame frame)
ThrowCompletionOr<NonnullGCPtr<GeneratorObject>> GeneratorObject::create(Realm& realm, Value initial_value, ECMAScriptFunctionObject* generating_function, NonnullOwnPtr<ExecutionContext> execution_context, NonnullOwnPtr<Bytecode::CallFrame> frame)
{
auto& vm = realm.vm();
// This is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png)
@ -49,7 +49,7 @@ void GeneratorObject::visit_edges(Cell::Visitor& visitor)
Base::visit_edges(visitor);
visitor.visit(m_generating_function);
visitor.visit(m_previous_value);
if (m_frame.has_value())
if (m_frame)
m_frame->visit_edges(visitor);
}
@ -113,11 +113,11 @@ ThrowCompletionOr<Value> GeneratorObject::execute(VM& vm, Completion const& comp
VERIFY(!m_generating_function->bytecode_executable()->basic_blocks.find_if([next_block](auto& block) { return block == next_block; }).is_end());
Bytecode::CallFrame* frame = nullptr;
if (m_frame.has_value())
frame = &m_frame.value();
if (m_frame)
frame = m_frame;
if (frame)
frame->registers[0] = completion_object;
frame->registers()[0] = completion_object;
else
bytecode_interpreter.accumulator() = completion_object;
@ -125,8 +125,8 @@ ThrowCompletionOr<Value> GeneratorObject::execute(VM& vm, Completion const& comp
vm.pop_execution_context();
if (!m_frame.has_value())
m_frame = move(*next_result.frame);
if (!m_frame)
m_frame = move(next_result.frame);
auto result_value = move(next_result.value);
if (result_value.is_throw_completion()) {

View file

@ -17,7 +17,7 @@ class GeneratorObject : public Object {
JS_DECLARE_ALLOCATOR(GeneratorObject);
public:
static ThrowCompletionOr<NonnullGCPtr<GeneratorObject>> create(Realm&, Value, ECMAScriptFunctionObject*, NonnullOwnPtr<ExecutionContext>, Bytecode::CallFrame);
static ThrowCompletionOr<NonnullGCPtr<GeneratorObject>> create(Realm&, Value, ECMAScriptFunctionObject*, NonnullOwnPtr<ExecutionContext>, NonnullOwnPtr<Bytecode::CallFrame>);
virtual ~GeneratorObject() override = default;
void visit_edges(Cell::Visitor&) override;
@ -43,7 +43,7 @@ private:
NonnullOwnPtr<ExecutionContext> m_execution_context;
GCPtr<ECMAScriptFunctionObject> m_generating_function;
Value m_previous_value;
Optional<Bytecode::CallFrame> m_frame;
OwnPtr<Bytecode::CallFrame> m_frame;
GeneratorState m_generator_state { GeneratorState::SuspendedStart };
Optional<StringView> m_generator_brand;
};

View file

@ -185,7 +185,7 @@ ThrowCompletionOr<Value> perform_shadow_realm_eval(VM& vm, StringView source_tex
result = value_and_frame.value.release_error();
} else {
// Resulting value is in the accumulator.
result = value_and_frame.frame->registers.at(0).value_or(js_undefined());
result = value_and_frame.frame->registers()[0].value_or(js_undefined());
}
}
}

View file

@ -285,7 +285,7 @@ ThrowCompletionOr<Value> VM::execute_ast_node(ASTNode const& node)
auto result_or_error = bytecode_interpreter().run_and_return_frame(*executable, nullptr);
if (result_or_error.value.is_error())
return result_or_error.value.release_error();
return result_or_error.frame->registers[0];
return result_or_error.frame->registers()[0];
}
// 13.15.5.3 Runtime Semantics: PropertyDestructuringAssignmentEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-propertydestructuringassignmentevaluation