mirror of
https://git.suyu.dev/suyu/suyu.git
synced 2024-11-15 22:54:00 +00:00
Common: Ported over boilerplate x86 JIT code from Dolphin/PPSSPP.
This commit is contained in:
parent
4d51792285
commit
ddbeebb887
11 changed files with 4382 additions and 6 deletions
|
@ -14,7 +14,7 @@ set(HEADERS
|
||||||
create_directory_groups(${SRCS} ${HEADERS})
|
create_directory_groups(${SRCS} ${HEADERS})
|
||||||
|
|
||||||
add_executable(citra ${SRCS} ${HEADERS})
|
add_executable(citra ${SRCS} ${HEADERS})
|
||||||
target_link_libraries(citra core common video_core)
|
target_link_libraries(citra core video_core common)
|
||||||
target_link_libraries(citra ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} inih)
|
target_link_libraries(citra ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} inih)
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
target_link_libraries(citra getopt)
|
target_link_libraries(citra getopt)
|
||||||
|
|
|
@ -71,7 +71,7 @@ if (APPLE)
|
||||||
else()
|
else()
|
||||||
add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})
|
add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})
|
||||||
endif()
|
endif()
|
||||||
target_link_libraries(citra-qt core common video_core qhexedit)
|
target_link_libraries(citra-qt core video_core common qhexedit)
|
||||||
target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS})
|
target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS})
|
||||||
target_link_libraries(citra-qt ${PLATFORM_LIBRARIES})
|
target_link_libraries(citra-qt ${PLATFORM_LIBRARIES})
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp" @ONLY)
|
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp" @ONLY)
|
||||||
|
|
||||||
set(SRCS
|
set(SRCS
|
||||||
|
abi.cpp
|
||||||
break_points.cpp
|
break_points.cpp
|
||||||
emu_window.cpp
|
emu_window.cpp
|
||||||
file_util.cpp
|
file_util.cpp
|
||||||
|
@ -20,10 +21,12 @@ set(SRCS
|
||||||
)
|
)
|
||||||
|
|
||||||
set(HEADERS
|
set(HEADERS
|
||||||
|
abi.h
|
||||||
assert.h
|
assert.h
|
||||||
bit_field.h
|
bit_field.h
|
||||||
break_points.h
|
break_points.h
|
||||||
chunk_file.h
|
chunk_file.h
|
||||||
|
code_block.h
|
||||||
color.h
|
color.h
|
||||||
common_funcs.h
|
common_funcs.h
|
||||||
common_paths.h
|
common_paths.h
|
||||||
|
@ -58,10 +61,17 @@ set(HEADERS
|
||||||
|
|
||||||
if(_M_X86)
|
if(_M_X86)
|
||||||
set(SRCS ${SRCS}
|
set(SRCS ${SRCS}
|
||||||
cpu_detect_x86.cpp)
|
cpu_detect_x86.cpp
|
||||||
|
x64_emitter.cpp)
|
||||||
|
|
||||||
|
set(HEADERS ${HEADERS}
|
||||||
|
x64_emitter.h)
|
||||||
else()
|
else()
|
||||||
set(SRCS ${SRCS}
|
set(SRCS ${SRCS}
|
||||||
cpu_detect_generic.cpp)
|
cpu_detect_generic.cpp)
|
||||||
|
|
||||||
|
set(HEADERS ${HEADERS}
|
||||||
|
fake_emitter.h)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
create_directory_groups(${SRCS} ${HEADERS})
|
create_directory_groups(${SRCS} ${HEADERS})
|
||||||
|
|
680
src/common/abi.cpp
Normal file
680
src/common/abi.cpp
Normal file
|
@ -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
|
78
src/common/abi.h
Normal file
78
src/common/abi.h
Normal file
|
@ -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
|
87
src/common/code_block.h
Normal file
87
src/common/code_block.h
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
};
|
|
@ -35,7 +35,7 @@
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
|
|
||||||
#if defined(__x86_64__) || defined(_M_X64)
|
#if defined(__x86_64__) || defined(_M_X86_64)
|
||||||
#define Crash() __asm__ __volatile__("int $3")
|
#define Crash() __asm__ __volatile__("int $3")
|
||||||
#elif defined(_M_ARM)
|
#elif defined(_M_ARM)
|
||||||
#define Crash() __asm__ __volatile__("trap")
|
#define Crash() __asm__ __volatile__("trap")
|
||||||
|
|
465
src/common/fake_emitter.h
Normal file
465
src/common/fake_emitter.h
Normal file
|
@ -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
|
|
@ -27,7 +27,7 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Platform detection
|
// Platform detection
|
||||||
|
|
||||||
#if defined(__x86_64__) || defined(_M_X64) || defined(__aarch64__)
|
#if defined(__x86_64__) || defined(_M_X86_64) || defined(__aarch64__)
|
||||||
#define EMU_ARCH_BITS 64
|
#define EMU_ARCH_BITS 64
|
||||||
#elif defined(__i386) || defined(_M_IX86) || defined(__arm__) || defined(_M_ARM)
|
#elif defined(__i386) || defined(_M_IX86) || defined(__arm__) || defined(_M_ARM)
|
||||||
#define EMU_ARCH_BITS 32
|
#define EMU_ARCH_BITS 32
|
||||||
|
|
1989
src/common/x64_emitter.cpp
Normal file
1989
src/common/x64_emitter.cpp
Normal file
File diff suppressed because it is too large
Load diff
1067
src/common/x64_emitter.h
Normal file
1067
src/common/x64_emitter.h
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue