瀏覽代碼

LibJS/Bytecode: Only emit ResolveThisBinding once per basic block

Once executed, this instruction will always produce the same result
in subsequent executions, so it's okay to cache it.

Unfortunately it may throw, so we can't just hoist it to the top of
every executable, since that would break observable execution order.
Andreas Kling 1 年之前
父節點
當前提交
0f70ff9a67

+ 3 - 7
Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp

@@ -426,8 +426,7 @@ Bytecode::CodeGenerationErrorOr<Optional<Bytecode::Operand>> AssignmentExpressio
                         // https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation
                         // https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation
                         // 1. Let env be GetThisEnvironment().
                         // 1. Let env be GetThisEnvironment().
                         // 2. Let actualThis be ? env.GetThisBinding().
                         // 2. Let actualThis be ? env.GetThisBinding().
-                        this_value = Bytecode::Operand(generator.allocate_register());
-                        generator.emit<Bytecode::Op::ResolveThisBinding>(*this_value);
+                        this_value = generator.get_this();
 
 
                         // SuperProperty : super [ Expression ]
                         // SuperProperty : super [ Expression ]
                         // 3. Let propertyNameReference be ? Evaluation of Expression.
                         // 3. Let propertyNameReference be ? Evaluation of Expression.
@@ -1486,8 +1485,7 @@ static Bytecode::CodeGenerationErrorOr<BaseAndValue> get_base_and_value_from_mem
     if (is<SuperExpression>(member_expression.object())) {
     if (is<SuperExpression>(member_expression.object())) {
         // 1. Let env be GetThisEnvironment().
         // 1. Let env be GetThisEnvironment().
         // 2. Let actualThis be ? env.GetThisBinding().
         // 2. Let actualThis be ? env.GetThisBinding().
-        auto this_value = Bytecode::Operand(generator.allocate_register());
-        generator.emit<Bytecode::Op::ResolveThisBinding>(this_value);
+        auto this_value = generator.get_this();
 
 
         Optional<Bytecode::Operand> computed_property;
         Optional<Bytecode::Operand> computed_property;
 
 
@@ -2718,9 +2716,7 @@ Bytecode::CodeGenerationErrorOr<Optional<Bytecode::Operand>> SpreadExpression::g
 Bytecode::CodeGenerationErrorOr<Optional<Bytecode::Operand>> ThisExpression::generate_bytecode(Bytecode::Generator& generator, Optional<Bytecode::Operand> preferred_dst) const
 Bytecode::CodeGenerationErrorOr<Optional<Bytecode::Operand>> ThisExpression::generate_bytecode(Bytecode::Generator& generator, Optional<Bytecode::Operand> preferred_dst) const
 {
 {
     Bytecode::Generator::SourceLocationScope scope(generator, *this);
     Bytecode::Generator::SourceLocationScope scope(generator, *this);
-    auto dst = choose_dst(generator, preferred_dst);
-    generator.emit<Bytecode::Op::ResolveThisBinding>(dst);
-    return dst;
+    return generator.get_this(preferred_dst);
 }
 }
 
 
 static Bytecode::Operand generate_await(
 static Bytecode::Operand generate_await(

+ 6 - 0
Userland/Libraries/LibJS/Bytecode/BasicBlock.h

@@ -9,6 +9,7 @@
 #include <AK/Badge.h>
 #include <AK/Badge.h>
 #include <AK/String.h>
 #include <AK/String.h>
 #include <LibJS/Bytecode/Executable.h>
 #include <LibJS/Bytecode/Executable.h>
+#include <LibJS/Bytecode/Operand.h>
 #include <LibJS/Forward.h>
 #include <LibJS/Forward.h>
 #include <LibJS/Heap/Handle.h>
 #include <LibJS/Heap/Handle.h>
 
 
@@ -51,6 +52,9 @@ public:
     auto const& source_map() const { return m_source_map; }
     auto const& source_map() const { return m_source_map; }
     void add_source_map_entry(size_t bytecode_offset, SourceRecord const& source_record) { m_source_map.set(bytecode_offset, source_record); }
     void add_source_map_entry(size_t bytecode_offset, SourceRecord const& source_record) { m_source_map.set(bytecode_offset, source_record); }
 
 
+    auto const& this_() const { return m_this; }
+    void set_this(Operand operand) { m_this = operand; }
+
 private:
 private:
     explicit BasicBlock(u32 index, String name);
     explicit BasicBlock(u32 index, String name);
 
 
@@ -62,6 +66,8 @@ private:
     bool m_terminated { false };
     bool m_terminated { false };
 
 
     HashMap<size_t, SourceRecord> m_source_map;
     HashMap<size_t, SourceRecord> m_source_map;
+
+    Optional<Operand> m_this;
 };
 };
 
 
 }
 }

+ 10 - 0
Userland/Libraries/LibJS/Bytecode/Generator.cpp

@@ -798,4 +798,14 @@ void Generator::set_local_initialized(u32 local_index)
     m_initialized_locals.set(local_index);
     m_initialized_locals.set(local_index);
 }
 }
 
 
+Operand Generator::get_this(Optional<Operand> preferred_dst)
+{
+    if (m_current_basic_block->this_().has_value())
+        return m_current_basic_block->this_().value();
+    auto dst = preferred_dst.has_value() ? preferred_dst.value() : Operand(allocate_register());
+    emit<Bytecode::Op::ResolveThisBinding>(dst);
+    m_current_basic_block->set_this(dst);
+    return dst;
+}
+
 }
 }

+ 2 - 0
Userland/Libraries/LibJS/Bytecode/Generator.h

@@ -257,6 +257,8 @@ public:
         m_boundaries.take_last();
         m_boundaries.take_last();
     }
     }
 
 
+    [[nodiscard]] Operand get_this(Optional<Operand> preferred_dst = {});
+
     void emit_get_by_id(Operand dst, Operand base, IdentifierTableIndex property_identifier, Optional<IdentifierTableIndex> base_identifier = {});
     void emit_get_by_id(Operand dst, Operand base, IdentifierTableIndex property_identifier, Optional<IdentifierTableIndex> base_identifier = {});
 
 
     void emit_get_by_id_with_this(Operand dst, Operand base, IdentifierTableIndex, Operand this_value);
     void emit_get_by_id_with_this(Operand dst, Operand base, IdentifierTableIndex, Operand this_value);