浏览代码

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.
Andreas Kling 1 年之前
父节点
当前提交
e5474c384d

+ 59 - 28
Userland/Libraries/LibJS/Bytecode/Interpreter.cpp

@@ -171,12 +171,67 @@ void Interpreter::run_bytecode()
         bool will_return = false;
         bool will_return = false;
         bool will_yield = false;
         bool will_yield = false;
 
 
+        ThrowCompletionOr<void> result;
+
         while (!pc.at_end()) {
         while (!pc.at_end()) {
             auto& instruction = *pc;
             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<Op::Jump const&>(instruction).true_target()->block();
+                goto start;
+            case Instruction::Type::JumpConditional:
+                if (accumulator().to_boolean())
+                    m_current_block = &static_cast<Op::Jump const&>(instruction).true_target()->block();
+                else
+                    m_current_block = &static_cast<Op::Jump const&>(instruction).false_target()->block();
+                goto start;
+            case Instruction::Type::JumpNullish:
+                if (accumulator().is_nullish())
+                    m_current_block = &static_cast<Op::Jump const&>(instruction).true_target()->block();
+                else
+                    m_current_block = &static_cast<Op::Jump const&>(instruction).false_target()->block();
+                goto start;
+            case Instruction::Type::JumpUndefined:
+                if (accumulator().is_undefined())
+                    m_current_block = &static_cast<Op::Jump const&>(instruction).true_target()->block();
+                else
+                    m_current_block = &static_cast<Op::Jump const&>(instruction).false_target()->block();
+                goto start;
+            case Instruction::Type::EnterUnwindContext:
+                enter_unwind_context(
+                    static_cast<Op::EnterUnwindContext const&>(instruction).handler_target(),
+                    static_cast<Op::EnterUnwindContext const&>(instruction).finalizer_target());
+                m_current_block = &static_cast<Op::EnterUnwindContext const&>(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<Op::ContinuePendingUnwind const&>(instruction).resume_target().block();
+                }
+                goto start;
+            case Instruction::Type::ScheduleJump:
+                m_scheduled_jump = &static_cast<Op::ScheduleJump const&>(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())
                 if (unwind_contexts().is_empty())
                     return;
                     return;
                 auto& unwind_context = unwind_contexts().last();
                 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.
                 // If you run into this, you probably forgot to remove the current unwind_context somewhere.
                 VERIFY_NOT_REACHED();
                 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()) {
             if (!reg(Register::return_value()).is_empty()) {
                 will_return = true;
                 will_return = true;
                 // Note: A `yield` statement will not go through a finally statement,
                 // Note: A `yield` statement will not go through a finally statement,
@@ -315,27 +367,6 @@ void Interpreter::leave_unwind_context()
     unwind_contexts().take_last();
     unwind_contexts().take_last();
 }
 }
 
 
-ThrowCompletionOr<void> 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<NonnullOwnPtr<Bytecode::Executable>> compile(VM& vm, ASTNode const& node, FunctionKind kind, DeprecatedFlyString const& name)
 ThrowCompletionOr<NonnullOwnPtr<Bytecode::Executable>> compile(VM& vm, ASTNode const& node, FunctionKind kind, DeprecatedFlyString const& name)
 {
 {
     auto executable_result = Bytecode::Generator::generate(node, kind);
     auto executable_result = Bytecode::Generator::generate(node, kind);

+ 0 - 12
Userland/Libraries/LibJS/Bytecode/Interpreter.h

@@ -64,16 +64,6 @@ public:
     auto& saved_lexical_environment_stack() { return call_frame().saved_lexical_environments; }
     auto& saved_lexical_environment_stack() { return call_frame().saved_lexical_environments; }
     auto& unwind_contexts() { return call_frame().unwind_contexts; }
     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)
     void do_return(Value value)
     {
     {
         reg(Register::return_value()) = value;
         reg(Register::return_value()) = value;
@@ -82,7 +72,6 @@ public:
 
 
     void enter_unwind_context(Optional<Label> handler_target, Optional<Label> finalizer_target);
     void enter_unwind_context(Optional<Label> handler_target, Optional<Label> finalizer_target);
     void leave_unwind_context();
     void leave_unwind_context();
-    ThrowCompletionOr<void> continue_pending_unwind(Label const& resume_label);
 
 
     Executable& current_executable() { return *m_current_executable; }
     Executable& current_executable() { return *m_current_executable; }
     Executable const& current_executable() const { return *m_current_executable; }
     Executable const& current_executable() const { return *m_current_executable; }
@@ -113,7 +102,6 @@ private:
     VM& m_vm;
     VM& m_vm;
     Vector<Variant<NonnullOwnPtr<CallFrame>, CallFrame*>> m_call_frames;
     Vector<Variant<NonnullOwnPtr<CallFrame>, CallFrame*>> m_call_frames;
     Span<Value> m_current_call_frame;
     Span<Value> m_current_call_frame;
-    Optional<BasicBlock const*> m_pending_jump;
     BasicBlock const* m_scheduled_jump { nullptr };
     BasicBlock const* m_scheduled_jump { nullptr };
     Executable* m_current_executable { nullptr };
     Executable* m_current_executable { nullptr };
     BasicBlock const* m_current_block { nullptr };
     BasicBlock const* m_current_block { nullptr };

+ 21 - 39
Userland/Libraries/LibJS/Bytecode/Op.cpp

@@ -759,10 +759,10 @@ ThrowCompletionOr<void> DeleteByIdWithThis::execute_impl(Bytecode::Interpreter&
     return {};
     return {};
 }
 }
 
 
-ThrowCompletionOr<void> Jump::execute_impl(Bytecode::Interpreter& interpreter) const
+ThrowCompletionOr<void> Jump::execute_impl(Bytecode::Interpreter&) const
 {
 {
-    interpreter.jump(*m_true_target);
-    return {};
+    // Handled in the interpreter loop.
+    VERIFY_NOT_REACHED();
 }
 }
 
 
 ThrowCompletionOr<void> ResolveThisBinding::execute_impl(Bytecode::Interpreter& interpreter) const
 ThrowCompletionOr<void> ResolveThisBinding::execute_impl(Bytecode::Interpreter& interpreter) const
@@ -807,40 +807,22 @@ ThrowCompletionOr<void> GetImportMeta::execute_impl(Bytecode::Interpreter& inter
     return {};
     return {};
 }
 }
 
 
-ThrowCompletionOr<void> JumpConditional::execute_impl(Bytecode::Interpreter& interpreter) const
+ThrowCompletionOr<void> JumpConditional::execute_impl(Bytecode::Interpreter&) const
 {
 {
-    VERIFY(m_true_target.has_value());
-    VERIFY(m_false_target.has_value());
-    auto result = interpreter.accumulator();
-    if (result.to_boolean())
-        interpreter.jump(m_true_target.value());
-    else
-        interpreter.jump(m_false_target.value());
-    return {};
+    // Handled in the interpreter loop.
+    VERIFY_NOT_REACHED();
 }
 }
 
 
-ThrowCompletionOr<void> JumpNullish::execute_impl(Bytecode::Interpreter& interpreter) const
+ThrowCompletionOr<void> JumpNullish::execute_impl(Bytecode::Interpreter&) const
 {
 {
-    VERIFY(m_true_target.has_value());
-    VERIFY(m_false_target.has_value());
-    auto result = interpreter.accumulator();
-    if (result.is_nullish())
-        interpreter.jump(m_true_target.value());
-    else
-        interpreter.jump(m_false_target.value());
-    return {};
+    // Handled in the interpreter loop.
+    VERIFY_NOT_REACHED();
 }
 }
 
 
-ThrowCompletionOr<void> JumpUndefined::execute_impl(Bytecode::Interpreter& interpreter) const
+ThrowCompletionOr<void> JumpUndefined::execute_impl(Bytecode::Interpreter&) const
 {
 {
-    VERIFY(m_true_target.has_value());
-    VERIFY(m_false_target.has_value());
-    auto result = interpreter.accumulator();
-    if (result.is_undefined())
-        interpreter.jump(m_true_target.value());
-    else
-        interpreter.jump(m_false_target.value());
-    return {};
+    // Handled in the interpreter loop.
+    VERIFY_NOT_REACHED();
 }
 }
 
 
 // 13.3.8.1 https://tc39.es/ecma262/#sec-runtime-semantics-argumentlistevaluation
 // 13.3.8.1 https://tc39.es/ecma262/#sec-runtime-semantics-argumentlistevaluation
@@ -1058,17 +1040,16 @@ ThrowCompletionOr<void> ThrowIfNullish::execute_impl(Bytecode::Interpreter& inte
     return {};
     return {};
 }
 }
 
 
-ThrowCompletionOr<void> EnterUnwindContext::execute_impl(Bytecode::Interpreter& interpreter) const
+ThrowCompletionOr<void> EnterUnwindContext::execute_impl(Bytecode::Interpreter&) const
 {
 {
-    interpreter.enter_unwind_context(m_handler_target, m_finalizer_target);
-    interpreter.jump(m_entry_point);
-    return {};
+    // Handled in the interpreter loop.
+    VERIFY_NOT_REACHED();
 }
 }
 
 
-ThrowCompletionOr<void> ScheduleJump::execute_impl(Bytecode::Interpreter& interpreter) const
+ThrowCompletionOr<void> ScheduleJump::execute_impl(Bytecode::Interpreter&) const
 {
 {
-    interpreter.schedule_jump(m_target);
-    return {};
+    // Handled in the interpreter loop.
+    VERIFY_NOT_REACHED();
 }
 }
 
 
 ThrowCompletionOr<void> LeaveLexicalEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const
 ThrowCompletionOr<void> LeaveLexicalEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const
@@ -1083,9 +1064,10 @@ ThrowCompletionOr<void> LeaveUnwindContext::execute_impl(Bytecode::Interpreter&
     return {};
     return {};
 }
 }
 
 
-ThrowCompletionOr<void> ContinuePendingUnwind::execute_impl(Bytecode::Interpreter& interpreter) const
+ThrowCompletionOr<void> ContinuePendingUnwind::execute_impl(Bytecode::Interpreter&) const
 {
 {
-    return interpreter.continue_pending_unwind(m_resume_target);
+    // Handled in the interpreter loop.
+    VERIFY_NOT_REACHED();
 }
 }
 
 
 ThrowCompletionOr<void> PushDeclarativeEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const
 ThrowCompletionOr<void> PushDeclarativeEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const