#include "ppsspp_config.h"
#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
#include "Common/Log.h"
#include "Core/CoreTiming.h"
#include "Core/MemMap.h"
#include "Core/MIPS/x86/X64IRJit.h"
#include "Core/MIPS/x86/X64IRRegCache.h"
#include "Core/MIPS/JitCommon/JitCommon.h"
#include "Core/MIPS/JitCommon/JitState.h"
#include "Core/Core.h"
namespace MIPSComp {
using namespace Gen;
using namespace X64IRJitConstants;
static const bool enableDebug = false;
static const bool enableDisasm = false;
static void ShowPC(void *membase, void *jitbase) {
static int count = 0;
if (currentMIPS) {
u32 downcount = currentMIPS->downcount;
ERROR_LOG(Log::JIT, "[%08x] ShowPC Downcount : %08x %d %p %p", currentMIPS->pc, downcount, count, membase, jitbase);
} else {
ERROR_LOG(Log::JIT, "Universe corrupt?");
}
count++;
}
void X64JitBackend::GenerateFixedCode(MIPSState *mipsState) {
const u8 *start = AlignCodePage();
if (DebugProfilerEnabled()) {
ProtectMemoryPages(start, GetMemoryProtectPageSize(), MEM_PROT_READ | MEM_PROT_WRITE);
hooks_.profilerPC = (uint32_t *)GetWritableCodePtr();
Write32(0);
hooks_.profilerStatus = (IRProfilerStatus *)GetWritableCodePtr();
Write32(0);
}
EmitFPUConstants();
EmitVecConstants();
const u8 *disasmStart = AlignCodePage();
BeginWrite(GetMemoryProtectPageSize());
jo.downcountInRegister = false;
#if PPSSPP_ARCH(AMD64)
bool jitbaseInR15 = false;
int jitbaseCtxDisp = 0;
intptr_t jitbase = (intptr_t)GetBasePtr() - MIPS_EMUHACK_OPCODE;
if ((jitbase < -0x80000000LL || jitbase > 0x7FFFFFFFLL) && !Accessible((const u8 *)&mipsState->f[0], (const u8 *)jitbase)) {
jo.reserveR15ForAsm = true;
jitbaseInR15 = true;
} else {
jo.downcountInRegister = true;
jo.reserveR15ForAsm = true;
if (jitbase < -0x80000000LL || jitbase > 0x7FFFFFFFLL) {
jitbaseCtxDisp = (int)(jitbase - (intptr_t)&mipsState->f[0]);
}
}
#endif
if (jo.useStaticAlloc && false) {
saveStaticRegisters_ = AlignCode16();
if (jo.downcountInRegister)
MOV(32, MDisp(CTXREG, downcountOffset), R(DOWNCOUNTREG));
RET();
loadStaticRegisters_ = AlignCode16();
if (jo.downcountInRegister)
MOV(32, R(DOWNCOUNTREG), MDisp(CTXREG, downcountOffset));
RET();
} else {
saveStaticRegisters_ = nullptr;
loadStaticRegisters_ = nullptr;
}
restoreRoundingMode_ = AlignCode16();
{
STMXCSR(MDisp(CTXREG, tempOffset));
AND(32, MDisp(CTXREG, tempOffset), Imm32(~(7 << 13)));
LDMXCSR(MDisp(CTXREG, tempOffset));
RET();
}
applyRoundingMode_ = AlignCode16();
{
MOV(32, R(SCRATCH1), MDisp(CTXREG, fcr31Offset));
AND(32, R(SCRATCH1), Imm32(0x01000003));
FixupBranch skip = J_CC(CC_Z);
STMXCSR(MDisp(CTXREG, tempOffset));
TEST(8, R(AL), Imm8(1));
FixupBranch skip2 = J_CC(CC_Z);
XOR(32, R(SCRATCH1), Imm8(2));
SetJumpTarget(skip2);
SHL(32, R(SCRATCH1), Imm8(13));
AND(32, MDisp(CTXREG, tempOffset), Imm32(~(7 << 13)));
OR(32, MDisp(CTXREG, tempOffset), R(SCRATCH1));
TEST(32, MDisp(CTXREG, fcr31Offset), Imm32(1 << 24));
FixupBranch skip3 = J_CC(CC_Z);
OR(32, MDisp(CTXREG, tempOffset), Imm32(1 << 15));
SetJumpTarget(skip3);
LDMXCSR(MDisp(CTXREG, tempOffset));
SetJumpTarget(skip);
RET();
}
hooks_.enterDispatcher = (IRNativeFuncNoArg)AlignCode16();
ABI_PushAllCalleeSavedRegsAndAdjustStack();
#if PPSSPP_ARCH(AMD64)
MOV(64, R(MEMBASEREG), ImmPtr(Memory::base));
if (jitbaseInR15)
MOV(64, R(JITBASEREG), ImmPtr((const void *)jitbase));
#endif
MOV(PTRBITS, R(CTXREG), ImmPtr(&mipsState->f[0]));
LoadStaticRegisters();
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
MovFromPC(SCRATCH1);
WriteDebugPC(SCRATCH1);
outerLoopPCInSCRATCH1_ = GetCodePtr();
MovToPC(SCRATCH1);
outerLoop_ = GetCodePtr();
SaveStaticRegisters();
RestoreRoundingMode(true);
WriteDebugProfilerStatus(IRProfilerStatus::TIMER_ADVANCE);
ABI_CallFunction(reinterpret_cast<void *>(&CoreTiming::Advance));
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
ApplyRoundingMode(true);
LoadStaticRegisters();
dispatcherCheckCoreState_ = GetCodePtr();
if (RipAccessible((const void *)&coreState)) {
CMP(32, M(&coreState), Imm8(0));
} else {
MOV(PTRBITS, R(RAX), ImmPtr((const void *)&coreState));
CMP(32, MatR(RAX), Imm8(0));
}
FixupBranch badCoreState = J_CC(CC_NZ, true);
if (jo.downcountInRegister) {
TEST(32, R(DOWNCOUNTREG), R(DOWNCOUNTREG));
} else {
CMP(32, MDisp(CTXREG, downcountOffset), Imm8(0));
}
J_CC(CC_S, outerLoop_);
FixupBranch skipToRealDispatch = J();
dispatcherPCInSCRATCH1_ = GetCodePtr();
MovToPC(SCRATCH1);
hooks_.dispatcher = GetCodePtr();
if (jo.downcountInRegister) {
TEST(32, R(DOWNCOUNTREG), R(DOWNCOUNTREG));
} else {
CMP(32, MDisp(CTXREG, downcountOffset), Imm8(0));
}
FixupBranch bail = J_CC(CC_S, true);
SetJumpTarget(skipToRealDispatch);
dispatcherNoCheck_ = GetCodePtr();
if (enableDebug) {
#if PPSSPP_ARCH(AMD64)
if (jitbaseInR15) {
ABI_CallFunctionAA(reinterpret_cast<void *>(&ShowPC), R(MEMBASEREG), R(JITBASEREG));
} else if (jitbaseCtxDisp != 0) {
LEA(64, SCRATCH1, MDisp(CTXREG, jitbaseCtxDisp));
ABI_CallFunctionAA(reinterpret_cast<void *>(&ShowPC), R(MEMBASEREG), R(SCRATCH1));
} else {
ABI_CallFunctionAC(reinterpret_cast<void *>(&ShowPC), R(MEMBASEREG), (u32)jitbase);
}
#else
ABI_CallFunctionCC(reinterpret_cast<void *>(&ShowPC), (u32)Memory::base, (u32)GetBasePtr());
#endif
}
MovFromPC(SCRATCH1);
WriteDebugPC(SCRATCH1);
#ifdef MASKED_PSP_MEMORY
AND(32, R(SCRATCH1), Imm32(Memory::MEMVIEW32_MASK));
#endif
hooks_.dispatchFetch = GetCodePtr();
#if PPSSPP_ARCH(X86)
_assert_msg_( Memory::base != 0, "Memory base bogus");
MOV(32, R(SCRATCH1), MDisp(SCRATCH1, (u32)Memory::base));
#elif PPSSPP_ARCH(AMD64)
MOV(32, R(SCRATCH1), MComplex(MEMBASEREG, SCRATCH1, SCALE_1, 0));
#endif
_assert_msg_(MIPS_JITBLOCK_MASK == 0xFF000000, "Hardcoded assumption of emuhack mask");
if (cpu_info.bBMI2) {
RORX(32, EDX, R(SCRATCH1), 24);
CMP(8, R(EDX), Imm8(MIPS_EMUHACK_OPCODE >> 24));
} else {
MOV(32, R(EDX), R(SCRATCH1));
SHR(32, R(EDX), Imm8(24));
CMP(32, R(EDX), Imm8(MIPS_EMUHACK_OPCODE >> 24));
}
FixupBranch needsCompile = J_CC(CC_NE);
#if PPSSPP_ARCH(X86)
LEA(32, SCRATCH1, MDisp(SCRATCH1, (u32)GetBasePtr() - MIPS_EMUHACK_OPCODE));
#elif PPSSPP_ARCH(AMD64)
if (jitbaseInR15) {
ADD(64, R(SCRATCH1), R(JITBASEREG));
} else if (jitbaseCtxDisp) {
LEA(64, SCRATCH1, MComplex(CTXREG, SCRATCH1, SCALE_1, jitbaseCtxDisp));
} else {
LEA(64, SCRATCH1, MDisp(SCRATCH1, (s32)jitbase));
}
#endif
JMPptr(R(SCRATCH1));
SetJumpTarget(needsCompile);
RestoreRoundingMode(true);
WriteDebugProfilerStatus(IRProfilerStatus::COMPILING);
ABI_CallFunction(&MIPSComp::JitAt);
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
ApplyRoundingMode(true);
JMP(dispatcherNoCheck_, true);
SetJumpTarget(bail);
if (RipAccessible((const void *)&coreState)) {
CMP(32, M(&coreState), Imm8(0));
} else {
MOV(PTRBITS, R(RAX), ImmPtr((const void *)&coreState));
CMP(32, MatR(RAX), Imm8(0));
}
J_CC(CC_Z, outerLoop_, true);
const uint8_t *quitLoop = GetCodePtr();
SetJumpTarget(badCoreState);
WriteDebugProfilerStatus(IRProfilerStatus::NOT_RUNNING);
SaveStaticRegisters();
RestoreRoundingMode(true);
ABI_PopAllCalleeSavedRegsAndAdjustStack();
RET();
hooks_.crashHandler = GetCodePtr();
if (RipAccessible((const void *)&coreState)) {
MOV(32, M(&coreState), Imm32(CORE_RUNTIME_ERROR));
} else {
MOV(PTRBITS, R(RAX), ImmPtr((const void *)&coreState));
MOV(32, MatR(RAX), Imm32(CORE_RUNTIME_ERROR));
}
JMP(quitLoop, true);
if (enableDisasm) {
#if PPSSPP_ARCH(AMD64)
std::vector<std::string> lines = DisassembleX86(disasmStart, (int)(GetCodePtr() - disasmStart));
for (const auto &s : lines) {
INFO_LOG(Log::JIT, "%s", s.c_str());
}
#endif
}
AlignCodePage();
jitStartOffset_ = (int)(GetCodePtr() - start);
EndWrite();
}
}
#endif