From fb979dcf349f08e9ae9c2750b71e0e8435cf397c Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 2 Jul 2023 12:53:10 +0200 Subject: [PATCH] LibJS/Bytecode: Make Bytecode::Interpreter participate in GC marking Since the relationship between VM and Bytecode::Interpreter is now clear, we can have VM ask the Interpreter for roots in the GC marking pass. This avoids having to register and unregister handles and MarkedVectors over and over. Since GeneratorObject can also own a RegisterWindow, we share the code in a RegisterWindow::visit_edges() helper. ~4% speed-up on Kraken/stanford-crypto-ccm.js :^) --- .../Libraries/LibJS/Bytecode/BasicBlock.h | 4 +- .../Libraries/LibJS/Bytecode/Interpreter.cpp | 44 +++++++++++-------- .../Libraries/LibJS/Bytecode/Interpreter.h | 28 +++++++++--- Userland/Libraries/LibJS/Heap/Heap.cpp | 5 +++ .../LibJS/Runtime/GeneratorObject.cpp | 2 + 5 files changed, 56 insertions(+), 27 deletions(-) diff --git a/Userland/Libraries/LibJS/Bytecode/BasicBlock.h b/Userland/Libraries/LibJS/Bytecode/BasicBlock.h index fe046434a5..653989de31 100644 --- a/Userland/Libraries/LibJS/Bytecode/BasicBlock.h +++ b/Userland/Libraries/LibJS/Bytecode/BasicBlock.h @@ -18,8 +18,8 @@ struct UnwindInfo { BasicBlock const* handler; BasicBlock const* finalizer; - Handle lexical_environment; - Handle variable_environment; + JS::GCPtr lexical_environment; + JS::GCPtr variable_environment; }; class BasicBlock { diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index d0ec45c665..317ebe5bd1 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -50,6 +50,19 @@ Interpreter::~Interpreter() { } +void Interpreter::visit_edges(Cell::Visitor& visitor) +{ + if (m_return_value.has_value()) + visitor.visit(*m_return_value); + if (m_saved_return_value.has_value()) + visitor.visit(*m_saved_return_value); + if (m_saved_exception.has_value()) + visitor.visit(*m_saved_exception); + for (auto& window : m_register_windows) { + window.visit([&](auto& value) { value->visit_edges(visitor); }); + } +} + // 16.1.6 ScriptEvaluation ( scriptRecord ), https://tc39.es/ecma262/#sec-runtime-semantics-scriptevaluation ThrowCompletionOr Interpreter::run(Script& script_record, JS::GCPtr lexical_environment_override) { @@ -223,7 +236,7 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Realm& realm, Execu auto ran_or_error = instruction.execute(*this); if (ran_or_error.is_error()) { auto exception_value = *ran_or_error.throw_completion().value(); - m_saved_exception = make_handle(exception_value); + m_saved_exception = exception_value; if (unwind_contexts().is_empty()) break; auto& unwind_context = unwind_contexts().last(); @@ -254,7 +267,7 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Realm& realm, Execu will_jump = true; break; } - if (!m_return_value.is_empty()) { + if (m_return_value.has_value()) { will_return = true; // Note: A `yield` statement will not go through a finally statement, // hence we need to set a flag to not do so, @@ -273,7 +286,7 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Realm& realm, Execu if (!unwind_contexts().is_empty() && !will_yield) { auto& unwind_context = unwind_contexts().last(); if (unwind_context.executable == m_current_executable && unwind_context.finalizer) { - m_saved_return_value = make_handle(m_return_value); + m_saved_return_value = m_return_value; m_return_value = {}; m_current_block = unwind_context.finalizer; // the unwind_context will be pop'ed when entering the finally block @@ -284,7 +297,7 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Realm& realm, Execu if (pc.at_end()) break; - if (!m_saved_exception.is_null()) + if (m_saved_exception.has_value()) break; if (will_return) @@ -307,12 +320,10 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Realm& realm, Execu auto frame = pop_register_window(); Value return_value = js_undefined(); - if (!m_return_value.is_empty()) { - return_value = m_return_value; - m_return_value = {}; - } else if (!m_saved_return_value.is_null() && m_saved_exception.is_null()) { - return_value = m_saved_return_value.value(); - m_saved_return_value = {}; + if (m_return_value.has_value()) { + return_value = m_return_value.release_value(); + } else if (m_saved_return_value.has_value() && !m_saved_exception.has_value()) { + return_value = m_saved_return_value.release_value(); } // NOTE: The return value from a called function is put into $0 in the caller context. @@ -330,7 +341,7 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Realm& realm, Execu vm().finish_execution_generation(); - if (!m_saved_exception.is_null()) { + if (m_saved_exception.has_value()) { Value thrown_value = m_saved_exception.value(); m_saved_exception = {}; m_saved_return_value = {}; @@ -361,15 +372,12 @@ void Interpreter::leave_unwind_context() ThrowCompletionOr Interpreter::continue_pending_unwind(Label const& resume_label) { - if (!m_saved_exception.is_null()) { - auto result = throw_completion(m_saved_exception.value()); - m_saved_exception = {}; - return result; + if (m_saved_exception.has_value()) { + return throw_completion(m_saved_exception.release_value()); } - if (!m_saved_return_value.is_null()) { - do_return(m_saved_return_value.value()); - m_saved_return_value = {}; + if (m_saved_return_value.has_value()) { + do_return(m_saved_return_value.release_value()); return {}; } diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.h b/Userland/Libraries/LibJS/Bytecode/Interpreter.h index ba14691723..515c50089e 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.h +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.h @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -21,9 +20,22 @@ class InstructionStreamIterator; class PassManager; struct RegisterWindow { - MarkedVector registers; - MarkedVector> saved_lexical_environments; - MarkedVector> saved_variable_environments; + void visit_edges(Cell::Visitor& visitor) + { + for (auto const& value : registers) + visitor.visit(value); + for (auto const& environment : saved_lexical_environments) + visitor.visit(environment); + for (auto const& environment : saved_variable_environments) + visitor.visit(environment); + for (auto& context : unwind_contexts) { + visitor.visit(context.lexical_environment); + visitor.visit(context.variable_environment); + } + } + Vector registers; + Vector> saved_lexical_environments; + Vector> saved_variable_environments; Vector unwind_contexts; }; @@ -90,6 +102,8 @@ public: VM::InterpreterExecutionScope ast_interpreter_scope(Realm&); + void visit_edges(Cell::Visitor&); + private: RegisterWindow& window() { @@ -112,10 +126,10 @@ private: Span m_current_register_window; Optional m_pending_jump; BasicBlock const* m_scheduled_jump { nullptr }; - Value m_return_value; - Handle m_saved_return_value; + Optional m_return_value; + Optional m_saved_return_value; + Optional m_saved_exception; Executable const* m_current_executable { nullptr }; - Handle m_saved_exception; OwnPtr m_ast_interpreter; BasicBlock const* m_current_block { nullptr }; InstructionStreamIterator* m_pc { nullptr }; diff --git a/Userland/Libraries/LibJS/Heap/Heap.cpp b/Userland/Libraries/LibJS/Heap/Heap.cpp index eceee1264c..659582f766 100644 --- a/Userland/Libraries/LibJS/Heap/Heap.cpp +++ b/Userland/Libraries/LibJS/Heap/Heap.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -270,6 +271,10 @@ void Heap::mark_live_cells(HashTable const& roots) dbgln_if(HEAP_DEBUG, "mark_live_cells:"); MarkingVisitor visitor(roots); + + if (auto* bytecode_interpreter = vm().bytecode_interpreter_if_exists()) + bytecode_interpreter->visit_edges(visitor); + visitor.mark_all_live_cells(); for (auto& inverse_root : m_uprooted_cells) diff --git a/Userland/Libraries/LibJS/Runtime/GeneratorObject.cpp b/Userland/Libraries/LibJS/Runtime/GeneratorObject.cpp index 4ec105b18d..192034e3ef 100644 --- a/Userland/Libraries/LibJS/Runtime/GeneratorObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/GeneratorObject.cpp @@ -52,6 +52,8 @@ void GeneratorObject::visit_edges(Cell::Visitor& visitor) visitor.visit(m_generating_function); visitor.visit(m_previous_value); m_execution_context.visit_edges(visitor); + if (m_frame.has_value()) + m_frame->visit_edges(visitor); } // 27.5.3.2 GeneratorValidate ( generator, generatorBrand ), https://tc39.es/ecma262/#sec-generatorvalidate