mirror of
https://github.com/izzy2lost/dolphin.git
synced 2026-06-19 01:16:48 -07:00
Turn the X86 emitter into a class, so the code pointer is no longer a global, yay! Created XCodeBlock that derives from XEmitter, and the Jit now derives from XCodeBlock so it can call all ADD SUB JNZ etc without having to prefix them with "emit.". I think someone's gonna like this.
There's some cleanup still to be done, but hey, it works. There shouldn't be a noticable speed difference. I hope GCC doesn't have a problem with the "member function pointers" I used. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@1594 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
@@ -25,7 +25,7 @@ using namespace Gen;
|
||||
// ====================================
|
||||
|
||||
// Sets up a __cdecl function.
|
||||
void ABI_EmitPrologue(int maxCallParams)
|
||||
void XEmitter::ABI_EmitPrologue(int maxCallParams)
|
||||
{
|
||||
#ifdef _M_IX86
|
||||
// Don't really need to do anything
|
||||
@@ -40,7 +40,8 @@ void ABI_EmitPrologue(int maxCallParams)
|
||||
#error Arch not supported
|
||||
#endif
|
||||
}
|
||||
void ABI_EmitEpilogue(int maxCallParams)
|
||||
|
||||
void XEmitter::ABI_EmitEpilogue(int maxCallParams)
|
||||
{
|
||||
#ifdef _M_IX86
|
||||
RET();
|
||||
@@ -60,14 +61,14 @@ void ABI_EmitEpilogue(int maxCallParams)
|
||||
// Shared code between Win32 and Unix32
|
||||
// ====================================
|
||||
|
||||
void ABI_CallFunctionC(void *func, u32 param1) {
|
||||
void XEmitter::ABI_CallFunctionC(void *func, u32 param1) {
|
||||
ABI_AlignStack(1 * 4);
|
||||
PUSH(32, Imm32(param1));
|
||||
CALL(func);
|
||||
ABI_RestoreStack(1 * 4);
|
||||
}
|
||||
|
||||
void ABI_CallFunctionCC(void *func, u32 param1, u32 param2) {
|
||||
void XEmitter::ABI_CallFunctionCC(void *func, u32 param1, u32 param2) {
|
||||
ABI_AlignStack(2 * 4);
|
||||
PUSH(32, Imm32(param2));
|
||||
PUSH(32, Imm32(param1));
|
||||
@@ -76,14 +77,14 @@ void ABI_CallFunctionCC(void *func, u32 param1, u32 param2) {
|
||||
}
|
||||
|
||||
// Pass a register as a paremeter.
|
||||
void ABI_CallFunctionR(void *func, X64Reg reg1) {
|
||||
void XEmitter::ABI_CallFunctionR(void *func, X64Reg reg1) {
|
||||
ABI_AlignStack(1 * 4);
|
||||
PUSH(32, R(reg1));
|
||||
CALL(func);
|
||||
ABI_RestoreStack(1 * 4);
|
||||
}
|
||||
|
||||
void ABI_CallFunctionRR(void *func, Gen::X64Reg reg1, Gen::X64Reg reg2)
|
||||
void XEmitter::ABI_CallFunctionRR(void *func, Gen::X64Reg reg1, Gen::X64Reg reg2)
|
||||
{
|
||||
ABI_AlignStack(2 * 4);
|
||||
PUSH(32, R(reg2));
|
||||
@@ -92,7 +93,7 @@ void ABI_CallFunctionRR(void *func, Gen::X64Reg reg1, Gen::X64Reg reg2)
|
||||
ABI_RestoreStack(2 * 4);
|
||||
}
|
||||
|
||||
void ABI_CallFunctionAC(void *func, const Gen::OpArg &arg1, u32 param2)
|
||||
void XEmitter::ABI_CallFunctionAC(void *func, const Gen::OpArg &arg1, u32 param2)
|
||||
{
|
||||
ABI_AlignStack(2 * 4);
|
||||
PUSH(32, arg1);
|
||||
@@ -101,7 +102,7 @@ void ABI_CallFunctionAC(void *func, const Gen::OpArg &arg1, u32 param2)
|
||||
ABI_RestoreStack(2 * 4);
|
||||
}
|
||||
|
||||
void ABI_PushAllCalleeSavedRegsAndAdjustStack() {
|
||||
void XEmitter::ABI_PushAllCalleeSavedRegsAndAdjustStack() {
|
||||
// Note: 4 * 4 = 16 bytes, so alignment is preserved.
|
||||
PUSH(EBP);
|
||||
PUSH(EBX);
|
||||
@@ -109,14 +110,14 @@ void ABI_PushAllCalleeSavedRegsAndAdjustStack() {
|
||||
PUSH(EDI);
|
||||
}
|
||||
|
||||
void ABI_PopAllCalleeSavedRegsAndAdjustStack() {
|
||||
void XEmitter::ABI_PopAllCalleeSavedRegsAndAdjustStack() {
|
||||
POP(EDI);
|
||||
POP(ESI);
|
||||
POP(EBX);
|
||||
POP(EBP);
|
||||
}
|
||||
|
||||
unsigned int ABI_GetAlignedFrameSize(unsigned int frameSize) {
|
||||
unsigned int XEmitter::ABI_GetAlignedFrameSize(unsigned int frameSize) {
|
||||
frameSize += 4; // reserve space for return address
|
||||
unsigned int alignedSize =
|
||||
#ifdef __GNUC__
|
||||
@@ -128,7 +129,7 @@ unsigned int ABI_GetAlignedFrameSize(unsigned int frameSize) {
|
||||
}
|
||||
|
||||
|
||||
void ABI_AlignStack(unsigned int frameSize) {
|
||||
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,
|
||||
@@ -145,7 +146,7 @@ void ABI_AlignStack(unsigned int frameSize) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void ABI_RestoreStack(unsigned int frameSize) {
|
||||
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) {
|
||||
@@ -155,26 +156,26 @@ void ABI_RestoreStack(unsigned int frameSize) {
|
||||
|
||||
#else
|
||||
|
||||
void ABI_CallFunctionC(void *func, u32 param1) {
|
||||
void XEmitter::ABI_CallFunctionC(void *func, u32 param1) {
|
||||
MOV(32, R(ABI_PARAM1), Imm32(param1));
|
||||
CALL(func);
|
||||
}
|
||||
|
||||
void ABI_CallFunctionCC(void *func, u32 param1, u32 param2) {
|
||||
void XEmitter::ABI_CallFunctionCC(void *func, u32 param1, u32 param2) {
|
||||
MOV(32, R(ABI_PARAM1), Imm32(param1));
|
||||
MOV(32, R(ABI_PARAM2), Imm32(param2));
|
||||
CALL(func);
|
||||
}
|
||||
|
||||
// Pass a register as a paremeter.
|
||||
void ABI_CallFunctionR(void *func, X64Reg reg1) {
|
||||
void XEmitter::ABI_CallFunctionR(void *func, X64Reg reg1) {
|
||||
if (reg1 != ABI_PARAM1)
|
||||
MOV(32, R(ABI_PARAM1), R(reg1));
|
||||
CALL(func);
|
||||
}
|
||||
|
||||
// Pass a register as a paremeter.
|
||||
void ABI_CallFunctionRR(void *func, X64Reg reg1, X64Reg reg2) {
|
||||
void XEmitter::ABI_CallFunctionRR(void *func, X64Reg reg1, X64Reg reg2) {
|
||||
if (reg1 != ABI_PARAM1)
|
||||
MOV(32, R(ABI_PARAM1), R(reg1));
|
||||
if (reg2 != ABI_PARAM2)
|
||||
@@ -182,7 +183,7 @@ void ABI_CallFunctionRR(void *func, X64Reg reg1, X64Reg reg2) {
|
||||
CALL(func);
|
||||
}
|
||||
|
||||
void ABI_CallFunctionAC(void *func, const Gen::OpArg &arg1, u32 param2)
|
||||
void XEmitter::ABI_CallFunctionAC(void *func, const Gen::OpArg &arg1, u32 param2)
|
||||
{
|
||||
if (!arg1.IsSimpleReg(ABI_PARAM1))
|
||||
MOV(32, R(ABI_PARAM1), arg1);
|
||||
@@ -190,21 +191,21 @@ void ABI_CallFunctionAC(void *func, const Gen::OpArg &arg1, u32 param2)
|
||||
CALL(func);
|
||||
}
|
||||
|
||||
unsigned int ABI_GetAlignedFrameSize(unsigned int frameSize) {
|
||||
unsigned int XEmitter::ABI_GetAlignedFrameSize(unsigned int frameSize) {
|
||||
return frameSize;
|
||||
}
|
||||
|
||||
void ABI_AlignStack(unsigned int /*frameSize*/) {
|
||||
void XEmitter::ABI_AlignStack(unsigned int /*frameSize*/) {
|
||||
}
|
||||
|
||||
void ABI_RestoreStack(unsigned int /*frameSize*/) {
|
||||
void XEmitter::ABI_RestoreStack(unsigned int /*frameSize*/) {
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
// Win64 Specific Code
|
||||
// ====================================
|
||||
void ABI_PushAllCalleeSavedRegsAndAdjustStack() {
|
||||
void XEmitter::ABI_PushAllCalleeSavedRegsAndAdjustStack() {
|
||||
//we only want to do this once
|
||||
PUSH(RBX);
|
||||
PUSH(RSI);
|
||||
@@ -218,7 +219,7 @@ void ABI_PushAllCalleeSavedRegsAndAdjustStack() {
|
||||
SUB(64, R(RSP), Imm8(0x28));
|
||||
}
|
||||
|
||||
void ABI_PopAllCalleeSavedRegsAndAdjustStack() {
|
||||
void XEmitter::ABI_PopAllCalleeSavedRegsAndAdjustStack() {
|
||||
ADD(64, R(RSP), Imm8(0x28));
|
||||
POP(R15);
|
||||
POP(R14);
|
||||
@@ -232,7 +233,7 @@ void ABI_PopAllCalleeSavedRegsAndAdjustStack() {
|
||||
|
||||
// Win64 Specific Code
|
||||
// ====================================
|
||||
void ABI_PushAllCallerSavedRegsAndAdjustStack() {
|
||||
void XEmitter::ABI_PushAllCallerSavedRegsAndAdjustStack() {
|
||||
PUSH(RCX);
|
||||
PUSH(RDX);
|
||||
PUSH(RSI);
|
||||
@@ -245,7 +246,7 @@ void ABI_PushAllCallerSavedRegsAndAdjustStack() {
|
||||
SUB(64, R(RSP), Imm8(0x28));
|
||||
}
|
||||
|
||||
void ABI_PopAllCallerSavedRegsAndAdjustStack() {
|
||||
void XEmitter::ABI_PopAllCallerSavedRegsAndAdjustStack() {
|
||||
ADD(64, R(RSP), Imm8(0x28));
|
||||
POP(R11);
|
||||
POP(R10);
|
||||
@@ -260,7 +261,7 @@ void ABI_PopAllCallerSavedRegsAndAdjustStack() {
|
||||
#else
|
||||
// Unix64 Specific Code
|
||||
// ====================================
|
||||
void ABI_PushAllCalleeSavedRegsAndAdjustStack() {
|
||||
void XEmitter::ABI_PushAllCalleeSavedRegsAndAdjustStack() {
|
||||
PUSH(RBX);
|
||||
PUSH(RBP);
|
||||
PUSH(R12);
|
||||
@@ -270,7 +271,7 @@ void ABI_PushAllCalleeSavedRegsAndAdjustStack() {
|
||||
PUSH(R15); //just to align stack. duped push/pop doesn't hurt.
|
||||
}
|
||||
|
||||
void ABI_PopAllCalleeSavedRegsAndAdjustStack() {
|
||||
void XEmitter::ABI_PopAllCalleeSavedRegsAndAdjustStack() {
|
||||
POP(R15);
|
||||
POP(R15);
|
||||
POP(R14);
|
||||
@@ -280,7 +281,7 @@ void ABI_PopAllCalleeSavedRegsAndAdjustStack() {
|
||||
POP(RBX);
|
||||
}
|
||||
|
||||
void ABI_PushAllCallerSavedRegsAndAdjustStack() {
|
||||
void XEmitter::ABI_PushAllCallerSavedRegsAndAdjustStack() {
|
||||
PUSH(RCX);
|
||||
PUSH(RDX);
|
||||
PUSH(RSI);
|
||||
@@ -292,7 +293,7 @@ void ABI_PushAllCallerSavedRegsAndAdjustStack() {
|
||||
PUSH(R11);
|
||||
}
|
||||
|
||||
void ABI_PopAllCallerSavedRegsAndAdjustStack() {
|
||||
void XEmitter::ABI_PopAllCallerSavedRegsAndAdjustStack() {
|
||||
POP(R11);
|
||||
POP(R11);
|
||||
POP(R10);
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
#ifndef _JIT_ABI_H
|
||||
#define _JIT_ABI_H
|
||||
|
||||
#include "x64Emitter.h"
|
||||
|
||||
// x86/x64 ABI:s, and helpers to help follow them when JIT-ing code.
|
||||
// All convensions return values in EAX (+ possibly EDX).
|
||||
|
||||
@@ -81,42 +79,5 @@
|
||||
|
||||
#endif
|
||||
|
||||
// Utility functions
|
||||
// These only support u32 parameters, but that's enough for a lot of uses.
|
||||
// These will destroy the 1 or 2 first "parameter regs".
|
||||
void ABI_CallFunctionC(void *func, u32 param1);
|
||||
void ABI_CallFunctionCC(void *func, u32 param1, u32 param2);
|
||||
void ABI_CallFunctionAC(void *func, const Gen::OpArg &arg1, u32 param2);
|
||||
|
||||
// Pass a register as a paremeter.
|
||||
void ABI_CallFunctionR(void *func, Gen::X64Reg reg1);
|
||||
void ABI_CallFunctionRR(void *func, Gen::X64Reg reg1, Gen::X64Reg reg2);
|
||||
|
||||
// A function that doesn't have any control over what it will do to regs,
|
||||
// such as the dispatcher, should be surrounded by these.
|
||||
void ABI_PushAllCalleeSavedRegsAndAdjustStack();
|
||||
void ABI_PopAllCalleeSavedRegsAndAdjustStack();
|
||||
|
||||
// A function that doesn't know anything about it's surroundings, should
|
||||
// be surrounded by these to establish a safe environment, where it can roam free.
|
||||
// An example is a backpatch injected function.
|
||||
void ABI_PushAllCallerSavedRegsAndAdjustStack();
|
||||
void ABI_PopAllCallerSavedRegsAndAdjustStack();
|
||||
|
||||
unsigned int ABI_GetAlignedFrameSize(unsigned int frameSize);
|
||||
void ABI_AlignStack(unsigned int frameSize);
|
||||
void ABI_RestoreStack(unsigned int frameSize);
|
||||
|
||||
// Sets up a __cdecl function.
|
||||
// Only x64 really needs the parameter.
|
||||
void ABI_EmitPrologue(int maxCallParams);
|
||||
void ABI_EmitEpilogue(int maxCallParams);
|
||||
|
||||
#ifdef _M_IX86
|
||||
inline int ABI_GetNumXMMRegs() { return 8; }
|
||||
#else
|
||||
inline int ABI_GetNumXMMRegs() { return 16; }
|
||||
#endif
|
||||
|
||||
#endif // _JIT_ABI_H
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
// This is purposedely not a full wrapper for virtualalloc/mmap, but it
|
||||
// provides exactly the primitive operations that Dolphin needs.
|
||||
|
||||
void* AllocateExecutableMemory(int size, bool low)
|
||||
void* AllocateExecutableMemory(size_t size, bool low)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
void* ptr = VirtualAlloc(0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||
@@ -71,7 +71,7 @@ void* AllocateExecutableMemory(int size, bool low)
|
||||
}
|
||||
|
||||
|
||||
void* AllocateMemoryPages(int size)
|
||||
void* AllocateMemoryPages(size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
void* ptr = VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
@@ -99,7 +99,7 @@ void* AllocateMemoryPages(int size)
|
||||
}
|
||||
|
||||
|
||||
void FreeMemoryPages(void* ptr, int size)
|
||||
void FreeMemoryPages(void* ptr, size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (ptr)
|
||||
@@ -113,7 +113,7 @@ void FreeMemoryPages(void* ptr, int size)
|
||||
}
|
||||
|
||||
|
||||
void WriteProtectMemory(void* ptr, int size, bool allowExecute)
|
||||
void WriteProtectMemory(void* ptr, size_t size, bool allowExecute)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, 0);
|
||||
@@ -123,7 +123,7 @@ void WriteProtectMemory(void* ptr, int size, bool allowExecute)
|
||||
}
|
||||
|
||||
|
||||
void UnWriteProtectMemory(void* ptr, int size, bool allowExecute)
|
||||
void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READONLY, 0);
|
||||
|
||||
@@ -18,14 +18,14 @@
|
||||
#ifndef _MEMORYUTIL_H
|
||||
#define _MEMORYUTIL_H
|
||||
|
||||
void* AllocateExecutableMemory(int size, bool low = true);
|
||||
void* AllocateMemoryPages(int size);
|
||||
void FreeMemoryPages(void* ptr, int size);
|
||||
void WriteProtectMemory(void* ptr, int size, bool executable = false);
|
||||
void UnWriteProtectMemory(void* ptr, int size, bool allowExecute);
|
||||
void* AllocateExecutableMemory(size_t size, bool low = true);
|
||||
void* AllocateMemoryPages(size_t size);
|
||||
void FreeMemoryPages(void* ptr, size_t size);
|
||||
void WriteProtectMemory(void* ptr, size_t size, bool executable = false);
|
||||
void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute);
|
||||
|
||||
|
||||
inline int GetPageSize() {return(4096);}
|
||||
inline int GetPageSize() {return 4096;}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -18,33 +18,29 @@
|
||||
#include <map>
|
||||
|
||||
#include "Common.h"
|
||||
#include "Thunk.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "MemoryUtil.h"
|
||||
#include "ABI.h"
|
||||
#include "Thunk.h"
|
||||
|
||||
using namespace Gen;
|
||||
ThunkManager thunks;
|
||||
|
||||
#define THUNK_ARENA_SIZE 1024*1024*1
|
||||
|
||||
namespace {
|
||||
static std::map<void *, const u8 *> thunks;
|
||||
u8 GC_ALIGNED32(saved_fp_state[16 * 4 * 4]);
|
||||
u8 GC_ALIGNED32(saved_gpr_state[16 * 8]);
|
||||
|
||||
static u8 *thunk_memory;
|
||||
static u8 *thunk_code;
|
||||
static const u8 *save_regs;
|
||||
static const u8 *load_regs;
|
||||
static u16 saved_mxcsr;
|
||||
}
|
||||
|
||||
void Thunk_Init()
|
||||
namespace
|
||||
{
|
||||
thunk_memory = (u8 *)AllocateExecutableMemory(THUNK_ARENA_SIZE);
|
||||
thunk_code = thunk_memory;
|
||||
|
||||
GenContext ctx(&thunk_code);
|
||||
static u8 GC_ALIGNED32(saved_fp_state[16 * 4 * 4]);
|
||||
static u8 GC_ALIGNED32(saved_gpr_state[16 * 8]);
|
||||
static u16 saved_mxcsr;
|
||||
|
||||
} // namespace
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
void ThunkManager::Init()
|
||||
{
|
||||
AllocCodeSpace(THUNK_ARENA_SIZE);
|
||||
save_regs = GetCodePtr();
|
||||
for (int i = 2; i < ABI_GetNumXMMRegs(); i++)
|
||||
MOVAPS(M(saved_fp_state + i * 16), (X64Reg)(XMM0 + i));
|
||||
@@ -89,31 +85,27 @@ void Thunk_Init()
|
||||
RET();
|
||||
}
|
||||
|
||||
void Thunk_Reset()
|
||||
void ThunkManager::Reset()
|
||||
{
|
||||
thunks.clear();
|
||||
thunk_code = thunk_memory;
|
||||
ResetCodePtr();
|
||||
}
|
||||
|
||||
void Thunk_Shutdown()
|
||||
void ThunkManager::Shutdown()
|
||||
{
|
||||
Thunk_Reset();
|
||||
FreeMemoryPages(thunk_memory, THUNK_ARENA_SIZE);
|
||||
thunk_memory = 0;
|
||||
thunk_code = 0;
|
||||
Reset();
|
||||
FreeCodeSpace();
|
||||
}
|
||||
|
||||
void *ProtectFunction(void *function, int num_params)
|
||||
void *ThunkManager::ProtectFunction(void *function, int num_params)
|
||||
{
|
||||
std::map<void *, const u8 *>::iterator iter;
|
||||
iter = thunks.find(function);
|
||||
if (iter != thunks.end())
|
||||
return (void *)iter->second;
|
||||
|
||||
if (!thunk_memory)
|
||||
if (!region)
|
||||
PanicAlert("Trying to protect functions before the emu is started. Bad bad bad.");
|
||||
|
||||
GenContext gen(&thunk_code);
|
||||
const u8 *call_point = GetCodePtr();
|
||||
// Make sure to align stack.
|
||||
#ifdef _M_X64
|
||||
|
||||
@@ -18,6 +18,11 @@
|
||||
#ifndef _THUNK_H
|
||||
#define _THUNK_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "Common.h"
|
||||
#include "x64Emitter.h"
|
||||
|
||||
// This simple class creates a wrapper around a C/C++ function that saves all fp state
|
||||
// before entering it, and restores it upon exit. This is required to be able to selectively
|
||||
// call functions from generated code, without inflicting the performance hit and increase
|
||||
@@ -30,10 +35,21 @@
|
||||
// NOT THREAD SAFE. This may only be used from the CPU thread.
|
||||
// Any other thread using this stuff will be FATAL.
|
||||
|
||||
void Thunk_Init();
|
||||
void Thunk_Reset();
|
||||
void Thunk_Shutdown();
|
||||
class ThunkManager : public Gen::XCodeBlock
|
||||
{
|
||||
std::map<void *, const u8 *> thunks;
|
||||
|
||||
void *ProtectFunction(void *function, int num_params);
|
||||
const u8 *save_regs;
|
||||
const u8 *load_regs;
|
||||
|
||||
public:
|
||||
void Init();
|
||||
void Reset();
|
||||
void Shutdown();
|
||||
|
||||
void *ProtectFunction(void *function, int num_params);
|
||||
};
|
||||
|
||||
extern ThunkManager thunks;
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+448
-307
File diff suppressed because it is too large
Load Diff
@@ -46,7 +46,7 @@ namespace HW
|
||||
{
|
||||
CoreTiming::Init();
|
||||
|
||||
Thunk_Init(); // not really hw, but this way we know it's inited early :P
|
||||
thunks.Init(); // not really hw, but this way we know it's inited early :P
|
||||
State_Init();
|
||||
|
||||
// Init the whole Hardware
|
||||
@@ -88,7 +88,7 @@ namespace HW
|
||||
}
|
||||
|
||||
State_Shutdown();
|
||||
Thunk_Shutdown();
|
||||
thunks.Shutdown();
|
||||
CoreTiming::Shutdown();
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ LONG NTAPI Handler(PEXCEPTION_POINTERS pPtrs)
|
||||
|
||||
//We could emulate the memory accesses here, but then they would still be around to take up
|
||||
//execution resources. Instead, we backpatch into a generic memory call and retry.
|
||||
u8 *new_rip = jit.BackPatch(codePtr, accessType, emAddress, ctx);
|
||||
const u8 *new_rip = jit.BackPatch(codePtr, accessType, emAddress, ctx);
|
||||
|
||||
// Rip/Eip needs to be updated.
|
||||
if (new_rip)
|
||||
|
||||
@@ -164,6 +164,8 @@ ps_adds1
|
||||
Jit64 jit;
|
||||
PPCAnalyst::CodeBuffer code_buffer(32000);
|
||||
|
||||
int CODE_SIZE = 1024*1024*16;
|
||||
|
||||
namespace CPUCompare
|
||||
{
|
||||
extern u32 m_BlockStart;
|
||||
@@ -171,6 +173,11 @@ namespace CPUCompare
|
||||
|
||||
void Jit64::Init()
|
||||
{
|
||||
if (Core::g_CoreStartupParameter.bJITUnlimitedCache)
|
||||
{
|
||||
CODE_SIZE = 1024*1024*8*8;
|
||||
}
|
||||
|
||||
jo.optimizeStack = true;
|
||||
jo.enableBlocklink = true; // Speed boost, but not 100% safe
|
||||
#ifdef _M_X64
|
||||
@@ -182,6 +189,23 @@ namespace CPUCompare
|
||||
jo.fpAccurateFlags = true;
|
||||
jo.optimizeGatherPipe = true;
|
||||
jo.fastInterrupts = false;
|
||||
|
||||
gpr.SetEmitter(this);
|
||||
fpr.SetEmitter(this);
|
||||
|
||||
trampolines.Init();
|
||||
AllocCodeSpace(CODE_SIZE);
|
||||
InitCache();
|
||||
asm_routines.Init();
|
||||
}
|
||||
|
||||
void Jit64::Shutdown()
|
||||
{
|
||||
FreeCodeSpace();
|
||||
ShutdownCache();
|
||||
|
||||
trampolines.Shutdown();
|
||||
asm_routines.Shutdown();
|
||||
}
|
||||
|
||||
void Jit64::WriteCallInterpreter(UGeckoInstruction _inst)
|
||||
@@ -271,7 +295,7 @@ namespace CPUCompare
|
||||
else
|
||||
{
|
||||
MOV(32, M(&PC), Imm32(destination));
|
||||
JMP(Asm::dispatcher, true);
|
||||
JMP(asm_routines.dispatcher, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,7 +304,7 @@ namespace CPUCompare
|
||||
MOV(32, M(&PC), R(EAX));
|
||||
Cleanup();
|
||||
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
|
||||
JMP(Asm::dispatcher, true);
|
||||
JMP(asm_routines.dispatcher, true);
|
||||
}
|
||||
|
||||
void Jit64::WriteRfiExitDestInEAX()
|
||||
@@ -288,7 +312,7 @@ namespace CPUCompare
|
||||
MOV(32, M(&PC), R(EAX));
|
||||
Cleanup();
|
||||
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
|
||||
JMP(Asm::testExceptions, true);
|
||||
JMP(asm_routines.testExceptions, true);
|
||||
}
|
||||
|
||||
void Jit64::WriteExceptionExit(u32 exception)
|
||||
@@ -296,7 +320,7 @@ namespace CPUCompare
|
||||
Cleanup();
|
||||
OR(32, M(&PowerPC::ppcState.Exceptions), Imm32(exception));
|
||||
MOV(32, M(&PC), Imm32(js.compilerPC + 4));
|
||||
JMP(Asm::testExceptions, true);
|
||||
JMP(asm_routines.testExceptions, true);
|
||||
}
|
||||
|
||||
const u8* Jit64::DoJit(u32 emaddress, JitBlock &b)
|
||||
@@ -326,11 +350,13 @@ namespace CPUCompare
|
||||
// Downcount flag check. The last block decremented downcounter, and the flag should still be available.
|
||||
FixupBranch skip = J_CC(CC_NBE);
|
||||
MOV(32, M(&PC), Imm32(js.blockStart));
|
||||
JMP(Asm::doTiming, true); // downcount hit zero - go doTiming.
|
||||
JMP(asm_routines.doTiming, true); // downcount hit zero - go doTiming.
|
||||
SetJumpTarget(skip);
|
||||
|
||||
const u8 *normalEntry = GetCodePtr();
|
||||
if (ImHereDebug) CALL((void *)&ImHere); //Used to get a trace of the last few blocks before a crash, sometimes VERY useful
|
||||
|
||||
if (ImHereDebug)
|
||||
CALL((void *)&ImHere); //Used to get a trace of the last few blocks before a crash, sometimes VERY useful
|
||||
|
||||
if (js.fpa.any)
|
||||
{
|
||||
@@ -338,7 +364,7 @@ namespace CPUCompare
|
||||
TEST(32, M(&PowerPC::ppcState.msr), Imm32(1 << 13)); //Test FP enabled bit
|
||||
FixupBranch b1 = J_CC(CC_NZ);
|
||||
MOV(32, M(&PC), Imm32(js.blockStart));
|
||||
JMP(Asm::fpException, true);
|
||||
JMP(asm_routines.fpException, true);
|
||||
SetJumpTarget(b1);
|
||||
}
|
||||
|
||||
@@ -348,7 +374,7 @@ namespace CPUCompare
|
||||
TEST(32, M(&PowerPC::ppcState.Exceptions), Imm32(0xFFFFFFFF));
|
||||
FixupBranch b1 = J_CC(CC_Z);
|
||||
MOV(32, M(&PC), Imm32(js.blockStart));
|
||||
JMP(Asm::testExceptions, true);
|
||||
JMP(asm_routines.testExceptions, true);
|
||||
SetJumpTarget(b1);
|
||||
}
|
||||
|
||||
@@ -404,7 +430,7 @@ namespace CPUCompare
|
||||
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock >= 32)
|
||||
{
|
||||
js.fifoBytesThisBlock -= 32;
|
||||
CALL(ProtectFunction((void *)&GPFifo::CheckGatherPipe, 0));
|
||||
CALL(thunks.ProtectFunction((void *)&GPFifo::CheckGatherPipe, 0));
|
||||
}
|
||||
|
||||
PPCTables::CompileInstruction(ops[i].inst);
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
|
||||
#include "../PPCAnalyst.h"
|
||||
#include "JitCache.h"
|
||||
#include "JitRegCache.h"
|
||||
#include "x64Emitter.h"
|
||||
#include "x64Analyzer.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
@@ -47,8 +49,24 @@ struct CONTEXT
|
||||
|
||||
#endif
|
||||
|
||||
class Jit64
|
||||
|
||||
class TrampolineCache : public Gen::XCodeBlock
|
||||
{
|
||||
public:
|
||||
void Init();
|
||||
void Shutdown();
|
||||
|
||||
const u8 *GetReadTrampoline(const InstructionInfo &info);
|
||||
const u8 *GetWriteTrampoline(const InstructionInfo &info);
|
||||
};
|
||||
|
||||
|
||||
class Jit64 : public Gen::XCodeBlock
|
||||
{
|
||||
TrampolineCache trampolines;
|
||||
GPRRegCache gpr;
|
||||
FPURegCache fpr;
|
||||
|
||||
public:
|
||||
typedef void (*CompiledCode)();
|
||||
|
||||
@@ -157,7 +175,7 @@ public:
|
||||
bool RangeIntersect(int s1, int e1, int s2, int e2) const;
|
||||
bool IsInJitCode(const u8 *codePtr);
|
||||
|
||||
u8 *BackPatch(u8 *codePtr, int accessType, u32 emAddress, CONTEXT *ctx);
|
||||
const u8 *BackPatch(u8 *codePtr, int accessType, u32 emAddress, CONTEXT *ctx);
|
||||
|
||||
#define JIT_OPCODE 0
|
||||
|
||||
@@ -165,6 +183,7 @@ public:
|
||||
const u8* DoJit(u32 emaddress, JitBlock &b);
|
||||
|
||||
void Init();
|
||||
void Shutdown();
|
||||
|
||||
// Utilities for use by opcodes
|
||||
|
||||
@@ -188,10 +207,10 @@ public:
|
||||
void ForceSinglePrecisionP(Gen::X64Reg xmm);
|
||||
void JitClearCA();
|
||||
void JitSetCA();
|
||||
void tri_op(int d, int a, int b, bool reversible, void (*op)(Gen::X64Reg, Gen::OpArg));
|
||||
void tri_op(int d, int a, int b, bool reversible, void (XEmitter::*op)(Gen::X64Reg, Gen::OpArg));
|
||||
typedef u32 (*Operation)(u32 a, u32 b);
|
||||
void regimmop(int d, int a, bool binary, u32 value, Operation doop, void(*op)(int, const Gen::OpArg&, const Gen::OpArg&), bool Rc = false, bool carry = false);
|
||||
void fp_tri_op(int d, int a, int b, bool reversible, bool dupe, void (*op)(Gen::X64Reg, Gen::OpArg));
|
||||
void regimmop(int d, int a, bool binary, u32 value, Operation doop, void (XEmitter::*op)(int, const Gen::OpArg&, const Gen::OpArg&), bool Rc = false, bool carry = false);
|
||||
void fp_tri_op(int d, int a, int b, bool reversible, bool dupe, void (XEmitter::*op)(Gen::X64Reg, Gen::OpArg));
|
||||
|
||||
|
||||
// OPCODES
|
||||
|
||||
@@ -31,27 +31,12 @@
|
||||
#include "../../HW/CPUCompare.h"
|
||||
#include "../../HW/GPFifo.h"
|
||||
#include "../../Core.h"
|
||||
#include "JitAsm.h"
|
||||
|
||||
using namespace Gen;
|
||||
int blocksExecuted;
|
||||
|
||||
namespace Asm
|
||||
{
|
||||
const u8 *enterCode;
|
||||
const u8 *testExceptions;
|
||||
const u8 *fpException;
|
||||
const u8 *doTiming;
|
||||
const u8 *dispatcher;
|
||||
const u8 *dispatcherNoCheck;
|
||||
const u8 *dispatcherPcInEAX;
|
||||
const u8 *computeRc;
|
||||
const u8 *computeRcFp;
|
||||
|
||||
const u8 *fifoDirectWrite8;
|
||||
const u8 *fifoDirectWrite16;
|
||||
const u8 *fifoDirectWrite32;
|
||||
const u8 *fifoDirectWriteFloat;
|
||||
const u8 *fifoDirectWriteXmm64;
|
||||
static int temp32;
|
||||
|
||||
bool compareEnabled = false;
|
||||
|
||||
@@ -72,16 +57,15 @@ static bool enableStatistics = false;
|
||||
//RBX - Base pointer of memory
|
||||
//R15 - Pointer to array of block pointers
|
||||
|
||||
AsmRoutineManager asm_routines;
|
||||
|
||||
// PLAN: no more block numbers - crazy opcodes just contain offset within
|
||||
// dynarec buffer
|
||||
// At this offset - 4, there is an int specifying the block number.
|
||||
|
||||
|
||||
void GenerateCommon();
|
||||
|
||||
#ifdef _M_IX86
|
||||
void Generate()
|
||||
void AsmRoutineManager::Generate()
|
||||
{
|
||||
enterCode = AlignCode16();
|
||||
PUSH(EBP);
|
||||
@@ -129,7 +113,6 @@ void Generate()
|
||||
ADD(32, M(&PowerPC::ppcState.DebugCount), Imm8(1));
|
||||
}
|
||||
//grab from list and jump to it
|
||||
//INT3();
|
||||
MOV(32, R(EDX), ImmPtr(jit.GetCodePointers()));
|
||||
JMPptr(MComplex(EDX, EAX, 4, 0));
|
||||
SetJumpTarget(notfound);
|
||||
@@ -180,12 +163,14 @@ void Generate()
|
||||
|
||||
#elif defined(_M_X64)
|
||||
|
||||
void Generate()
|
||||
void AsmRoutineManager::Generate()
|
||||
{
|
||||
enterCode = AlignCode16();
|
||||
|
||||
ABI_PushAllCalleeSavedRegsAndAdjustStack();
|
||||
|
||||
if (!jit.GetCodePointers() || !Memory::base)
|
||||
PanicAlert("Memory::base and jit.GetCodePointers() must return valid values");
|
||||
MOV(64, R(RBX), Imm64((u64)Memory::base));
|
||||
MOV(64, R(R15), Imm64((u64)jit.GetCodePointers())); //It's below 2GB so 32 bits are good enough
|
||||
const u8 *outerLoop = GetCodePtr();
|
||||
@@ -264,7 +249,7 @@ void Generate()
|
||||
}
|
||||
#endif
|
||||
|
||||
void GenFifoWrite(int size)
|
||||
void AsmRoutineManager::GenFifoWrite(int size)
|
||||
{
|
||||
// Assume value in ABI_PARAM1
|
||||
PUSH(ESI);
|
||||
@@ -287,8 +272,7 @@ void GenFifoWrite(int size)
|
||||
RET();
|
||||
}
|
||||
|
||||
static int temp32;
|
||||
void GenFifoFloatWrite()
|
||||
void AsmRoutineManager::GenFifoFloatWrite()
|
||||
{
|
||||
// Assume value in XMM0
|
||||
PUSH(ESI);
|
||||
@@ -306,7 +290,7 @@ void GenFifoFloatWrite()
|
||||
RET();
|
||||
}
|
||||
|
||||
void GenFifoXmm64Write()
|
||||
void AsmRoutineManager::GenFifoXmm64Write()
|
||||
{
|
||||
// Assume value in XMM0. Assume pre-byteswapped (unlike the others here!)
|
||||
PUSH(ESI);
|
||||
@@ -319,7 +303,7 @@ void GenFifoXmm64Write()
|
||||
RET();
|
||||
}
|
||||
|
||||
void GenerateCommon()
|
||||
void AsmRoutineManager::GenerateCommon()
|
||||
{
|
||||
// USES_CR
|
||||
computeRc = AlignCode16();
|
||||
@@ -364,5 +348,3 @@ void GenerateCommon()
|
||||
SetJumpTarget(skip_fast_write);
|
||||
CALL((void *)&Memory::Write_U8);*/
|
||||
}
|
||||
|
||||
} // namespace Asm
|
||||
|
||||
@@ -14,33 +14,71 @@
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _JITASM_H
|
||||
#define _JITASM_H
|
||||
|
||||
namespace Asm
|
||||
#include "x64Emitter.h"
|
||||
|
||||
// In Dolphin, we don't use inline assembly. Instead, we generate all machine-near
|
||||
// code at runtime. In the case of fixed code like this, after writing it, we write
|
||||
// protect the memory, essentially making it work just like precompiled code.
|
||||
|
||||
// There are some advantages to this approach:
|
||||
// 1) No need to setup an external assembler in the build.
|
||||
// 2) Cross platform, as long as it's x86/x64.
|
||||
// 3) Can optimize code at runtime for the specific CPU model.
|
||||
// There aren't really any disadvantages other than having to maintain a x86 emitter,
|
||||
// which we have to do anyway :)
|
||||
//
|
||||
// To add a new asm routine, just add another const here, and add the code to Generate.
|
||||
// Also, possibly increase the size of the code buffer.
|
||||
|
||||
class AsmRoutineManager : public Gen::XCodeBlock
|
||||
{
|
||||
extern const u8 *enterCode;
|
||||
|
||||
extern const u8 *dispatcher;
|
||||
extern const u8 *dispatcherNoCheck;
|
||||
extern const u8 *dispatcherPcInEAX;
|
||||
|
||||
extern const u8 *fpException;
|
||||
extern const u8 *computeRc;
|
||||
extern const u8 *computeRcFp;
|
||||
extern const u8 *testExceptions;
|
||||
extern const u8 *dispatchPcInEAX;
|
||||
extern const u8 *doTiming;
|
||||
|
||||
extern const u8 *fifoDirectWrite8;
|
||||
extern const u8 *fifoDirectWrite16;
|
||||
extern const u8 *fifoDirectWrite32;
|
||||
extern const u8 *fifoDirectWriteFloat;
|
||||
extern const u8 *fifoDirectWriteXmm64;
|
||||
|
||||
extern bool compareEnabled;
|
||||
private:
|
||||
void Generate();
|
||||
}
|
||||
void GenerateCommon();
|
||||
void GenFifoWrite(int size);
|
||||
void GenFifoFloatWrite();
|
||||
void GenFifoXmm64Write();
|
||||
|
||||
public:
|
||||
void Init() {
|
||||
AllocCodeSpace(8192);
|
||||
Generate();
|
||||
WriteProtect();
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
FreeCodeSpace();
|
||||
}
|
||||
|
||||
|
||||
// Public generated functions. Just CALL(M((void*)func)) them.
|
||||
|
||||
const u8 *enterCode;
|
||||
|
||||
const u8 *dispatcher;
|
||||
const u8 *dispatcherNoCheck;
|
||||
const u8 *dispatcherPcInEAX;
|
||||
|
||||
const u8 *fpException;
|
||||
const u8 *computeRc;
|
||||
const u8 *computeRcFp;
|
||||
const u8 *testExceptions;
|
||||
const u8 *dispatchPcInEAX;
|
||||
const u8 *doTiming;
|
||||
|
||||
const u8 *fifoDirectWrite8;
|
||||
const u8 *fifoDirectWrite16;
|
||||
const u8 *fifoDirectWrite32;
|
||||
const u8 *fifoDirectWriteFloat;
|
||||
const u8 *fifoDirectWriteXmm64;
|
||||
|
||||
bool compareEnabled;
|
||||
};
|
||||
|
||||
extern AsmRoutineManager asm_routines;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
using namespace Gen;
|
||||
|
||||
extern u8 *trampolineCodePtr;
|
||||
|
||||
|
||||
void BackPatchError(const std::string &text, u8 *codePtr, u32 emAddress) {
|
||||
u64 code_addr = (u64)codePtr;
|
||||
disassembler disasm;
|
||||
@@ -51,17 +51,105 @@ void BackPatchError(const std::string &text, u8 *codePtr, u32 emAddress) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void TrampolineCache::Init()
|
||||
{
|
||||
AllocCodeSpace(1024 * 1024);
|
||||
}
|
||||
|
||||
void TrampolineCache::Shutdown()
|
||||
{
|
||||
AllocCodeSpace(1024 * 1024);
|
||||
}
|
||||
|
||||
// Extremely simplistic - just generate the requested trampoline. May reuse them in the future.
|
||||
const u8 *TrampolineCache::GetReadTrampoline(const InstructionInfo &info)
|
||||
{
|
||||
if (GetSpaceLeft() < 1024)
|
||||
PanicAlert("Trampoline cache full");
|
||||
|
||||
X64Reg addrReg = (X64Reg)info.scaledReg;
|
||||
X64Reg dataReg = (X64Reg)info.regOperandReg;
|
||||
const u8 *trampoline = GetCodePtr();
|
||||
#ifdef _M_X64
|
||||
// It's a read. Easy.
|
||||
ABI_PushAllCallerSavedRegsAndAdjustStack();
|
||||
if (addrReg != ABI_PARAM1)
|
||||
MOV(32, R(ABI_PARAM1), R((X64Reg)addrReg));
|
||||
if (info.displacement) {
|
||||
ADD(32, R(ABI_PARAM1), Imm32(info.displacement));
|
||||
}
|
||||
switch (info.operandSize) {
|
||||
case 4:
|
||||
CALL(thunks.ProtectFunction((void *)&Memory::Read_U32, 1));
|
||||
break;
|
||||
}
|
||||
ABI_PopAllCallerSavedRegsAndAdjustStack();
|
||||
MOV(32, R(dataReg), R(EAX));
|
||||
RET();
|
||||
#endif
|
||||
return trampoline;
|
||||
}
|
||||
|
||||
// Extremely simplistic - just generate the requested trampoline. May reuse them in the future.
|
||||
const u8 *TrampolineCache::GetWriteTrampoline(const InstructionInfo &info)
|
||||
{
|
||||
if (GetSpaceLeft() < 1024)
|
||||
PanicAlert("Trampoline cache full");
|
||||
|
||||
X64Reg addrReg = (X64Reg)info.scaledReg;
|
||||
X64Reg dataReg = (X64Reg)info.regOperandReg;
|
||||
if (dataReg != EAX)
|
||||
PanicAlert("Backpatch write - not through EAX");
|
||||
|
||||
const u8 *trampoline = GetCodePtr();
|
||||
|
||||
#ifdef _M_X64
|
||||
|
||||
// It's a write. Yay. Remember that we don't have to be super efficient since it's "just" a
|
||||
// hardware access - we can take shortcuts.
|
||||
//if (emAddress == 0xCC008000)
|
||||
// PanicAlert("caught a fifo write");
|
||||
CMP(32, R(addrReg), Imm32(0xCC008000));
|
||||
FixupBranch skip_fast = J_CC(CC_NE, false);
|
||||
MOV(32, R(ABI_PARAM1), R((X64Reg)dataReg));
|
||||
CALL((void*)asm_routines.fifoDirectWrite32);
|
||||
RET();
|
||||
SetJumpTarget(skip_fast);
|
||||
ABI_PushAllCallerSavedRegsAndAdjustStack();
|
||||
if (addrReg != ABI_PARAM1) {
|
||||
MOV(32, R(ABI_PARAM1), R((X64Reg)dataReg));
|
||||
MOV(32, R(ABI_PARAM2), R((X64Reg)addrReg));
|
||||
} else {
|
||||
MOV(32, R(ABI_PARAM2), R((X64Reg)addrReg));
|
||||
MOV(32, R(ABI_PARAM1), R((X64Reg)dataReg));
|
||||
}
|
||||
if (info.displacement) {
|
||||
ADD(32, R(ABI_PARAM2), Imm32(info.displacement));
|
||||
}
|
||||
switch (info.operandSize) {
|
||||
case 4:
|
||||
CALL(thunks.ProtectFunction((void *)&Memory::Write_U32, 2));
|
||||
break;
|
||||
}
|
||||
ABI_PopAllCallerSavedRegsAndAdjustStack();
|
||||
RET();
|
||||
#endif
|
||||
|
||||
return trampoline;
|
||||
}
|
||||
|
||||
|
||||
// This generates some fairly heavy trampolines, but:
|
||||
// 1) It's really necessary. We don't know anything about the context.
|
||||
// 2) It doesn't really hurt. Only instructions that access I/O will get these, and there won't be
|
||||
// that many of them in a typical program/game.
|
||||
u8 *Jit64::BackPatch(u8 *codePtr, int accessType, u32 emAddress, CONTEXT *ctx)
|
||||
const u8 *Jit64::BackPatch(u8 *codePtr, int accessType, u32 emAddress, CONTEXT *ctx)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
if (!IsInJitCode(codePtr))
|
||||
return 0; // this will become a regular crash real soon after this
|
||||
|
||||
u8 *oldCodePtr = GetWritableCodePtr();
|
||||
InstructionInfo info;
|
||||
if (!DisassembleMov(codePtr, info, accessType)) {
|
||||
BackPatchError("BackPatch - failed to disassemble MOV instruction", codePtr, emAddress);
|
||||
@@ -81,108 +169,42 @@ u8 *Jit64::BackPatch(u8 *codePtr, int accessType, u32 emAddress, CONTEXT *ctx)
|
||||
BackPatchError(StringFromFormat("BackPatch - no support for operand size %i", info.operandSize), codePtr, emAddress);
|
||||
}
|
||||
|
||||
X64Reg addrReg = (X64Reg)info.scaledReg;
|
||||
X64Reg dataReg = (X64Reg)info.regOperandReg;
|
||||
if (info.otherReg != RBX)
|
||||
PanicAlert("BackPatch : Base reg not RBX."
|
||||
"\n\nAttempted to access %08x.", emAddress);
|
||||
//if (accessType == OP_ACCESS_WRITE)
|
||||
// PanicAlert("BackPatch : Currently only supporting reads."
|
||||
// "\n\nAttempted to write to %08x.", emAddress);
|
||||
|
||||
// OK, let's write a trampoline, and a jump to it.
|
||||
// Later, let's share trampolines.
|
||||
|
||||
if (accessType == OP_ACCESS_WRITE)
|
||||
PanicAlert("BackPatch : Currently only supporting reads."
|
||||
"\n\nAttempted to write to %08x.", emAddress);
|
||||
|
||||
// In the first iteration, we assume that all accesses are 32-bit. We also only deal with reads.
|
||||
// Next step - support writes, special case FIFO writes. Also, support 32-bit mode.
|
||||
u8 *trampoline = trampolineCodePtr;
|
||||
SetCodePtr(trampolineCodePtr);
|
||||
|
||||
if (accessType == 0)
|
||||
{
|
||||
// It's a read. Easy.
|
||||
ABI_PushAllCallerSavedRegsAndAdjustStack();
|
||||
if (addrReg != ABI_PARAM1)
|
||||
MOV(32, R(ABI_PARAM1), R((X64Reg)addrReg));
|
||||
if (info.displacement) {
|
||||
ADD(32, R(ABI_PARAM1), Imm32(info.displacement));
|
||||
}
|
||||
switch (info.operandSize) {
|
||||
case 4:
|
||||
CALL(ProtectFunction((void *)&Memory::Read_U32, 1));
|
||||
break;
|
||||
default:
|
||||
BackPatchError(StringFromFormat("We don't handle the size %i yet in backpatch", info.operandSize), codePtr, emAddress);
|
||||
break;
|
||||
}
|
||||
ABI_PopAllCallerSavedRegsAndAdjustStack();
|
||||
MOV(32, R(dataReg), R(EAX));
|
||||
RET();
|
||||
trampolineCodePtr = GetWritableCodePtr();
|
||||
|
||||
SetCodePtr(codePtr);
|
||||
XEmitter emitter(codePtr);
|
||||
int bswapNopCount;
|
||||
// Check the following BSWAP for REX byte
|
||||
if ((GetCodePtr()[info.instructionSize] & 0xF0) == 0x40)
|
||||
if ((codePtr[info.instructionSize] & 0xF0) == 0x40)
|
||||
bswapNopCount = 3;
|
||||
else
|
||||
bswapNopCount = 2;
|
||||
CALL(trampoline);
|
||||
NOP((int)info.instructionSize + bswapNopCount - 5);
|
||||
SetCodePtr(oldCodePtr);
|
||||
|
||||
const u8 *trampoline = trampolines.GetReadTrampoline(info);
|
||||
emitter.CALL((void *)trampoline);
|
||||
emitter.NOP((int)info.instructionSize + bswapNopCount - 5);
|
||||
return codePtr;
|
||||
}
|
||||
else if (accessType == 1)
|
||||
{
|
||||
// It's a write. Yay. Remember that we don't have to be super efficient since it's "just" a
|
||||
// hardware access - we can take shortcuts.
|
||||
//if (emAddress == 0xCC008000)
|
||||
// PanicAlert("caught a fifo write");
|
||||
if (dataReg != EAX)
|
||||
PanicAlert("Backpatch write - not through EAX");
|
||||
CMP(32, R(addrReg), Imm32(0xCC008000));
|
||||
FixupBranch skip_fast = J_CC(CC_NE, false);
|
||||
MOV(32, R(ABI_PARAM1), R((X64Reg)dataReg));
|
||||
CALL((void*)Asm::fifoDirectWrite32);
|
||||
RET();
|
||||
SetJumpTarget(skip_fast);
|
||||
ABI_PushAllCallerSavedRegsAndAdjustStack();
|
||||
if (addrReg != ABI_PARAM1) {
|
||||
//INT3();
|
||||
MOV(32, R(ABI_PARAM1), R((X64Reg)dataReg));
|
||||
MOV(32, R(ABI_PARAM2), R((X64Reg)addrReg));
|
||||
} else {
|
||||
MOV(32, R(ABI_PARAM2), R((X64Reg)addrReg));
|
||||
MOV(32, R(ABI_PARAM1), R((X64Reg)dataReg));
|
||||
}
|
||||
if (info.displacement) {
|
||||
ADD(32, R(ABI_PARAM2), Imm32(info.displacement));
|
||||
}
|
||||
switch (info.operandSize) {
|
||||
case 4:
|
||||
CALL(ProtectFunction((void *)&Memory::Write_U32, 2));
|
||||
break;
|
||||
default:
|
||||
BackPatchError(StringFromFormat("We don't handle the size %i yet in backpatch", info.operandSize), codePtr, emAddress);
|
||||
break;
|
||||
}
|
||||
ABI_PopAllCallerSavedRegsAndAdjustStack();
|
||||
RET();
|
||||
|
||||
trampolineCodePtr = GetWritableCodePtr();
|
||||
|
||||
// TODO: special case FIFO writes. Also, support 32-bit mode.
|
||||
// Also, debug this so that it actually works correctly :P
|
||||
XEmitter emitter(codePtr - 2);
|
||||
// We know it's EAX so the BSWAP before will be two byte. Overwrite it.
|
||||
SetCodePtr(codePtr - 2);
|
||||
CALL(trampoline);
|
||||
NOP((int)info.instructionSize - 3);
|
||||
const u8 *trampoline = trampolines.GetWriteTrampoline(info);
|
||||
emitter.CALL((void *)trampoline);
|
||||
emitter.NOP((int)info.instructionSize - 3);
|
||||
if (info.instructionSize < 3)
|
||||
PanicAlert("instruction too small");
|
||||
SetCodePtr(oldCodePtr);
|
||||
|
||||
// We entered here with a BSWAP-ed EAX. We'll have to swap it back.
|
||||
ctx->Rax = Common::swap32(ctx->Rax);
|
||||
|
||||
return codePtr - 2;
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -56,19 +56,15 @@ using namespace Gen;
|
||||
op_agent_t agent;
|
||||
#endif
|
||||
static u8 *codeCache;
|
||||
static u8 *genFunctions;
|
||||
static u8 *trampolineCache;
|
||||
u8 *trampolineCodePtr;
|
||||
#define INVALID_EXIT 0xFFFFFFFF
|
||||
|
||||
enum
|
||||
{
|
||||
//CODE_SIZE = 1024*1024*8,
|
||||
GEN_SIZE = 4096,
|
||||
TRAMPOLINE_SIZE = 1024*1024,
|
||||
//MAX_NUM_BLOCKS = 65536,
|
||||
};
|
||||
int CODE_SIZE = 1024*1024*16;
|
||||
|
||||
int MAX_NUM_BLOCKS = 65536*2;
|
||||
|
||||
static u8 **blockCodePointers;
|
||||
@@ -89,36 +85,22 @@ using namespace Gen;
|
||||
|
||||
void Jit64::InitCache()
|
||||
{
|
||||
if(Core::g_CoreStartupParameter.bJITUnlimitedCache)
|
||||
if (Core::g_CoreStartupParameter.bJITUnlimitedCache)
|
||||
{
|
||||
CODE_SIZE = 1024*1024*8*8;
|
||||
MAX_NUM_BLOCKS = 65536*8;
|
||||
}
|
||||
|
||||
codeCache = (u8*)AllocateExecutableMemory(CODE_SIZE);
|
||||
genFunctions = (u8*)AllocateExecutableMemory(GEN_SIZE);
|
||||
trampolineCache = (u8*)AllocateExecutableMemory(TRAMPOLINE_SIZE);
|
||||
trampolineCodePtr = trampolineCache;
|
||||
|
||||
#ifdef OPROFILE_REPORT
|
||||
agent = op_open_agent();
|
||||
#endif
|
||||
blocks = new JitBlock[MAX_NUM_BLOCKS];
|
||||
blockCodePointers = new u8*[MAX_NUM_BLOCKS];
|
||||
|
||||
ClearCache();
|
||||
SetCodePtr(genFunctions);
|
||||
Asm::Generate();
|
||||
// Protect the generated functions
|
||||
WriteProtectMemory(genFunctions, GEN_SIZE, true);
|
||||
SetCodePtr(codeCache);
|
||||
}
|
||||
|
||||
void Jit64::ShutdownCache()
|
||||
{
|
||||
UnWriteProtectMemory(genFunctions, GEN_SIZE, true);
|
||||
FreeMemoryPages(codeCache, CODE_SIZE);
|
||||
FreeMemoryPages(genFunctions, GEN_SIZE);
|
||||
FreeMemoryPages(trampolineCache, TRAMPOLINE_SIZE);
|
||||
delete [] blocks;
|
||||
delete [] blockCodePointers;
|
||||
blocks = 0;
|
||||
@@ -135,21 +117,23 @@ using namespace Gen;
|
||||
{
|
||||
Core::DisplayMessage("Cleared code cache.", 3000);
|
||||
// Is destroying the blocks really necessary?
|
||||
for (int i = 0; i < numBlocks; i++) {
|
||||
for (int i = 0; i < numBlocks; i++)
|
||||
{
|
||||
DestroyBlock(i, false);
|
||||
}
|
||||
links_to.clear();
|
||||
trampolineCodePtr = trampolineCache;
|
||||
numBlocks = 0;
|
||||
memset(blockCodePointers, 0, sizeof(u8*)*MAX_NUM_BLOCKS);
|
||||
memset(codeCache, 0xCC, CODE_SIZE);
|
||||
SetCodePtr(codeCache);
|
||||
|
||||
trampolines.ClearCodeSpace();
|
||||
}
|
||||
|
||||
void Jit64::DestroyBlocksWithFlag(BlockFlag death_flag)
|
||||
{
|
||||
for (int i = 0; i < numBlocks; i++) {
|
||||
if (blocks[i].flags & death_flag) {
|
||||
for (int i = 0; i < numBlocks; i++)
|
||||
{
|
||||
if (blocks[i].flags & death_flag)
|
||||
{
|
||||
DestroyBlock(i, false);
|
||||
}
|
||||
}
|
||||
@@ -190,10 +174,10 @@ using namespace Gen;
|
||||
|
||||
const u8 *Jit64::Jit(u32 emAddress)
|
||||
{
|
||||
if (GetCodePtr() >= codeCache + CODE_SIZE - 0x10000 || numBlocks >= MAX_NUM_BLOCKS - 1)
|
||||
if (GetSpaceLeft() < 0x10000 || numBlocks >= MAX_NUM_BLOCKS - 1)
|
||||
{
|
||||
LOG(DYNA_REC, "JIT cache full - clearing.")
|
||||
if(Core::g_CoreStartupParameter.bJITUnlimitedCache)
|
||||
if (Core::g_CoreStartupParameter.bJITUnlimitedCache)
|
||||
{
|
||||
PanicAlert("What? JIT cache still full - clearing.");
|
||||
}
|
||||
@@ -221,10 +205,8 @@ using namespace Gen;
|
||||
}
|
||||
}
|
||||
|
||||
u8 *oldCodePtr = GetWritableCodePtr();
|
||||
LinkBlock(numBlocks);
|
||||
LinkBlockExits(numBlocks);
|
||||
SetCodePtr(oldCodePtr);
|
||||
}
|
||||
|
||||
#ifdef OPROFILE_REPORT
|
||||
@@ -257,7 +239,7 @@ using namespace Gen;
|
||||
|
||||
void Jit64::EnterFastRun()
|
||||
{
|
||||
CompiledCode pExecAddr = (CompiledCode)Asm::enterCode;
|
||||
CompiledCode pExecAddr = (CompiledCode)asm_routines.enterCode;
|
||||
pExecAddr();
|
||||
//Will return when PowerPC::state changes
|
||||
}
|
||||
@@ -336,8 +318,8 @@ using namespace Gen;
|
||||
int destinationBlock = GetBlockNumberFromAddress(b.exitAddress[e]);
|
||||
if (destinationBlock != -1)
|
||||
{
|
||||
SetCodePtr(b.exitPtrs[e]);
|
||||
JMP(blocks[destinationBlock].checkedEntry, true);
|
||||
XEmitter emit(b.exitPtrs[e]);
|
||||
emit.JMP(blocks[destinationBlock].checkedEntry, true);
|
||||
b.linkStatus[e] = true;
|
||||
}
|
||||
}
|
||||
@@ -345,6 +327,7 @@ using namespace Gen;
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
void Jit64::LinkBlock(int i)
|
||||
{
|
||||
LinkBlockExits(i);
|
||||
@@ -386,15 +369,15 @@ using namespace Gen;
|
||||
// Not entirely ideal, but .. pretty good.
|
||||
|
||||
// TODO - make sure that the below stuff really is safe.
|
||||
u8 *prev_code = GetWritableCodePtr();
|
||||
|
||||
// Spurious entrances from previously linked blocks can only come through checkedEntry
|
||||
SetCodePtr((u8*)b.checkedEntry);
|
||||
MOV(32, M(&PC), Imm32(b.originalAddress));
|
||||
JMP(Asm::dispatcher, true);
|
||||
SetCodePtr(blockCodePointers[blocknum]);
|
||||
MOV(32, M(&PC), Imm32(b.originalAddress));
|
||||
JMP(Asm::dispatcher, true);
|
||||
SetCodePtr(prev_code); // reset code pointer
|
||||
XEmitter emit((u8*)b.checkedEntry);
|
||||
emit.MOV(32, M(&PC), Imm32(b.originalAddress));
|
||||
emit.JMP(asm_routines.dispatcher, true);
|
||||
|
||||
emit.SetCodePtr(blockCodePointers[blocknum]);
|
||||
emit.MOV(32, M(&PC), Imm32(b.originalAddress));
|
||||
emit.JMP(asm_routines.dispatcher, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -19,6 +19,6 @@
|
||||
|
||||
#include "../Gekko.h"
|
||||
|
||||
// Will soon introduced the JitBlockCache class here.
|
||||
// Will soon introduce the JitBlockCache class here.
|
||||
|
||||
#endif
|
||||
|
||||
@@ -34,13 +34,12 @@ namespace JitCore
|
||||
void Init()
|
||||
{
|
||||
jit.Init();
|
||||
jit.InitCache();
|
||||
Asm::compareEnabled = ::Core::g_CoreStartupParameter.bRunCompareClient;
|
||||
asm_routines.compareEnabled = ::Core::g_CoreStartupParameter.bRunCompareClient;
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
jit.ShutdownCache();
|
||||
jit.Shutdown();
|
||||
}
|
||||
|
||||
void SingleStep()
|
||||
|
||||
@@ -27,8 +27,6 @@ using namespace Gen;
|
||||
using namespace PowerPC;
|
||||
|
||||
|
||||
GPRRegCache gpr;
|
||||
FPURegCache fpr;
|
||||
|
||||
void RegCache::Start(PPCAnalyst::BlockRegStats &stats)
|
||||
{
|
||||
@@ -267,7 +265,7 @@ using namespace PowerPC;
|
||||
xregs[xr].dirty = makeDirty || regs[i].location.IsImm();
|
||||
OpArg newloc = ::Gen::R(xr);
|
||||
if (doLoad || regs[i].location.IsImm())
|
||||
MOV(32, newloc, regs[i].location);
|
||||
emit->MOV(32, newloc, regs[i].location);
|
||||
for (int j = 0; j < 32; j++)
|
||||
{
|
||||
if (i != j && regs[j].location.IsSimpleReg() && regs[j].location.GetSimpleReg() == xr)
|
||||
@@ -309,7 +307,7 @@ using namespace PowerPC;
|
||||
}
|
||||
OpArg newLoc = GetDefaultLocation(i);
|
||||
// if (doStore) //<-- Breaks JIT compilation
|
||||
MOV(32, newLoc, regs[i].location);
|
||||
emit->MOV(32, newLoc, regs[i].location);
|
||||
regs[i].location = newLoc;
|
||||
regs[i].away = false;
|
||||
}
|
||||
@@ -327,11 +325,13 @@ using namespace PowerPC;
|
||||
xregs[xr].free = false;
|
||||
xregs[xr].dirty = makeDirty;
|
||||
OpArg newloc = ::Gen::R(xr);
|
||||
if (doLoad) {
|
||||
if (!regs[i].location.IsImm() && (regs[i].location.offset & 0xF)) {
|
||||
if (doLoad)
|
||||
{
|
||||
if (!regs[i].location.IsImm() && (regs[i].location.offset & 0xF))
|
||||
{
|
||||
PanicAlert("WARNING - misaligned fp register location %i", i);
|
||||
}
|
||||
MOVAPD(xr, regs[i].location);
|
||||
emit->MOVAPD(xr, regs[i].location);
|
||||
}
|
||||
regs[i].location = newloc;
|
||||
regs[i].away = true;
|
||||
@@ -352,7 +352,7 @@ using namespace PowerPC;
|
||||
xregs[xr].dirty = false;
|
||||
xregs[xr].ppcReg = -1;
|
||||
OpArg newLoc = GetDefaultLocation(i);
|
||||
MOVAPD(newLoc, xr);
|
||||
emit->MOVAPD(newLoc, xr);
|
||||
regs[i].location = newLoc;
|
||||
regs[i].away = false;
|
||||
}
|
||||
|
||||
@@ -72,10 +72,15 @@
|
||||
|
||||
void DiscardRegContentsIfCached(int preg);
|
||||
virtual const int *GetAllocationOrder(int &count) = 0;
|
||||
|
||||
XEmitter *emit;
|
||||
|
||||
public:
|
||||
virtual ~RegCache() {}
|
||||
virtual void Start(PPCAnalyst::BlockRegStats &stats) = 0;
|
||||
|
||||
void SetEmitter(XEmitter *emitter) {emit = emitter;}
|
||||
|
||||
void FlushR(X64Reg reg);
|
||||
void FlushR(X64Reg reg, X64Reg reg2) {FlushR(reg); FlushR(reg2);}
|
||||
void FlushLockX(X64Reg reg) {
|
||||
@@ -142,8 +147,5 @@
|
||||
OpArg GetDefaultLocation(int reg) const;
|
||||
};
|
||||
|
||||
extern GPRRegCache gpr;
|
||||
extern FPURegCache fpr;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user