Selaa lähdekoodia

LibJS: Cache bytecode executables on the corresponding AST nodes

This greatly reduces the number of compilations necessary when functions
declaring local functions are re-executed.

For example Octane/typescript.js goes from 58080 bytecode executables
to 960.
Simon Wanner 1 vuosi sitten
vanhempi
commit
ae8c98104a

+ 9 - 1
Userland/Libraries/LibJS/AST.h

@@ -16,6 +16,7 @@
 #include <AK/Variant.h>
 #include <AK/Variant.h>
 #include <AK/Vector.h>
 #include <AK/Vector.h>
 #include <LibJS/Bytecode/CodeGenerationError.h>
 #include <LibJS/Bytecode/CodeGenerationError.h>
+#include <LibJS/Bytecode/Executable.h>
 #include <LibJS/Bytecode/IdentifierTable.h>
 #include <LibJS/Bytecode/IdentifierTable.h>
 #include <LibJS/Forward.h>
 #include <LibJS/Forward.h>
 #include <LibJS/Heap/Handle.h>
 #include <LibJS/Heap/Handle.h>
@@ -153,6 +154,12 @@ public:
         : ASTNode(move(source_range))
         : ASTNode(move(source_range))
     {
     {
     }
     }
+
+    Bytecode::Executable const* bytecode_executable() const { return m_bytecode_executable; }
+    void set_bytecode_executable(Bytecode::Executable const* bytecode_executable) { m_bytecode_executable = bytecode_executable; }
+
+private:
+    RefPtr<Bytecode::Executable> m_bytecode_executable;
 };
 };
 
 
 // 14.13 Labelled Statements, https://tc39.es/ecma262/#sec-labelled-statements
 // 14.13 Labelled Statements, https://tc39.es/ecma262/#sec-labelled-statements
@@ -678,6 +685,7 @@ struct FunctionParameter {
     Variant<NonnullRefPtr<Identifier const>, NonnullRefPtr<BindingPattern const>> binding;
     Variant<NonnullRefPtr<Identifier const>, NonnullRefPtr<BindingPattern const>> binding;
     RefPtr<Expression const> default_value;
     RefPtr<Expression const> default_value;
     bool is_rest { false };
     bool is_rest { false };
+    RefPtr<Bytecode::Executable> bytecode_executable {};
 };
 };
 
 
 class FunctionNode {
 class FunctionNode {
@@ -721,7 +729,7 @@ private:
     DeprecatedString m_source_text;
     DeprecatedString m_source_text;
     NonnullRefPtr<Statement const> m_body;
     NonnullRefPtr<Statement const> m_body;
     Vector<FunctionParameter> const m_parameters;
     Vector<FunctionParameter> const m_parameters;
-    const i32 m_function_length;
+    i32 const m_function_length;
     FunctionKind m_kind;
     FunctionKind m_kind;
     bool m_is_strict_mode : 1 { false };
     bool m_is_strict_mode : 1 { false };
     bool m_might_need_arguments_object : 1 { false };
     bool m_might_need_arguments_object : 1 { false };

+ 12 - 4
Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp

@@ -1174,8 +1174,13 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
         for (auto& parameter : m_formal_parameters) {
         for (auto& parameter : m_formal_parameters) {
             if (!parameter.default_value)
             if (!parameter.default_value)
                 continue;
                 continue;
-            auto executable = TRY(Bytecode::compile(vm, *parameter.default_value, FunctionKind::Normal, DeprecatedString::formatted("default parameter #{} for {}", default_parameter_index, m_name)));
-            m_default_parameter_bytecode_executables.append(move(executable));
+            if (parameter.bytecode_executable.is_null()) {
+                auto executable = TRY(Bytecode::compile(vm, *parameter.default_value, FunctionKind::Normal, DeprecatedString::formatted("default parameter #{} for {}", default_parameter_index++, m_name)));
+                const_cast<FunctionParameter&>(parameter).bytecode_executable = executable;
+                m_default_parameter_bytecode_executables.append(move(executable));
+            } else {
+                m_default_parameter_bytecode_executables.append(*parameter.bytecode_executable);
+            }
         }
         }
     }
     }
 
 
@@ -1186,8 +1191,11 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
             return declaration_result.release_error();
             return declaration_result.release_error();
     }
     }
 
 
-    if (!m_bytecode_executable)
-        m_bytecode_executable = TRY(Bytecode::compile(vm, *m_ecmascript_code, m_kind, m_name));
+    if (!m_bytecode_executable) {
+        if (!m_ecmascript_code->bytecode_executable())
+            const_cast<Statement&>(*m_ecmascript_code).set_bytecode_executable(TRY(Bytecode::compile(vm, *m_ecmascript_code, m_kind, m_name)));
+        m_bytecode_executable = m_ecmascript_code->bytecode_executable();
+    }
 
 
     if (m_kind == FunctionKind::Async) {
     if (m_kind == FunctionKind::Async) {
         if (declaration_result.is_throw_completion()) {
         if (declaration_result.is_throw_completion()) {