mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-09-06 20:55:23 +02:00
Merge pull request #18226 from unknownbrackets/x86-ir-breakpoints
x86jit: Improve memory breakpoint speed
This commit is contained in:
@@ -20,9 +20,11 @@
|
||||
|
||||
#include "Common/Profiler/Profiler.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/Debugger/Breakpoints.h"
|
||||
#include "Core/HLE/HLE.h"
|
||||
#include "Core/HLE/ReplaceTables.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/MIPS/MIPSAnalyst.h"
|
||||
#include "Core/MIPS/IR/IRInterpreter.h"
|
||||
#include "Core/MIPS/x86/X64IRJit.h"
|
||||
#include "Core/MIPS/x86/X64IRRegCache.h"
|
||||
@@ -88,6 +90,7 @@ void X64JitBackend::CompIR_Basic(IRInst inst) {
|
||||
break;
|
||||
|
||||
case IROp::SetPCConst:
|
||||
lastConstPC_ = inst.constant;
|
||||
MOV(32, R(SCRATCH1), Imm32(inst.constant));
|
||||
MovToPC(SCRATCH1);
|
||||
break;
|
||||
@@ -111,17 +114,80 @@ void X64JitBackend::CompIR_Breakpoint(IRInst inst) {
|
||||
break;
|
||||
|
||||
case IROp::MemoryCheck:
|
||||
{
|
||||
X64Reg addrBase = regs_.MapGPR(inst.src1);
|
||||
FlushAll();
|
||||
LEA(32, addrBase, MDisp(addrBase, inst.constant));
|
||||
MovFromPC(SCRATCH1);
|
||||
LEA(32, SCRATCH1, MDisp(SCRATCH1, inst.dest));
|
||||
ABI_CallFunctionRR((const void *)&IRRunMemCheck, SCRATCH1, addrBase);
|
||||
TEST(32, R(EAX), R(EAX));
|
||||
J_CC(CC_NZ, dispatcherCheckCoreState_, true);
|
||||
if (regs_.IsGPRImm(inst.src1)) {
|
||||
uint32_t iaddr = regs_.GetGPRImm(inst.src1) + inst.constant;
|
||||
uint32_t checkedPC = lastConstPC_ + inst.dest;
|
||||
int size = MIPSAnalyst::OpMemoryAccessSize(checkedPC);
|
||||
if (size == 0) {
|
||||
checkedPC += 4;
|
||||
size = MIPSAnalyst::OpMemoryAccessSize(checkedPC);
|
||||
}
|
||||
bool isWrite = MIPSAnalyst::IsOpMemoryWrite(checkedPC);
|
||||
|
||||
MemCheck check;
|
||||
if (CBreakPoints::GetMemCheckInRange(iaddr, size, &check)) {
|
||||
if (!(check.cond & MEMCHECK_READ) && !isWrite)
|
||||
break;
|
||||
if (!(check.cond & (MEMCHECK_WRITE | MEMCHECK_WRITE_ONCHANGE)) && isWrite)
|
||||
break;
|
||||
|
||||
// We need to flush, or conditions and log expressions will see old register values.
|
||||
FlushAll();
|
||||
|
||||
ABI_CallFunctionCC((const void *)&IRRunMemCheck, checkedPC, iaddr);
|
||||
TEST(32, R(EAX), R(EAX));
|
||||
J_CC(CC_NZ, dispatcherCheckCoreState_, true);
|
||||
}
|
||||
} else {
|
||||
uint32_t checkedPC = lastConstPC_ + inst.dest;
|
||||
int size = MIPSAnalyst::OpMemoryAccessSize(checkedPC);
|
||||
if (size == 0) {
|
||||
checkedPC += 4;
|
||||
size = MIPSAnalyst::OpMemoryAccessSize(checkedPC);
|
||||
}
|
||||
bool isWrite = MIPSAnalyst::IsOpMemoryWrite(checkedPC);
|
||||
|
||||
const auto memchecks = CBreakPoints::GetMemCheckRanges(isWrite);
|
||||
// We can trivially skip if there are no checks for this type (i.e. read vs write.)
|
||||
if (memchecks.empty())
|
||||
break;
|
||||
|
||||
X64Reg addrBase = regs_.MapGPR(inst.src1);
|
||||
LEA(32, SCRATCH1, MDisp(addrBase, inst.constant));
|
||||
|
||||
// We need to flush, or conditions and log expressions will see old register values.
|
||||
FlushAll();
|
||||
|
||||
std::vector<FixupBranch> hitChecks;
|
||||
for (auto it : memchecks) {
|
||||
if (it.end != 0) {
|
||||
CMP(32, R(SCRATCH1), Imm32(it.start - size));
|
||||
FixupBranch skipNext = J_CC(CC_BE);
|
||||
|
||||
CMP(32, R(SCRATCH1), Imm32(it.end));
|
||||
hitChecks.push_back(J_CC(CC_B, true));
|
||||
|
||||
SetJumpTarget(skipNext);
|
||||
} else {
|
||||
CMP(32, R(SCRATCH1), Imm32(it.start));
|
||||
hitChecks.push_back(J_CC(CC_E, true));
|
||||
}
|
||||
}
|
||||
|
||||
FixupBranch noHits = J(true);
|
||||
|
||||
// Okay, now land any hit here.
|
||||
for (auto &fixup : hitChecks)
|
||||
SetJumpTarget(fixup);
|
||||
hitChecks.clear();
|
||||
|
||||
ABI_CallFunctionAA((const void *)&IRRunMemCheck, Imm32(checkedPC), R(SCRATCH1));
|
||||
TEST(32, R(EAX), R(EAX));
|
||||
J_CC(CC_NZ, dispatcherCheckCoreState_, true);
|
||||
|
||||
SetJumpTarget(noHits);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
@@ -271,23 +337,6 @@ void X64JitBackend::CompIR_Transfer(IRInst inst) {
|
||||
}
|
||||
}
|
||||
|
||||
int ReportBadAddress(uint32_t addr, uint32_t alignment, uint32_t isWrite) {
|
||||
const auto toss = [&](MemoryExceptionType t) {
|
||||
Core_MemoryException(addr, alignment, currentMIPS->pc, t);
|
||||
return coreState != CORE_RUNNING ? 1 : 0;
|
||||
};
|
||||
|
||||
if (!Memory::IsValidRange(addr, alignment)) {
|
||||
MemoryExceptionType t = isWrite == 1 ? MemoryExceptionType::WRITE_WORD : MemoryExceptionType::READ_WORD;
|
||||
if (alignment > 4)
|
||||
t = isWrite ? MemoryExceptionType::WRITE_BLOCK : MemoryExceptionType::READ_BLOCK;
|
||||
return toss(t);
|
||||
} else if (alignment > 1 && (addr & (alignment - 1)) != 0) {
|
||||
return toss(MemoryExceptionType::ALIGNMENT);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void X64JitBackend::CompIR_ValidateAddress(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
@@ -315,10 +364,17 @@ void X64JitBackend::CompIR_ValidateAddress(IRInst inst) {
|
||||
break;
|
||||
}
|
||||
|
||||
// This is unfortunate...
|
||||
FlushAll();
|
||||
regs_.Map(inst);
|
||||
LEA(PTRBITS, SCRATCH1, MDisp(regs_.RX(inst.src1), inst.constant));
|
||||
if (regs_.IsGPRMappedAsPointer(inst.src1)) {
|
||||
LEA(PTRBITS, SCRATCH1, MDisp(regs_.RXPtr(inst.src1), inst.constant));
|
||||
#if defined(MASKED_PSP_MEMORY)
|
||||
SUB(PTRBITS, R(SCRATCH1), ImmPtr(Memory::base));
|
||||
#else
|
||||
SUB(PTRBITS, R(SCRATCH1), R(MEMBASEREG));
|
||||
#endif
|
||||
} else {
|
||||
regs_.Map(inst);
|
||||
LEA(PTRBITS, SCRATCH1, MDisp(regs_.RX(inst.src1), inst.constant));
|
||||
}
|
||||
AND(32, R(SCRATCH1), Imm32(0x3FFFFFFF));
|
||||
|
||||
std::vector<FixupBranch> validJumps;
|
||||
@@ -332,25 +388,32 @@ void X64JitBackend::CompIR_ValidateAddress(IRInst inst) {
|
||||
CMP(32, R(SCRATCH1), Imm32(PSP_GetUserMemoryEnd() - alignment));
|
||||
FixupBranch tooHighRAM = J_CC(CC_A);
|
||||
CMP(32, R(SCRATCH1), Imm32(PSP_GetKernelMemoryBase()));
|
||||
validJumps.push_back(J_CC(CC_AE));
|
||||
validJumps.push_back(J_CC(CC_AE, true));
|
||||
|
||||
CMP(32, R(SCRATCH1), Imm32(PSP_GetVidMemEnd() - alignment));
|
||||
FixupBranch tooHighVid = J_CC(CC_A);
|
||||
CMP(32, R(SCRATCH1), Imm32(PSP_GetVidMemBase()));
|
||||
validJumps.push_back(J_CC(CC_AE));
|
||||
validJumps.push_back(J_CC(CC_AE, true));
|
||||
|
||||
CMP(32, R(SCRATCH1), Imm32(PSP_GetScratchpadMemoryEnd() - alignment));
|
||||
FixupBranch tooHighScratch = J_CC(CC_A);
|
||||
CMP(32, R(SCRATCH1), Imm32(PSP_GetScratchpadMemoryBase()));
|
||||
validJumps.push_back(J_CC(CC_AE));
|
||||
validJumps.push_back(J_CC(CC_AE, true));
|
||||
|
||||
if (alignment != 1)
|
||||
SetJumpTarget(unaligned);
|
||||
SetJumpTarget(tooHighRAM);
|
||||
SetJumpTarget(tooHighVid);
|
||||
SetJumpTarget(tooHighScratch);
|
||||
|
||||
// If we got here, something unusual and bad happened, so we'll always go back to the dispatcher.
|
||||
// Because of that, we can avoid flushing outside this case.
|
||||
auto regsCopy = regs_;
|
||||
regsCopy.FlushAll();
|
||||
|
||||
// Ignores the return value, always returns to the dispatcher.
|
||||
// Otherwise would need a thunk to restore regs.
|
||||
ABI_CallFunctionACC((const void *)&ReportBadAddress, R(SCRATCH1), alignment, isWrite);
|
||||
TEST(32, R(EAX), R(EAX));
|
||||
validJumps.push_back(J_CC(CC_Z));
|
||||
JMP(dispatcherCheckCoreState_, true);
|
||||
|
||||
for (FixupBranch &b : validJumps)
|
||||
|
||||
Reference in New Issue
Block a user