LibJS: Skip allocating locals for arguments that allowed to be local

This allows us to get rid of instructions that move arguments to locals
and allocate smaller JS::Value vector in ExecutionContext by reusing
slots that were already allocated for arguments.

With this change for following function:
```js
function f(x, y) {
    return x + y;
}
```

we now produce following bytecode:
```
[   0]    0: Add dst:reg6, lhs:arg0, rhs:arg1
[  10]       Return value:reg6
```

instead of:
```
[   0]    0: GetArgument 0, dst:x~1
[  10]       GetArgument 1, dst:y~0
[  20]       Add dst:reg6, lhs:x~1, rhs:y~0
[  30]       Return value:reg6
```
This commit is contained in:
Aliaksandr Kalenik 2025-04-25 17:10:47 +02:00 committed by Andreas Kling
parent 3f04d18ef7
commit 2d732b2251
Notes: github-actions[bot] 2025-04-26 09:03:23 +00:00
6 changed files with 102 additions and 39 deletions

View file

@ -58,7 +58,7 @@ CodeGenerationErrorOr<void> Generator::emit_function_declaration_instantiation(E
Optional<Operand> dst;
auto local_var_index = function.shared_data().m_local_variables_names.find_first_index("arguments"_fly_string);
if (local_var_index.has_value())
dst = local(local_var_index.value());
dst = local(Identifier::Local::variable(local_var_index.value()));
if (function.is_strict_mode() || !function.has_simple_parameter_list()) {
emit<Op::CreateArguments>(dst, Op::CreateArguments::Kind::Unmapped, function.is_strict_mode());
@ -77,11 +77,8 @@ CodeGenerationErrorOr<void> Generator::emit_function_declaration_instantiation(E
auto& if_undefined_block = make_block();
auto& if_not_undefined_block = make_block();
auto argument_reg = allocate_register();
emit<Op::Mov>(argument_reg.operand(), Operand { Operand::Type::Argument, param_index });
emit<Op::JumpUndefined>(
argument_reg.operand(),
Operand { Operand::Type::Argument, param_index },
Label { if_undefined_block },
Label { if_not_undefined_block });
@ -95,9 +92,7 @@ CodeGenerationErrorOr<void> Generator::emit_function_declaration_instantiation(E
if (auto const* identifier = parameter.binding.get_pointer<NonnullRefPtr<Identifier const>>(); identifier) {
if ((*identifier)->is_local()) {
auto local_variable_index = (*identifier)->local_variable_index();
emit<Op::Mov>(local(local_variable_index), Operand { Operand::Type::Argument, param_index });
set_local_initialized((*identifier)->local_variable_index());
set_local_initialized((*identifier)->local_index());
} else {
auto id = intern_identifier((*identifier)->string());
if (function.shared_data().m_has_duplicates) {
@ -122,7 +117,7 @@ CodeGenerationErrorOr<void> Generator::emit_function_declaration_instantiation(E
for (auto const& variable_to_initialize : function.shared_data().m_var_names_to_initialize_binding) {
auto const& id = variable_to_initialize.identifier;
if (id.is_local()) {
emit<Op::Mov>(local(id.local_variable_index()), add_constant(js_undefined()));
emit<Op::Mov>(local(id.local_index()), add_constant(js_undefined()));
} else {
auto intern_id = intern_identifier(id.string());
emit<Op::CreateVariable>(intern_id, Op::EnvironmentMode::Var, false);
@ -153,14 +148,14 @@ CodeGenerationErrorOr<void> Generator::emit_function_declaration_instantiation(E
emit<Op::Mov>(initial_value, add_constant(js_undefined()));
} else {
if (id.is_local()) {
emit<Op::Mov>(initial_value, local(id.local_variable_index()));
emit<Op::Mov>(initial_value, local(id.local_index()));
} else {
emit<Op::GetBinding>(initial_value, intern_identifier(id.string()));
}
}
if (id.is_local()) {
emit<Op::Mov>(local(id.local_variable_index()), initial_value);
emit<Op::Mov>(local(id.local_index()), initial_value);
} else {
auto intern_id = intern_identifier(id.string());
emit<Op::CreateVariable>(intern_id, Op::EnvironmentMode::Var, false);
@ -204,7 +199,7 @@ CodeGenerationErrorOr<void> Generator::emit_function_declaration_instantiation(E
for (auto const& declaration : function.shared_data().m_functions_to_initialize) {
auto const& identifier = *declaration.name_identifier();
if (identifier.is_local()) {
auto local_index = identifier.local_variable_index();
auto local_index = identifier.local_index();
emit<Op::NewFunction>(local(local_index), declaration, OptionalNone {});
set_local_initialized(local_index);
} else {
@ -516,9 +511,11 @@ void Generator::free_register(Register reg)
m_free_registers.append(reg);
}
ScopedOperand Generator::local(u32 local_index)
ScopedOperand Generator::local(Identifier::Local const& local)
{
return ScopedOperand { *this, Operand { Operand::Type::Local, static_cast<u32>(local_index) } };
if (local.is_variable())
return ScopedOperand { *this, Operand { Operand::Type::Local, static_cast<u32>(local.index) } };
return ScopedOperand { *this, Operand { Operand::Type::Argument, static_cast<u32>(local.index) } };
}
Generator::SourceLocationScope::SourceLocationScope(Generator& generator, ASTNode const& node)
@ -880,11 +877,12 @@ CodeGenerationErrorOr<Optional<ScopedOperand>> Generator::emit_delete_reference(
void Generator::emit_set_variable(JS::Identifier const& identifier, ScopedOperand value, Bytecode::Op::BindingInitializationMode initialization_mode, Bytecode::Op::EnvironmentMode environment_mode)
{
if (identifier.is_local()) {
if (value.operand().is_local() && value.operand().index() == identifier.local_variable_index()) {
auto local_index = identifier.local_index();
if (value.operand().is_local() && local_index.is_variable() && value.operand().index() == local_index.index) {
// Moving a local to itself is a no-op.
return;
}
emit<Bytecode::Op::Mov>(local(identifier.local_variable_index()), value);
emit<Bytecode::Op::Mov>(local(local_index), value);
} else {
auto identifier_index = intern_identifier(identifier.string());
if (environment_mode == Bytecode::Op::EnvironmentMode::Lexical) {
@ -1186,9 +1184,24 @@ bool Generator::is_local_initialized(u32 local_index) const
return m_initialized_locals.find(local_index) != m_initialized_locals.end();
}
void Generator::set_local_initialized(u32 local_index)
bool Generator::is_local_initialized(Identifier::Local const& local) const
{
m_initialized_locals.set(local_index);
if (local.is_variable())
return m_initialized_locals.find(local.index) != m_initialized_locals.end();
if (local.is_argument())
return m_initialized_arguments.find(local.index) != m_initialized_arguments.end();
return true;
}
void Generator::set_local_initialized(Identifier::Local const& local)
{
if (local.is_variable()) {
m_initialized_locals.set(local.index);
} else if (local.is_argument()) {
m_initialized_arguments.set(local.index);
} else {
VERIFY_NOT_REACHED();
}
}
ScopedOperand Generator::get_this(Optional<ScopedOperand> preferred_dst)