From fae1527a18623cd0bf3d88ba4d954b3ea8b2e9fe Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 6 May 2024 10:15:17 +0200 Subject: [PATCH] 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. --- .../Libraries/LibJS/Bytecode/Generator.cpp | 24 +++++++++ .../Libraries/LibJS/Bytecode/Instruction.h | 2 + .../Libraries/LibJS/Bytecode/Interpreter.cpp | 42 +++++++++++++++ Userland/Libraries/LibJS/Bytecode/Op.h | 52 +++++++++++++++++++ 4 files changed, 120 insertions(+) diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.cpp b/Userland/Libraries/LibJS/Bytecode/Generator.cpp index 34d9f79205e..278e39c5803 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Generator.cpp @@ -132,6 +132,30 @@ CodeGenerationErrorOr> 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(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(&label) - bit_cast(&jump_false)); + label_offsets.append(label_offset); + bytecode.append(reinterpret_cast(&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(&label) - bit_cast(&jump_true)); + label_offsets.append(label_offset); + bytecode.append(reinterpret_cast(&jump_true), jump_true.length()); + ++it; + continue; + } + } + instruction.visit_labels([&](Label& label) { size_t label_offset = bytecode.size() + (bit_cast(&label) - bit_cast(&instruction)); label_offsets.append(label_offset); diff --git a/Userland/Libraries/LibJS/Bytecode/Instruction.h b/Userland/Libraries/LibJS/Bytecode/Instruction.h index 5a6375bb551..63dbfb9b45b 100644 --- a/Userland/Libraries/LibJS/Bytecode/Instruction.h +++ b/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) \ diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index 350b4f00350..92696c5314b 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/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(instruction); + if (get(jump.condition()).to_boolean()) { + program_counter = jump.target().address(); + goto start; + } + NEXT_INSTRUCTION(); + } + case Instruction::Type::JumpFalse: { + auto& jump = static_cast(instruction); + if (!get(jump.condition()).to_boolean()) { + program_counter = jump.target().address(); + goto start; + } + NEXT_INSTRUCTION(); + } case Instruction::Type::JumpNullish: { auto& jump = static_cast(instruction); if (get(jump.condition()).is_nullish()) @@ -1241,6 +1257,18 @@ ThrowCompletionOr JumpIf::execute_impl(Bytecode::Interpreter&) const __builtin_unreachable(); } +ThrowCompletionOr JumpTrue::execute_impl(Bytecode::Interpreter&) const +{ + // Handled in the interpreter loop. + __builtin_unreachable(); +} + +ThrowCompletionOr JumpFalse::execute_impl(Bytecode::Interpreter&) const +{ + // Handled in the interpreter loop. + __builtin_unreachable(); +} + ThrowCompletionOr 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:{}", diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index 9b96325ddc5..e4a1d822336 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/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 execute_impl(Bytecode::Interpreter&) const; + ByteString to_byte_string_impl(Bytecode::Executable const&) const; + void visit_labels_impl(Function 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 execute_impl(Bytecode::Interpreter&) const; + ByteString to_byte_string_impl(Bytecode::Executable const&) const; + void visit_labels_impl(Function 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;