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; }
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; }
void set_this(ScopedOperand operand) { m_this = operand; }
[[nodiscard]] bool has_resolved_this() const { return m_has_resolved_this; }
void set_has_resolved_this() { m_has_resolved_this = true; }
[[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; }
@ -73,11 +73,10 @@ private:
BasicBlock const* m_finalizer { nullptr };
String m_name;
bool m_terminated { false };
bool m_has_resolved_this { false };
HashMap<size_t, SourceRecord> m_source_map;
Optional<ScopedOperand> m_this;
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_constants(vm.heap())
, m_accumulator(*this, Operand(Register::accumulator()))
, m_this_value(*this, Operand(Register::this_value()))
, m_must_propagate_completion(must_propagate_completion == MustPropagateCompletion::Yes)
, m_function(function)
{
@ -571,8 +572,7 @@ CodeGenerationErrorOr<Generator::ReferenceOperands> Generator::emit_super_refere
// https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation
// 1. Let env be GetThisEnvironment().
// 2. Let actualThis be ? env.GetThisBinding().
auto actual_this = allocate_register();
emit<Bytecode::Op::ResolveThisBinding>(actual_this);
auto actual_this = get_this();
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)
{
if (m_current_basic_block->this_().has_value())
return m_current_basic_block->this_().value();
if (m_root_basic_blocks[0]->this_().has_value()) {
m_current_basic_block->set_this(m_root_basic_blocks[0]->this_().value());
return m_root_basic_blocks[0]->this_().value();
if (m_current_basic_block->has_resolved_this())
return this_value();
if (m_root_basic_blocks[0]->has_resolved_this()) {
m_current_basic_block->set_has_resolved_this();
return this_value();
}
auto dst = preferred_dst.has_value() ? preferred_dst.value() : allocate_register();
emit<Bytecode::Op::ResolveThisBinding>(dst);
m_current_basic_block->set_this(dst);
return dst;
emit<Bytecode::Op::ResolveThisBinding>();
m_current_basic_block->set_has_resolved_this();
return this_value();
}
ScopedOperand Generator::accumulator()
@ -1098,6 +1098,11 @@ ScopedOperand Generator::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)
{
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 local(u32 local_index);
[[nodiscard]] ScopedOperand accumulator();
[[nodiscard]] ScopedOperand this_value();
void free_register(Register);
@ -377,6 +378,7 @@ private:
MarkedVector<Value> m_constants;
ScopedOperand m_accumulator;
ScopedOperand m_this_value;
Vector<Register> m_free_registers;
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
{
auto& cached_this_value = interpreter.reg(Register::this_value());
if (cached_this_value.is_empty()) {
// OPTIMIZATION: Because the value of 'this' cannot be reassigned during a function execution, it's
// resolved once and then saved for subsequent use.
auto& running_execution_context = interpreter.running_execution_context();
if (auto function = running_execution_context.function; function && is<ECMAScriptFunctionObject>(*function) && !static_cast<ECMAScriptFunctionObject&>(*function).allocates_function_environment()) {
cached_this_value = running_execution_context.this_value;
} else {
auto& vm = interpreter.vm();
cached_this_value = TRY(vm.resolve_this_binding());
}
if (!cached_this_value.is_empty())
return {};
// OPTIMIZATION: Because the value of 'this' cannot be reassigned during a function execution, it's
// resolved once and then saved for subsequent use.
auto& running_execution_context = interpreter.running_execution_context();
if (auto function = running_execution_context.function; function && is<ECMAScriptFunctionObject>(*function) && !static_cast<ECMAScriptFunctionObject&>(*function).allocates_function_environment()) {
cached_this_value = running_execution_context.this_value;
} else {
auto& vm = interpreter.vm();
cached_this_value = TRY(vm.resolve_this_binding());
}
interpreter.set(dst(), cached_this_value);
return {};
}
@ -2655,9 +2654,9 @@ ByteString IteratorNext::to_byte_string_impl(Executable const& executable) const
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

View file

@ -2550,23 +2550,14 @@ private:
class ResolveThisBinding final : public Instruction {
public:
explicit ResolveThisBinding(Operand dst)
ResolveThisBinding()
: Instruction(Type::ResolveThisBinding)
, m_dst(dst)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst);
}
Operand dst() const { return m_dst; }
private:
Operand m_dst;
void visit_operands_impl(Function<void(Operand&)>) { }
};
class ResolveSuperBase final : public Instruction {

View file

@ -11,7 +11,7 @@ namespace JS::Bytecode {
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());
}