mirror of
				https://git.suyu.dev/suyu/suyu.git
				synced 2025-11-04 20:44:02 +08:00 
			
		
		
		
	Common: Ported over boilerplate x86 JIT code from Dolphin/PPSSPP.
This commit is contained in:
		
							parent
							
								
									4d51792285
								
							
						
					
					
						commit
						ddbeebb887
					
				@ -14,7 +14,7 @@ set(HEADERS
 | 
			
		||||
create_directory_groups(${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)
 | 
			
		||||
if (MSVC)
 | 
			
		||||
    target_link_libraries(citra getopt)
 | 
			
		||||
 | 
			
		||||
@ -71,7 +71,7 @@ if (APPLE)
 | 
			
		||||
else()
 | 
			
		||||
    add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})
 | 
			
		||||
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 ${PLATFORM_LIBRARIES})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@
 | 
			
		||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp" @ONLY)
 | 
			
		||||
 | 
			
		||||
set(SRCS
 | 
			
		||||
            abi.cpp
 | 
			
		||||
            break_points.cpp
 | 
			
		||||
            emu_window.cpp
 | 
			
		||||
            file_util.cpp
 | 
			
		||||
@ -20,10 +21,12 @@ set(SRCS
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
set(HEADERS
 | 
			
		||||
            abi.h
 | 
			
		||||
            assert.h
 | 
			
		||||
            bit_field.h
 | 
			
		||||
            break_points.h
 | 
			
		||||
            chunk_file.h
 | 
			
		||||
            code_block.h
 | 
			
		||||
            color.h
 | 
			
		||||
            common_funcs.h
 | 
			
		||||
            common_paths.h
 | 
			
		||||
@ -58,10 +61,17 @@ set(HEADERS
 | 
			
		||||
 | 
			
		||||
if(_M_X86)
 | 
			
		||||
    set(SRCS ${SRCS}
 | 
			
		||||
             cpu_detect_x86.cpp)
 | 
			
		||||
            cpu_detect_x86.cpp
 | 
			
		||||
            x64_emitter.cpp)
 | 
			
		||||
 | 
			
		||||
    set(HEADERS ${HEADERS}
 | 
			
		||||
            x64_emitter.h)
 | 
			
		||||
else()
 | 
			
		||||
    set(SRCS ${SRCS}
 | 
			
		||||
             cpu_detect_generic.cpp)
 | 
			
		||||
            cpu_detect_generic.cpp)
 | 
			
		||||
 | 
			
		||||
    set(HEADERS ${HEADERS}
 | 
			
		||||
            fake_emitter.h)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
#if defined(__x86_64__) || defined(_M_X64)
 | 
			
		||||
#if defined(__x86_64__) || defined(_M_X86_64)
 | 
			
		||||
#define Crash() __asm__ __volatile__("int $3")
 | 
			
		||||
#elif defined(_M_ARM)
 | 
			
		||||
#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
 | 
			
		||||
 | 
			
		||||
#if defined(__x86_64__) || defined(_M_X64) || defined(__aarch64__)
 | 
			
		||||
#if defined(__x86_64__) || defined(_M_X86_64) || defined(__aarch64__)
 | 
			
		||||
    #define EMU_ARCH_BITS 64
 | 
			
		||||
#elif defined(__i386) || defined(_M_IX86) || defined(__arm__) || defined(_M_ARM)
 | 
			
		||||
    #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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user