From fff82c5ffe1e96b7b13716593c3a51d12a0991d9 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 28 Oct 2023 14:22:36 +0200 Subject: [PATCH] LibJS/JIT: Only preserve VM& when making native call to C++ Instead of pushing and popping every single caller-saved registers, we can optimize code size (and speed!) by only pushing the one register we actually care about: RDI (since it holds our VM&). This means that native calls may clobber every other caller-saved register, so this is something that you have to be aware of when emitting native calls in the JIT. This reduces code size on Kraken/ai-astar.js by 553 KiB and makes execution time ~6% faster as well! :^) --- Userland/Libraries/LibJS/JIT/Compiler.cpp | 31 +++++++++-------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/Userland/Libraries/LibJS/JIT/Compiler.cpp b/Userland/Libraries/LibJS/JIT/Compiler.cpp index d72980b671..934b078f1b 100644 --- a/Userland/Libraries/LibJS/JIT/Compiler.cpp +++ b/Userland/Libraries/LibJS/JIT/Compiler.cpp @@ -1006,28 +1006,21 @@ void Compiler::jump_to_exit() void Compiler::native_call(void* function_address, Vector const& stack_arguments) { - // push caller-saved registers on the stack - // (callee-saved registers: RBX, RSP, RBP, and R12–R15) - m_assembler.push(Assembler::Operand::Register(Assembler::Reg::RCX)); - m_assembler.push(Assembler::Operand::Register(Assembler::Reg::RDX)); - m_assembler.push(Assembler::Operand::Register(Assembler::Reg::RSI)); - m_assembler.push(Assembler::Operand::Register(Assembler::Reg::RDI)); - m_assembler.push(Assembler::Operand::Register(Assembler::Reg::R8)); - m_assembler.push(Assembler::Operand::Register(Assembler::Reg::R9)); - m_assembler.push(Assembler::Operand::Register(Assembler::Reg::R10)); - m_assembler.push(Assembler::Operand::Register(Assembler::Reg::R11)); + // Make sure we don't clobber the VM&. + m_assembler.push(Assembler::Operand::Register(ARG0)); + // Align the stack pointer. + m_assembler.sub(Assembler::Operand::Register(STACK_POINTER), Assembler::Operand::Imm(8)); + + // NOTE: We don't preserve caller-saved registers when making a native call. + // This means that they may have changed after we return from the call. m_assembler.native_call(function_address, stack_arguments); - // restore caller-saved registers from the stack - m_assembler.pop(Assembler::Operand::Register(Assembler::Reg::R11)); - m_assembler.pop(Assembler::Operand::Register(Assembler::Reg::R10)); - m_assembler.pop(Assembler::Operand::Register(Assembler::Reg::R9)); - m_assembler.pop(Assembler::Operand::Register(Assembler::Reg::R8)); - m_assembler.pop(Assembler::Operand::Register(Assembler::Reg::RDI)); - m_assembler.pop(Assembler::Operand::Register(Assembler::Reg::RSI)); - m_assembler.pop(Assembler::Operand::Register(Assembler::Reg::RDX)); - m_assembler.pop(Assembler::Operand::Register(Assembler::Reg::RCX)); + // Restore the stack pointer. + m_assembler.add(Assembler::Operand::Register(STACK_POINTER), Assembler::Operand::Imm(8)); + + // Restore our VM&. + m_assembler.pop(Assembler::Operand::Register(ARG0)); } OwnPtr Compiler::compile(Bytecode::Executable& bytecode_executable)