mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 20:17:44 +00:00
LibJS/Bytecode: Add fast paths for many binary expression instructions
By handling common cases like Int32 arithmetic directly in the instruction handler, we can avoid the cost of calling the generic helper functions in Value.cpp.
This commit is contained in:
parent
9d9b737a58
commit
da107ec9fb
2 changed files with 221 additions and 32 deletions
|
@ -608,7 +608,7 @@ static ThrowCompletionOr<Value> strict_equals(VM&, Value src1, Value src2)
|
||||||
return Value(is_strictly_equal(src1, 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<void> OpTitleCase::execute_impl(Bytecode::Interpreter& interpreter) const \
|
ThrowCompletionOr<void> OpTitleCase::execute_impl(Bytecode::Interpreter& interpreter) const \
|
||||||
{ \
|
{ \
|
||||||
auto& vm = interpreter.vm(); \
|
auto& vm = interpreter.vm(); \
|
||||||
|
@ -616,16 +616,202 @@ static ThrowCompletionOr<Value> strict_equals(VM&, Value src1, Value src2)
|
||||||
auto rhs = interpreter.get(m_rhs); \
|
auto rhs = interpreter.get(m_rhs); \
|
||||||
interpreter.set(m_dst, TRY(op_snake_case(vm, lhs, rhs))); \
|
interpreter.set(m_dst, TRY(op_snake_case(vm, lhs, rhs))); \
|
||||||
return {}; \
|
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<void> 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<i32>::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<void> 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<i32>::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<void> 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<i32>::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<void> 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<void> 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<void> 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<void> 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<u32>(rhs.as_i32()) % 32;
|
||||||
|
interpreter.set(m_dst, Value(static_cast<u32>(lhs.as_i32()) >> shift_count));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
interpreter.set(m_dst, TRY(unsigned_right_shift(vm, lhs, rhs)));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ThrowCompletionOr<void> 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<u32>(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<void> 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<void> 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<void> 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<void> 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<Value> not_(VM&, Value value)
|
static ThrowCompletionOr<Value> not_(VM&, Value value)
|
||||||
{
|
{
|
||||||
|
|
|
@ -51,30 +51,32 @@ private:
|
||||||
Operand m_src;
|
Operand m_src;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define JS_ENUMERATE_COMMON_BINARY_OPS(O) \
|
#define JS_ENUMERATE_COMMON_BINARY_OPS_WITH_FAST_PATH(O) \
|
||||||
O(Add, add) \
|
O(Add, add) \
|
||||||
O(Sub, sub) \
|
O(BitwiseAnd, bitwise_and) \
|
||||||
O(Mul, mul) \
|
O(BitwiseOr, bitwise_or) \
|
||||||
O(Div, div) \
|
O(BitwiseXor, bitwise_xor) \
|
||||||
O(Exp, exp) \
|
O(GreaterThan, greater_than) \
|
||||||
O(Mod, mod) \
|
O(GreaterThanEquals, greater_than_equals) \
|
||||||
O(In, in) \
|
O(LessThan, less_than) \
|
||||||
O(InstanceOf, instance_of) \
|
O(LessThanEquals, less_than_equals) \
|
||||||
O(GreaterThan, greater_than) \
|
O(Mul, mul) \
|
||||||
O(GreaterThanEquals, greater_than_equals) \
|
O(RightShift, right_shift) \
|
||||||
O(LessThan, less_than) \
|
O(Sub, sub) \
|
||||||
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) \
|
|
||||||
O(UnsignedRightShift, unsigned_right_shift)
|
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) \
|
#define JS_DECLARE_COMMON_BINARY_OP(OpTitleCase, op_snake_case) \
|
||||||
class OpTitleCase final : public Instruction { \
|
class OpTitleCase final : public Instruction { \
|
||||||
public: \
|
public: \
|
||||||
|
@ -99,7 +101,8 @@ private:
|
||||||
Operand m_rhs; \
|
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
|
#undef JS_DECLARE_COMMON_BINARY_OP
|
||||||
|
|
||||||
#define JS_ENUMERATE_COMMON_UNARY_OPS(O) \
|
#define JS_ENUMERATE_COMMON_UNARY_OPS(O) \
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue