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:
Andreas Kling 2024-05-31 20:41:29 +02:00
parent 9d57b55f24
commit 507f83a615
Notes: sideshowbarker 2024-07-17 01:27:18 +09:00
6 changed files with 35 additions and 39 deletions

View file

@ -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 };
}; };

View file

@ -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());

View file

@ -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 };

View file

@ -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

View file

@ -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 {

View file

@ -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());
} }