mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 04:09:13 +00:00
LibJS: Add new operand type for function arguments
This allows us to directly access passed arguments instead of copying them to register/local first using GetArgument instruction.
This commit is contained in:
parent
81a3bfd492
commit
3f04d18ef7
Notes:
github-actions[bot]
2025-04-26 09:03:30 +00:00
Author: https://github.com/kalenikaliaksandr
Commit: 3f04d18ef7
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4481
7 changed files with 27 additions and 93 deletions
|
@ -87,6 +87,7 @@ public:
|
|||
|
||||
Vector<FlyString> local_variable_names;
|
||||
size_t local_index_base { 0 };
|
||||
size_t argument_index_base { 0 };
|
||||
|
||||
Optional<IdentifierTableIndex> length_identifier;
|
||||
|
||||
|
|
|
@ -72,15 +72,13 @@ CodeGenerationErrorOr<void> Generator::emit_function_declaration_instantiation(E
|
|||
auto const& parameter = formal_parameters.parameters()[param_index];
|
||||
|
||||
if (parameter.is_rest) {
|
||||
auto argument_reg = allocate_register();
|
||||
emit<Op::CreateRestParams>(argument_reg.operand(), param_index);
|
||||
emit<Op::SetArgument>(param_index, argument_reg.operand());
|
||||
emit<Op::CreateRestParams>(Operand { Operand::Type::Argument, param_index }, param_index);
|
||||
} else if (parameter.default_value) {
|
||||
auto& if_undefined_block = make_block();
|
||||
auto& if_not_undefined_block = make_block();
|
||||
|
||||
auto argument_reg = allocate_register();
|
||||
emit<Op::GetArgument>(argument_reg.operand(), param_index);
|
||||
emit<Op::Mov>(argument_reg.operand(), Operand { Operand::Type::Argument, param_index });
|
||||
|
||||
emit<Op::JumpUndefined>(
|
||||
argument_reg.operand(),
|
||||
|
@ -89,7 +87,7 @@ CodeGenerationErrorOr<void> Generator::emit_function_declaration_instantiation(E
|
|||
|
||||
switch_to_basic_block(if_undefined_block);
|
||||
auto operand = TRY(parameter.default_value->generate_bytecode(*this));
|
||||
emit<Op::SetArgument>(param_index, *operand);
|
||||
emit<Op::Mov>(Operand { Operand::Type::Argument, param_index }, *operand);
|
||||
emit<Op::Jump>(Label { if_not_undefined_block });
|
||||
|
||||
switch_to_basic_block(if_not_undefined_block);
|
||||
|
@ -98,23 +96,20 @@ 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::GetArgument>(local(local_variable_index), param_index);
|
||||
emit<Op::Mov>(local(local_variable_index), Operand { Operand::Type::Argument, param_index });
|
||||
set_local_initialized((*identifier)->local_variable_index());
|
||||
} else {
|
||||
auto id = intern_identifier((*identifier)->string());
|
||||
auto argument_reg = allocate_register();
|
||||
emit<Op::GetArgument>(argument_reg.operand(), param_index);
|
||||
if (function.shared_data().m_has_duplicates) {
|
||||
emit<Op::SetLexicalBinding>(id, argument_reg.operand());
|
||||
emit<Op::SetLexicalBinding>(id, Operand { Operand::Type::Argument, param_index });
|
||||
} else {
|
||||
emit<Op::InitializeLexicalBinding>(id, argument_reg.operand());
|
||||
emit<Op::InitializeLexicalBinding>(id, Operand { Operand::Type::Argument, param_index });
|
||||
}
|
||||
}
|
||||
} else if (auto const* binding_pattern = parameter.binding.get_pointer<NonnullRefPtr<BindingPattern const>>(); binding_pattern) {
|
||||
auto input_operand = allocate_register();
|
||||
emit<Op::GetArgument>(input_operand.operand(), param_index);
|
||||
ScopedOperand argument { *this, Operand { Operand::Type::Argument, param_index } };
|
||||
auto init_mode = function.shared_data().m_has_duplicates ? Op::BindingInitializationMode::Set : Bytecode::Op::BindingInitializationMode::Initialize;
|
||||
TRY((*binding_pattern)->generate_bytecode(*this, init_mode, input_operand));
|
||||
TRY((*binding_pattern)->generate_bytecode(*this, init_mode, argument));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -312,6 +307,7 @@ CodeGenerationErrorOr<GC::Ref<Executable>> Generator::compile(VM& vm, ASTNode co
|
|||
|
||||
auto number_of_registers = generator.m_next_register;
|
||||
auto number_of_constants = generator.m_constants.size();
|
||||
auto number_of_locals = function ? function->local_variables_names().size() : 0;
|
||||
|
||||
// Pass: Rewrite the bytecode to use the correct register and constant indices.
|
||||
for (auto& block : generator.m_root_basic_blocks) {
|
||||
|
@ -319,7 +315,7 @@ CodeGenerationErrorOr<GC::Ref<Executable>> Generator::compile(VM& vm, ASTNode co
|
|||
while (!it.at_end()) {
|
||||
auto& instruction = const_cast<Instruction&>(*it);
|
||||
|
||||
instruction.visit_operands([number_of_registers, number_of_constants](Operand& operand) {
|
||||
instruction.visit_operands([number_of_registers, number_of_constants, number_of_locals](Operand& operand) {
|
||||
switch (operand.type()) {
|
||||
case Operand::Type::Register:
|
||||
break;
|
||||
|
@ -329,6 +325,9 @@ CodeGenerationErrorOr<GC::Ref<Executable>> Generator::compile(VM& vm, ASTNode co
|
|||
case Operand::Type::Constant:
|
||||
operand.offset_index_by(number_of_registers);
|
||||
break;
|
||||
case Operand::Type::Argument:
|
||||
operand.offset_index_by(number_of_registers + number_of_constants + number_of_locals);
|
||||
break;
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
@ -476,6 +475,7 @@ CodeGenerationErrorOr<GC::Ref<Executable>> Generator::compile(VM& vm, ASTNode co
|
|||
executable->source_map = move(source_map);
|
||||
executable->local_variable_names = move(local_variable_names);
|
||||
executable->local_index_base = number_of_registers + number_of_constants;
|
||||
executable->argument_index_base = number_of_registers + number_of_constants + number_of_locals;
|
||||
executable->length_identifier = generator.m_length_identifier;
|
||||
|
||||
generator.m_finished = true;
|
||||
|
|
|
@ -51,7 +51,6 @@
|
|||
O(EnterObjectEnvironment) \
|
||||
O(EnterUnwindContext) \
|
||||
O(Exp) \
|
||||
O(GetArgument) \
|
||||
O(GetById) \
|
||||
O(GetByIdWithThis) \
|
||||
O(GetByValue) \
|
||||
|
@ -131,7 +130,6 @@
|
|||
O(Return) \
|
||||
O(RightShift) \
|
||||
O(ScheduleJump) \
|
||||
O(SetArgument) \
|
||||
O(SetCompletionType) \
|
||||
O(SetLexicalBinding) \
|
||||
O(SetVariableBinding) \
|
||||
|
|
|
@ -83,6 +83,9 @@ static ByteString format_operand(StringView name, Operand operand, Bytecode::Exe
|
|||
case Operand::Type::Local:
|
||||
builder.appendff("\033[34m{}~{}\033[0m", executable.local_variable_names[operand.index() - executable.local_index_base], operand.index() - executable.local_index_base);
|
||||
break;
|
||||
case Operand::Type::Argument:
|
||||
builder.appendff("\033[34marg{}\033[0m", operand.index() - executable.argument_index_base);
|
||||
break;
|
||||
case Operand::Type::Constant: {
|
||||
builder.append("\033[36m"sv);
|
||||
auto value = executable.constants[operand.index() - executable.number_of_registers];
|
||||
|
@ -184,12 +187,12 @@ Interpreter::~Interpreter()
|
|||
|
||||
ALWAYS_INLINE Value Interpreter::get(Operand op) const
|
||||
{
|
||||
return m_registers_and_constants_and_locals.data()[op.index()];
|
||||
return m_registers_and_constants_and_locals_arguments.data()[op.index()];
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void Interpreter::set(Operand op, Value value)
|
||||
{
|
||||
m_registers_and_constants_and_locals.data()[op.index()] = value;
|
||||
m_registers_and_constants_and_locals_arguments.data()[op.index()] = value;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Value Interpreter::do_yield(Value value, Optional<Label> continuation)
|
||||
|
@ -374,7 +377,6 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
|
|||
}
|
||||
|
||||
auto& running_execution_context = this->running_execution_context();
|
||||
auto* arguments = running_execution_context.arguments.data();
|
||||
auto& accumulator = this->accumulator();
|
||||
auto& executable = current_executable();
|
||||
auto const* bytecode = executable.bytecode.data();
|
||||
|
@ -408,18 +410,6 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
|
|||
for (;;) {
|
||||
goto* bytecode_dispatch_table[static_cast<size_t>((*reinterpret_cast<Instruction const*>(&bytecode[program_counter])).type())];
|
||||
|
||||
handle_GetArgument: {
|
||||
auto const& instruction = *reinterpret_cast<Op::GetArgument const*>(&bytecode[program_counter]);
|
||||
set(instruction.dst(), arguments[instruction.index()]);
|
||||
DISPATCH_NEXT(GetArgument);
|
||||
}
|
||||
|
||||
handle_SetArgument: {
|
||||
auto const& instruction = *reinterpret_cast<Op::SetArgument const*>(&bytecode[program_counter]);
|
||||
arguments[instruction.index()] = get(instruction.src());
|
||||
DISPATCH_NEXT(SetArgument);
|
||||
}
|
||||
|
||||
handle_Mov: {
|
||||
auto& instruction = *reinterpret_cast<Op::Mov const*>(&bytecode[program_counter]);
|
||||
set(instruction.dst(), get(instruction.src()));
|
||||
|
@ -745,7 +735,7 @@ Interpreter::ResultAndReturnRegister Interpreter::run_executable(Executable& exe
|
|||
VERIFY(registers_and_constants_and_locals_count <= running_execution_context.registers_and_constants_and_locals_and_arguments_span().size());
|
||||
|
||||
TemporaryChange restore_running_execution_context { m_running_execution_context, &running_execution_context };
|
||||
TemporaryChange restore_registers_and_constants_and_locals { m_registers_and_constants_and_locals, running_execution_context.registers_and_constants_and_locals_and_arguments_span() };
|
||||
TemporaryChange restore_registers_and_constants_and_locals { m_registers_and_constants_and_locals_arguments, running_execution_context.registers_and_constants_and_locals_and_arguments_span() };
|
||||
|
||||
reg(Register::accumulator()) = initial_accumulator_value;
|
||||
reg(Register::return_value()) = js_special_empty_value();
|
||||
|
@ -3219,16 +3209,6 @@ ByteString SetVariableBinding::to_byte_string_impl(Bytecode::Executable const& e
|
|||
format_operand("src"sv, src(), executable));
|
||||
}
|
||||
|
||||
ByteString GetArgument::to_byte_string_impl(Bytecode::Executable const& executable) const
|
||||
{
|
||||
return ByteString::formatted("GetArgument {}, {}", index(), format_operand("dst"sv, dst(), executable));
|
||||
}
|
||||
|
||||
ByteString SetArgument::to_byte_string_impl(Bytecode::Executable const& executable) const
|
||||
{
|
||||
return ByteString::formatted("SetArgument {}, {}", index(), format_operand("src"sv, src(), executable));
|
||||
}
|
||||
|
||||
static StringView property_kind_to_string(PropertyKind kind)
|
||||
{
|
||||
switch (kind) {
|
||||
|
|
|
@ -49,11 +49,11 @@ public:
|
|||
ALWAYS_INLINE Value& saved_return_value() { return reg(Register::saved_return_value()); }
|
||||
Value& reg(Register const& r)
|
||||
{
|
||||
return m_registers_and_constants_and_locals.data()[r.index()];
|
||||
return m_registers_and_constants_and_locals_arguments.data()[r.index()];
|
||||
}
|
||||
Value reg(Register const& r) const
|
||||
{
|
||||
return m_registers_and_constants_and_locals.data()[r.index()];
|
||||
return m_registers_and_constants_and_locals_arguments.data()[r.index()];
|
||||
}
|
||||
|
||||
[[nodiscard]] Value get(Operand) const;
|
||||
|
@ -101,7 +101,7 @@ private:
|
|||
GC::Ptr<Object> m_global_object { nullptr };
|
||||
GC::Ptr<DeclarativeEnvironment> m_global_declarative_environment { nullptr };
|
||||
Optional<size_t&> m_program_counter;
|
||||
Span<Value> m_registers_and_constants_and_locals;
|
||||
Span<Value> m_registers_and_constants_and_locals_arguments;
|
||||
Vector<Value> m_argument_values_buffer;
|
||||
ExecutionContext* m_running_execution_context { nullptr };
|
||||
};
|
||||
|
|
|
@ -775,52 +775,6 @@ private:
|
|||
mutable EnvironmentCoordinate m_cache;
|
||||
};
|
||||
|
||||
class SetArgument final : public Instruction {
|
||||
public:
|
||||
SetArgument(size_t index, Operand src)
|
||||
: Instruction(Type::SetArgument)
|
||||
, m_index(index)
|
||||
, m_src(src)
|
||||
{
|
||||
}
|
||||
|
||||
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
|
||||
void visit_operands_impl(Function<void(Operand&)> visitor)
|
||||
{
|
||||
visitor(m_src);
|
||||
}
|
||||
|
||||
size_t index() const { return m_index; }
|
||||
Operand src() const { return m_src; }
|
||||
|
||||
private:
|
||||
u32 m_index;
|
||||
Operand m_src;
|
||||
};
|
||||
|
||||
class GetArgument final : public Instruction {
|
||||
public:
|
||||
GetArgument(Operand dst, size_t index)
|
||||
: Instruction(Type::GetArgument)
|
||||
, m_index(index)
|
||||
, m_dst(dst)
|
||||
{
|
||||
}
|
||||
|
||||
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
|
||||
void visit_operands_impl(Function<void(Operand&)> visitor)
|
||||
{
|
||||
visitor(m_dst);
|
||||
}
|
||||
|
||||
u32 index() const { return m_index; }
|
||||
Operand dst() const { return m_dst; }
|
||||
|
||||
private:
|
||||
u32 m_index;
|
||||
Operand m_dst;
|
||||
};
|
||||
|
||||
class GetCalleeAndThisFromEnvironment final : public Instruction {
|
||||
public:
|
||||
explicit GetCalleeAndThisFromEnvironment(Operand callee, Operand this_value, IdentifierTableIndex identifier)
|
||||
|
|
|
@ -22,6 +22,7 @@ public:
|
|||
Register,
|
||||
Local,
|
||||
Constant,
|
||||
Argument,
|
||||
};
|
||||
|
||||
[[nodiscard]] bool operator==(Operand const&) const = default;
|
||||
|
@ -53,8 +54,8 @@ private:
|
|||
// Because this type is absolutely essential to the interpreter, we allow
|
||||
// ourselves this little ifdef.
|
||||
#if ARCH(AARCH64)
|
||||
Type m_type : 2 {};
|
||||
u32 m_index : 30 { 0 };
|
||||
Type m_type : 3 {};
|
||||
u32 m_index : 29 { 0 };
|
||||
#else
|
||||
Type m_type { Type::Invalid };
|
||||
u32 m_index { 0 };
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue