11 changed files with 4382 additions and 6 deletions
-
2src/citra/CMakeLists.txt
-
2src/citra_qt/CMakeLists.txt
-
12src/common/CMakeLists.txt
-
680src/common/abi.cpp
-
78src/common/abi.h
-
87src/common/code_block.h
-
2src/common/common_funcs.h
-
465src/common/fake_emitter.h
-
2src/common/platform.h
-
1989src/common/x64_emitter.cpp
-
1067src/common/x64_emitter.h
@ -0,0 +1,680 @@ |
|||||
|
// Copyright (C) 2003 Dolphin Project.
|
||||
|
|
||||
|
// This program is free software: you can redistribute it and/or modify
|
||||
|
// it under the terms of the GNU General Public License as published by
|
||||
|
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
|
||||
|
// This program is distributed in the hope that it will be useful,
|
||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
|
// GNU General Public License 2.0 for more details.
|
||||
|
|
||||
|
// A copy of the GPL 2.0 should have been included with the program.
|
||||
|
// If not, see http://www.gnu.org/licenses/
|
||||
|
|
||||
|
// Official SVN repository and contact information can be found at
|
||||
|
// http://code.google.com/p/dolphin-emu/
|
||||
|
|
||||
|
#include "x64_emitter.h"
|
||||
|
#include "abi.h"
|
||||
|
|
||||
|
using namespace Gen; |
||||
|
|
||||
|
// Shared code between Win64 and Unix64
|
||||
|
|
||||
|
// Sets up a __cdecl function.
|
||||
|
void XEmitter::ABI_EmitPrologue(int maxCallParams) |
||||
|
{ |
||||
|
#ifdef _M_IX86
|
||||
|
// Don't really need to do anything
|
||||
|
#elif defined(_M_X86_64)
|
||||
|
#if _WIN32
|
||||
|
int stacksize = ((maxCallParams + 1) & ~1) * 8 + 8; |
||||
|
// Set up a stack frame so that we can call functions
|
||||
|
// TODO: use maxCallParams
|
||||
|
SUB(64, R(RSP), Imm8(stacksize)); |
||||
|
#endif
|
||||
|
#else
|
||||
|
#error Arch not supported
|
||||
|
#endif
|
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_EmitEpilogue(int maxCallParams) |
||||
|
{ |
||||
|
#ifdef _M_IX86
|
||||
|
RET(); |
||||
|
#elif defined(_M_X86_64)
|
||||
|
#ifdef _WIN32
|
||||
|
int stacksize = ((maxCallParams+1)&~1)*8 + 8; |
||||
|
ADD(64, R(RSP), Imm8(stacksize)); |
||||
|
#endif
|
||||
|
RET(); |
||||
|
#else
|
||||
|
#error Arch not supported
|
||||
|
|
||||
|
|
||||
|
#endif
|
||||
|
} |
||||
|
|
||||
|
#ifdef _M_IX86 // All32
|
||||
|
|
||||
|
// Shared code between Win32 and Unix32
|
||||
|
void XEmitter::ABI_CallFunction(const void *func) { |
||||
|
ABI_AlignStack(0); |
||||
|
CALL(func); |
||||
|
ABI_RestoreStack(0); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionC16(const void *func, u16 param1) { |
||||
|
ABI_AlignStack(1 * 2); |
||||
|
PUSH(16, Imm16(param1)); |
||||
|
CALL(func); |
||||
|
ABI_RestoreStack(1 * 2); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionCC16(const void *func, u32 param1, u16 param2) { |
||||
|
ABI_AlignStack(1 * 2 + 1 * 4); |
||||
|
PUSH(16, Imm16(param2)); |
||||
|
PUSH(32, Imm32(param1)); |
||||
|
CALL(func); |
||||
|
ABI_RestoreStack(1 * 2 + 1 * 4); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionC(const void *func, u32 param1) { |
||||
|
ABI_AlignStack(1 * 4); |
||||
|
PUSH(32, Imm32(param1)); |
||||
|
CALL(func); |
||||
|
ABI_RestoreStack(1 * 4); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionCC(const void *func, u32 param1, u32 param2) { |
||||
|
ABI_AlignStack(2 * 4); |
||||
|
PUSH(32, Imm32(param2)); |
||||
|
PUSH(32, Imm32(param1)); |
||||
|
CALL(func); |
||||
|
ABI_RestoreStack(2 * 4); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionCCC(const void *func, u32 param1, u32 param2, u32 param3) { |
||||
|
ABI_AlignStack(3 * 4); |
||||
|
PUSH(32, Imm32(param3)); |
||||
|
PUSH(32, Imm32(param2)); |
||||
|
PUSH(32, Imm32(param1)); |
||||
|
CALL(func); |
||||
|
ABI_RestoreStack(3 * 4); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionCCP(const void *func, u32 param1, u32 param2, void *param3) { |
||||
|
ABI_AlignStack(3 * 4); |
||||
|
PUSH(32, ImmPtr(param3)); |
||||
|
PUSH(32, Imm32(param2)); |
||||
|
PUSH(32, Imm32(param1)); |
||||
|
CALL(func); |
||||
|
ABI_RestoreStack(3 * 4); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionCCCP(const void *func, u32 param1, u32 param2,u32 param3, void *param4) { |
||||
|
ABI_AlignStack(4 * 4); |
||||
|
PUSH(32, ImmPtr(param4)); |
||||
|
PUSH(32, Imm32(param3)); |
||||
|
PUSH(32, Imm32(param2)); |
||||
|
PUSH(32, Imm32(param1)); |
||||
|
CALL(func); |
||||
|
ABI_RestoreStack(4 * 4); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionP(const void *func, void *param1) { |
||||
|
ABI_AlignStack(1 * 4); |
||||
|
PUSH(32, ImmPtr(param1)); |
||||
|
CALL(func); |
||||
|
ABI_RestoreStack(1 * 4); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionPA(const void *func, void *param1, const Gen::OpArg &arg2) { |
||||
|
ABI_AlignStack(2 * 4); |
||||
|
PUSH(32, arg2); |
||||
|
PUSH(32, ImmPtr(param1)); |
||||
|
CALL(func); |
||||
|
ABI_RestoreStack(2 * 4); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionPAA(const void *func, void *param1, const Gen::OpArg &arg2, const Gen::OpArg &arg3) { |
||||
|
ABI_AlignStack(3 * 4); |
||||
|
PUSH(32, arg3); |
||||
|
PUSH(32, arg2); |
||||
|
PUSH(32, ImmPtr(param1)); |
||||
|
CALL(func); |
||||
|
ABI_RestoreStack(3 * 4); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionPPC(const void *func, void *param1, void *param2, u32 param3) { |
||||
|
ABI_AlignStack(3 * 4); |
||||
|
PUSH(32, Imm32(param3)); |
||||
|
PUSH(32, ImmPtr(param2)); |
||||
|
PUSH(32, ImmPtr(param1)); |
||||
|
CALL(func); |
||||
|
ABI_RestoreStack(3 * 4); |
||||
|
} |
||||
|
|
||||
|
// Pass a register as a parameter.
|
||||
|
void XEmitter::ABI_CallFunctionR(const void *func, X64Reg reg1) { |
||||
|
ABI_AlignStack(1 * 4); |
||||
|
PUSH(32, R(reg1)); |
||||
|
CALL(func); |
||||
|
ABI_RestoreStack(1 * 4); |
||||
|
} |
||||
|
|
||||
|
// Pass two registers as parameters.
|
||||
|
void XEmitter::ABI_CallFunctionRR(const void *func, Gen::X64Reg reg1, Gen::X64Reg reg2) |
||||
|
{ |
||||
|
ABI_AlignStack(2 * 4); |
||||
|
PUSH(32, R(reg2)); |
||||
|
PUSH(32, R(reg1)); |
||||
|
CALL(func); |
||||
|
ABI_RestoreStack(2 * 4); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionAC(const void *func, const Gen::OpArg &arg1, u32 param2) |
||||
|
{ |
||||
|
ABI_AlignStack(2 * 4); |
||||
|
PUSH(32, Imm32(param2)); |
||||
|
PUSH(32, arg1); |
||||
|
CALL(func); |
||||
|
ABI_RestoreStack(2 * 4); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionACC(const void *func, const Gen::OpArg &arg1, u32 param2, u32 param3) |
||||
|
{ |
||||
|
ABI_AlignStack(3 * 4); |
||||
|
PUSH(32, Imm32(param3)); |
||||
|
PUSH(32, Imm32(param2)); |
||||
|
PUSH(32, arg1); |
||||
|
CALL(func); |
||||
|
ABI_RestoreStack(3 * 4); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionA(const void *func, const Gen::OpArg &arg1) |
||||
|
{ |
||||
|
ABI_AlignStack(1 * 4); |
||||
|
PUSH(32, arg1); |
||||
|
CALL(func); |
||||
|
ABI_RestoreStack(1 * 4); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionAA(const void *func, const Gen::OpArg &arg1, const Gen::OpArg &arg2) |
||||
|
{ |
||||
|
ABI_AlignStack(2 * 4); |
||||
|
PUSH(32, arg2); |
||||
|
PUSH(32, arg1); |
||||
|
CALL(func); |
||||
|
ABI_RestoreStack(2 * 4); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_PushAllCalleeSavedRegsAndAdjustStack() { |
||||
|
// Note: 4 * 4 = 16 bytes, so alignment is preserved.
|
||||
|
PUSH(EBP); |
||||
|
PUSH(EBX); |
||||
|
PUSH(ESI); |
||||
|
PUSH(EDI); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_PopAllCalleeSavedRegsAndAdjustStack() { |
||||
|
POP(EDI); |
||||
|
POP(ESI); |
||||
|
POP(EBX); |
||||
|
POP(EBP); |
||||
|
} |
||||
|
|
||||
|
unsigned int XEmitter::ABI_GetAlignedFrameSize(unsigned int frameSize) { |
||||
|
frameSize += 4; // reserve space for return address
|
||||
|
unsigned int alignedSize = |
||||
|
#ifdef __GNUC__
|
||||
|
(frameSize + 15) & -16; |
||||
|
#else
|
||||
|
(frameSize + 3) & -4; |
||||
|
#endif
|
||||
|
return alignedSize; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void XEmitter::ABI_AlignStack(unsigned int frameSize) { |
||||
|
// Mac OS X requires the stack to be 16-byte aligned before every call.
|
||||
|
// Linux requires the stack to be 16-byte aligned before calls that put SSE
|
||||
|
// vectors on the stack, but since we do not keep track of which calls do that,
|
||||
|
// it is effectively every call as well.
|
||||
|
// Windows binaries compiled with MSVC do not have such a restriction*, but I
|
||||
|
// expect that GCC on Windows acts the same as GCC on Linux in this respect.
|
||||
|
// It would be nice if someone could verify this.
|
||||
|
// *However, the MSVC optimizing compiler assumes a 4-byte-aligned stack at times.
|
||||
|
unsigned int fillSize = |
||||
|
ABI_GetAlignedFrameSize(frameSize) - (frameSize + 4); |
||||
|
if (fillSize != 0) { |
||||
|
SUB(32, R(ESP), Imm8(fillSize)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_RestoreStack(unsigned int frameSize) { |
||||
|
unsigned int alignedSize = ABI_GetAlignedFrameSize(frameSize); |
||||
|
alignedSize -= 4; // return address is POPped at end of call
|
||||
|
if (alignedSize != 0) { |
||||
|
ADD(32, R(ESP), Imm8(alignedSize)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#else //64bit
|
||||
|
|
||||
|
// Common functions
|
||||
|
void XEmitter::ABI_CallFunction(const void *func) { |
||||
|
u64 distance = u64(func) - (u64(code) + 5); |
||||
|
if (distance >= 0x0000000080000000ULL |
||||
|
&& distance < 0xFFFFFFFF80000000ULL) { |
||||
|
// Far call
|
||||
|
MOV(64, R(RAX), ImmPtr(func)); |
||||
|
CALLptr(R(RAX)); |
||||
|
} else { |
||||
|
CALL(func); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionC16(const void *func, u16 param1) { |
||||
|
MOV(32, R(ABI_PARAM1), Imm32((u32)param1)); |
||||
|
u64 distance = u64(func) - (u64(code) + 5); |
||||
|
if (distance >= 0x0000000080000000ULL |
||||
|
&& distance < 0xFFFFFFFF80000000ULL) { |
||||
|
// Far call
|
||||
|
MOV(64, R(RAX), ImmPtr(func)); |
||||
|
CALLptr(R(RAX)); |
||||
|
} else { |
||||
|
CALL(func); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionCC16(const void *func, u32 param1, u16 param2) { |
||||
|
MOV(32, R(ABI_PARAM1), Imm32(param1)); |
||||
|
MOV(32, R(ABI_PARAM2), Imm32((u32)param2)); |
||||
|
u64 distance = u64(func) - (u64(code) + 5); |
||||
|
if (distance >= 0x0000000080000000ULL |
||||
|
&& distance < 0xFFFFFFFF80000000ULL) { |
||||
|
// Far call
|
||||
|
MOV(64, R(RAX), ImmPtr(func)); |
||||
|
CALLptr(R(RAX)); |
||||
|
} else { |
||||
|
CALL(func); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionC(const void *func, u32 param1) { |
||||
|
MOV(32, R(ABI_PARAM1), Imm32(param1)); |
||||
|
u64 distance = u64(func) - (u64(code) + 5); |
||||
|
if (distance >= 0x0000000080000000ULL |
||||
|
&& distance < 0xFFFFFFFF80000000ULL) { |
||||
|
// Far call
|
||||
|
MOV(64, R(RAX), ImmPtr(func)); |
||||
|
CALLptr(R(RAX)); |
||||
|
} else { |
||||
|
CALL(func); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionCC(const void *func, u32 param1, u32 param2) { |
||||
|
MOV(32, R(ABI_PARAM1), Imm32(param1)); |
||||
|
MOV(32, R(ABI_PARAM2), Imm32(param2)); |
||||
|
u64 distance = u64(func) - (u64(code) + 5); |
||||
|
if (distance >= 0x0000000080000000ULL |
||||
|
&& distance < 0xFFFFFFFF80000000ULL) { |
||||
|
// Far call
|
||||
|
MOV(64, R(RAX), ImmPtr(func)); |
||||
|
CALLptr(R(RAX)); |
||||
|
} else { |
||||
|
CALL(func); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionCCC(const 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)); |
||||
|
u64 distance = u64(func) - (u64(code) + 5); |
||||
|
if (distance >= 0x0000000080000000ULL |
||||
|
&& distance < 0xFFFFFFFF80000000ULL) { |
||||
|
// Far call
|
||||
|
MOV(64, R(RAX), ImmPtr(func)); |
||||
|
CALLptr(R(RAX)); |
||||
|
} else { |
||||
|
CALL(func); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionCCP(const 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), ImmPtr(param3)); |
||||
|
u64 distance = u64(func) - (u64(code) + 5); |
||||
|
if (distance >= 0x0000000080000000ULL |
||||
|
&& distance < 0xFFFFFFFF80000000ULL) { |
||||
|
// Far call
|
||||
|
MOV(64, R(RAX), ImmPtr(func)); |
||||
|
CALLptr(R(RAX)); |
||||
|
} else { |
||||
|
CALL(func); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionCCCP(const void *func, u32 param1, u32 param2, u32 param3, void *param4) { |
||||
|
MOV(32, R(ABI_PARAM1), Imm32(param1)); |
||||
|
MOV(32, R(ABI_PARAM2), Imm32(param2)); |
||||
|
MOV(32, R(ABI_PARAM3), Imm32(param3)); |
||||
|
MOV(64, R(ABI_PARAM4), ImmPtr(param4)); |
||||
|
u64 distance = u64(func) - (u64(code) + 5); |
||||
|
if (distance >= 0x0000000080000000ULL |
||||
|
&& distance < 0xFFFFFFFF80000000ULL) { |
||||
|
// Far call
|
||||
|
MOV(64, R(RAX), ImmPtr(func)); |
||||
|
CALLptr(R(RAX)); |
||||
|
} else { |
||||
|
CALL(func); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionP(const void *func, void *param1) { |
||||
|
MOV(64, R(ABI_PARAM1), ImmPtr(param1)); |
||||
|
u64 distance = u64(func) - (u64(code) + 5); |
||||
|
if (distance >= 0x0000000080000000ULL |
||||
|
&& distance < 0xFFFFFFFF80000000ULL) { |
||||
|
// Far call
|
||||
|
MOV(64, R(RAX), ImmPtr(func)); |
||||
|
CALLptr(R(RAX)); |
||||
|
} else { |
||||
|
CALL(func); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionPA(const void *func, void *param1, const Gen::OpArg &arg2) { |
||||
|
MOV(64, R(ABI_PARAM1), ImmPtr(param1)); |
||||
|
if (!arg2.IsSimpleReg(ABI_PARAM2)) |
||||
|
MOV(32, R(ABI_PARAM2), arg2); |
||||
|
u64 distance = u64(func) - (u64(code) + 5); |
||||
|
if (distance >= 0x0000000080000000ULL |
||||
|
&& distance < 0xFFFFFFFF80000000ULL) { |
||||
|
// Far call
|
||||
|
MOV(64, R(RAX), ImmPtr(func)); |
||||
|
CALLptr(R(RAX)); |
||||
|
} else { |
||||
|
CALL(func); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionPAA(const void *func, void *param1, const Gen::OpArg &arg2, const Gen::OpArg &arg3) { |
||||
|
MOV(64, R(ABI_PARAM1), ImmPtr(param1)); |
||||
|
if (!arg2.IsSimpleReg(ABI_PARAM2)) |
||||
|
MOV(32, R(ABI_PARAM2), arg2); |
||||
|
if (!arg3.IsSimpleReg(ABI_PARAM3)) |
||||
|
MOV(32, R(ABI_PARAM3), arg3); |
||||
|
u64 distance = u64(func) - (u64(code) + 5); |
||||
|
if (distance >= 0x0000000080000000ULL |
||||
|
&& distance < 0xFFFFFFFF80000000ULL) { |
||||
|
// Far call
|
||||
|
MOV(64, R(RAX), ImmPtr(func)); |
||||
|
CALLptr(R(RAX)); |
||||
|
} else { |
||||
|
CALL(func); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionPPC(const void *func, void *param1, void *param2, u32 param3) { |
||||
|
MOV(64, R(ABI_PARAM1), ImmPtr(param1)); |
||||
|
MOV(64, R(ABI_PARAM2), ImmPtr(param2)); |
||||
|
MOV(32, R(ABI_PARAM3), Imm32(param3)); |
||||
|
u64 distance = u64(func) - (u64(code) + 5); |
||||
|
if (distance >= 0x0000000080000000ULL |
||||
|
&& distance < 0xFFFFFFFF80000000ULL) { |
||||
|
// Far call
|
||||
|
MOV(64, R(RAX), ImmPtr(func)); |
||||
|
CALLptr(R(RAX)); |
||||
|
} else { |
||||
|
CALL(func); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Pass a register as a parameter.
|
||||
|
void XEmitter::ABI_CallFunctionR(const void *func, X64Reg reg1) { |
||||
|
if (reg1 != ABI_PARAM1) |
||||
|
MOV(32, R(ABI_PARAM1), R(reg1)); |
||||
|
u64 distance = u64(func) - (u64(code) + 5); |
||||
|
if (distance >= 0x0000000080000000ULL |
||||
|
&& distance < 0xFFFFFFFF80000000ULL) { |
||||
|
// Far call
|
||||
|
MOV(64, R(RAX), ImmPtr(func)); |
||||
|
CALLptr(R(RAX)); |
||||
|
} else { |
||||
|
CALL(func); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Pass two registers as parameters.
|
||||
|
void XEmitter::ABI_CallFunctionRR(const void *func, X64Reg reg1, X64Reg reg2) { |
||||
|
if (reg2 != ABI_PARAM1) { |
||||
|
if (reg1 != ABI_PARAM1) |
||||
|
MOV(64, R(ABI_PARAM1), R(reg1)); |
||||
|
if (reg2 != ABI_PARAM2) |
||||
|
MOV(64, R(ABI_PARAM2), R(reg2)); |
||||
|
} else { |
||||
|
if (reg2 != ABI_PARAM2) |
||||
|
MOV(64, R(ABI_PARAM2), R(reg2)); |
||||
|
if (reg1 != ABI_PARAM1) |
||||
|
MOV(64, R(ABI_PARAM1), R(reg1)); |
||||
|
} |
||||
|
u64 distance = u64(func) - (u64(code) + 5); |
||||
|
if (distance >= 0x0000000080000000ULL |
||||
|
&& distance < 0xFFFFFFFF80000000ULL) { |
||||
|
// Far call
|
||||
|
MOV(64, R(RAX), ImmPtr(func)); |
||||
|
CALLptr(R(RAX)); |
||||
|
} else { |
||||
|
CALL(func); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionAC(const 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)); |
||||
|
u64 distance = u64(func) - (u64(code) + 5); |
||||
|
if (distance >= 0x0000000080000000ULL |
||||
|
&& distance < 0xFFFFFFFF80000000ULL) { |
||||
|
// Far call
|
||||
|
MOV(64, R(RAX), ImmPtr(func)); |
||||
|
CALLptr(R(RAX)); |
||||
|
} else { |
||||
|
CALL(func); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionACC(const void *func, const Gen::OpArg &arg1, u32 param2, u32 param3) |
||||
|
{ |
||||
|
if (!arg1.IsSimpleReg(ABI_PARAM1)) |
||||
|
MOV(32, R(ABI_PARAM1), arg1); |
||||
|
MOV(32, R(ABI_PARAM2), Imm32(param2)); |
||||
|
MOV(64, R(ABI_PARAM3), Imm64(param3)); |
||||
|
u64 distance = u64(func) - (u64(code) + 5); |
||||
|
if (distance >= 0x0000000080000000ULL |
||||
|
&& distance < 0xFFFFFFFF80000000ULL) { |
||||
|
// Far call
|
||||
|
MOV(64, R(RAX), ImmPtr(func)); |
||||
|
CALLptr(R(RAX)); |
||||
|
} else { |
||||
|
CALL(func); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionA(const void *func, const Gen::OpArg &arg1) |
||||
|
{ |
||||
|
if (!arg1.IsSimpleReg(ABI_PARAM1)) |
||||
|
MOV(32, R(ABI_PARAM1), arg1); |
||||
|
u64 distance = u64(func) - (u64(code) + 5); |
||||
|
if (distance >= 0x0000000080000000ULL |
||||
|
&& distance < 0xFFFFFFFF80000000ULL) { |
||||
|
// Far call
|
||||
|
MOV(64, R(RAX), ImmPtr(func)); |
||||
|
CALLptr(R(RAX)); |
||||
|
} else { |
||||
|
CALL(func); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_CallFunctionAA(const void *func, const Gen::OpArg &arg1, const Gen::OpArg &arg2) |
||||
|
{ |
||||
|
if (!arg1.IsSimpleReg(ABI_PARAM1)) |
||||
|
MOV(32, R(ABI_PARAM1), arg1); |
||||
|
if (!arg2.IsSimpleReg(ABI_PARAM2)) |
||||
|
MOV(32, R(ABI_PARAM2), arg2); |
||||
|
u64 distance = u64(func) - (u64(code) + 5); |
||||
|
if (distance >= 0x0000000080000000ULL |
||||
|
&& distance < 0xFFFFFFFF80000000ULL) { |
||||
|
// Far call
|
||||
|
MOV(64, R(RAX), ImmPtr(func)); |
||||
|
CALLptr(R(RAX)); |
||||
|
} else { |
||||
|
CALL(func); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
unsigned int XEmitter::ABI_GetAlignedFrameSize(unsigned int frameSize) { |
||||
|
return frameSize; |
||||
|
} |
||||
|
|
||||
|
#ifdef _WIN32
|
||||
|
|
||||
|
// The Windows x64 ABI requires XMM6 - XMM15 to be callee saved. 10 regs.
|
||||
|
// But, not saving XMM4 and XMM5 breaks things in VS 2010, even though they are volatile regs.
|
||||
|
// Let's just save all 16.
|
||||
|
const int XMM_STACK_SPACE = 16 * 16; |
||||
|
|
||||
|
// Win64 Specific Code
|
||||
|
void XEmitter::ABI_PushAllCalleeSavedRegsAndAdjustStack() { |
||||
|
//we only want to do this once
|
||||
|
PUSH(RBX); |
||||
|
PUSH(RSI); |
||||
|
PUSH(RDI); |
||||
|
PUSH(RBP); |
||||
|
PUSH(R12); |
||||
|
PUSH(R13); |
||||
|
PUSH(R14); |
||||
|
PUSH(R15); |
||||
|
ABI_AlignStack(0); |
||||
|
|
||||
|
// Do this after aligning, because before it's offset by 8.
|
||||
|
SUB(64, R(RSP), Imm32(XMM_STACK_SPACE)); |
||||
|
for (int i = 0; i < 16; ++i) |
||||
|
MOVAPS(MDisp(RSP, i * 16), (X64Reg)(XMM0 + i)); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_PopAllCalleeSavedRegsAndAdjustStack() { |
||||
|
for (int i = 0; i < 16; ++i) |
||||
|
MOVAPS((X64Reg)(XMM0 + i), MDisp(RSP, i * 16)); |
||||
|
ADD(64, R(RSP), Imm32(XMM_STACK_SPACE)); |
||||
|
|
||||
|
ABI_RestoreStack(0); |
||||
|
POP(R15); |
||||
|
POP(R14); |
||||
|
POP(R13); |
||||
|
POP(R12); |
||||
|
POP(RBP); |
||||
|
POP(RDI); |
||||
|
POP(RSI); |
||||
|
POP(RBX); |
||||
|
} |
||||
|
|
||||
|
// Win64 Specific Code
|
||||
|
void XEmitter::ABI_PushAllCallerSavedRegsAndAdjustStack() { |
||||
|
PUSH(RCX); |
||||
|
PUSH(RDX); |
||||
|
PUSH(RSI); |
||||
|
PUSH(RDI); |
||||
|
PUSH(R8); |
||||
|
PUSH(R9); |
||||
|
PUSH(R10); |
||||
|
PUSH(R11); |
||||
|
// TODO: Callers preserve XMM4-5 (XMM0-3 are args.)
|
||||
|
ABI_AlignStack(0); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_PopAllCallerSavedRegsAndAdjustStack() { |
||||
|
ABI_RestoreStack(0); |
||||
|
POP(R11); |
||||
|
POP(R10); |
||||
|
POP(R9); |
||||
|
POP(R8); |
||||
|
POP(RDI); |
||||
|
POP(RSI); |
||||
|
POP(RDX); |
||||
|
POP(RCX); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_AlignStack(unsigned int /*frameSize*/) { |
||||
|
SUB(64, R(RSP), Imm8(0x28)); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_RestoreStack(unsigned int /*frameSize*/) { |
||||
|
ADD(64, R(RSP), Imm8(0x28)); |
||||
|
} |
||||
|
|
||||
|
#else
|
||||
|
// Unix64 Specific Code
|
||||
|
void XEmitter::ABI_PushAllCalleeSavedRegsAndAdjustStack() { |
||||
|
PUSH(RBX); |
||||
|
PUSH(RBP); |
||||
|
PUSH(R12); |
||||
|
PUSH(R13); |
||||
|
PUSH(R14); |
||||
|
PUSH(R15); |
||||
|
PUSH(R15); //just to align stack. duped push/pop doesn't hurt.
|
||||
|
// TODO: XMM?
|
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_PopAllCalleeSavedRegsAndAdjustStack() { |
||||
|
POP(R15); |
||||
|
POP(R15); |
||||
|
POP(R14); |
||||
|
POP(R13); |
||||
|
POP(R12); |
||||
|
POP(RBP); |
||||
|
POP(RBX); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_PushAllCallerSavedRegsAndAdjustStack() { |
||||
|
PUSH(RCX); |
||||
|
PUSH(RDX); |
||||
|
PUSH(RSI); |
||||
|
PUSH(RDI); |
||||
|
PUSH(R8); |
||||
|
PUSH(R9); |
||||
|
PUSH(R10); |
||||
|
PUSH(R11); |
||||
|
PUSH(R11); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_PopAllCallerSavedRegsAndAdjustStack() { |
||||
|
POP(R11); |
||||
|
POP(R11); |
||||
|
POP(R10); |
||||
|
POP(R9); |
||||
|
POP(R8); |
||||
|
POP(RDI); |
||||
|
POP(RSI); |
||||
|
POP(RDX); |
||||
|
POP(RCX); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_AlignStack(unsigned int /*frameSize*/) { |
||||
|
SUB(64, R(RSP), Imm8(0x08)); |
||||
|
} |
||||
|
|
||||
|
void XEmitter::ABI_RestoreStack(unsigned int /*frameSize*/) { |
||||
|
ADD(64, R(RSP), Imm8(0x08)); |
||||
|
} |
||||
|
|
||||
|
#endif // WIN32
|
||||
|
|
||||
|
#endif // 32bit
|
||||
@ -0,0 +1,78 @@ |
|||||
|
// Copyright (C) 2003 Dolphin Project. |
||||
|
|
||||
|
// This program is free software: you can redistribute it and/or modify |
||||
|
// it under the terms of the GNU General Public License as published by |
||||
|
// the Free Software Foundation, version 2.0 or later versions. |
||||
|
|
||||
|
// This program is distributed in the hope that it will be useful, |
||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
// GNU General Public License 2.0 for more details. |
||||
|
|
||||
|
// A copy of the GPL 2.0 should have been included with the program. |
||||
|
// If not, see http://www.gnu.org/licenses/ |
||||
|
|
||||
|
// Official SVN repository and contact information can be found at |
||||
|
// http://code.google.com/p/dolphin-emu/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include "common_types.h" |
||||
|
|
||||
|
// x86/x64 ABI:s, and helpers to help follow them when JIT-ing code. |
||||
|
// All convensions return values in EAX (+ possibly EDX). |
||||
|
|
||||
|
// Linux 32-bit, Windows 32-bit (cdecl, System V): |
||||
|
// * Caller pushes left to right |
||||
|
// * Caller fixes stack after call |
||||
|
// * function subtract from stack for local storage only. |
||||
|
// Scratch: EAX ECX EDX |
||||
|
// Callee-save: EBX ESI EDI EBP |
||||
|
// Parameters: - |
||||
|
|
||||
|
// Windows 64-bit |
||||
|
// * 4-reg "fastcall" variant, very new-skool stack handling |
||||
|
// * Callee moves stack pointer, to make room for shadow regs for the biggest function _it itself calls_ |
||||
|
// * Parameters passed in RCX, RDX, ... further parameters are MOVed into the allocated stack space. |
||||
|
// Scratch: RAX RCX RDX R8 R9 R10 R11 |
||||
|
// Callee-save: RBX RSI RDI RBP R12 R13 R14 R15 |
||||
|
// Parameters: RCX RDX R8 R9, further MOV-ed |
||||
|
|
||||
|
// Linux 64-bit |
||||
|
// * 6-reg "fastcall" variant, old skool stack handling (parameters are pushed) |
||||
|
// Scratch: RAX RCX RDX RSI RDI R8 R9 R10 R11 |
||||
|
// Callee-save: RBX RBP R12 R13 R14 R15 |
||||
|
// Parameters: RDI RSI RDX RCX R8 R9 |
||||
|
|
||||
|
#ifdef _M_IX86 // 32 bit calling convention, shared by all |
||||
|
|
||||
|
// 32-bit don't pass parameters in regs, but these are convenient to have anyway when we have to |
||||
|
// choose regs to put stuff in. |
||||
|
#define ABI_PARAM1 RCX |
||||
|
#define ABI_PARAM2 RDX |
||||
|
|
||||
|
// There are no ABI_PARAM* here, since args are pushed. |
||||
|
// 32-bit bog standard cdecl, shared between linux and windows |
||||
|
// MacOSX 32-bit is same as System V with a few exceptions that we probably don't care much about. |
||||
|
|
||||
|
#elif _M_X86_64 // 64 bit calling convention |
||||
|
|
||||
|
#ifdef _WIN32 // 64-bit Windows - the really exotic calling convention |
||||
|
|
||||
|
#define ABI_PARAM1 RCX |
||||
|
#define ABI_PARAM2 RDX |
||||
|
#define ABI_PARAM3 R8 |
||||
|
#define ABI_PARAM4 R9 |
||||
|
|
||||
|
#else //64-bit Unix (hopefully MacOSX too) |
||||
|
|
||||
|
#define ABI_PARAM1 RDI |
||||
|
#define ABI_PARAM2 RSI |
||||
|
#define ABI_PARAM3 RDX |
||||
|
#define ABI_PARAM4 RCX |
||||
|
#define ABI_PARAM5 R8 |
||||
|
#define ABI_PARAM6 R9 |
||||
|
|
||||
|
#endif // WIN32 |
||||
|
|
||||
|
#endif // X86 |
||||
@ -0,0 +1,87 @@ |
|||||
|
// Copyright 2013 Dolphin Emulator Project |
||||
|
// Licensed under GPLv2 |
||||
|
// Refer to the license.txt file included. |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include "common_types.h" |
||||
|
#include "memory_util.h" |
||||
|
|
||||
|
// Everything that needs to generate code should inherit from this. |
||||
|
// You get memory management for free, plus, you can use all emitter functions without |
||||
|
// having to prefix them with gen-> or something similar. |
||||
|
// Example implementation: |
||||
|
// class JIT : public CodeBlock<ARMXEmitter> {} |
||||
|
template<class T> class CodeBlock : public T, NonCopyable |
||||
|
{ |
||||
|
private: |
||||
|
// A privately used function to set the executable RAM space to something invalid. |
||||
|
// For debugging usefulness it should be used to set the RAM to a host specific breakpoint instruction |
||||
|
virtual void PoisonMemory() = 0; |
||||
|
|
||||
|
protected: |
||||
|
u8 *region; |
||||
|
size_t region_size; |
||||
|
|
||||
|
public: |
||||
|
CodeBlock() : region(nullptr), region_size(0) {} |
||||
|
virtual ~CodeBlock() { if (region) FreeCodeSpace(); } |
||||
|
|
||||
|
// Call this before you generate any code. |
||||
|
void AllocCodeSpace(int size) |
||||
|
{ |
||||
|
region_size = size; |
||||
|
region = (u8*)AllocateExecutableMemory(region_size); |
||||
|
T::SetCodePtr(region); |
||||
|
} |
||||
|
|
||||
|
// Always clear code space with breakpoints, so that if someone accidentally executes |
||||
|
// uninitialized, it just breaks into the debugger. |
||||
|
void ClearCodeSpace() |
||||
|
{ |
||||
|
PoisonMemory(); |
||||
|
ResetCodePtr(); |
||||
|
} |
||||
|
|
||||
|
// Call this when shutting down. Don't rely on the destructor, even though it'll do the job. |
||||
|
void FreeCodeSpace() |
||||
|
{ |
||||
|
#ifdef __SYMBIAN32__ |
||||
|
ResetExecutableMemory(region); |
||||
|
#else |
||||
|
FreeMemoryPages(region, region_size); |
||||
|
#endif |
||||
|
region = nullptr; |
||||
|
region_size = 0; |
||||
|
} |
||||
|
|
||||
|
bool IsInSpace(const u8 *ptr) |
||||
|
{ |
||||
|
return (ptr >= region) && (ptr < (region + region_size)); |
||||
|
} |
||||
|
|
||||
|
// Cannot currently be undone. Will write protect the entire code region. |
||||
|
// Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()). |
||||
|
void WriteProtect() |
||||
|
{ |
||||
|
WriteProtectMemory(region, region_size, true); |
||||
|
} |
||||
|
|
||||
|
void ResetCodePtr() |
||||
|
{ |
||||
|
T::SetCodePtr(region); |
||||
|
} |
||||
|
|
||||
|
size_t GetSpaceLeft() const |
||||
|
{ |
||||
|
return region_size - (T::GetCodePtr() - region); |
||||
|
} |
||||
|
|
||||
|
u8 *GetBasePtr() { |
||||
|
return region; |
||||
|
} |
||||
|
|
||||
|
size_t GetOffset(const u8 *ptr) const { |
||||
|
return ptr - region; |
||||
|
} |
||||
|
}; |
||||
@ -0,0 +1,465 @@ |
|||||
|
// Copyright (C) 2003 Dolphin Project. |
||||
|
|
||||
|
// This program is free software: you can redistribute it and/or modify |
||||
|
// it under the terms of the GNU General Public License as published by |
||||
|
// the Free Software Foundation, version 2.0. |
||||
|
|
||||
|
// This program is distributed in the hope that it will be useful, |
||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
// GNU General Public License 2.0 for more details. |
||||
|
|
||||
|
// A copy of the GPL 2.0 should have been included with the program. |
||||
|
// If not, see http://www.gnu.org/licenses/ |
||||
|
|
||||
|
// Official SVN repository and contact information can be found at |
||||
|
// http://code.google.com/p/dolphin-emu/ |
||||
|
|
||||
|
// WARNING - THIS LIBRARY IS NOT THREAD SAFE!!! |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <vector> |
||||
|
#include <stdint.h> |
||||
|
|
||||
|
#include "assert.h" |
||||
|
#include "common_types.h" |
||||
|
|
||||
|
// TODO: Check if Pandora still needs signal.h/kill here. Symbian doesn't. |
||||
|
|
||||
|
// VCVT flags |
||||
|
#define TO_FLOAT 0 |
||||
|
#define TO_INT 1 << 0 |
||||
|
#define IS_SIGNED 1 << 1 |
||||
|
#define ROUND_TO_ZERO 1 << 2 |
||||
|
|
||||
|
namespace FakeGen |
||||
|
{ |
||||
|
enum FakeReg |
||||
|
{ |
||||
|
// GPRs |
||||
|
R0 = 0, R1, R2, R3, R4, R5, |
||||
|
R6, R7, R8, R9, R10, R11, |
||||
|
|
||||
|
// SPRs |
||||
|
// R13 - R15 are SP, LR, and PC. |
||||
|
// Almost always referred to by name instead of register number |
||||
|
R12 = 12, R13 = 13, R14 = 14, R15 = 15, |
||||
|
R_IP = 12, R_SP = 13, R_LR = 14, R_PC = 15, |
||||
|
|
||||
|
|
||||
|
// VFP single precision registers |
||||
|
S0, S1, S2, S3, S4, S5, S6, |
||||
|
S7, S8, S9, S10, S11, S12, S13, |
||||
|
S14, S15, S16, S17, S18, S19, S20, |
||||
|
S21, S22, S23, S24, S25, S26, S27, |
||||
|
S28, S29, S30, S31, |
||||
|
|
||||
|
// VFP Double Precision registers |
||||
|
D0, D1, D2, D3, D4, D5, D6, D7, |
||||
|
D8, D9, D10, D11, D12, D13, D14, D15, |
||||
|
D16, D17, D18, D19, D20, D21, D22, D23, |
||||
|
D24, D25, D26, D27, D28, D29, D30, D31, |
||||
|
|
||||
|
// ASIMD Quad-Word registers |
||||
|
Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, |
||||
|
Q8, Q9, Q10, Q11, Q12, Q13, Q14, Q15, |
||||
|
|
||||
|
// for NEON VLD/VST instructions |
||||
|
REG_UPDATE = R13, |
||||
|
INVALID_REG = 0xFFFFFFFF |
||||
|
}; |
||||
|
|
||||
|
enum CCFlags |
||||
|
{ |
||||
|
CC_EQ = 0, // Equal |
||||
|
CC_NEQ, // Not equal |
||||
|
CC_CS, // Carry Set |
||||
|
CC_CC, // Carry Clear |
||||
|
CC_MI, // Minus (Negative) |
||||
|
CC_PL, // Plus |
||||
|
CC_VS, // Overflow |
||||
|
CC_VC, // No Overflow |
||||
|
CC_HI, // Unsigned higher |
||||
|
CC_LS, // Unsigned lower or same |
||||
|
CC_GE, // Signed greater than or equal |
||||
|
CC_LT, // Signed less than |
||||
|
CC_GT, // Signed greater than |
||||
|
CC_LE, // Signed less than or equal |
||||
|
CC_AL, // Always (unconditional) 14 |
||||
|
CC_HS = CC_CS, // Alias of CC_CS Unsigned higher or same |
||||
|
CC_LO = CC_CC, // Alias of CC_CC Unsigned lower |
||||
|
}; |
||||
|
const u32 NO_COND = 0xE0000000; |
||||
|
|
||||
|
enum ShiftType |
||||
|
{ |
||||
|
ST_LSL = 0, |
||||
|
ST_ASL = 0, |
||||
|
ST_LSR = 1, |
||||
|
ST_ASR = 2, |
||||
|
ST_ROR = 3, |
||||
|
ST_RRX = 4 |
||||
|
}; |
||||
|
enum IntegerSize |
||||
|
{ |
||||
|
I_I8 = 0, |
||||
|
I_I16, |
||||
|
I_I32, |
||||
|
I_I64 |
||||
|
}; |
||||
|
|
||||
|
enum |
||||
|
{ |
||||
|
NUMGPRs = 13, |
||||
|
}; |
||||
|
|
||||
|
class FakeXEmitter; |
||||
|
|
||||
|
enum OpType |
||||
|
{ |
||||
|
TYPE_IMM = 0, |
||||
|
TYPE_REG, |
||||
|
TYPE_IMMSREG, |
||||
|
TYPE_RSR, |
||||
|
TYPE_MEM |
||||
|
}; |
||||
|
|
||||
|
// This is no longer a proper operand2 class. Need to split up. |
||||
|
class Operand2 |
||||
|
{ |
||||
|
friend class FakeXEmitter; |
||||
|
protected: |
||||
|
u32 Value; |
||||
|
|
||||
|
private: |
||||
|
OpType Type; |
||||
|
|
||||
|
// IMM types |
||||
|
u8 Rotation; // Only for u8 values |
||||
|
|
||||
|
// Register types |
||||
|
u8 IndexOrShift; |
||||
|
ShiftType Shift; |
||||
|
public: |
||||
|
OpType GetType() |
||||
|
{ |
||||
|
return Type; |
||||
|
} |
||||
|
Operand2() {} |
||||
|
Operand2(u32 imm, OpType type = TYPE_IMM) |
||||
|
{ |
||||
|
Type = type; |
||||
|
Value = imm; |
||||
|
Rotation = 0; |
||||
|
} |
||||
|
|
||||
|
Operand2(FakeReg Reg) |
||||
|
{ |
||||
|
Type = TYPE_REG; |
||||
|
Value = Reg; |
||||
|
Rotation = 0; |
||||
|
} |
||||
|
Operand2(u8 imm, u8 rotation) |
||||
|
{ |
||||
|
Type = TYPE_IMM; |
||||
|
Value = imm; |
||||
|
Rotation = rotation; |
||||
|
} |
||||
|
Operand2(FakeReg base, ShiftType type, FakeReg shift) // RSR |
||||
|
{ |
||||
|
Type = TYPE_RSR; |
||||
|
ASSERT_MSG(type != ST_RRX, "Invalid Operand2: RRX does not take a register shift amount"); |
||||
|
IndexOrShift = shift; |
||||
|
Shift = type; |
||||
|
Value = base; |
||||
|
} |
||||
|
|
||||
|
Operand2(FakeReg base, ShiftType type, u8 shift)// For IMM shifted register |
||||
|
{ |
||||
|
if(shift == 32) shift = 0; |
||||
|
switch (type) |
||||
|
{ |
||||
|
case ST_LSL: |
||||
|
ASSERT_MSG(shift < 32, "Invalid Operand2: LSL %u", shift); |
||||
|
break; |
||||
|
case ST_LSR: |
||||
|
ASSERT_MSG(shift <= 32, "Invalid Operand2: LSR %u", shift); |
||||
|
if (!shift) |
||||
|
type = ST_LSL; |
||||
|
if (shift == 32) |
||||
|
shift = 0; |
||||
|
break; |
||||
|
case ST_ASR: |
||||
|
ASSERT_MSG(shift < 32, "Invalid Operand2: ASR %u", shift); |
||||
|
if (!shift) |
||||
|
type = ST_LSL; |
||||
|
if (shift == 32) |
||||
|
shift = 0; |
||||
|
break; |
||||
|
case ST_ROR: |
||||
|
ASSERT_MSG(shift < 32, "Invalid Operand2: ROR %u", shift); |
||||
|
if (!shift) |
||||
|
type = ST_LSL; |
||||
|
break; |
||||
|
case ST_RRX: |
||||
|
ASSERT_MSG(shift == 0, "Invalid Operand2: RRX does not take an immediate shift amount"); |
||||
|
type = ST_ROR; |
||||
|
break; |
||||
|
} |
||||
|
IndexOrShift = shift; |
||||
|
Shift = type; |
||||
|
Value = base; |
||||
|
Type = TYPE_IMMSREG; |
||||
|
} |
||||
|
u32 GetData() |
||||
|
{ |
||||
|
switch(Type) |
||||
|
{ |
||||
|
case TYPE_IMM: |
||||
|
return Imm12Mod(); // This'll need to be changed later |
||||
|
case TYPE_REG: |
||||
|
return Rm(); |
||||
|
case TYPE_IMMSREG: |
||||
|
return IMMSR(); |
||||
|
case TYPE_RSR: |
||||
|
return RSR(); |
||||
|
default: |
||||
|
ASSERT_MSG(false, "GetData with Invalid Type"); |
||||
|
return 0; |
||||
|
} |
||||
|
} |
||||
|
u32 IMMSR() // IMM shifted register |
||||
|
{ |
||||
|
ASSERT_MSG(Type == TYPE_IMMSREG, "IMMSR must be imm shifted register"); |
||||
|
return ((IndexOrShift & 0x1f) << 7 | (Shift << 5) | Value); |
||||
|
} |
||||
|
u32 RSR() // Register shifted register |
||||
|
{ |
||||
|
ASSERT_MSG(Type == TYPE_RSR, "RSR must be RSR Of Course"); |
||||
|
return (IndexOrShift << 8) | (Shift << 5) | 0x10 | Value; |
||||
|
} |
||||
|
u32 Rm() |
||||
|
{ |
||||
|
ASSERT_MSG(Type == TYPE_REG, "Rm must be with Reg"); |
||||
|
return Value; |
||||
|
} |
||||
|
|
||||
|
u32 Imm5() |
||||
|
{ |
||||
|
ASSERT_MSG((Type == TYPE_IMM), "Imm5 not IMM value"); |
||||
|
return ((Value & 0x0000001F) << 7); |
||||
|
} |
||||
|
u32 Imm8() |
||||
|
{ |
||||
|
ASSERT_MSG((Type == TYPE_IMM), "Imm8Rot not IMM value"); |
||||
|
return Value & 0xFF; |
||||
|
} |
||||
|
u32 Imm8Rot() // IMM8 with Rotation |
||||
|
{ |
||||
|
ASSERT_MSG((Type == TYPE_IMM), "Imm8Rot not IMM value"); |
||||
|
ASSERT_MSG((Rotation & 0xE1) != 0, "Invalid Operand2: immediate rotation %u", Rotation); |
||||
|
return (1 << 25) | (Rotation << 7) | (Value & 0x000000FF); |
||||
|
} |
||||
|
u32 Imm12() |
||||
|
{ |
||||
|
ASSERT_MSG((Type == TYPE_IMM), "Imm12 not IMM"); |
||||
|
return (Value & 0x00000FFF); |
||||
|
} |
||||
|
|
||||
|
u32 Imm12Mod() |
||||
|
{ |
||||
|
// This is an IMM12 with the top four bits being rotation and the |
||||
|
// bottom eight being an IMM. This is for instructions that need to |
||||
|
// expand a 8bit IMM to a 32bit value and gives you some rotation as |
||||
|
// well. |
||||
|
// Each rotation rotates to the right by 2 bits |
||||
|
ASSERT_MSG((Type == TYPE_IMM), "Imm12Mod not IMM"); |
||||
|
return ((Rotation & 0xF) << 8) | (Value & 0xFF); |
||||
|
} |
||||
|
u32 Imm16() |
||||
|
{ |
||||
|
ASSERT_MSG((Type == TYPE_IMM), "Imm16 not IMM"); |
||||
|
return ( (Value & 0xF000) << 4) | (Value & 0x0FFF); |
||||
|
} |
||||
|
u32 Imm16Low() |
||||
|
{ |
||||
|
return Imm16(); |
||||
|
} |
||||
|
u32 Imm16High() // Returns high 16bits |
||||
|
{ |
||||
|
ASSERT_MSG((Type == TYPE_IMM), "Imm16 not IMM"); |
||||
|
return ( ((Value >> 16) & 0xF000) << 4) | ((Value >> 16) & 0x0FFF); |
||||
|
} |
||||
|
u32 Imm24() |
||||
|
{ |
||||
|
ASSERT_MSG((Type == TYPE_IMM), "Imm16 not IMM"); |
||||
|
return (Value & 0x0FFFFFFF); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// Use these when you don't know if an imm can be represented as an operand2. |
||||
|
// This lets you generate both an optimal and a fallback solution by checking |
||||
|
// the return value, which will be false if these fail to find a Operand2 that |
||||
|
// represents your 32-bit imm value. |
||||
|
bool TryMakeOperand2(u32 imm, Operand2 &op2); |
||||
|
bool TryMakeOperand2_AllowInverse(u32 imm, Operand2 &op2, bool *inverse); |
||||
|
bool TryMakeOperand2_AllowNegation(s32 imm, Operand2 &op2, bool *negated); |
||||
|
|
||||
|
// Use this only when you know imm can be made into an Operand2. |
||||
|
Operand2 AssumeMakeOperand2(u32 imm); |
||||
|
|
||||
|
inline Operand2 R(FakeReg Reg) { return Operand2(Reg, TYPE_REG); } |
||||
|
inline Operand2 IMM(u32 Imm) { return Operand2(Imm, TYPE_IMM); } |
||||
|
inline Operand2 Mem(void *ptr) { return Operand2((u32)(uintptr_t)ptr, TYPE_IMM); } |
||||
|
//usage: struct {int e;} s; STRUCT_OFFSET(s,e) |
||||
|
#define STRUCT_OFF(str,elem) ((u32)((u32)&(str).elem-(u32)&(str))) |
||||
|
|
||||
|
|
||||
|
struct FixupBranch |
||||
|
{ |
||||
|
u8 *ptr; |
||||
|
u32 condition; // Remembers our codition at the time |
||||
|
int type; //0 = B 1 = BL |
||||
|
}; |
||||
|
|
||||
|
typedef const u8* JumpTarget; |
||||
|
|
||||
|
// XXX: Stop polluting the global namespace |
||||
|
const u32 I_8 = (1 << 0); |
||||
|
const u32 I_16 = (1 << 1); |
||||
|
const u32 I_32 = (1 << 2); |
||||
|
const u32 I_64 = (1 << 3); |
||||
|
const u32 I_SIGNED = (1 << 4); |
||||
|
const u32 I_UNSIGNED = (1 << 5); |
||||
|
const u32 F_32 = (1 << 6); |
||||
|
const u32 I_POLYNOMIAL = (1 << 7); // Only used in VMUL/VMULL |
||||
|
|
||||
|
u32 EncodeVd(FakeReg Vd); |
||||
|
u32 EncodeVn(FakeReg Vn); |
||||
|
u32 EncodeVm(FakeReg Vm); |
||||
|
|
||||
|
u32 encodedSize(u32 value); |
||||
|
|
||||
|
// Subtracts the base from the register to give us the real one |
||||
|
FakeReg SubBase(FakeReg Reg); |
||||
|
|
||||
|
// See A.7.1 in the Fakev7-A |
||||
|
// VMUL F32 scalars can only be up to D15[0], D15[1] - higher scalars cannot be individually addressed |
||||
|
FakeReg DScalar(FakeReg dreg, int subScalar); |
||||
|
FakeReg QScalar(FakeReg qreg, int subScalar); |
||||
|
|
||||
|
enum NEONAlignment { |
||||
|
ALIGN_NONE = 0, |
||||
|
ALIGN_64 = 1, |
||||
|
ALIGN_128 = 2, |
||||
|
ALIGN_256 = 3 |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
class NEONXEmitter; |
||||
|
|
||||
|
class FakeXEmitter |
||||
|
{ |
||||
|
friend struct OpArg; // for Write8 etc |
||||
|
private: |
||||
|
u8 *code, *startcode; |
||||
|
u8 *lastCacheFlushEnd; |
||||
|
u32 condition; |
||||
|
|
||||
|
protected: |
||||
|
inline void Write32(u32 value) {*(u32*)code = value; code+=4;} |
||||
|
|
||||
|
public: |
||||
|
FakeXEmitter() : code(0), startcode(0), lastCacheFlushEnd(0) { |
||||
|
condition = CC_AL << 28; |
||||
|
} |
||||
|
FakeXEmitter(u8 *code_ptr) { |
||||
|
code = code_ptr; |
||||
|
lastCacheFlushEnd = code_ptr; |
||||
|
startcode = code_ptr; |
||||
|
condition = CC_AL << 28; |
||||
|
} |
||||
|
virtual ~FakeXEmitter() {} |
||||
|
|
||||
|
void SetCodePtr(u8 *ptr) {} |
||||
|
void ReserveCodeSpace(u32 bytes) {} |
||||
|
const u8 *AlignCode16() { return nullptr; } |
||||
|
const u8 *AlignCodePage() { return nullptr; } |
||||
|
const u8 *GetCodePtr() const { return nullptr; } |
||||
|
void FlushIcache() {} |
||||
|
void FlushIcacheSection(u8 *start, u8 *end) {} |
||||
|
u8 *GetWritableCodePtr() { return nullptr; } |
||||
|
|
||||
|
CCFlags GetCC() { return CCFlags(condition >> 28); } |
||||
|
void SetCC(CCFlags cond = CC_AL) {} |
||||
|
|
||||
|
// Special purpose instructions |
||||
|
|
||||
|
// Do nothing |
||||
|
void NOP(int count = 1) {} //nop padding - TODO: fast nop slides, for amd and intel (check their manuals) |
||||
|
|
||||
|
#ifdef CALL |
||||
|
#undef CALL |
||||
|
#endif |
||||
|
|
||||
|
void QuickCallFunction(FakeReg scratchreg, const void *func); |
||||
|
template <typename T> void QuickCallFunction(FakeReg scratchreg, T func) { |
||||
|
QuickCallFunction(scratchreg, (const void *)func); |
||||
|
} |
||||
|
}; // class FakeXEmitter |
||||
|
|
||||
|
|
||||
|
// Everything that needs to generate machine code should inherit from this. |
||||
|
// You get memory management for free, plus, you can use all the MOV etc functions without |
||||
|
// having to prefix them with gen-> or something similar. |
||||
|
class FakeXCodeBlock : public FakeXEmitter |
||||
|
{ |
||||
|
protected: |
||||
|
u8 *region; |
||||
|
size_t region_size; |
||||
|
|
||||
|
public: |
||||
|
FakeXCodeBlock() : region(NULL), region_size(0) {} |
||||
|
virtual ~FakeXCodeBlock() { if (region) FreeCodeSpace(); } |
||||
|
|
||||
|
// Call this before you generate any code. |
||||
|
void AllocCodeSpace(int size) { } |
||||
|
|
||||
|
// Always clear code space with breakpoints, so that if someone accidentally executes |
||||
|
// uninitialized, it just breaks into the debugger. |
||||
|
void ClearCodeSpace() { } |
||||
|
|
||||
|
// Call this when shutting down. Don't rely on the destructor, even though it'll do the job. |
||||
|
void FreeCodeSpace() { } |
||||
|
|
||||
|
bool IsInSpace(const u8 *ptr) const |
||||
|
{ |
||||
|
return ptr >= region && ptr < region + region_size; |
||||
|
} |
||||
|
|
||||
|
// Cannot currently be undone. Will write protect the entire code region. |
||||
|
// Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()). |
||||
|
void WriteProtect() { } |
||||
|
void UnWriteProtect() { } |
||||
|
|
||||
|
void ResetCodePtr() |
||||
|
{ |
||||
|
SetCodePtr(region); |
||||
|
} |
||||
|
|
||||
|
size_t GetSpaceLeft() const |
||||
|
{ |
||||
|
return region_size - (GetCodePtr() - region); |
||||
|
} |
||||
|
|
||||
|
u8 *GetBasePtr() { |
||||
|
return region; |
||||
|
} |
||||
|
|
||||
|
size_t GetOffset(const u8 *ptr) const { |
||||
|
return ptr - region; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
} // namespace |
||||
1989
src/common/x64_emitter.cpp
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1067
src/common/x64_emitter.h
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save
Reference in new issue