Explorar o código

LibJS/Bytecode: Turn JumpIf condition,@a,@next into JumpTrue/JumpFalse

If one of the jump targets is the very next block, we can convert the
jump instruction into a smaller JumpTrue or JumpFalse.
Andreas Kling hai 1 ano
pai
achega
fae1527a18

+ 24 - 0
Userland/Libraries/LibJS/Bytecode/Generator.cpp

@@ -132,6 +132,30 @@ CodeGenerationErrorOr<NonnullGCPtr<Executable>> Generator::generate(VM& vm, ASTN
                 }
             }
 
+            // OPTIMIZATION: For `JumpIf` where one of the targets is the very next block,
+            //               we can emit a `JumpTrue` or `JumpFalse` (to the other block) instead.
+            if (instruction.type() == Instruction::Type::JumpIf) {
+                auto& jump = static_cast<Bytecode::Op::JumpIf&>(instruction);
+                if (jump.true_target().basic_block_index() == block->index() + 1) {
+                    Op::JumpFalse jump_false(jump.condition(), Label { jump.false_target() });
+                    auto& label = jump_false.target();
+                    size_t label_offset = bytecode.size() + (bit_cast<FlatPtr>(&label) - bit_cast<FlatPtr>(&jump_false));
+                    label_offsets.append(label_offset);
+                    bytecode.append(reinterpret_cast<u8 const*>(&jump_false), jump_false.length());
+                    ++it;
+                    continue;
+                }
+                if (jump.false_target().basic_block_index() == block->index() + 1) {
+                    Op::JumpTrue jump_true(jump.condition(), Label { jump.true_target() });
+                    auto& label = jump_true.target();
+                    size_t label_offset = bytecode.size() + (bit_cast<FlatPtr>(&label) - bit_cast<FlatPtr>(&jump_true));
+                    label_offsets.append(label_offset);
+                    bytecode.append(reinterpret_cast<u8 const*>(&jump_true), jump_true.length());
+                    ++it;
+                    continue;
+                }
+            }
+
             instruction.visit_labels([&](Label& label) {
                 size_t label_offset = bytecode.size() + (bit_cast<FlatPtr>(&label) - bit_cast<FlatPtr>(&instruction));
                 label_offsets.append(label_offset);

+ 2 - 0
Userland/Libraries/LibJS/Bytecode/Instruction.h

@@ -69,8 +69,10 @@
     O(IteratorNext)                    \
     O(IteratorToArray)                 \
     O(Jump)                            \
+    O(JumpFalse)                       \
     O(JumpIf)                          \
     O(JumpNullish)                     \
+    O(JumpTrue)                        \
     O(JumpUndefined)                   \
     O(LeaveFinally)                    \
     O(LeaveLexicalEnvironment)         \

+ 42 - 0
Userland/Libraries/LibJS/Bytecode/Interpreter.cpp

@@ -375,6 +375,22 @@ void Interpreter::run_bytecode(size_t entry_point)
                     program_counter = jump.false_target().address();
                 goto start;
             }
+            case Instruction::Type::JumpTrue: {
+                auto& jump = static_cast<Op::JumpTrue const&>(instruction);
+                if (get(jump.condition()).to_boolean()) {
+                    program_counter = jump.target().address();
+                    goto start;
+                }
+                NEXT_INSTRUCTION();
+            }
+            case Instruction::Type::JumpFalse: {
+                auto& jump = static_cast<Op::JumpFalse const&>(instruction);
+                if (!get(jump.condition()).to_boolean()) {
+                    program_counter = jump.target().address();
+                    goto start;
+                }
+                NEXT_INSTRUCTION();
+            }
             case Instruction::Type::JumpNullish: {
                 auto& jump = static_cast<Op::JumpNullish const&>(instruction);
                 if (get(jump.condition()).is_nullish())
@@ -1241,6 +1257,18 @@ ThrowCompletionOr<void> JumpIf::execute_impl(Bytecode::Interpreter&) const
     __builtin_unreachable();
 }
 
+ThrowCompletionOr<void> JumpTrue::execute_impl(Bytecode::Interpreter&) const
+{
+    // Handled in the interpreter loop.
+    __builtin_unreachable();
+}
+
+ThrowCompletionOr<void> JumpFalse::execute_impl(Bytecode::Interpreter&) const
+{
+    // Handled in the interpreter loop.
+    __builtin_unreachable();
+}
+
 ThrowCompletionOr<void> JumpUndefined::execute_impl(Bytecode::Interpreter&) const
 {
     // Handled in the interpreter loop.
@@ -1909,6 +1937,20 @@ ByteString JumpIf::to_byte_string_impl(Bytecode::Executable const& executable) c
         m_false_target);
 }
 
+ByteString JumpTrue::to_byte_string_impl(Bytecode::Executable const& executable) const
+{
+    return ByteString::formatted("JumpTrue {}, {}",
+        format_operand("condition"sv, m_condition, executable),
+        m_target);
+}
+
+ByteString JumpFalse::to_byte_string_impl(Bytecode::Executable const& executable) const
+{
+    return ByteString::formatted("JumpFalse {}, {}",
+        format_operand("condition"sv, m_condition, executable),
+        m_target);
+}
+
 ByteString JumpNullish::to_byte_string_impl(Bytecode::Executable const& executable) const
 {
     return ByteString::formatted("JumpNullish {}, null:{} nonnull:{}",

+ 52 - 0
Userland/Libraries/LibJS/Bytecode/Op.h

@@ -1110,6 +1110,58 @@ private:
     Label m_false_target;
 };
 
+class JumpTrue final : public Instruction {
+public:
+    constexpr static bool IsTerminator = true;
+
+    explicit JumpTrue(Operand condition, Label target)
+        : Instruction(Type::JumpTrue, sizeof(*this))
+        , m_condition(condition)
+        , m_target(target)
+    {
+    }
+
+    ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
+    ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_labels_impl(Function<void(Label&)> visitor)
+    {
+        visitor(m_target);
+    }
+
+    Operand condition() const { return m_condition; }
+    auto& target() const { return m_target; }
+
+private:
+    Operand m_condition;
+    Label m_target;
+};
+
+class JumpFalse final : public Instruction {
+public:
+    constexpr static bool IsTerminator = true;
+
+    explicit JumpFalse(Operand condition, Label target)
+        : Instruction(Type::JumpFalse, sizeof(*this))
+        , m_condition(condition)
+        , m_target(target)
+    {
+    }
+
+    ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
+    ByteString to_byte_string_impl(Bytecode::Executable const&) const;
+    void visit_labels_impl(Function<void(Label&)> visitor)
+    {
+        visitor(m_target);
+    }
+
+    Operand condition() const { return m_condition; }
+    auto& target() const { return m_target; }
+
+private:
+    Operand m_condition;
+    Label m_target;
+};
+
 class JumpNullish final : public Instruction {
 public:
     constexpr static bool IsTerminator = true;