ソースを参照

LibJS/Bytecode: Round constant operands of bitwise binary expressions

This helps some of the Cloudflare Turnstile stuff run faster, since they
are deliberately screwing with JS engines by asking us to do a bunch of
bitwise operations on e.g 65535.56

By rounding such values in bytecode generation, the interpreter can stay
on the happy path while executing, and finish quite a bit faster.
Andreas Kling 1 年間 前
コミット
448f837d38
1 ファイル変更52 行追加2 行削除
  1. 52 2
      Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp

+ 52 - 2
Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp

@@ -90,9 +90,59 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> BinaryExpression::gener
         return dst;
         return dst;
     }
     }
 
 
-    auto lhs = TRY(m_lhs->generate_bytecode(generator)).value();
-    auto rhs = TRY(m_rhs->generate_bytecode(generator)).value();
+    // OPTIMIZATION: If LHS and/or RHS are numeric literals, we make sure they are converted to i32/u32
+    //               as appropriate, to avoid having to perform these conversions at runtime.
+
+    auto get_left_side = [&](Expression const& side) -> CodeGenerationErrorOr<Optional<ScopedOperand>> {
+        switch (m_op) {
+        case BinaryOp::BitwiseAnd:
+        case BinaryOp::BitwiseOr:
+        case BinaryOp::BitwiseXor:
+        case BinaryOp::LeftShift:
+        case BinaryOp::RightShift:
+        case BinaryOp::UnsignedRightShift:
+            // LHS will always be converted to i32 for these ops.
+            if (side.is_numeric_literal()) {
+                auto value = MUST(static_cast<NumericLiteral const&>(side).value().to_i32(generator.vm()));
+                return generator.add_constant(Value(value));
+            }
+            break;
+        default:
+            break;
+        }
+
+        return side.generate_bytecode(generator);
+    };
+
+    auto get_right_side = [&](Expression const& side) -> CodeGenerationErrorOr<Optional<ScopedOperand>> {
+        switch (m_op) {
+        case BinaryOp::BitwiseAnd:
+        case BinaryOp::BitwiseOr:
+        case BinaryOp::BitwiseXor:
+            // RHS will always be converted to i32 for these ops.
+            if (side.is_numeric_literal()) {
+                auto value = MUST(static_cast<NumericLiteral const&>(side).value().to_i32(generator.vm()));
+                return generator.add_constant(Value(value));
+            }
+            break;
+        case BinaryOp::LeftShift:
+        case BinaryOp::RightShift:
+        case BinaryOp::UnsignedRightShift:
+            // RHS will always be converted to u32 for these ops.
+            if (side.is_numeric_literal()) {
+                auto value = MUST(static_cast<NumericLiteral const&>(side).value().to_u32(generator.vm()));
+                return generator.add_constant(Value(value));
+            }
+            break;
+        default:
+            break;
+        }
+
+        return side.generate_bytecode(generator);
+    };
 
 
+    auto lhs = TRY(get_left_side(*m_lhs)).value();
+    auto rhs = TRY(get_right_side(*m_rhs)).value();
     auto dst = choose_dst(generator, preferred_dst);
     auto dst = choose_dst(generator, preferred_dst);
     switch (m_op) {
     switch (m_op) {
     case BinaryOp::Addition:
     case BinaryOp::Addition: