mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-26 09:30:24 +00:00
LibJS/Bytecode: Always resolve this
binding into dedicated register
We already have a dedicated register slot for `this`, so instead of having ResolveThisBinding take a `dst` operand, just write the value directly into the `this` register every time.
This commit is contained in:
parent
9d57b55f24
commit
507f83a615
Notes:
sideshowbarker
2024-07-17 01:27:18 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/507f83a615 Pull-request: https://github.com/SerenityOS/serenity/pull/24504
6 changed files with 35 additions and 39 deletions
|
@ -58,8 +58,8 @@ public:
|
||||||
auto const& source_map() const { return m_source_map; }
|
auto const& source_map() const { return m_source_map; }
|
||||||
void add_source_map_entry(size_t bytecode_offset, SourceRecord const& source_record) { m_source_map.set(bytecode_offset, source_record); }
|
void add_source_map_entry(size_t bytecode_offset, SourceRecord const& source_record) { m_source_map.set(bytecode_offset, source_record); }
|
||||||
|
|
||||||
auto const& this_() const { return m_this; }
|
[[nodiscard]] bool has_resolved_this() const { return m_has_resolved_this; }
|
||||||
void set_this(ScopedOperand operand) { m_this = operand; }
|
void set_has_resolved_this() { m_has_resolved_this = true; }
|
||||||
|
|
||||||
[[nodiscard]] size_t last_instruction_start_offset() const { return m_last_instruction_start_offset; }
|
[[nodiscard]] size_t last_instruction_start_offset() const { return m_last_instruction_start_offset; }
|
||||||
void set_last_instruction_start_offset(size_t offset) { m_last_instruction_start_offset = offset; }
|
void set_last_instruction_start_offset(size_t offset) { m_last_instruction_start_offset = offset; }
|
||||||
|
@ -73,11 +73,10 @@ private:
|
||||||
BasicBlock const* m_finalizer { nullptr };
|
BasicBlock const* m_finalizer { nullptr };
|
||||||
String m_name;
|
String m_name;
|
||||||
bool m_terminated { false };
|
bool m_terminated { false };
|
||||||
|
bool m_has_resolved_this { false };
|
||||||
|
|
||||||
HashMap<size_t, SourceRecord> m_source_map;
|
HashMap<size_t, SourceRecord> m_source_map;
|
||||||
|
|
||||||
Optional<ScopedOperand> m_this;
|
|
||||||
|
|
||||||
size_t m_last_instruction_start_offset { 0 };
|
size_t m_last_instruction_start_offset { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ Generator::Generator(VM& vm, GCPtr<ECMAScriptFunctionObject const> function, Mus
|
||||||
, m_regex_table(make<RegexTable>())
|
, m_regex_table(make<RegexTable>())
|
||||||
, m_constants(vm.heap())
|
, m_constants(vm.heap())
|
||||||
, m_accumulator(*this, Operand(Register::accumulator()))
|
, m_accumulator(*this, Operand(Register::accumulator()))
|
||||||
|
, m_this_value(*this, Operand(Register::this_value()))
|
||||||
, m_must_propagate_completion(must_propagate_completion == MustPropagateCompletion::Yes)
|
, m_must_propagate_completion(must_propagate_completion == MustPropagateCompletion::Yes)
|
||||||
, m_function(function)
|
, m_function(function)
|
||||||
{
|
{
|
||||||
|
@ -571,8 +572,7 @@ CodeGenerationErrorOr<Generator::ReferenceOperands> Generator::emit_super_refere
|
||||||
// https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation
|
// https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation
|
||||||
// 1. Let env be GetThisEnvironment().
|
// 1. Let env be GetThisEnvironment().
|
||||||
// 2. Let actualThis be ? env.GetThisBinding().
|
// 2. Let actualThis be ? env.GetThisBinding().
|
||||||
auto actual_this = allocate_register();
|
auto actual_this = get_this();
|
||||||
emit<Bytecode::Op::ResolveThisBinding>(actual_this);
|
|
||||||
|
|
||||||
Optional<ScopedOperand> computed_property_value;
|
Optional<ScopedOperand> computed_property_value;
|
||||||
|
|
||||||
|
@ -1081,16 +1081,16 @@ void Generator::set_local_initialized(u32 local_index)
|
||||||
|
|
||||||
ScopedOperand Generator::get_this(Optional<ScopedOperand> preferred_dst)
|
ScopedOperand Generator::get_this(Optional<ScopedOperand> preferred_dst)
|
||||||
{
|
{
|
||||||
if (m_current_basic_block->this_().has_value())
|
if (m_current_basic_block->has_resolved_this())
|
||||||
return m_current_basic_block->this_().value();
|
return this_value();
|
||||||
if (m_root_basic_blocks[0]->this_().has_value()) {
|
if (m_root_basic_blocks[0]->has_resolved_this()) {
|
||||||
m_current_basic_block->set_this(m_root_basic_blocks[0]->this_().value());
|
m_current_basic_block->set_has_resolved_this();
|
||||||
return m_root_basic_blocks[0]->this_().value();
|
return this_value();
|
||||||
}
|
}
|
||||||
auto dst = preferred_dst.has_value() ? preferred_dst.value() : allocate_register();
|
auto dst = preferred_dst.has_value() ? preferred_dst.value() : allocate_register();
|
||||||
emit<Bytecode::Op::ResolveThisBinding>(dst);
|
emit<Bytecode::Op::ResolveThisBinding>();
|
||||||
m_current_basic_block->set_this(dst);
|
m_current_basic_block->set_has_resolved_this();
|
||||||
return dst;
|
return this_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedOperand Generator::accumulator()
|
ScopedOperand Generator::accumulator()
|
||||||
|
@ -1098,6 +1098,11 @@ ScopedOperand Generator::accumulator()
|
||||||
return m_accumulator;
|
return m_accumulator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ScopedOperand Generator::this_value()
|
||||||
|
{
|
||||||
|
return m_this_value;
|
||||||
|
}
|
||||||
|
|
||||||
bool Generator::fuse_compare_and_jump(ScopedOperand const& condition, Label true_target, Label false_target)
|
bool Generator::fuse_compare_and_jump(ScopedOperand const& condition, Label true_target, Label false_target)
|
||||||
{
|
{
|
||||||
auto& last_instruction = *reinterpret_cast<Instruction const*>(m_current_basic_block->data() + m_current_basic_block->last_instruction_start_offset());
|
auto& last_instruction = *reinterpret_cast<Instruction const*>(m_current_basic_block->data() + m_current_basic_block->last_instruction_start_offset());
|
||||||
|
|
|
@ -46,6 +46,7 @@ public:
|
||||||
[[nodiscard]] ScopedOperand allocate_register();
|
[[nodiscard]] ScopedOperand allocate_register();
|
||||||
[[nodiscard]] ScopedOperand local(u32 local_index);
|
[[nodiscard]] ScopedOperand local(u32 local_index);
|
||||||
[[nodiscard]] ScopedOperand accumulator();
|
[[nodiscard]] ScopedOperand accumulator();
|
||||||
|
[[nodiscard]] ScopedOperand this_value();
|
||||||
|
|
||||||
void free_register(Register);
|
void free_register(Register);
|
||||||
|
|
||||||
|
@ -377,6 +378,7 @@ private:
|
||||||
MarkedVector<Value> m_constants;
|
MarkedVector<Value> m_constants;
|
||||||
|
|
||||||
ScopedOperand m_accumulator;
|
ScopedOperand m_accumulator;
|
||||||
|
ScopedOperand m_this_value;
|
||||||
Vector<Register> m_free_registers;
|
Vector<Register> m_free_registers;
|
||||||
|
|
||||||
u32 m_next_register { Register::reserved_register_count };
|
u32 m_next_register { Register::reserved_register_count };
|
||||||
|
|
|
@ -1563,18 +1563,17 @@ ThrowCompletionOr<void> DeleteByIdWithThis::execute_impl(Bytecode::Interpreter&
|
||||||
ThrowCompletionOr<void> ResolveThisBinding::execute_impl(Bytecode::Interpreter& interpreter) const
|
ThrowCompletionOr<void> ResolveThisBinding::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
auto& cached_this_value = interpreter.reg(Register::this_value());
|
auto& cached_this_value = interpreter.reg(Register::this_value());
|
||||||
if (cached_this_value.is_empty()) {
|
if (!cached_this_value.is_empty())
|
||||||
// OPTIMIZATION: Because the value of 'this' cannot be reassigned during a function execution, it's
|
return {};
|
||||||
// resolved once and then saved for subsequent use.
|
// OPTIMIZATION: Because the value of 'this' cannot be reassigned during a function execution, it's
|
||||||
auto& running_execution_context = interpreter.running_execution_context();
|
// resolved once and then saved for subsequent use.
|
||||||
if (auto function = running_execution_context.function; function && is<ECMAScriptFunctionObject>(*function) && !static_cast<ECMAScriptFunctionObject&>(*function).allocates_function_environment()) {
|
auto& running_execution_context = interpreter.running_execution_context();
|
||||||
cached_this_value = running_execution_context.this_value;
|
if (auto function = running_execution_context.function; function && is<ECMAScriptFunctionObject>(*function) && !static_cast<ECMAScriptFunctionObject&>(*function).allocates_function_environment()) {
|
||||||
} else {
|
cached_this_value = running_execution_context.this_value;
|
||||||
auto& vm = interpreter.vm();
|
} else {
|
||||||
cached_this_value = TRY(vm.resolve_this_binding());
|
auto& vm = interpreter.vm();
|
||||||
}
|
cached_this_value = TRY(vm.resolve_this_binding());
|
||||||
}
|
}
|
||||||
interpreter.set(dst(), cached_this_value);
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2655,9 +2654,9 @@ ByteString IteratorNext::to_byte_string_impl(Executable const& executable) const
|
||||||
format_operand("iterator_record"sv, m_iterator_record, executable));
|
format_operand("iterator_record"sv, m_iterator_record, executable));
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteString ResolveThisBinding::to_byte_string_impl(Bytecode::Executable const& executable) const
|
ByteString ResolveThisBinding::to_byte_string_impl(Bytecode::Executable const&) const
|
||||||
{
|
{
|
||||||
return ByteString::formatted("ResolveThisBinding {}", format_operand("dst"sv, m_dst, executable));
|
return "ResolveThisBinding"sv;
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteString ResolveSuperBase::to_byte_string_impl(Bytecode::Executable const& executable) const
|
ByteString ResolveSuperBase::to_byte_string_impl(Bytecode::Executable const& executable) const
|
||||||
|
|
|
@ -2550,23 +2550,14 @@ private:
|
||||||
|
|
||||||
class ResolveThisBinding final : public Instruction {
|
class ResolveThisBinding final : public Instruction {
|
||||||
public:
|
public:
|
||||||
explicit ResolveThisBinding(Operand dst)
|
ResolveThisBinding()
|
||||||
: Instruction(Type::ResolveThisBinding)
|
: Instruction(Type::ResolveThisBinding)
|
||||||
, m_dst(dst)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
|
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
|
||||||
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
|
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
|
||||||
void visit_operands_impl(Function<void(Operand&)> visitor)
|
void visit_operands_impl(Function<void(Operand&)>) { }
|
||||||
{
|
|
||||||
visitor(m_dst);
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand dst() const { return m_dst; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
Operand m_dst;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ResolveSuperBase final : public Instruction {
|
class ResolveSuperBase final : public Instruction {
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace JS::Bytecode {
|
||||||
|
|
||||||
ScopedOperandImpl::~ScopedOperandImpl()
|
ScopedOperandImpl::~ScopedOperandImpl()
|
||||||
{
|
{
|
||||||
if (!m_generator.is_finished() && m_operand.is_register() && m_operand.as_register().index() != 0)
|
if (!m_generator.is_finished() && m_operand.is_register() && m_operand.as_register().index() >= Register::reserved_register_count)
|
||||||
m_generator.free_register(m_operand.as_register());
|
m_generator.free_register(m_operand.as_register());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue