From b1af91d8c4d886d7aabca77802018a30061199e2 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Thu, 6 Jul 2023 21:25:13 +0200 Subject: [PATCH] LibJS: Use local variables to store function parameters in some cases Using local variables to store function parameters makes Kraken tests run 7-10% faster. For now this optimization is limited to only be applied if: - Parameter does not use destructuring assignment - None of the function params has default value - There is no access to "arguments" variable inside function body --- Userland/Libraries/LibJS/Parser.cpp | 16 ++++++++++++++++ .../LibJS/Runtime/ECMAScriptFunctionObject.cpp | 18 ++++++++++++------ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp index 4a07074748..36143737d4 100644 --- a/Userland/Libraries/LibJS/Parser.cpp +++ b/Userland/Libraries/LibJS/Parser.cpp @@ -77,9 +77,15 @@ public: { ScopePusher scope_pusher(parser, &function_body, ScopeLevel::FunctionTopLevel, ScopeType::Function); scope_pusher.m_function_parameters = parameters; + + auto has_parameters_with_default_values = false; for (auto& parameter : parameters) { parameter.binding.visit( [&](Identifier const& identifier) { + if (parameter.default_value) + has_parameters_with_default_values = true; + scope_pusher.register_identifier(identifier); + scope_pusher.m_function_parameters_candidates_for_local_variables.set(identifier.string()); scope_pusher.m_forbidden_lexical_names.set(identifier.string()); }, [&](NonnullRefPtr const& binding_pattern) { @@ -89,6 +95,11 @@ public: })); }); } + + if (has_parameters_with_default_values) { + scope_pusher.m_function_parameters_candidates_for_local_variables.clear(); + } + return scope_pusher; } @@ -313,6 +324,10 @@ public: scope_has_declaration = false; } + if (m_type == ScopeType::Function && !m_contains_access_to_arguments_object && m_function_parameters_candidates_for_local_variables.contains(identifier_group_name)) { + scope_has_declaration = true; + } + if (scope_has_declaration) { if (function_declaration) continue; @@ -413,6 +428,7 @@ private: Vector> m_functions_to_hoist; HashTable m_bound_names; + HashTable m_function_parameters_candidates_for_local_variables; struct IdentifierGroup { bool captured_by_nested_function { false }; diff --git a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp index ef31371a07..3ba4f3ba34 100644 --- a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp @@ -479,12 +479,18 @@ ThrowCompletionOr ECMAScriptFunctionObject::function_declaration_instantia Environment* used_environment = has_duplicates ? nullptr : environment; if constexpr (IsSame const&, decltype(param)>) { - Reference reference = TRY(vm.resolve_binding(param->string(), used_environment)); - // Here the difference from hasDuplicates is important - if (has_duplicates) - return reference.put_value(vm, argument_value); - else - return reference.initialize_referenced_binding(vm, argument_value); + if (vm.bytecode_interpreter_if_exists() && param->is_local()) { + // NOTE: Local variables are supported only in bytecode interpreter + callee_context.local_variables[param->local_variable_index()] = argument_value; + return {}; + } else { + Reference reference = TRY(vm.resolve_binding(param->string(), used_environment)); + // Here the difference from hasDuplicates is important + if (has_duplicates) + return reference.put_value(vm, argument_value); + else + return reference.initialize_referenced_binding(vm, argument_value); + } } if constexpr (IsSame const&, decltype(param)>) { // Here the difference from hasDuplicates is important