Compare commits

..

2 Commits

Author SHA1 Message Date
JosJuice
eb611c72b1
Merge e1379fffc0 into 04f71e5e6d 2025-12-15 20:55:28 +01:00
JosJuice
e1379fffc0 Jit64: Make ABI_CallFunction generic
This replaces all the hand-coded variants of ABI_CallFunction with a
single generic function. It handles all existing cases, plus many new
cases that would have been annoying to hand-code.

The implementation is based on our AArch64 ABI_CallFunction, but
adapted for x64. The most notable differences are support for memory
operands and a new way for callers to specify the sizes of operands.
2025-12-15 20:47:07 +01:00
3 changed files with 64 additions and 49 deletions

View File

@ -60,7 +60,14 @@ void XEmitter::ParallelMoves(RegisterMove* begin, RegisterMove* end,
{
RegisterMove* prev_move = move;
--move;
if ((*source_reg_uses)[move->destination] == 0)
const auto destination_reg_uses_minus_this_source = [&] {
return (*source_reg_uses)[move->destination] -
(move->source.GetBaseReg() == move->destination) -
(move->source.GetIndexReg() == move->destination);
};
if (destination_reg_uses_minus_this_source() == 0)
{
// TODO: Use sign extension instead of zero extension if the relevant type is signed.
if (move->bits < 32)

View File

@ -1126,13 +1126,18 @@ public:
const X64Reg destination_reg = ABI_PARAMS[i];
const auto destination_reg_uses_minus_this_source = [&] {
return source_reg_uses[destination_reg] - (arg.op_arg.GetBaseReg() == destination_reg) -
(arg.op_arg.GetIndexReg() == destination_reg);
};
if (arg.op_arg.IsSimpleReg(destination_reg) && !zero_extend)
{
// The value is already in the right register.
destination_reg_uses[destination_reg]++;
source_reg_uses[destination_reg]--;
}
else if (source_reg_uses[destination_reg] == 0)
else if (destination_reg_uses_minus_this_source() == 0)
{
// The destination register isn't used as the source of another move.
// We can go ahead and do the move right away.

View File

@ -76,21 +76,21 @@ private:
} // namespace
TEST(Arm64Emitter, CallFunction_ZeroParameters)
TEST(x64ABITest, CallFunction_ZeroParameters)
{
TestCallFunction test;
test.Emit([&] { test.ABI_CallFunction(&ZeroParameterFunction); });
test.Run();
}
TEST(Arm64Emitter, CallFunction_OneConstantParameter)
TEST(x64ABITest, CallFunction_OneConstantParameter)
{
TestCallFunction test;
test.Emit([&] { test.ABI_CallFunction(&OneParameterFunction<u64>, 100); });
test.Run();
}
TEST(Arm64Emitter, CallFunction_OneImm64Parameter)
TEST(x64ABITest, CallFunction_OneImm64Parameter)
{
TestCallFunction test;
test.Emit([&] {
@ -99,7 +99,7 @@ TEST(Arm64Emitter, CallFunction_OneImm64Parameter)
test.Run();
}
TEST(Arm64Emitter, CallFunction_OneImm32Parameter)
TEST(x64ABITest, CallFunction_OneImm32Parameter)
{
TestCallFunction test;
test.Emit([&] {
@ -108,7 +108,7 @@ TEST(Arm64Emitter, CallFunction_OneImm32Parameter)
test.Run();
}
TEST(Arm64Emitter, CallFunction_OneImm16Parameter)
TEST(x64ABITest, CallFunction_OneImm16Parameter)
{
TestCallFunction test;
test.Emit([&] {
@ -117,7 +117,7 @@ TEST(Arm64Emitter, CallFunction_OneImm16Parameter)
test.Run();
}
TEST(Arm64Emitter, CallFunction_OneImm8Parameter)
TEST(x64ABITest, CallFunction_OneImm8Parameter)
{
TestCallFunction test;
test.Emit([&] {
@ -126,7 +126,7 @@ TEST(Arm64Emitter, CallFunction_OneImm8Parameter)
test.Run();
}
TEST(Arm64Emitter, CallFunction_One64BitRegisterParameterNoMov)
TEST(x64ABITest, CallFunction_One64BitRegisterParameterNoMov)
{
TestCallFunction test;
test.Emit([&] {
@ -136,7 +136,7 @@ TEST(Arm64Emitter, CallFunction_One64BitRegisterParameterNoMov)
test.Run();
}
TEST(Arm64Emitter, CallFunction_One64BitRegisterParameterMov)
TEST(x64ABITest, CallFunction_One64BitRegisterParameterMov)
{
TestCallFunction test;
test.Emit([&] {
@ -146,7 +146,7 @@ TEST(Arm64Emitter, CallFunction_One64BitRegisterParameterMov)
test.Run();
}
TEST(Arm64Emitter, CallFunction_One32BitRegisterParameterNoMov)
TEST(x64ABITest, CallFunction_One32BitRegisterParameterNoMov)
{
TestCallFunction test;
test.Emit([&] {
@ -156,7 +156,7 @@ TEST(Arm64Emitter, CallFunction_One32BitRegisterParameterNoMov)
test.Run();
}
TEST(Arm64Emitter, CallFunction_One32BitRegisterParameterMov)
TEST(x64ABITest, CallFunction_One32BitRegisterParameterMov)
{
TestCallFunction test;
test.Emit([&] {
@ -166,7 +166,7 @@ TEST(Arm64Emitter, CallFunction_One32BitRegisterParameterMov)
test.Run();
}
TEST(Arm64Emitter, CallFunction_One16BitRegisterParameterNoMov)
TEST(x64ABITest, CallFunction_One16BitRegisterParameterNoMov)
{
TestCallFunction test;
test.Emit([&] {
@ -176,7 +176,7 @@ TEST(Arm64Emitter, CallFunction_One16BitRegisterParameterNoMov)
test.Run();
}
TEST(Arm64Emitter, CallFunction_One16BitRegisterParameterMov)
TEST(x64ABITest, CallFunction_One16BitRegisterParameterMov)
{
TestCallFunction test;
test.Emit([&] {
@ -186,7 +186,7 @@ TEST(Arm64Emitter, CallFunction_One16BitRegisterParameterMov)
test.Run();
}
TEST(Arm64Emitter, CallFunction_One8BitRegisterParameterNoMov)
TEST(x64ABITest, CallFunction_One8BitRegisterParameterNoMov)
{
TestCallFunction test;
test.Emit([&] {
@ -196,7 +196,7 @@ TEST(Arm64Emitter, CallFunction_One8BitRegisterParameterNoMov)
test.Run();
}
TEST(Arm64Emitter, CallFunction_One8BitRegisterParameterMov)
TEST(x64ABITest, CallFunction_One8BitRegisterParameterMov)
{
TestCallFunction test;
test.Emit([&] {
@ -206,51 +206,54 @@ TEST(Arm64Emitter, CallFunction_One8BitRegisterParameterMov)
test.Run();
}
TEST(Arm64Emitter, CallFunction_One64BitMemoryParameter)
TEST(x64ABITest, CallFunction_One64BitMemoryParameter)
{
constexpr u64 value = 100;
TestCallFunction test;
test.Emit([&] {
constexpr u64 value = 100;
test.MOV(64, R(RAX), ImmPtr(&value));
test.ABI_CallFunction(&OneParameterFunction<u64>, XEmitter::CallFunctionArg(64, MatR(RAX)));
});
test.Run();
}
TEST(Arm64Emitter, CallFunction_One32BitMemoryParameter)
TEST(x64ABITest, CallFunction_One32BitMemoryParameter)
{
constexpr u32 value = 100;
TestCallFunction test;
test.Emit([&] {
constexpr u32 value = 100;
test.MOV(64, R(RAX), ImmPtr(&value));
test.ABI_CallFunction(&OneParameterFunction<u32>, XEmitter::CallFunctionArg(32, MatR(RAX)));
test.MOV(64, R(ABI_PARAM3), ImmPtr(&value));
test.ABI_CallFunction(&OneParameterFunction<u32>,
XEmitter::CallFunctionArg(32, MatR(ABI_PARAM3)));
});
test.Run();
}
TEST(Arm64Emitter, CallFunction_One16BitMemoryParameter)
TEST(x64ABITest, CallFunction_One16BitMemoryParameter)
{
constexpr u16 value = 100;
TestCallFunction test;
test.Emit([&] {
constexpr u16 value = 100;
test.MOV(64, R(RAX), ImmPtr(&value));
test.ABI_CallFunction(&OneParameterFunction<u16>, XEmitter::CallFunctionArg(16, MatR(RAX)));
test.MOV(64, R(ABI_PARAM2), ImmPtr(&value));
test.ABI_CallFunction(&OneParameterFunction<u16>,
XEmitter::CallFunctionArg(16, MatR(ABI_PARAM2)));
});
test.Run();
}
TEST(Arm64Emitter, CallFunction_One8BitMemoryParameter)
TEST(x64ABITest, CallFunction_One8BitMemoryParameter)
{
constexpr u8 value = 100;
TestCallFunction test;
test.Emit([&] {
constexpr u8 value = 100;
test.MOV(64, R(RAX), ImmPtr(&value));
test.ABI_CallFunction(&OneParameterFunction<u8>, XEmitter::CallFunctionArg(8, MatR(RAX)));
test.MOV(64, R(ABI_PARAM1), ImmPtr(&value));
test.ABI_CallFunction(&OneParameterFunction<u8>,
XEmitter::CallFunctionArg(8, MatR(ABI_PARAM1)));
});
test.Run();
}
TEST(Arm64Emitter, CallFunction_TwoRegistersMixed)
TEST(x64ABITest, CallFunction_TwoRegistersMixed)
{
TestCallFunction test;
test.Emit([&] {
@ -260,7 +263,7 @@ TEST(Arm64Emitter, CallFunction_TwoRegistersMixed)
test.Run();
}
TEST(Arm64Emitter, CallFunction_TwoRegistersCycle)
TEST(x64ABITest, CallFunction_TwoRegistersCycle)
{
TestCallFunction test;
test.Emit([&] {
@ -272,7 +275,7 @@ TEST(Arm64Emitter, CallFunction_TwoRegistersCycle)
test.Run();
}
TEST(Arm64Emitter, CallFunction_ThreeRegistersMixed)
TEST(x64ABITest, CallFunction_ThreeRegistersMixed)
{
TestCallFunction test;
test.Emit([&] {
@ -284,7 +287,7 @@ TEST(Arm64Emitter, CallFunction_ThreeRegistersMixed)
test.Run();
}
TEST(Arm64Emitter, CallFunction_ThreeRegistersCycle1)
TEST(x64ABITest, CallFunction_ThreeRegistersCycle1)
{
TestCallFunction test;
test.Emit([&] {
@ -298,7 +301,7 @@ TEST(Arm64Emitter, CallFunction_ThreeRegistersCycle1)
test.Run();
}
TEST(Arm64Emitter, CallFunction_ThreeRegistersCycle2)
TEST(x64ABITest, CallFunction_ThreeRegistersCycle2)
{
TestCallFunction test;
test.Emit([&] {
@ -312,13 +315,13 @@ TEST(Arm64Emitter, CallFunction_ThreeRegistersCycle2)
test.Run();
}
TEST(Arm64Emitter, CallFunction_ThreeMemoryRegistersCycle1)
TEST(x64ABITest, CallFunction_ThreeMemoryRegistersCycle1)
{
constexpr u32 value1 = 10;
constexpr u32 value2 = 20;
constexpr u32 value3 = 30;
TestCallFunction test;
test.Emit([&] {
constexpr u32 value1 = 10;
constexpr u32 value2 = 20;
constexpr u32 value3 = 30;
test.MOV(64, R(ABI_PARAM1), ImmPtr(&value3));
test.MOV(64, R(ABI_PARAM2), ImmPtr(&value1));
test.MOV(64, R(ABI_PARAM3), ImmPtr(&value2));
@ -329,13 +332,13 @@ TEST(Arm64Emitter, CallFunction_ThreeMemoryRegistersCycle1)
test.Run();
}
TEST(Arm64Emitter, CallFunction_ThreeMemoryRegistersCycle2)
TEST(x64ABITest, CallFunction_ThreeMemoryRegistersCycle2)
{
constexpr u32 value1 = 10;
constexpr u32 value2 = 20;
constexpr u32 value3 = 30;
TestCallFunction test;
test.Emit([&] {
constexpr u32 value1 = 10;
constexpr u32 value2 = 20;
constexpr u32 value3 = 30;
test.MOV(64, R(ABI_PARAM1), ImmPtr(&value2));
test.MOV(64, R(ABI_PARAM2), ImmPtr(&value3));
test.MOV(64, R(ABI_PARAM3), ImmPtr(&value1));
@ -346,7 +349,7 @@ TEST(Arm64Emitter, CallFunction_ThreeMemoryRegistersCycle2)
test.Run();
}
TEST(Arm64Emitter, CallFunction_FourRegistersMixed)
TEST(x64ABITest, CallFunction_FourRegistersMixed)
{
// Loosely based on an actual piece of code where we call Core::BranchWatch::HitVirtualTrue_fk_n
TestCallFunction test;
@ -359,14 +362,14 @@ TEST(Arm64Emitter, CallFunction_FourRegistersMixed)
test.Run();
}
TEST(Arm64Emitter, CallFunction_FourRegistersComplexAddressing)
TEST(x64ABITest, CallFunction_FourRegistersComplexAddressing)
{
constexpr u64 value1 = 0x0102030405060708;
constexpr u64 value2 = 0x090a0b0c0d0e0f10;
constexpr u32 value3 = 0x11121314;
constexpr u32 value4 = 0x15161718;
TestCallFunction test;
test.Emit([&] {
constexpr u64 value1 = 0x0102030405060708;
constexpr u64 value2 = 0x090a0b0c0d0e0f10;
constexpr u32 value3 = 0x11121314;
constexpr u32 value4 = 0x15161718;
test.MOV(64, R(ABI_PARAM1), Imm32(3));
test.MOV(64, R(ABI_PARAM2), Imm64(reinterpret_cast<uintptr_t>(&value1) - 3));
test.MOV(64, R(ABI_PARAM4), Imm64(reinterpret_cast<uintptr_t>(&value2) / 4 + 3));