diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index fc8f8c47c7..de2cdbeff3 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -608,7 +608,7 @@ static ThrowCompletionOr strict_equals(VM&, Value src1, Value src2) return Value(is_strictly_equal(src1, src2)); } -#define JS_DEFINE_COMMON_BINARY_OP(OpTitleCase, op_snake_case) \ +#define JS_DEFINE_EXECUTE_FOR_COMMON_BINARY_OP(OpTitleCase, op_snake_case) \ ThrowCompletionOr OpTitleCase::execute_impl(Bytecode::Interpreter& interpreter) const \ { \ auto& vm = interpreter.vm(); \ @@ -616,16 +616,202 @@ static ThrowCompletionOr strict_equals(VM&, Value src1, Value src2) auto rhs = interpreter.get(m_rhs); \ interpreter.set(m_dst, TRY(op_snake_case(vm, lhs, rhs))); \ return {}; \ - } \ - ByteString OpTitleCase::to_byte_string_impl(Bytecode::Executable const& executable) const \ - { \ - return ByteString::formatted(#OpTitleCase " {}, {}, {}", \ - format_operand("dst"sv, m_dst, executable), \ - format_operand("lhs"sv, m_lhs, executable), \ - format_operand("rhs"sv, m_rhs, executable)); \ } -JS_ENUMERATE_COMMON_BINARY_OPS(JS_DEFINE_COMMON_BINARY_OP) +#define JS_DEFINE_TO_BYTE_STRING_FOR_COMMON_BINARY_OP(OpTitleCase, op_snake_case) \ + ByteString OpTitleCase::to_byte_string_impl(Bytecode::Executable const& executable) const \ + { \ + return ByteString::formatted(#OpTitleCase " {}, {}, {}", \ + format_operand("dst"sv, m_dst, executable), \ + format_operand("lhs"sv, m_lhs, executable), \ + format_operand("rhs"sv, m_rhs, executable)); \ + } + +JS_ENUMERATE_COMMON_BINARY_OPS_WITHOUT_FAST_PATH(JS_DEFINE_EXECUTE_FOR_COMMON_BINARY_OP) +JS_ENUMERATE_COMMON_BINARY_OPS_WITHOUT_FAST_PATH(JS_DEFINE_TO_BYTE_STRING_FOR_COMMON_BINARY_OP) +JS_ENUMERATE_COMMON_BINARY_OPS_WITH_FAST_PATH(JS_DEFINE_TO_BYTE_STRING_FOR_COMMON_BINARY_OP) + +ThrowCompletionOr Add::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& vm = interpreter.vm(); + auto const lhs = interpreter.get(m_lhs); + auto const rhs = interpreter.get(m_rhs); + + if (lhs.is_number() && rhs.is_number()) { + if (lhs.is_int32() && rhs.is_int32()) { + if (!Checked::addition_would_overflow(lhs.as_i32(), rhs.as_i32())) { + interpreter.set(m_dst, Value(lhs.as_i32() + rhs.as_i32())); + return {}; + } + } + interpreter.set(m_dst, Value(lhs.as_double() + rhs.as_double())); + return {}; + } + + interpreter.set(m_dst, TRY(add(vm, lhs, rhs))); + return {}; +} + +ThrowCompletionOr Mul::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& vm = interpreter.vm(); + auto const lhs = interpreter.get(m_lhs); + auto const rhs = interpreter.get(m_rhs); + + if (lhs.is_number() && rhs.is_number()) { + if (lhs.is_int32() && rhs.is_int32()) { + if (!Checked::multiplication_would_overflow(lhs.as_i32(), rhs.as_i32())) { + interpreter.set(m_dst, Value(lhs.as_i32() * rhs.as_i32())); + return {}; + } + } + interpreter.set(m_dst, Value(lhs.as_double() * rhs.as_double())); + return {}; + } + + interpreter.set(m_dst, TRY(mul(vm, lhs, rhs))); + return {}; +} + +ThrowCompletionOr Sub::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& vm = interpreter.vm(); + auto const lhs = interpreter.get(m_lhs); + auto const rhs = interpreter.get(m_rhs); + + if (lhs.is_number() && rhs.is_number()) { + if (lhs.is_int32() && rhs.is_int32()) { + if (!Checked::addition_would_overflow(lhs.as_i32(), -rhs.as_i32())) { + interpreter.set(m_dst, Value(lhs.as_i32() - rhs.as_i32())); + return {}; + } + } + interpreter.set(m_dst, Value(lhs.as_double() - rhs.as_double())); + return {}; + } + + interpreter.set(m_dst, TRY(sub(vm, lhs, rhs))); + return {}; +} + +ThrowCompletionOr BitwiseXor::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& vm = interpreter.vm(); + auto const lhs = interpreter.get(m_lhs); + auto const rhs = interpreter.get(m_rhs); + if (lhs.is_int32() && rhs.is_int32()) { + interpreter.set(m_dst, Value(lhs.as_i32() ^ rhs.as_i32())); + return {}; + } + interpreter.set(m_dst, TRY(bitwise_xor(vm, lhs, rhs))); + return {}; +} + +ThrowCompletionOr BitwiseAnd::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& vm = interpreter.vm(); + auto const lhs = interpreter.get(m_lhs); + auto const rhs = interpreter.get(m_rhs); + if (lhs.is_int32() && rhs.is_int32()) { + interpreter.set(m_dst, Value(lhs.as_i32() & rhs.as_i32())); + return {}; + } + interpreter.set(m_dst, TRY(bitwise_and(vm, lhs, rhs))); + return {}; +} + +ThrowCompletionOr BitwiseOr::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& vm = interpreter.vm(); + auto const lhs = interpreter.get(m_lhs); + auto const rhs = interpreter.get(m_rhs); + if (lhs.is_int32() && rhs.is_int32()) { + interpreter.set(m_dst, Value(lhs.as_i32() | rhs.as_i32())); + return {}; + } + interpreter.set(m_dst, TRY(bitwise_or(vm, lhs, rhs))); + return {}; +} + +ThrowCompletionOr UnsignedRightShift::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& vm = interpreter.vm(); + auto const lhs = interpreter.get(m_lhs); + auto const rhs = interpreter.get(m_rhs); + if (lhs.is_int32() && rhs.is_int32() && lhs.as_i32() >= 0 && rhs.as_i32() >= 0) { + auto const shift_count = static_cast(rhs.as_i32()) % 32; + interpreter.set(m_dst, Value(static_cast(lhs.as_i32()) >> shift_count)); + return {}; + } + interpreter.set(m_dst, TRY(unsigned_right_shift(vm, lhs, rhs))); + return {}; +} + +ThrowCompletionOr RightShift::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& vm = interpreter.vm(); + auto const lhs = interpreter.get(m_lhs); + auto const rhs = interpreter.get(m_rhs); + if (lhs.is_int32() && rhs.is_int32() && rhs.as_i32() >= 0) { + auto const shift_count = static_cast(rhs.as_i32()) % 32; + interpreter.set(m_dst, Value(lhs.as_i32() >> shift_count)); + return {}; + } + interpreter.set(m_dst, TRY(right_shift(vm, lhs, rhs))); + return {}; +} + +ThrowCompletionOr LessThan::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& vm = interpreter.vm(); + auto const lhs = interpreter.get(m_lhs); + auto const rhs = interpreter.get(m_rhs); + if (lhs.is_int32() && rhs.is_int32()) { + interpreter.set(m_dst, Value(lhs.as_i32() < rhs.as_i32())); + return {}; + } + interpreter.set(m_dst, TRY(less_than(vm, lhs, rhs))); + return {}; +} + +ThrowCompletionOr LessThanEquals::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& vm = interpreter.vm(); + auto const lhs = interpreter.get(m_lhs); + auto const rhs = interpreter.get(m_rhs); + if (lhs.is_int32() && rhs.is_int32()) { + interpreter.set(m_dst, Value(lhs.as_i32() <= rhs.as_i32())); + return {}; + } + interpreter.set(m_dst, TRY(less_than_equals(vm, lhs, rhs))); + return {}; +} + +ThrowCompletionOr GreaterThan::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& vm = interpreter.vm(); + auto const lhs = interpreter.get(m_lhs); + auto const rhs = interpreter.get(m_rhs); + if (lhs.is_int32() && rhs.is_int32()) { + interpreter.set(m_dst, Value(lhs.as_i32() > rhs.as_i32())); + return {}; + } + interpreter.set(m_dst, TRY(greater_than(vm, lhs, rhs))); + return {}; +} + +ThrowCompletionOr GreaterThanEquals::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& vm = interpreter.vm(); + auto const lhs = interpreter.get(m_lhs); + auto const rhs = interpreter.get(m_rhs); + if (lhs.is_int32() && rhs.is_int32()) { + interpreter.set(m_dst, Value(lhs.as_i32() >= rhs.as_i32())); + return {}; + } + interpreter.set(m_dst, TRY(greater_than_equals(vm, lhs, rhs))); + return {}; +} static ThrowCompletionOr not_(VM&, Value value) { diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index f78690f5b0..89ceaaa474 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/Userland/Libraries/LibJS/Bytecode/Op.h @@ -51,30 +51,32 @@ private: Operand m_src; }; -#define JS_ENUMERATE_COMMON_BINARY_OPS(O) \ - O(Add, add) \ - O(Sub, sub) \ - O(Mul, mul) \ - O(Div, div) \ - O(Exp, exp) \ - O(Mod, mod) \ - O(In, in) \ - O(InstanceOf, instance_of) \ - O(GreaterThan, greater_than) \ - O(GreaterThanEquals, greater_than_equals) \ - O(LessThan, less_than) \ - O(LessThanEquals, less_than_equals) \ - O(LooselyInequals, loosely_inequals) \ - O(LooselyEquals, loosely_equals) \ - O(StrictlyInequals, strict_inequals) \ - O(StrictlyEquals, strict_equals) \ - O(BitwiseAnd, bitwise_and) \ - O(BitwiseOr, bitwise_or) \ - O(BitwiseXor, bitwise_xor) \ - O(LeftShift, left_shift) \ - O(RightShift, right_shift) \ +#define JS_ENUMERATE_COMMON_BINARY_OPS_WITH_FAST_PATH(O) \ + O(Add, add) \ + O(BitwiseAnd, bitwise_and) \ + O(BitwiseOr, bitwise_or) \ + O(BitwiseXor, bitwise_xor) \ + O(GreaterThan, greater_than) \ + O(GreaterThanEquals, greater_than_equals) \ + O(LessThan, less_than) \ + O(LessThanEquals, less_than_equals) \ + O(Mul, mul) \ + O(RightShift, right_shift) \ + O(Sub, sub) \ O(UnsignedRightShift, unsigned_right_shift) +#define JS_ENUMERATE_COMMON_BINARY_OPS_WITHOUT_FAST_PATH(O) \ + O(Div, div) \ + O(Exp, exp) \ + O(Mod, mod) \ + O(In, in) \ + O(InstanceOf, instance_of) \ + O(LooselyInequals, loosely_inequals) \ + O(LooselyEquals, loosely_equals) \ + O(StrictlyInequals, strict_inequals) \ + O(StrictlyEquals, strict_equals) \ + O(LeftShift, left_shift) + #define JS_DECLARE_COMMON_BINARY_OP(OpTitleCase, op_snake_case) \ class OpTitleCase final : public Instruction { \ public: \ @@ -99,7 +101,8 @@ private: Operand m_rhs; \ }; -JS_ENUMERATE_COMMON_BINARY_OPS(JS_DECLARE_COMMON_BINARY_OP) +JS_ENUMERATE_COMMON_BINARY_OPS_WITHOUT_FAST_PATH(JS_DECLARE_COMMON_BINARY_OP) +JS_ENUMERATE_COMMON_BINARY_OPS_WITH_FAST_PATH(JS_DECLARE_COMMON_BINARY_OP) #undef JS_DECLARE_COMMON_BINARY_OP #define JS_ENUMERATE_COMMON_UNARY_OPS(O) \