Skip to content

Commit fff82c5

Browse files
committed
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! :^)
1 parent 926786e commit fff82c5

File tree

1 file changed

+12
-19
lines changed

1 file changed

+12
-19
lines changed

Userland/Libraries/LibJS/JIT/Compiler.cpp

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,28 +1006,21 @@ void Compiler::jump_to_exit()
10061006

10071007
void Compiler::native_call(void* function_address, Vector<Assembler::Operand> const& stack_arguments)
10081008
{
1009-
// push caller-saved registers on the stack
1010-
// (callee-saved registers: RBX, RSP, RBP, and R12–R15)
1011-
m_assembler.push(Assembler::Operand::Register(Assembler::Reg::RCX));
1012-
m_assembler.push(Assembler::Operand::Register(Assembler::Reg::RDX));
1013-
m_assembler.push(Assembler::Operand::Register(Assembler::Reg::RSI));
1014-
m_assembler.push(Assembler::Operand::Register(Assembler::Reg::RDI));
1015-
m_assembler.push(Assembler::Operand::Register(Assembler::Reg::R8));
1016-
m_assembler.push(Assembler::Operand::Register(Assembler::Reg::R9));
1017-
m_assembler.push(Assembler::Operand::Register(Assembler::Reg::R10));
1018-
m_assembler.push(Assembler::Operand::Register(Assembler::Reg::R11));
1009+
// Make sure we don't clobber the VM&.
1010+
m_assembler.push(Assembler::Operand::Register(ARG0));
10191011

1012+
// Align the stack pointer.
1013+
m_assembler.sub(Assembler::Operand::Register(STACK_POINTER), Assembler::Operand::Imm(8));
1014+
1015+
// NOTE: We don't preserve caller-saved registers when making a native call.
1016+
// This means that they may have changed after we return from the call.
10201017
m_assembler.native_call(function_address, stack_arguments);
10211018

1022-
// restore caller-saved registers from the stack
1023-
m_assembler.pop(Assembler::Operand::Register(Assembler::Reg::R11));
1024-
m_assembler.pop(Assembler::Operand::Register(Assembler::Reg::R10));
1025-
m_assembler.pop(Assembler::Operand::Register(Assembler::Reg::R9));
1026-
m_assembler.pop(Assembler::Operand::Register(Assembler::Reg::R8));
1027-
m_assembler.pop(Assembler::Operand::Register(Assembler::Reg::RDI));
1028-
m_assembler.pop(Assembler::Operand::Register(Assembler::Reg::RSI));
1029-
m_assembler.pop(Assembler::Operand::Register(Assembler::Reg::RDX));
1030-
m_assembler.pop(Assembler::Operand::Register(Assembler::Reg::RCX));
1019+
// Restore the stack pointer.
1020+
m_assembler.add(Assembler::Operand::Register(STACK_POINTER), Assembler::Operand::Imm(8));
1021+
1022+
// Restore our VM&.
1023+
m_assembler.pop(Assembler::Operand::Register(ARG0));
10311024
}
10321025

10331026
OwnPtr<NativeExecutable> Compiler::compile(Bytecode::Executable& bytecode_executable)

0 commit comments

Comments
 (0)