LibJS: Cache JS-to-JS inline call eligibility
Store whether a function can participate in JS-to-JS inline calls on SharedFunctionInstanceData instead of recomputing the function kind, class-constructor bit, and bytecode availability at each fast-path call site.
This commit is contained in:
parent
4ad800b594
commit
df0fdee2a0
8 changed files with 46 additions and 12 deletions
|
|
@ -867,15 +867,15 @@ i64 asm_try_inline_call(VM* vm, u32 pc)
|
|||
if (!is<ECMAScriptFunctionObject>(callee_object))
|
||||
return 1;
|
||||
auto& callee_function = static_cast<ECMAScriptFunctionObject&>(callee_object);
|
||||
if (callee_function.kind() != FunctionKind::Normal
|
||||
|| callee_function.is_class_constructor()
|
||||
|| !callee_function.bytecode_executable())
|
||||
if (!callee_function.can_inline_call())
|
||||
return 1;
|
||||
|
||||
auto& callee_executable = callee_function.inline_call_executable();
|
||||
|
||||
u32 return_pc = pc + insn.length();
|
||||
|
||||
auto* callee_context = vm->push_inline_frame(
|
||||
callee_function, *callee_function.bytecode_executable(),
|
||||
callee_function, callee_executable,
|
||||
insn.arguments(), return_pc, insn.dst().raw(),
|
||||
vm->get(insn.this_value()), nullptr, false);
|
||||
|
||||
|
|
|
|||
|
|
@ -323,15 +323,15 @@ NEVER_INLINE bool VM::try_inline_call(Instruction const& insn, u32 current_pc)
|
|||
if (!is<ECMAScriptFunctionObject>(callee_object))
|
||||
return false;
|
||||
auto& callee_function = static_cast<ECMAScriptFunctionObject&>(callee_object);
|
||||
if (callee_function.kind() != FunctionKind::Normal
|
||||
|| callee_function.is_class_constructor()
|
||||
|| !callee_function.bytecode_executable())
|
||||
if (!callee_function.can_inline_call())
|
||||
return false;
|
||||
|
||||
auto& callee_executable = callee_function.inline_call_executable();
|
||||
|
||||
u32 return_pc = current_pc + instruction.length();
|
||||
|
||||
auto* callee_context = push_inline_frame(
|
||||
callee_function, *callee_function.bytecode_executable(),
|
||||
callee_function, callee_executable,
|
||||
instruction.arguments(), return_pc, instruction.dst().raw(),
|
||||
get(instruction.this_value()), nullptr, false);
|
||||
|
||||
|
|
|
|||
|
|
@ -139,10 +139,11 @@ void ECMAScriptFunctionObject::initialize(Realm& realm)
|
|||
|
||||
void ECMAScriptFunctionObject::get_stack_frame_info(size_t& registers_and_locals_count, ReadonlySpan<Value>& constants, size_t& argument_count)
|
||||
{
|
||||
auto& executable = shared_data().m_executable;
|
||||
auto executable = shared_data().m_executable;
|
||||
if (!executable) {
|
||||
auto rust_executable = RustIntegration::compile_function(vm(), *m_shared_data, false);
|
||||
VERIFY(rust_executable);
|
||||
m_shared_data->set_executable(rust_executable);
|
||||
executable = rust_executable;
|
||||
executable->name = m_shared_data->m_name;
|
||||
if (Bytecode::g_dump_bytecode)
|
||||
|
|
|
|||
|
|
@ -62,9 +62,15 @@ public:
|
|||
Utf16FlyString const& name() const { return shared_data().m_name; }
|
||||
void set_name(Utf16FlyString const& name);
|
||||
|
||||
void set_is_class_constructor() { const_cast<SharedFunctionInstanceData&>(shared_data()).m_is_class_constructor = true; }
|
||||
void set_is_class_constructor() { const_cast<SharedFunctionInstanceData&>(shared_data()).set_is_class_constructor(); }
|
||||
|
||||
auto& bytecode_executable() const { return shared_data().m_executable; }
|
||||
[[nodiscard]] bool can_inline_call() const { return shared_data().can_inline_call(); }
|
||||
[[nodiscard]] Bytecode::Executable& inline_call_executable() const
|
||||
{
|
||||
VERIFY(can_inline_call());
|
||||
return *shared_data().m_executable;
|
||||
}
|
||||
|
||||
Environment* environment() { return m_environment; }
|
||||
virtual Realm* realm() const override { return &shape().realm(); }
|
||||
|
|
|
|||
|
|
@ -95,10 +95,11 @@ ThrowCompletionOr<Value> NativeJavaScriptBackedFunction::call()
|
|||
|
||||
Bytecode::Executable& NativeJavaScriptBackedFunction::bytecode_executable()
|
||||
{
|
||||
auto& executable = m_shared_function_instance_data->m_executable;
|
||||
auto executable = m_shared_function_instance_data->m_executable;
|
||||
if (!executable) {
|
||||
auto rust_executable = RustIntegration::compile_function(vm(), *m_shared_function_instance_data, true);
|
||||
VERIFY(rust_executable);
|
||||
m_shared_function_instance_data->set_executable(rust_executable);
|
||||
executable = rust_executable;
|
||||
executable->name = m_shared_function_instance_data->m_name;
|
||||
if (Bytecode::g_dump_bytecode)
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ SharedFunctionInstanceData::SharedFunctionInstanceData(
|
|||
m_this_mode = ThisMode::Strict;
|
||||
else
|
||||
m_this_mode = ThisMode::Global;
|
||||
|
||||
update_can_inline_call();
|
||||
}
|
||||
|
||||
void SharedFunctionInstanceData::visit_edges(Visitor& visitor)
|
||||
|
|
@ -52,6 +54,18 @@ void SharedFunctionInstanceData::visit_edges(Visitor& visitor)
|
|||
|
||||
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::finalize()
|
||||
{
|
||||
Base::finalize();
|
||||
|
|
@ -69,4 +83,9 @@ void SharedFunctionInstanceData::clear_compile_inputs()
|
|||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,6 +63,10 @@ public:
|
|||
Vector<Utf16FlyString> parameter_names_for_mapped_arguments,
|
||||
void* rust_function_ast);
|
||||
|
||||
void set_executable(GC::Ptr<Bytecode::Executable>);
|
||||
void set_is_class_constructor();
|
||||
[[nodiscard]] bool can_inline_call() const { return m_can_inline_call; }
|
||||
|
||||
mutable GC::Ptr<Bytecode::Executable> m_executable;
|
||||
|
||||
Utf16FlyString m_name;
|
||||
|
|
@ -141,6 +145,9 @@ public:
|
|||
|
||||
private:
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
void update_can_inline_call();
|
||||
|
||||
bool m_can_inline_call { false };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -493,7 +493,7 @@ Optional<Result<ModuleResult, Vector<ParserError>>> compile_parsed_module(Parsed
|
|||
builder.result.tla_shared_data->m_is_module_wrapper = true;
|
||||
builder.result.tla_shared_data->m_uses_this = true;
|
||||
builder.result.tla_shared_data->m_function_environment_needed = true;
|
||||
builder.result.tla_shared_data->m_executable = tla_exec;
|
||||
builder.result.tla_shared_data->set_executable(tla_exec);
|
||||
} else {
|
||||
builder.result.executable = static_cast<Bytecode::Executable*>(exec_ptr);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue