ladybird/Libraries/LibJS/Runtime/SharedFunctionInstanceData.cpp
Andreas Kling 517812647a LibJS: Pack asm Call shared-data metadata
Pack the asm Call fast path metadata next to the executable pointer
so the interpreter can fetch both values with one paired load. This
removes several dependent shared-data loads from the hot path.

Keep the executable pointer and packed metadata in separate registers
through this binding so the fast path can still use the paired-load
layout after any non-strict this adjustment.

Lower the packed metadata flag checks correctly on x86_64 as well.
Those bits now live above bit 31, so the generator uses bt for single-
bit high masks and covers that path with a unit test.

Add a runtime test that exercises both object and global this binding
through the asm Call fast path.
2026-04-14 12:37:12 +02:00

105 lines
3 KiB
C++

/*
* Copyright (c) 2025, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/SharedFunctionInstanceData.h>
#include <LibJS/RustIntegration.h>
namespace JS {
GC_DEFINE_ALLOCATOR(SharedFunctionInstanceData);
SharedFunctionInstanceData::SharedFunctionInstanceData(
VM&,
FunctionKind kind,
Utf16FlyString name,
i32 function_length,
u32 formal_parameter_count,
bool strict,
bool is_arrow_function,
bool has_simple_parameter_list,
Vector<Utf16FlyString> parameter_names_for_mapped_arguments,
void* rust_function_ast)
: m_name(move(name))
, m_function_length(function_length)
, m_formal_parameter_count(formal_parameter_count)
, m_parameter_names_for_mapped_arguments(move(parameter_names_for_mapped_arguments))
, m_kind(kind)
, m_strict(strict)
, m_is_arrow_function(is_arrow_function)
, m_has_simple_parameter_list(has_simple_parameter_list)
, m_rust_function_ast(rust_function_ast)
, m_use_rust_compilation(true)
{
if (m_is_arrow_function)
m_this_mode = ThisMode::Lexical;
else if (m_strict)
m_this_mode = ThisMode::Strict;
else
m_this_mode = ThisMode::Global;
update_can_inline_call();
}
void SharedFunctionInstanceData::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_executable);
for (auto& function : m_functions_to_initialize)
visitor.visit(function.shared_data);
m_class_field_initializer_name.visit([&](PropertyKey const& key) { key.visit_edges(visitor); }, [](auto&) {});
}
SharedFunctionInstanceData::~SharedFunctionInstanceData() = default;
void SharedFunctionInstanceData::set_executable(GC::Ptr<Bytecode::Executable> executable)
{
m_executable = executable;
update_can_inline_call();
}
void SharedFunctionInstanceData::set_is_class_constructor()
{
m_is_class_constructor = true;
update_can_inline_call();
}
void SharedFunctionInstanceData::update_asm_call_metadata()
{
m_asm_call_metadata = m_formal_parameter_count;
if (m_can_inline_call)
m_asm_call_metadata |= asm_call_metadata_can_inline_call;
if (m_function_environment_needed)
m_asm_call_metadata |= asm_call_metadata_function_environment_needed;
if (m_uses_this)
m_asm_call_metadata |= asm_call_metadata_uses_this;
if (m_strict)
m_asm_call_metadata |= asm_call_metadata_strict;
}
void SharedFunctionInstanceData::finalize()
{
Base::finalize();
RustIntegration::free_function_ast(m_rust_function_ast);
m_rust_function_ast = nullptr;
}
void SharedFunctionInstanceData::clear_compile_inputs()
{
VERIFY(m_executable);
m_functions_to_initialize.clear();
m_var_names_to_initialize_binding.clear();
m_lexical_bindings.clear();
RustIntegration::free_function_ast(m_rust_function_ast);
m_rust_function_ast = nullptr;
}
void SharedFunctionInstanceData::update_can_inline_call()
{
m_can_inline_call = m_executable && m_kind == FunctionKind::Normal && !m_is_class_constructor;
update_asm_call_metadata();
}
}