From e5474c384dcb9d6323854f33b2b5ec630522c37c Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Tue, 26 Sep 2023 19:43:44 +0200 Subject: [PATCH] LibJS: Inline flow control ops in the bytecode interpreter loop Instead of calling out to helper functions for flow control (and then checking control flags on every iteration), we now simply inline those ops in the interpreter loop directly. --- .../Libraries/LibJS/Bytecode/Interpreter.cpp | 87 +++++++++++++------ .../Libraries/LibJS/Bytecode/Interpreter.h | 12 --- Userland/Libraries/LibJS/Bytecode/Op.cpp | 60 +++++-------- 3 files changed, 80 insertions(+), 79 deletions(-) diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index 44cd8e7874..e493bfbb7b 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -171,12 +171,67 @@ void Interpreter::run_bytecode() bool will_return = false; bool will_yield = false; + ThrowCompletionOr result; + while (!pc.at_end()) { auto& instruction = *pc; - auto ran_or_error = instruction.execute(*this); - if (ran_or_error.is_error()) [[unlikely]] { - reg(Register::exception()) = *ran_or_error.throw_completion().value(); + switch (instruction.type()) { + case Instruction::Type::Jump: + m_current_block = &static_cast(instruction).true_target()->block(); + goto start; + case Instruction::Type::JumpConditional: + if (accumulator().to_boolean()) + m_current_block = &static_cast(instruction).true_target()->block(); + else + m_current_block = &static_cast(instruction).false_target()->block(); + goto start; + case Instruction::Type::JumpNullish: + if (accumulator().is_nullish()) + m_current_block = &static_cast(instruction).true_target()->block(); + else + m_current_block = &static_cast(instruction).false_target()->block(); + goto start; + case Instruction::Type::JumpUndefined: + if (accumulator().is_undefined()) + m_current_block = &static_cast(instruction).true_target()->block(); + else + m_current_block = &static_cast(instruction).false_target()->block(); + goto start; + case Instruction::Type::EnterUnwindContext: + enter_unwind_context( + static_cast(instruction).handler_target(), + static_cast(instruction).finalizer_target()); + m_current_block = &static_cast(instruction).entry_point().block(); + goto start; + case Instruction::Type::ContinuePendingUnwind: + if (auto exception = reg(Register::exception()); !exception.is_empty()) { + result = throw_completion(exception); + break; + } + if (!saved_return_value().is_empty()) { + do_return(saved_return_value()); + break; + } + if (m_scheduled_jump) { + // FIXME: If we `break` or `continue` in the finally, we need to clear + // this field + m_current_block = exchange(m_scheduled_jump, nullptr); + } else { + m_current_block = &static_cast(instruction).resume_target().block(); + } + goto start; + case Instruction::Type::ScheduleJump: + m_scheduled_jump = &static_cast(instruction).target().block(); + m_current_block = unwind_contexts().last().finalizer; + goto start; + default: + result = instruction.execute(*this); + break; + } + + if (result.is_error()) [[unlikely]] { + reg(Register::exception()) = *result.throw_completion().value(); if (unwind_contexts().is_empty()) return; auto& unwind_context = unwind_contexts().last(); @@ -204,10 +259,7 @@ void Interpreter::run_bytecode() // If you run into this, you probably forgot to remove the current unwind_context somewhere. VERIFY_NOT_REACHED(); } - if (m_pending_jump.has_value()) { - m_current_block = m_pending_jump.release_value(); - goto start; - } + if (!reg(Register::return_value()).is_empty()) { will_return = true; // Note: A `yield` statement will not go through a finally statement, @@ -315,27 +367,6 @@ void Interpreter::leave_unwind_context() unwind_contexts().take_last(); } -ThrowCompletionOr Interpreter::continue_pending_unwind(Label const& resume_label) -{ - if (auto exception = reg(Register::exception()); !exception.is_empty()) - return throw_completion(exception); - - if (!saved_return_value().is_empty()) { - do_return(saved_return_value()); - return {}; - } - - if (m_scheduled_jump) { - // FIXME: If we `break` or `continue` in the finally, we need to clear - // this field - jump(Label { *m_scheduled_jump }); - m_scheduled_jump = nullptr; - } else { - jump(resume_label); - } - return {}; -} - ThrowCompletionOr> compile(VM& vm, ASTNode const& node, FunctionKind kind, DeprecatedFlyString const& name) { auto executable_result = Bytecode::Generator::generate(node, kind); diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.h b/Userland/Libraries/LibJS/Bytecode/Interpreter.h index eae8bf7748..448939857a 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.h +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.h @@ -64,16 +64,6 @@ public: auto& saved_lexical_environment_stack() { return call_frame().saved_lexical_environments; } auto& unwind_contexts() { return call_frame().unwind_contexts; } - void jump(Label const& label) - { - m_pending_jump = &label.block(); - } - void schedule_jump(Label const& label) - { - m_scheduled_jump = &label.block(); - VERIFY(unwind_contexts().last().finalizer); - jump(Label { *unwind_contexts().last().finalizer }); - } void do_return(Value value) { reg(Register::return_value()) = value; @@ -82,7 +72,6 @@ public: void enter_unwind_context(Optional