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:
hrydgard
2008-12-19 21:24:52 +00:00
parent b5dcdcf779
commit 104acd5bc1
31 changed files with 1297 additions and 1153 deletions
+29 -28
View File
@@ -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);
-39
View File
@@ -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
+5 -5
View File
@@ -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);
+6 -6
View File
@@ -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
+21 -29
View File
@@ -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
+20 -4
View File
@@ -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
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -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();
}
+1 -1
View File
@@ -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)
+35 -9
View File
@@ -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 -5
View File
@@ -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
+11 -29
View File
@@ -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
+61 -23
View File
@@ -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;
+25 -42
View File
@@ -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