From ae8c98104ac4e98eb13a2767fae02a59a3b462e8 Mon Sep 17 00:00:00 2001 From: Simon Wanner Date: Fri, 10 Nov 2023 17:55:34 +0100 Subject: [PATCH] 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. --- Userland/Libraries/LibJS/AST.h | 10 +++++++++- .../LibJS/Runtime/ECMAScriptFunctionObject.cpp | 16 ++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index 076427e7be..a0144c2121 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -153,6 +154,12 @@ public: : 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 m_bytecode_executable; }; // 14.13 Labelled Statements, https://tc39.es/ecma262/#sec-labelled-statements @@ -678,6 +685,7 @@ struct FunctionParameter { Variant, NonnullRefPtr> binding; RefPtr default_value; bool is_rest { false }; + RefPtr bytecode_executable {}; }; class FunctionNode { @@ -721,7 +729,7 @@ private: DeprecatedString m_source_text; NonnullRefPtr m_body; Vector const m_parameters; - const i32 m_function_length; + i32 const m_function_length; FunctionKind m_kind; bool m_is_strict_mode : 1 { false }; bool m_might_need_arguments_object : 1 { false }; diff --git a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp index e50d7b9045..1161224a78 100644 --- a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp @@ -1174,8 +1174,13 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body() for (auto& parameter : m_formal_parameters) { if (!parameter.default_value) 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(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(); } - 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(*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 (declaration_result.is_throw_completion()) {