From aaeb21fe3b60ac98a6e283d13d0228dada9055df Mon Sep 17 00:00:00 2001 From: pierre Date: Sat, 2 Oct 2010 20:04:03 +0000 Subject: [PATCH] Core/Common: Automatically emit a MOV in ABI_CallFunction* when the function pointer cannot be reached by 32bit displacement. RAX is caller save and non-parameter in all 64 bit ABIs, so it should be safe to clobber it on function call. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6248 8ced0084-cf51-0410-be5f-012b33b47a6e --- Source/Core/Common/Src/ABI.cpp | 116 ++++++++++++++++++++++++++++----- 1 file changed, 99 insertions(+), 17 deletions(-) diff --git a/Source/Core/Common/Src/ABI.cpp b/Source/Core/Common/Src/ABI.cpp index dcfc593a50..c92053a28d 100644 --- a/Source/Core/Common/Src/ABI.cpp +++ b/Source/Core/Common/Src/ABI.cpp @@ -207,43 +207,85 @@ void XEmitter::ABI_RestoreStack(unsigned int frameSize) { // Common functions void XEmitter::ABI_CallFunction(void *func) { - // Far call - //MOV(64, R(RAX), Imm64((u64)func)); - //CALLptr(R(RAX)); - CALL(func); + u64 distance = u64(func) - (u64(code) + 5); + if (distance >= 0x0000000080000000ULL + && distance < 0xFFFFFFFF80000000ULL) { + // Far call + MOV(64, R(RAX), Imm64((u64)func)); + CALLptr(R(RAX)); + } else { + CALL(func); + } } void XEmitter::ABI_CallFunctionC16(void *func, u16 param1) { MOV(32, R(ABI_PARAM1), Imm32((u32)param1)); - // Far call - //MOV(64, R(RAX), Imm64((u64)func)); - //CALLptr(R(RAX)); - CALL(func); + u64 distance = u64(func) - (u64(code) + 5); + if (distance >= 0x0000000080000000ULL + && distance < 0xFFFFFFFF80000000ULL) { + // Far call + MOV(64, R(RAX), Imm64((u64)func)); + CALLptr(R(RAX)); + } else { + CALL(func); + } } void XEmitter::ABI_CallFunctionC(void *func, u32 param1) { MOV(32, R(ABI_PARAM1), Imm32(param1)); - CALL(func); + u64 distance = u64(func) - (u64(code) + 5); + if (distance >= 0x0000000080000000ULL + && distance < 0xFFFFFFFF80000000ULL) { + // Far call + MOV(64, R(RAX), Imm64((u64)func)); + CALLptr(R(RAX)); + } else { + CALL(func); + } } void XEmitter::ABI_CallFunctionCC(void *func, u32 param1, u32 param2) { MOV(32, R(ABI_PARAM1), Imm32(param1)); MOV(32, R(ABI_PARAM2), Imm32(param2)); - CALL(func); + u64 distance = u64(func) - (u64(code) + 5); + if (distance >= 0x0000000080000000ULL + && distance < 0xFFFFFFFF80000000ULL) { + // Far call + MOV(64, R(RAX), Imm64((u64)func)); + CALLptr(R(RAX)); + } else { + CALL(func); + } } void XEmitter::ABI_CallFunctionCCC(void *func, u32 param1, u32 param2, u32 param3) { MOV(32, R(ABI_PARAM1), Imm32(param1)); MOV(32, R(ABI_PARAM2), Imm32(param2)); MOV(32, R(ABI_PARAM3), Imm32(param3)); - CALL(func); + u64 distance = u64(func) - (u64(code) + 5); + if (distance >= 0x0000000080000000ULL + && distance < 0xFFFFFFFF80000000ULL) { + // Far call + MOV(64, R(RAX), Imm64((u64)func)); + CALLptr(R(RAX)); + } else { + CALL(func); + } } void XEmitter::ABI_CallFunctionCCP(void *func, u32 param1, u32 param2, void *param3) { MOV(32, R(ABI_PARAM1), Imm32(param1)); MOV(32, R(ABI_PARAM2), Imm32(param2)); MOV(64, R(ABI_PARAM3), Imm64((u64)param3)); - CALL(func); + u64 distance = u64(func) - (u64(code) + 5); + if (distance >= 0x0000000080000000ULL + && distance < 0xFFFFFFFF80000000ULL) { + // Far call + MOV(64, R(RAX), Imm64((u64)func)); + CALLptr(R(RAX)); + } else { + CALL(func); + } } void XEmitter::ABI_CallFunctionCCCP(void *func, u32 param1, u32 param2, u32 param3, void *param4) { @@ -251,14 +293,30 @@ void XEmitter::ABI_CallFunctionCCCP(void *func, u32 param1, u32 param2, u32 para MOV(32, R(ABI_PARAM2), Imm32(param2)); MOV(32, R(ABI_PARAM3), Imm32(param3)); MOV(64, R(ABI_PARAM4), Imm64((u64)param4)); - CALL(func); + u64 distance = u64(func) - (u64(code) + 5); + if (distance >= 0x0000000080000000ULL + && distance < 0xFFFFFFFF80000000ULL) { + // Far call + MOV(64, R(RAX), Imm64((u64)func)); + CALLptr(R(RAX)); + } else { + CALL(func); + } } // Pass a register as a parameter. void XEmitter::ABI_CallFunctionR(void *func, X64Reg reg1) { if (reg1 != ABI_PARAM1) MOV(32, R(ABI_PARAM1), R(reg1)); - CALL(func); + u64 distance = u64(func) - (u64(code) + 5); + if (distance >= 0x0000000080000000ULL + && distance < 0xFFFFFFFF80000000ULL) { + // Far call + MOV(64, R(RAX), Imm64((u64)func)); + CALLptr(R(RAX)); + } else { + CALL(func); + } } // Pass two registers as parameters. @@ -274,7 +332,15 @@ void XEmitter::ABI_CallFunctionRR(void *func, X64Reg reg1, X64Reg reg2) { if (reg1 != ABI_PARAM1) MOV(64, R(ABI_PARAM1), R(reg1)); } - CALL(func); + u64 distance = u64(func) - (u64(code) + 5); + if (distance >= 0x0000000080000000ULL + && distance < 0xFFFFFFFF80000000ULL) { + // Far call + MOV(64, R(RAX), Imm64((u64)func)); + CALLptr(R(RAX)); + } else { + CALL(func); + } } void XEmitter::ABI_CallFunctionAC(void *func, const Gen::OpArg &arg1, u32 param2) @@ -282,14 +348,30 @@ void XEmitter::ABI_CallFunctionAC(void *func, const Gen::OpArg &arg1, u32 param2 if (!arg1.IsSimpleReg(ABI_PARAM1)) MOV(32, R(ABI_PARAM1), arg1); MOV(32, R(ABI_PARAM2), Imm32(param2)); - CALL(func); + u64 distance = u64(func) - (u64(code) + 5); + if (distance >= 0x0000000080000000ULL + && distance < 0xFFFFFFFF80000000ULL) { + // Far call + MOV(64, R(RAX), Imm64((u64)func)); + CALLptr(R(RAX)); + } else { + CALL(func); + } } void XEmitter::ABI_CallFunctionA(void *func, const Gen::OpArg &arg1) { if (!arg1.IsSimpleReg(ABI_PARAM1)) MOV(32, R(ABI_PARAM1), arg1); - CALL(func); + u64 distance = u64(func) - (u64(code) + 5); + if (distance >= 0x0000000080000000ULL + && distance < 0xFFFFFFFF80000000ULL) { + // Far call + MOV(64, R(RAX), Imm64((u64)func)); + CALLptr(R(RAX)); + } else { + CALL(func); + } } unsigned int XEmitter::ABI_GetAlignedFrameSize(unsigned int frameSize) {