From 0cb9c9e81e39dc99947e5a2483753cfdb1ae2050 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 1 Jul 2023 15:08:37 +0200 Subject: [PATCH] LibJS: Add fast paths for bitwise ops on 2x Int32 JS::Value ~9% speed-up on Kraken/stanford-crypto-aes.js :^) --- Userland/Libraries/LibJS/Runtime/Value.cpp | 24 ++++++++++++++++++++++ Userland/Libraries/LibJS/Runtime/Value.h | 22 ++++++++++---------- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/Userland/Libraries/LibJS/Runtime/Value.cpp b/Userland/Libraries/LibJS/Runtime/Value.cpp index cc87d871f6..0380ad3681 100644 --- a/Userland/Libraries/LibJS/Runtime/Value.cpp +++ b/Userland/Libraries/LibJS/Runtime/Value.cpp @@ -1344,6 +1344,10 @@ ThrowCompletionOr less_than_equals(VM& vm, Value lhs, Value rhs) // BitwiseANDExpression : BitwiseANDExpression & EqualityExpression ThrowCompletionOr bitwise_and(VM& vm, Value lhs, Value rhs) { + // OPTIMIZATION: Fast path when both values are Int32. + if (lhs.is_int32() && rhs.is_int32()) + return Value(lhs.as_i32() & rhs.as_i32()); + // 13.15.3 ApplyStringOrNumericBinaryOperator ( lval, opText, rval ), https://tc39.es/ecma262/#sec-applystringornumericbinaryoperator // 1-2, 6. N/A. @@ -1377,6 +1381,10 @@ ThrowCompletionOr bitwise_and(VM& vm, Value lhs, Value rhs) // BitwiseORExpression : BitwiseORExpression | BitwiseXORExpression ThrowCompletionOr bitwise_or(VM& vm, Value lhs, Value rhs) { + // OPTIMIZATION: Fast path when both values are Int32. + if (lhs.is_int32() && rhs.is_int32()) + return Value(lhs.as_i32() | rhs.as_i32()); + // 13.15.3 ApplyStringOrNumericBinaryOperator ( lval, opText, rval ), https://tc39.es/ecma262/#sec-applystringornumericbinaryoperator // 1-2, 6. N/A. @@ -1414,6 +1422,10 @@ ThrowCompletionOr bitwise_or(VM& vm, Value lhs, Value rhs) // BitwiseXORExpression : BitwiseXORExpression ^ BitwiseANDExpression ThrowCompletionOr bitwise_xor(VM& vm, Value lhs, Value rhs) { + // OPTIMIZATION: Fast path when both values are Int32. + if (lhs.is_int32() && rhs.is_int32()) + return Value(lhs.as_i32() ^ rhs.as_i32()); + // 13.15.3 ApplyStringOrNumericBinaryOperator ( lval, opText, rval ), https://tc39.es/ecma262/#sec-applystringornumericbinaryoperator // 1-2, 6. N/A. @@ -1598,6 +1610,12 @@ ThrowCompletionOr left_shift(VM& vm, Value lhs, Value rhs) // ShiftExpression : ShiftExpression >> AdditiveExpression ThrowCompletionOr right_shift(VM& vm, Value lhs, Value rhs) { + // OPTIMIZATION: Fast path when both values are suitable Int32 values. + if (lhs.is_int32() && rhs.is_int32() && rhs.as_i32() >= 0) { + auto shift_count = static_cast(rhs.as_i32()) % 32; + return Value(lhs.as_i32() >> shift_count); + } + // 13.15.3 ApplyStringOrNumericBinaryOperator ( lval, opText, rval ), https://tc39.es/ecma262/#sec-applystringornumericbinaryoperator // 1-2, 6. N/A. @@ -1649,6 +1667,12 @@ ThrowCompletionOr right_shift(VM& vm, Value lhs, Value rhs) // ShiftExpression : ShiftExpression >>> AdditiveExpression ThrowCompletionOr unsigned_right_shift(VM& vm, Value lhs, Value rhs) { + // OPTIMIZATION: Fast path when both values are suitable Int32 values. + if (lhs.is_int32() && rhs.is_int32() && lhs.as_i32() >= 0 && rhs.as_i32() >= 0) { + auto shift_count = static_cast(rhs.as_i32()) % 32; + return Value(static_cast(lhs.as_i32()) >> shift_count); + } + // 13.15.3 ApplyStringOrNumericBinaryOperator ( lval, opText, rval ), https://tc39.es/ecma262/#sec-applystringornumericbinaryoperator // 1-2, 5-6. N/A. diff --git a/Userland/Libraries/LibJS/Runtime/Value.h b/Userland/Libraries/LibJS/Runtime/Value.h index e3c7f9ff7a..41569fc52d 100644 --- a/Userland/Libraries/LibJS/Runtime/Value.h +++ b/Userland/Libraries/LibJS/Runtime/Value.h @@ -428,6 +428,17 @@ public: #endif } + // A double is any Value which does not have the full exponent and top mantissa bit set or has + // exactly only those bits set. + bool is_double() const { return (m_value.encoded & CANON_NAN_BITS) != CANON_NAN_BITS || (m_value.encoded == CANON_NAN_BITS); } + bool is_int32() const { return m_value.tag == INT32_TAG; } + + i32 as_i32() const + { + VERIFY(is_int32()); + return static_cast(m_value.encoded & 0xFFFFFFFF); + } + private: Value(u64 tag, u64 val) { @@ -459,17 +470,6 @@ private: } } - // A double is any Value which does not have the full exponent and top mantissa bit set or has - // exactly only those bits set. - bool is_double() const { return (m_value.encoded & CANON_NAN_BITS) != CANON_NAN_BITS || (m_value.encoded == CANON_NAN_BITS); } - bool is_int32() const { return m_value.tag == INT32_TAG; } - - i32 as_i32() const - { - VERIFY(is_int32()); - return static_cast(m_value.encoded & 0xFFFFFFFF); - } - template PointerType* extract_pointer() const {