mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 08:48:11 +00:00
LibJS: Add an optimization to avoid needless arguments object creation
This gives FunctionNode a "might need arguments object" boolean flag and sets it based on the simplest possible heuristic for this: if we encounter an identifier called "arguments" or "eval" up to the next (nested) function declaration or expression, we won't need an arguments object. Otherwise, we *might* need one - the final decision is made in the FunctionDeclarationInstantiation AO. Now, this is obviously not perfect. Even if you avoid eval, something like `foo.arguments` will still trigger a false positive - but it's a start and already massively cuts down on needlessly allocated objects, especially in real-world code that is often minified, and so a full "arguments" identifier will be an actual arguments object more often than not. To illustrate the actual impact of this change, here's the number of allocated arguments objects during a full test-js run: Before: - Unmapped arguments objects: 78765 - Mapped arguments objects: 2455 After: - Unmapped arguments objects: 18 - Mapped arguments objects: 37 This results in a ~5% speedup of test-js on my Linux host machine, and about 3.5% on i686 Serenity in QEMU (warm runs, average of 5). The following microbenchmark (calling an empty function 1M times) runs 25% faster on Linux and 45% on Serenity: function foo() {} for (var i = 0; i < 1_000_000; ++i) foo(); test262 reports no changes in either direction, apart from a speedup :^)
This commit is contained in:
parent
fcb355f193
commit
4fa5748093
9 changed files with 47 additions and 26 deletions
|
@ -24,7 +24,7 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
ECMAScriptFunctionObject* ECMAScriptFunctionObject::create(GlobalObject& global_object, FlyString name, Statement const& ecmascript_code, Vector<FunctionNode::Parameter> parameters, i32 m_function_length, Environment* parent_scope, FunctionKind kind, bool is_strict, bool is_arrow_function)
|
||||
ECMAScriptFunctionObject* ECMAScriptFunctionObject::create(GlobalObject& global_object, FlyString name, Statement const& ecmascript_code, Vector<FunctionNode::Parameter> parameters, i32 m_function_length, Environment* parent_scope, FunctionKind kind, bool is_strict, bool might_need_arguments_object, bool is_arrow_function)
|
||||
{
|
||||
Object* prototype = nullptr;
|
||||
switch (kind) {
|
||||
|
@ -35,10 +35,10 @@ ECMAScriptFunctionObject* ECMAScriptFunctionObject::create(GlobalObject& global_
|
|||
prototype = global_object.generator_function_prototype();
|
||||
break;
|
||||
}
|
||||
return global_object.heap().allocate<ECMAScriptFunctionObject>(global_object, move(name), ecmascript_code, move(parameters), m_function_length, parent_scope, *prototype, kind, is_strict, is_arrow_function);
|
||||
return global_object.heap().allocate<ECMAScriptFunctionObject>(global_object, move(name), ecmascript_code, move(parameters), m_function_length, parent_scope, *prototype, kind, is_strict, might_need_arguments_object, is_arrow_function);
|
||||
}
|
||||
|
||||
ECMAScriptFunctionObject::ECMAScriptFunctionObject(FlyString name, Statement const& ecmascript_code, Vector<FunctionNode::Parameter> formal_parameters, i32 function_length, Environment* parent_scope, Object& prototype, FunctionKind kind, bool strict, bool is_arrow_function)
|
||||
ECMAScriptFunctionObject::ECMAScriptFunctionObject(FlyString name, Statement const& ecmascript_code, Vector<FunctionNode::Parameter> formal_parameters, i32 function_length, Environment* parent_scope, Object& prototype, FunctionKind kind, bool strict, bool might_need_arguments_object, bool is_arrow_function)
|
||||
: FunctionObject(prototype)
|
||||
, m_environment(parent_scope)
|
||||
, m_formal_parameters(move(formal_parameters))
|
||||
|
@ -48,6 +48,7 @@ ECMAScriptFunctionObject::ECMAScriptFunctionObject(FlyString name, Statement con
|
|||
, m_name(move(name))
|
||||
, m_function_length(function_length)
|
||||
, m_kind(kind)
|
||||
, m_might_need_arguments_object(might_need_arguments_object)
|
||||
, m_is_arrow_function(is_arrow_function)
|
||||
{
|
||||
// NOTE: This logic is from OrdinaryFunctionCreate, https://tc39.es/ecma262/#sec-ordinaryfunctioncreate
|
||||
|
@ -159,7 +160,11 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
|
|||
});
|
||||
}
|
||||
|
||||
auto arguments_object_needed = this_mode() != ThisMode::Lexical;
|
||||
auto arguments_object_needed = m_might_need_arguments_object;
|
||||
|
||||
if (this_mode() == ThisMode::Lexical)
|
||||
arguments_object_needed = false;
|
||||
|
||||
if (parameter_names.contains(vm.names.arguments.as_string()))
|
||||
arguments_object_needed = false;
|
||||
|
||||
|
@ -360,7 +365,7 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
|
|||
VERIFY(!vm.exception());
|
||||
|
||||
for (auto& declaration : functions_to_initialize) {
|
||||
auto* function = ECMAScriptFunctionObject::create(global_object(), declaration.name(), declaration.body(), declaration.parameters(), declaration.function_length(), lex_environment, declaration.kind(), declaration.is_strict_mode());
|
||||
auto* function = ECMAScriptFunctionObject::create(global_object(), declaration.name(), declaration.body(), declaration.parameters(), declaration.function_length(), lex_environment, declaration.kind(), declaration.is_strict_mode(), declaration.might_need_arguments_object());
|
||||
var_environment->set_mutable_binding(global_object(), declaration.name(), function, false);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue