11 changed files with 4382 additions and 6 deletions
-
2src/citra/CMakeLists.txt
-
2src/citra_qt/CMakeLists.txt
-
14src/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