mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
Merge pull request #21265 from hrydgard/delete-proxy-blocks
JIT: Remove the proxy block concept
This commit is contained in:
@@ -1611,7 +1611,7 @@ static const ReplacementTableEntry entries[] = {
|
||||
{ "strncpy", &Replace_strncpy, 0, REPFLAG_DISABLED },
|
||||
{ "strcmp", &Replace_strcmp, 0, REPFLAG_DISABLED },
|
||||
{ "strncmp", &Replace_strncmp, 0, REPFLAG_DISABLED },
|
||||
{ "fabsf", &Replace_fabsf, JITFUNC(Replace_fabsf), REPFLAG_ALLOWINLINE | REPFLAG_DISABLED },
|
||||
{ "fabsf", &Replace_fabsf, JITFUNC(Replace_fabsf), REPFLAG_DISABLED },
|
||||
{ "dl_write_matrix", &Replace_dl_write_matrix, 0, REPFLAG_DISABLED }, // &MIPSComp::Jit::Replace_dl_write_matrix, REPFLAG_DISABLED },
|
||||
{ "dl_write_matrix_2", &Replace_dl_write_matrix, 0, REPFLAG_DISABLED },
|
||||
{ "gta_dl_write_matrix", &Replace_gta_dl_write_matrix, 0, REPFLAG_DISABLED },
|
||||
@@ -1873,35 +1873,3 @@ bool GetReplacedOpAt(u32 address, u32 *op) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CanReplaceJalTo(u32 dest, const ReplacementTableEntry **entry, u32 *funcSize) {
|
||||
MIPSOpcode op(Memory::Read_Opcode_JIT(dest));
|
||||
if (!MIPS_IS_REPLACEMENT(op.encoding))
|
||||
return false;
|
||||
|
||||
// Make sure we don't replace if there are any breakpoints inside.
|
||||
*funcSize = g_symbolMap->GetFunctionSize(dest);
|
||||
if (*funcSize == SymbolMap::INVALID_ADDRESS) {
|
||||
if (g_breakpoints.IsAddressBreakPoint(dest)) {
|
||||
return false;
|
||||
}
|
||||
*funcSize = (u32)sizeof(u32);
|
||||
} else {
|
||||
if (g_breakpoints.RangeContainsBreakPoint(dest, *funcSize)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int index = op.encoding & MIPS_EMUHACK_VALUE_MASK;
|
||||
*entry = GetReplacementFunc(index);
|
||||
if (!*entry) {
|
||||
ERROR_LOG(Log::HLE, "ReplaceJalTo: Invalid replacement op %08x at %08x", op.encoding, dest);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((*entry)->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT | REPFLAG_DISABLED | REPFLAG_SLICED)) {
|
||||
// If it's a hook, we can't replace the jal, we have to go inside the func.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
typedef int (* ReplaceFunc)();
|
||||
|
||||
enum {
|
||||
REPFLAG_ALLOWINLINE = 0x01,
|
||||
// Used to keep things around but disable them.
|
||||
REPFLAG_DISABLED = 0x02,
|
||||
// Note that this will re-execute in a function that loops at start.
|
||||
@@ -77,7 +76,6 @@ void WriteReplaceInstructions(u32 address, u64 hash, int size);
|
||||
void RestoreReplacedInstruction(u32 address);
|
||||
void RestoreReplacedInstructions(u32 startAddr, u32 endAddr);
|
||||
bool GetReplacedOpAt(u32 address, u32 *op);
|
||||
bool CanReplaceJalTo(u32 dest, const ReplacementTableEntry **entry, u32 *funcSize);
|
||||
|
||||
// For savestates. If you call SaveAndClearReplacements(), you must call RestoreSavedReplacements().
|
||||
std::map<u32, u32> SaveAndClearReplacements();
|
||||
|
||||
@@ -98,24 +98,6 @@ void ArmJit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
|
||||
immBranchTaken = !immBranchNotTaken;
|
||||
}
|
||||
|
||||
if (jo.immBranches && immBranch && js.numInstructions < jo.continueMaxInstructions) {
|
||||
if (!immBranchTaken) {
|
||||
// Skip the delay slot if likely, otherwise it'll be the next instruction.
|
||||
if (likely)
|
||||
js.compilerPC += 4;
|
||||
return;
|
||||
}
|
||||
|
||||
// Branch taken. Always compile the delay slot, and then go to dest.
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
AddContinuedBlock(targetAddr);
|
||||
// Account for the increment in the loop.
|
||||
js.compilerPC = targetAddr - 4;
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
return;
|
||||
}
|
||||
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
|
||||
|
||||
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
|
||||
@@ -225,29 +207,6 @@ void ArmJit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool like
|
||||
immBranchTaken = !immBranchNotTaken;
|
||||
}
|
||||
|
||||
if (jo.immBranches && immBranch && js.numInstructions < jo.continueMaxInstructions) {
|
||||
if (!immBranchTaken) {
|
||||
// Skip the delay slot if likely, otherwise it'll be the next instruction.
|
||||
if (andLink)
|
||||
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
|
||||
if (likely)
|
||||
js.compilerPC += 4;
|
||||
return;
|
||||
}
|
||||
|
||||
// Branch taken. Always compile the delay slot, and then go to dest.
|
||||
if (andLink)
|
||||
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
|
||||
AddContinuedBlock(targetAddr);
|
||||
// Account for the increment in the loop.
|
||||
js.compilerPC = targetAddr - 4;
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
return;
|
||||
}
|
||||
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
|
||||
|
||||
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
|
||||
@@ -530,32 +489,13 @@ void ArmJit::Comp_Jump(MIPSOpcode op) {
|
||||
switch (op >> 26) {
|
||||
case 2: //j
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
if (jo.continueJumps && js.numInstructions < jo.continueMaxInstructions) {
|
||||
AddContinuedBlock(targetAddr);
|
||||
// Account for the increment in the loop.
|
||||
js.compilerPC = targetAddr - 4;
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
return;
|
||||
}
|
||||
FlushAll();
|
||||
WriteExit(targetAddr, js.nextExit++);
|
||||
break;
|
||||
|
||||
case 3: //jal
|
||||
if (ReplaceJalTo(targetAddr))
|
||||
return;
|
||||
|
||||
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
if (jo.continueJumps && js.numInstructions < jo.continueMaxInstructions) {
|
||||
AddContinuedBlock(targetAddr);
|
||||
// Account for the increment in the loop.
|
||||
js.compilerPC = targetAddr - 4;
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
return;
|
||||
}
|
||||
FlushAll();
|
||||
WriteExit(targetAddr, js.nextExit++);
|
||||
break;
|
||||
@@ -609,15 +549,6 @@ void ArmJit::Comp_JumpReg(MIPSOpcode op)
|
||||
gpr.DiscardR(MIPS_REG_T9);
|
||||
}
|
||||
|
||||
if (jo.continueJumps && gpr.IsImm(rs) && js.numInstructions < jo.continueMaxInstructions) {
|
||||
AddContinuedBlock(gpr.GetImm(rs));
|
||||
// Account for the increment in the loop.
|
||||
js.compilerPC = gpr.GetImm(rs) - 4;
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
return;
|
||||
}
|
||||
|
||||
gpr.MapReg(rs);
|
||||
destReg = gpr.R(rs); // Safe because FlushAll doesn't change any regs
|
||||
FlushAll();
|
||||
|
||||
@@ -405,24 +405,7 @@ void ArmJit::DoJit(u32 em_address, JitBlock *b) {
|
||||
// Don't forget to zap the newly written instructions in the instruction cache!
|
||||
FlushIcache();
|
||||
|
||||
if (js.lastContinuedPC == 0)
|
||||
b->originalSize = js.numInstructions;
|
||||
else
|
||||
{
|
||||
// We continued at least once. Add the last proxy and set the originalSize correctly.
|
||||
blocks.CreateProxyBlock(js.blockStart, js.lastContinuedPC, (GetCompilerPC() - js.lastContinuedPC) / sizeof(u32), GetCodePtr());
|
||||
b->originalSize = js.initialBlockSize;
|
||||
}
|
||||
}
|
||||
|
||||
void ArmJit::AddContinuedBlock(u32 dest)
|
||||
{
|
||||
// The first block is the root block. When we continue, we create proxy blocks after that.
|
||||
if (js.lastContinuedPC == 0)
|
||||
js.initialBlockSize = js.numInstructions;
|
||||
else
|
||||
blocks.CreateProxyBlock(js.blockStart, js.lastContinuedPC, (GetCompilerPC() - js.lastContinuedPC) / sizeof(u32), GetCodePtr());
|
||||
js.lastContinuedPC = dest;
|
||||
b->originalSize = js.numInstructions;
|
||||
}
|
||||
|
||||
bool ArmJit::DescribeCodePtr(const u8 *ptr, std::string &name)
|
||||
@@ -481,58 +464,6 @@ void ArmJit::UnlinkBlock(u8 *checkedEntry, u32 originalAddress) {
|
||||
}
|
||||
}
|
||||
|
||||
bool ArmJit::ReplaceJalTo(u32 dest) {
|
||||
const ReplacementTableEntry *entry = nullptr;
|
||||
u32 funcSize = 0;
|
||||
if (!CanReplaceJalTo(dest, &entry, &funcSize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Warning - this might be bad if the code at the destination changes...
|
||||
if (entry->flags & REPFLAG_ALLOWINLINE) {
|
||||
// Jackpot! Just do it, no flushing. The code will be entirely inlined.
|
||||
|
||||
// First, compile the delay slot. It's unconditional so no issues.
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
// Technically, we should write the unused return address to RA, but meh.
|
||||
MIPSReplaceFunc repl = entry->jitReplaceFunc;
|
||||
int cycles = (this->*repl)();
|
||||
js.downcountAmount += cycles;
|
||||
} else {
|
||||
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
FlushAll();
|
||||
SaveDowncount();
|
||||
RestoreRoundingMode();
|
||||
|
||||
if (BLInRange((const void *)(entry->replaceFunc))) {
|
||||
BL((const void *)(entry->replaceFunc));
|
||||
} else {
|
||||
MOVI2R(R0, (uintptr_t)entry->replaceFunc);
|
||||
BL(R0);
|
||||
}
|
||||
ApplyRoundingMode();
|
||||
RestoreDowncount();
|
||||
|
||||
WriteDownCountR(R0);
|
||||
}
|
||||
|
||||
js.compilerPC += 4;
|
||||
// No writing exits, keep going!
|
||||
|
||||
if (g_breakpoints.HasMemChecks()) {
|
||||
// We could modify coreState, so we need to write PC and check.
|
||||
// Otherwise, PC may end up on the jal. We add 4 to skip the delay slot.
|
||||
FlushAll();
|
||||
WriteExit(GetCompilerPC() + 4, js.nextExit++);
|
||||
js.compiling = false;
|
||||
}
|
||||
|
||||
// Add a trigger so that if the inlined code changes, we invalidate this block.
|
||||
blocks.CreateProxyBlock(js.blockStart, dest, funcSize / sizeof(u32), GetCodePtr());
|
||||
return true;
|
||||
}
|
||||
|
||||
void ArmJit::Comp_ReplacementFunc(MIPSOpcode op)
|
||||
{
|
||||
// We get here if we execute the first instruction of a replaced function. This means
|
||||
|
||||
@@ -202,7 +202,6 @@ private:
|
||||
u32 GetCompilerPC();
|
||||
void CompileDelaySlot(int flags);
|
||||
void EatInstruction(MIPSOpcode op);
|
||||
void AddContinuedBlock(u32 dest);
|
||||
MIPSOpcode GetOffsetInstruction(int offset);
|
||||
|
||||
void WriteDownCount(int offset = 0);
|
||||
@@ -213,8 +212,6 @@ private:
|
||||
void MovFromPC(ArmGen::ARMReg r);
|
||||
void MovToPC(ArmGen::ARMReg r);
|
||||
|
||||
bool ReplaceJalTo(u32 dest);
|
||||
|
||||
void SaveDowncount();
|
||||
void RestoreDowncount();
|
||||
|
||||
|
||||
@@ -98,24 +98,6 @@ void Arm64Jit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)
|
||||
immBranchTaken = !immBranchNotTaken;
|
||||
}
|
||||
|
||||
if (jo.immBranches && immBranch && js.numInstructions < jo.continueMaxInstructions) {
|
||||
if (!immBranchTaken) {
|
||||
// Skip the delay slot if likely, otherwise it'll be the next instruction.
|
||||
if (likely)
|
||||
js.compilerPC += 4;
|
||||
return;
|
||||
}
|
||||
|
||||
// Branch taken. Always compile the delay slot, and then go to dest.
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
AddContinuedBlock(targetAddr);
|
||||
// Account for the increment in the loop.
|
||||
js.compilerPC = targetAddr - 4;
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
return;
|
||||
}
|
||||
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
|
||||
|
||||
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
|
||||
@@ -242,29 +224,6 @@ void Arm64Jit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool li
|
||||
immBranchTaken = !immBranchNotTaken;
|
||||
}
|
||||
|
||||
if (jo.immBranches && immBranch && js.numInstructions < jo.continueMaxInstructions) {
|
||||
if (!immBranchTaken) {
|
||||
// Skip the delay slot if likely, otherwise it'll be the next instruction.
|
||||
if (andLink)
|
||||
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
|
||||
if (likely)
|
||||
js.compilerPC += 4;
|
||||
return;
|
||||
}
|
||||
|
||||
// Branch taken. Always compile the delay slot, and then go to dest.
|
||||
if (andLink)
|
||||
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
|
||||
AddContinuedBlock(targetAddr);
|
||||
// Account for the increment in the loop.
|
||||
js.compilerPC = targetAddr - 4;
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
return;
|
||||
}
|
||||
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
|
||||
|
||||
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
|
||||
@@ -545,32 +504,13 @@ void Arm64Jit::Comp_Jump(MIPSOpcode op) {
|
||||
switch (op >> 26) {
|
||||
case 2: //j
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
if (jo.continueJumps && js.numInstructions < jo.continueMaxInstructions) {
|
||||
AddContinuedBlock(targetAddr);
|
||||
// Account for the increment in the loop.
|
||||
js.compilerPC = targetAddr - 4;
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
return;
|
||||
}
|
||||
FlushAll();
|
||||
WriteExit(targetAddr, js.nextExit++);
|
||||
break;
|
||||
|
||||
case 3: //jal
|
||||
if (ReplaceJalTo(targetAddr))
|
||||
return;
|
||||
|
||||
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
if (jo.continueJumps && js.numInstructions < jo.continueMaxInstructions) {
|
||||
AddContinuedBlock(targetAddr);
|
||||
// Account for the increment in the loop.
|
||||
js.compilerPC = targetAddr - 4;
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
return;
|
||||
}
|
||||
FlushAll();
|
||||
WriteExit(targetAddr, js.nextExit++);
|
||||
break;
|
||||
@@ -624,15 +564,6 @@ void Arm64Jit::Comp_JumpReg(MIPSOpcode op)
|
||||
gpr.DiscardR(MIPS_REG_T9);
|
||||
}
|
||||
|
||||
if (jo.continueJumps && gpr.IsImm(rs) && js.numInstructions < jo.continueMaxInstructions) {
|
||||
AddContinuedBlock(gpr.GetImm(rs));
|
||||
// Account for the increment in the loop.
|
||||
js.compilerPC = gpr.GetImm(rs) - 4;
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
return;
|
||||
}
|
||||
|
||||
gpr.MapReg(rs);
|
||||
destReg = gpr.R(rs); // Safe because FlushAll doesn't change any regs
|
||||
FlushAll();
|
||||
|
||||
@@ -394,22 +394,7 @@ void Arm64Jit::DoJit(u32 em_address, JitBlock *b) {
|
||||
if (dontLogBlocks > 0)
|
||||
dontLogBlocks--;
|
||||
|
||||
if (js.lastContinuedPC == 0) {
|
||||
b->originalSize = js.numInstructions;
|
||||
} else {
|
||||
// We continued at least once. Add the last proxy and set the originalSize correctly.
|
||||
blocks.CreateProxyBlock(js.blockStart, js.lastContinuedPC, (GetCompilerPC() - js.lastContinuedPC) / sizeof(u32), GetCodePtr());
|
||||
b->originalSize = js.initialBlockSize;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64Jit::AddContinuedBlock(u32 dest) {
|
||||
// The first block is the root block. When we continue, we create proxy blocks after that.
|
||||
if (js.lastContinuedPC == 0)
|
||||
js.initialBlockSize = js.numInstructions;
|
||||
else
|
||||
blocks.CreateProxyBlock(js.blockStart, js.lastContinuedPC, (GetCompilerPC() - js.lastContinuedPC) / sizeof(u32), GetCodePtr());
|
||||
js.lastContinuedPC = dest;
|
||||
b->originalSize = js.numInstructions;
|
||||
}
|
||||
|
||||
bool Arm64Jit::DescribeCodePtr(const u8 *ptr, std::string &name) {
|
||||
@@ -493,52 +478,6 @@ void Arm64Jit::UnlinkBlock(u8 *checkedEntry, u32 originalAddress) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Arm64Jit::ReplaceJalTo(u32 dest) {
|
||||
#if PPSSPP_ARCH(ARM64)
|
||||
const ReplacementTableEntry *entry = nullptr;
|
||||
u32 funcSize = 0;
|
||||
if (!CanReplaceJalTo(dest, &entry, &funcSize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Warning - this might be bad if the code at the destination changes...
|
||||
if (entry->flags & REPFLAG_ALLOWINLINE) {
|
||||
// Jackpot! Just do it, no flushing. The code will be entirely inlined.
|
||||
// First, compile the delay slot. It's unconditional so no issues.
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
// Technically, we should write the unused return address to RA, but meh.
|
||||
MIPSReplaceFunc repl = entry->jitReplaceFunc;
|
||||
int cycles = (this->*repl)();
|
||||
js.downcountAmount += cycles;
|
||||
} else {
|
||||
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
FlushAll();
|
||||
SaveStaticRegisters();
|
||||
RestoreRoundingMode();
|
||||
QuickCallFunction(SCRATCH1_64, (const void *)(entry->replaceFunc));
|
||||
ApplyRoundingMode();
|
||||
LoadStaticRegisters();
|
||||
WriteDownCountR(W0); // W0 is the return value from entry->replaceFunc. Neither LoadStaticRegisters nor ApplyRoundingMode can trash it.
|
||||
}
|
||||
|
||||
js.compilerPC += 4;
|
||||
// No writing exits, keep going!
|
||||
|
||||
if (g_breakpoints.HasMemChecks()) {
|
||||
// We could modify coreState, so we need to write PC and check.
|
||||
// Otherwise, PC may end up on the jal. We add 4 to skip the delay slot.
|
||||
FlushAll();
|
||||
WriteExit(GetCompilerPC() + 4, js.nextExit++);
|
||||
js.compiling = false;
|
||||
}
|
||||
|
||||
// Add a trigger so that if the inlined code changes, we invalidate this block.
|
||||
blocks.CreateProxyBlock(js.blockStart, dest, funcSize / sizeof(u32), GetCodePtr());
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void Arm64Jit::Comp_ReplacementFunc(MIPSOpcode op)
|
||||
{
|
||||
// We get here if we execute the first instruction of a replaced function. This means
|
||||
|
||||
@@ -201,7 +201,6 @@ private:
|
||||
u32 GetCompilerPC();
|
||||
void CompileDelaySlot(int flags);
|
||||
void EatInstruction(MIPSOpcode op);
|
||||
void AddContinuedBlock(u32 dest);
|
||||
MIPSOpcode GetOffsetInstruction(int offset);
|
||||
|
||||
void WriteDownCount(int offset = 0, bool updateFlags = true);
|
||||
@@ -212,8 +211,6 @@ private:
|
||||
void MovFromPC(Arm64Gen::ARM64Reg r);
|
||||
void MovToPC(Arm64Gen::ARM64Reg r);
|
||||
|
||||
bool ReplaceJalTo(u32 dest);
|
||||
|
||||
// Clobbers SCRATCH2.
|
||||
void SaveStaticRegisters();
|
||||
// Clobbers SCRATCH2.
|
||||
|
||||
@@ -243,7 +243,6 @@ void IRFrontend::DoJit(u32 em_address, std::vector<IRInst> &instructions, u32 &m
|
||||
js.cancel = false;
|
||||
js.blockStart = em_address;
|
||||
js.compilerPC = em_address;
|
||||
js.lastContinuedPC = 0;
|
||||
js.initialBlockSize = 0;
|
||||
js.nextExit = 0;
|
||||
js.downcountAmount = 0;
|
||||
|
||||
@@ -511,7 +511,7 @@ void IRBlockCache::ComputeStats(BlockCacheStats &bcStats) const {
|
||||
bcStats.avgBloat = totalBloat / (double)blocks_.size();
|
||||
}
|
||||
|
||||
int IRBlockCache::GetBlockNumberFromStartAddress(u32 em_address, bool realBlocksOnly) const {
|
||||
int IRBlockCache::GetBlockNumberFromStartAddress(u32 em_address) const {
|
||||
u32 page = AddressToPage(em_address);
|
||||
|
||||
const auto iter = byPage_.find(page);
|
||||
|
||||
@@ -185,7 +185,7 @@ public:
|
||||
#endif
|
||||
}
|
||||
void ComputeStats(BlockCacheStats &bcStats) const override;
|
||||
int GetBlockNumberFromStartAddress(u32 em_address, bool realBlocksOnly = true) const override;
|
||||
int GetBlockNumberFromStartAddress(u32 em_address) const override;
|
||||
|
||||
bool SupportsProfiling() const override {
|
||||
#ifdef IR_PROFILING
|
||||
|
||||
@@ -719,8 +719,8 @@ int IRNativeBlockCacheDebugInterface::GetNumBlocks() const {
|
||||
return irBlocks_.GetNumBlocks();
|
||||
}
|
||||
|
||||
int IRNativeBlockCacheDebugInterface::GetBlockNumberFromStartAddress(u32 em_address, bool realBlocksOnly) const {
|
||||
return irBlocks_.GetBlockNumberFromStartAddress(em_address, realBlocksOnly);
|
||||
int IRNativeBlockCacheDebugInterface::GetBlockNumberFromStartAddress(u32 em_address) const {
|
||||
return irBlocks_.GetBlockNumberFromStartAddress(em_address);
|
||||
}
|
||||
|
||||
JitBlockProfileStats IRNativeBlockCacheDebugInterface::GetBlockProfileStats(int blockNum) const {
|
||||
|
||||
@@ -163,7 +163,7 @@ public:
|
||||
IRNativeBlockCacheDebugInterface(const MIPSComp::IRBlockCache &irBlocks);
|
||||
void Init(const IRNativeBackend *backend);
|
||||
int GetNumBlocks() const override;
|
||||
int GetBlockNumberFromStartAddress(u32 em_address, bool realBlocksOnly = true) const override;
|
||||
int GetBlockNumberFromStartAddress(u32 em_address) const override;
|
||||
JitBlockDebugInfo GetBlockDebugInfo(int blockNum) const override;
|
||||
JitBlockMeta GetBlockMeta(int blockNum) const override;
|
||||
JitBlockProfileStats GetBlockProfileStats(int blockNum) const override;
|
||||
|
||||
@@ -77,8 +77,8 @@ JitBlockCache::~JitBlockCache() {
|
||||
}
|
||||
|
||||
bool JitBlockCache::IsFull() const {
|
||||
// Subtract some amount to safely leave space for some proxy blocks, which we don't check before we allocate (not ideal, but should be enough).
|
||||
return num_blocks_ >= MAX_NUM_BLOCKS - 512;
|
||||
// Subtract some amount to safely leave space for some proxy blocks (now obsolete, but let's still have some extra margin).
|
||||
return num_blocks_ >= MAX_NUM_BLOCKS - 64;
|
||||
}
|
||||
|
||||
void JitBlockCache::Init() {
|
||||
@@ -101,7 +101,6 @@ void JitBlockCache::Clear() {
|
||||
for (int i = 0; i < num_blocks_; i++) {
|
||||
DestroyBlock(i, DestroyType::CLEAR);
|
||||
}
|
||||
proxyBlockMap_.clear();
|
||||
links_to_.clear();
|
||||
num_blocks_ = 0;
|
||||
|
||||
@@ -120,22 +119,6 @@ int JitBlockCache::AllocateBlock(u32 startAddress) {
|
||||
|
||||
JitBlock &b = blocks_[num_blocks_];
|
||||
|
||||
b.proxyFor = 0;
|
||||
// If there's an existing pure proxy block at the address, we need to ditch it and create a new one,
|
||||
// taking over the proxied blocks.
|
||||
int num = GetBlockNumberFromStartAddress(startAddress, false);
|
||||
if (num >= 0) {
|
||||
if (blocks_[num].IsPureProxy()) {
|
||||
RemoveBlockMap(num);
|
||||
blocks_[num].invalid = true;
|
||||
b.proxyFor = new std::vector<u32>();
|
||||
*b.proxyFor = *blocks_[num].proxyFor;
|
||||
blocks_[num].proxyFor->clear();
|
||||
delete blocks_[num].proxyFor;
|
||||
blocks_[num].proxyFor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
b.invalid = false;
|
||||
b.originalAddress = startAddress;
|
||||
for (int i = 0; i < MAX_JIT_BLOCK_EXITS; ++i) {
|
||||
@@ -149,43 +132,6 @@ int JitBlockCache::AllocateBlock(u32 startAddress) {
|
||||
return num_blocks_ - 1;
|
||||
}
|
||||
|
||||
void JitBlockCache::CreateProxyBlock(u32 rootAddress, u32 startAddress, u32 size, const u8 *codePtr) {
|
||||
_assert_(num_blocks_ < MAX_NUM_BLOCKS);
|
||||
|
||||
// If there's an existing block at the startAddress, add rootAddress as a proxy root of that block
|
||||
// instead of creating a new block.
|
||||
int num = GetBlockNumberFromStartAddress(startAddress, false);
|
||||
if (num != -1) {
|
||||
DEBUG_LOG(Log::HLE, "Adding proxy root %08x to block at %08x", rootAddress, startAddress);
|
||||
if (!blocks_[num].proxyFor) {
|
||||
blocks_[num].proxyFor = new std::vector<u32>();
|
||||
}
|
||||
blocks_[num].proxyFor->push_back(rootAddress);
|
||||
}
|
||||
|
||||
JitBlock &b = blocks_[num_blocks_];
|
||||
b.invalid = false;
|
||||
b.originalAddress = startAddress;
|
||||
b.originalSize = size;
|
||||
for (int i = 0; i < MAX_JIT_BLOCK_EXITS; ++i) {
|
||||
b.exitAddress[i] = INVALID_EXIT;
|
||||
b.exitPtrs[i] = 0;
|
||||
b.linkStatus[i] = false;
|
||||
}
|
||||
b.exitAddress[0] = rootAddress;
|
||||
b.blockNum = num_blocks_;
|
||||
b.proxyFor = new std::vector<u32>();
|
||||
b.SetPureProxy(); // flag as pure proxy block.
|
||||
|
||||
// Make binary searches and stuff work ok
|
||||
b.normalEntry = codePtr;
|
||||
b.checkedEntry = codePtr;
|
||||
proxyBlockMap_.emplace(startAddress, num_blocks_);
|
||||
AddBlockMap(num_blocks_);
|
||||
|
||||
num_blocks_++; //commit the current block
|
||||
}
|
||||
|
||||
void JitBlockCache::AddBlockMap(int block_num) {
|
||||
const JitBlock &b = blocks_[block_num];
|
||||
// Convert the logical address to a physical address for the block map
|
||||
@@ -223,7 +169,7 @@ static void ExpandRange(std::pair<u32, u32> &range, u32 newStart, u32 newEnd) {
|
||||
|
||||
void JitBlockCache::FinalizeBlock(int block_num, bool block_link) {
|
||||
JitBlock &b = blocks_[block_num];
|
||||
_assert_msg_(Memory::IsValidAddress(b.originalAddress), "FinalizeBlock: Bad originalAddress %08x in block %d (b.num: %d) proxy: %s sz: %d", b.originalAddress, block_num, b.blockNum, b.proxyFor ? "y" : "n", b.codeSize);
|
||||
_assert_msg_(Memory::IsValidAddress(b.originalAddress), "FinalizeBlock: Bad originalAddress %08x in block %d (b.num: %d) sz: %d", b.originalAddress, block_num, b.blockNum, b.codeSize);
|
||||
|
||||
b.originalFirstOpcode = Memory::Read_Opcode_JIT(b.originalAddress);
|
||||
MIPSOpcode opcode = GetEmuHackOpForBlock(block_num);
|
||||
@@ -307,22 +253,13 @@ MIPSOpcode JitBlockCache::GetEmuHackOpForBlock(int blockNum) const {
|
||||
return MIPSOpcode(MIPS_EMUHACK_OPCODE | off);
|
||||
}
|
||||
|
||||
int JitBlockCache::GetBlockNumberFromStartAddress(u32 addr, bool realBlocksOnly) const {
|
||||
int JitBlockCache::GetBlockNumberFromStartAddress(u32 addr) const {
|
||||
if (!blocks_ || !Memory::IsValidAddress(addr))
|
||||
return -1;
|
||||
|
||||
MIPSOpcode inst = MIPSOpcode(Memory::Read_U32(addr));
|
||||
int bl = GetBlockNumberFromEmuHackOp(inst);
|
||||
if (bl < 0) {
|
||||
if (!realBlocksOnly) {
|
||||
// Wasn't an emu hack op, look through proxyBlockMap_.
|
||||
auto range = proxyBlockMap_.equal_range(addr);
|
||||
for (auto it = range.first; it != range.second; ++it) {
|
||||
const int blockIndex = it->second;
|
||||
if (blocks_[blockIndex].originalAddress == addr && !blocks_[blockIndex].proxyFor && !blocks_[blockIndex].invalid)
|
||||
return blockIndex;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -374,14 +311,10 @@ void JitBlockCache::LinkBlockExits(int i) {
|
||||
// This block is dead. Don't relink it.
|
||||
return;
|
||||
}
|
||||
if (b.IsPureProxy()) {
|
||||
// Pure proxies can't link, since they don't have code.
|
||||
return;
|
||||
}
|
||||
|
||||
for (int e = 0; e < MAX_JIT_BLOCK_EXITS; e++) {
|
||||
if (b.exitAddress[e] != INVALID_EXIT && !b.linkStatus[e]) {
|
||||
int destinationBlock = GetBlockNumberFromStartAddress(b.exitAddress[e], true);
|
||||
int destinationBlock = GetBlockNumberFromStartAddress(b.exitAddress[e]);
|
||||
if (destinationBlock == -1) {
|
||||
continue;
|
||||
}
|
||||
@@ -477,32 +410,6 @@ void JitBlockCache::DestroyBlock(int block_num, DestroyType type) {
|
||||
// No point it being in there anymore.
|
||||
RemoveBlockMap(block_num);
|
||||
|
||||
// Pure proxy blocks always point directly to a real block, there should be no chains of
|
||||
// proxy-only blocks pointing to proxy-only blocks.
|
||||
// Follow a block proxy chain.
|
||||
// Destroy the block that transitively has this as a proxy. Likely the root block once inlined
|
||||
// this block or its 'parent', so now that this block has changed, the root block must be destroyed.
|
||||
if (b->proxyFor) {
|
||||
for (size_t i = 0; i < b->proxyFor->size(); i++) {
|
||||
int proxied_blocknum = GetBlockNumberFromStartAddress((*b->proxyFor)[i], false);
|
||||
// If it was already cleared, we don't know which to destroy.
|
||||
if (proxied_blocknum != -1) {
|
||||
DestroyBlock(proxied_blocknum, type);
|
||||
}
|
||||
}
|
||||
b->proxyFor->clear();
|
||||
delete b->proxyFor;
|
||||
b->proxyFor = 0;
|
||||
}
|
||||
auto range = proxyBlockMap_.equal_range(b->originalAddress);
|
||||
for (auto it = range.first; it != range.second; ++it) {
|
||||
if (it->second == block_num) {
|
||||
// Found it. Delete and bail.
|
||||
proxyBlockMap_.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Handle the case when there's a proxy block and a regular JIT block at the same location.
|
||||
// In this case we probably "leak" the proxy block currently (no memory leak but it'll stay enabled).
|
||||
|
||||
@@ -513,9 +420,8 @@ void JitBlockCache::DestroyBlock(int block_num, DestroyType type) {
|
||||
}
|
||||
|
||||
b->invalid = true;
|
||||
if (!b->IsPureProxy()) {
|
||||
if (Memory::ReadUnchecked_U32(b->originalAddress) == GetEmuHackOpForBlock(block_num).encoding)
|
||||
Memory::Write_Opcode_JIT(b->originalAddress, b->originalFirstOpcode);
|
||||
if (Memory::ReadUnchecked_U32(b->originalAddress) == GetEmuHackOpForBlock(block_num).encoding) {
|
||||
Memory::Write_Opcode_JIT(b->originalAddress, b->originalFirstOpcode);
|
||||
}
|
||||
|
||||
// It's not safe to set normalEntry to 0 here, since we use a binary search
|
||||
@@ -523,11 +429,6 @@ void JitBlockCache::DestroyBlock(int block_num, DestroyType type) {
|
||||
|
||||
UnlinkBlock(block_num);
|
||||
|
||||
// Don't change the jit code when invalidating a pure proxy block.
|
||||
if (b->IsPureProxy()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (b->checkedEntry) {
|
||||
// We can skip this if we're clearing anyway, which cuts down on protect back and forth on WX exclusive.
|
||||
if (type != DestroyType::CLEAR) {
|
||||
@@ -579,7 +480,7 @@ void JitBlockCache::InvalidateChangedBlocks() {
|
||||
// The primary goal of this is to make sure block linking is cleared up.
|
||||
for (int block_num = 0; block_num < num_blocks_; ++block_num) {
|
||||
JitBlock &b = blocks_[block_num];
|
||||
if (b.invalid || b.IsPureProxy())
|
||||
if (b.invalid)
|
||||
continue;
|
||||
|
||||
bool changed = false;
|
||||
|
||||
@@ -76,18 +76,6 @@ struct JitBlock {
|
||||
|
||||
bool invalid;
|
||||
bool linkStatus[MAX_JIT_BLOCK_EXITS];
|
||||
|
||||
// By having a pointer, we avoid a constructor/destructor call being generated if unused,
|
||||
// thus avoiding dog slow performance in debug build.
|
||||
std::vector<u32> *proxyFor;
|
||||
|
||||
bool IsPureProxy() const {
|
||||
return originalFirstOpcode.encoding == 0x68FF0000;
|
||||
}
|
||||
void SetPureProxy() {
|
||||
// Magic number that won't be a real opcode.
|
||||
originalFirstOpcode.encoding = 0x68FF0000;
|
||||
}
|
||||
};
|
||||
|
||||
typedef void (*CompiledCode)();
|
||||
@@ -114,7 +102,7 @@ struct JitBlockMeta {
|
||||
class JitBlockCacheDebugInterface {
|
||||
public:
|
||||
virtual int GetNumBlocks() const = 0;
|
||||
virtual int GetBlockNumberFromStartAddress(u32 em_address, bool realBlocksOnly = true) const = 0;
|
||||
virtual int GetBlockNumberFromStartAddress(u32 em_address) const = 0;
|
||||
virtual JitBlockDebugInfo GetBlockDebugInfo(int blockNum) const = 0; // Expensive
|
||||
virtual JitBlockMeta GetBlockMeta(int blockNum) const = 0;
|
||||
virtual JitBlockProfileStats GetBlockProfileStats(int blockNum) const = 0;
|
||||
@@ -131,8 +119,6 @@ public:
|
||||
~JitBlockCache();
|
||||
|
||||
int AllocateBlock(u32 em_address);
|
||||
// When a proxy block is invalidated, the block located at the rootAddress is invalidated too.
|
||||
void CreateProxyBlock(u32 rootAddress, u32 startAddress, u32 size, const u8 *codePtr);
|
||||
void FinalizeBlock(int block_num, bool block_link);
|
||||
|
||||
void Clear();
|
||||
@@ -148,7 +134,7 @@ public:
|
||||
const JitBlock *GetBlock(int no) const { return &blocks_[no]; }
|
||||
|
||||
// Fast way to get a block. Only works on the first source-cpu instruction of a block.
|
||||
int GetBlockNumberFromStartAddress(u32 em_address, bool realBlocksOnly = true) const override;
|
||||
int GetBlockNumberFromStartAddress(u32 em_address) const override;
|
||||
|
||||
// slower, but can get numbers from within blocks, not just the first instruction.
|
||||
// WARNING! WILL NOT WORK WITH JIT INLINING ENABLED (not yet a feature but will be soon)
|
||||
@@ -210,7 +196,6 @@ private:
|
||||
|
||||
CodeBlockCommon *codeBlock_;
|
||||
JitBlock *blocks_ = nullptr;
|
||||
std::unordered_multimap<u32, int> proxyBlockMap_;
|
||||
|
||||
int num_blocks_ = 0;
|
||||
std::unordered_multimap<u32, int> links_to_;
|
||||
@@ -222,7 +207,7 @@ private:
|
||||
JITBLOCK_RANGE_RAMTOP = 2,
|
||||
JITBLOCK_RANGE_COUNT = 3,
|
||||
};
|
||||
std::pair<u32, u32> blockMemRanges_[3];
|
||||
std::pair<u32, u32> blockMemRanges_[JITBLOCK_RANGE_COUNT];
|
||||
|
||||
enum {
|
||||
// Where does this number come from?
|
||||
|
||||
@@ -28,7 +28,6 @@ void JitState::Begin(JitBlock *block) {
|
||||
cancel = false;
|
||||
blockStart = block->originalAddress;
|
||||
compilerPC = block->originalAddress;
|
||||
lastContinuedPC = 0;
|
||||
initialBlockSize = 0;
|
||||
nextExit = 0;
|
||||
downcountAmount = 0;
|
||||
@@ -65,9 +64,6 @@ void JitState::Begin(JitBlock *block) {
|
||||
#else
|
||||
enableBlocklink = !Disabled(JitDisable::BLOCKLINK);
|
||||
#endif
|
||||
immBranches = false;
|
||||
continueJumps = false;
|
||||
continueMaxInstructions = 300;
|
||||
|
||||
useStaticAlloc = false;
|
||||
enablePointerify = false;
|
||||
|
||||
@@ -54,7 +54,6 @@ namespace MIPSComp {
|
||||
|
||||
u32 compilerPC;
|
||||
u32 blockStart;
|
||||
u32 lastContinuedPC;
|
||||
u32 initialBlockSize;
|
||||
int nextExit;
|
||||
bool cancel;
|
||||
@@ -240,8 +239,5 @@ namespace MIPSComp {
|
||||
|
||||
// Common
|
||||
bool enableBlocklink;
|
||||
bool immBranches;
|
||||
bool continueJumps;
|
||||
int continueMaxInstructions;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -136,10 +136,6 @@ void FakeJit::DoJit(u32 em_address, JitBlock *b) {
|
||||
_assert_(false);
|
||||
}
|
||||
|
||||
void FakeJit::AddContinuedBlock(u32 dest)
|
||||
{
|
||||
}
|
||||
|
||||
bool FakeJit::DescribeCodePtr(const u8 *ptr, std::string &name)
|
||||
{
|
||||
// TODO: Not used by anything yet.
|
||||
|
||||
@@ -53,7 +53,6 @@ public:
|
||||
|
||||
void CompileDelaySlot(int flags);
|
||||
void EatInstruction(MIPSOpcode op);
|
||||
void AddContinuedBlock(u32 dest);
|
||||
|
||||
void Comp_RunBlock(MIPSOpcode op) override;
|
||||
void Comp_ReplacementFunc(MIPSOpcode op) override;
|
||||
|
||||
@@ -299,26 +299,6 @@ void Jit::BranchRSRTComp(MIPSOpcode op, Gen::CCFlags cc, bool likely)
|
||||
immBranchTaken = !immBranchNotTaken;
|
||||
}
|
||||
|
||||
if (jo.immBranches && immBranch && js.numInstructions < jo.continueMaxInstructions)
|
||||
{
|
||||
if (!immBranchTaken)
|
||||
{
|
||||
// Skip the delay slot if likely, otherwise it'll be the next instruction.
|
||||
if (likely)
|
||||
js.compilerPC += 4;
|
||||
return;
|
||||
}
|
||||
|
||||
// Branch taken. Always compile the delay slot, and then go to dest.
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
AddContinuedBlock(targetAddr);
|
||||
// Account for the increment in the loop.
|
||||
js.compilerPC = targetAddr - 4;
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
return;
|
||||
}
|
||||
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
|
||||
|
||||
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
|
||||
@@ -379,31 +359,6 @@ void Jit::BranchRSZeroComp(MIPSOpcode op, Gen::CCFlags cc, bool andLink, bool li
|
||||
immBranchTaken = !immBranchNotTaken;
|
||||
}
|
||||
|
||||
if (jo.immBranches && immBranch && js.numInstructions < jo.continueMaxInstructions)
|
||||
{
|
||||
if (!immBranchTaken)
|
||||
{
|
||||
// Skip the delay slot if likely, otherwise it'll be the next instruction.
|
||||
if (andLink)
|
||||
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
|
||||
if (likely)
|
||||
js.compilerPC += 4;
|
||||
return;
|
||||
}
|
||||
|
||||
// Branch taken. Always compile the delay slot, and then go to dest.
|
||||
if (andLink)
|
||||
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
|
||||
AddContinuedBlock(targetAddr);
|
||||
// Account for the increment in the loop.
|
||||
js.compilerPC = targetAddr - 4;
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
return;
|
||||
}
|
||||
|
||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);
|
||||
|
||||
u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);
|
||||
@@ -586,40 +541,15 @@ void Jit::Comp_Jump(MIPSOpcode op) {
|
||||
switch (op >> 26) {
|
||||
case 2: //j
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
if (CanContinueJump(targetAddr))
|
||||
{
|
||||
AddContinuedBlock(targetAddr);
|
||||
// Account for the increment in the loop.
|
||||
js.compilerPC = targetAddr - 4;
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
return;
|
||||
}
|
||||
FlushAll();
|
||||
CONDITIONAL_LOG_EXIT(targetAddr);
|
||||
WriteExit(targetAddr, js.nextExit++);
|
||||
break;
|
||||
|
||||
case 3: //jal
|
||||
// Special case for branches to "replace functions":
|
||||
if (ReplaceJalTo(targetAddr))
|
||||
return;
|
||||
|
||||
// Check for small function inlining (future)
|
||||
|
||||
|
||||
// Save return address - might be overwritten by delay slot.
|
||||
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
if (CanContinueJump(targetAddr))
|
||||
{
|
||||
AddContinuedBlock(targetAddr);
|
||||
// Account for the increment in the loop.
|
||||
js.compilerPC = targetAddr - 4;
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
return;
|
||||
}
|
||||
FlushAll();
|
||||
CONDITIONAL_LOG_EXIT(targetAddr);
|
||||
WriteExit(targetAddr, js.nextExit++);
|
||||
@@ -682,16 +612,6 @@ void Jit::Comp_JumpReg(MIPSOpcode op)
|
||||
gpr.DiscardRegContentsIfCached(MIPS_REG_T9);
|
||||
}
|
||||
|
||||
if (gpr.IsImm(rs) && CanContinueJump(gpr.GetImm(rs)))
|
||||
{
|
||||
AddContinuedBlock(gpr.GetImm(rs));
|
||||
// Account for the increment in the loop.
|
||||
js.compilerPC = gpr.GetImm(rs) - 4;
|
||||
// In case the delay slot was a break or something.
|
||||
js.compiling = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (gpr.R(rs).IsSimpleReg()) {
|
||||
destReg = gpr.R(rs).GetSimpleReg();
|
||||
} else {
|
||||
|
||||
+1
-61
@@ -429,22 +429,7 @@ void Jit::DoJit(u32 em_address, JitBlock *b) {
|
||||
NOP();
|
||||
AlignCode4();
|
||||
|
||||
if (js.lastContinuedPC == 0) {
|
||||
b->originalSize = js.numInstructions;
|
||||
} else {
|
||||
// We continued at least once. Add the last proxy and set the originalSize correctly.
|
||||
blocks.CreateProxyBlock(js.blockStart, js.lastContinuedPC, (GetCompilerPC() - js.lastContinuedPC) / sizeof(u32), GetCodePtr());
|
||||
b->originalSize = js.initialBlockSize;
|
||||
}
|
||||
}
|
||||
|
||||
void Jit::AddContinuedBlock(u32 dest) {
|
||||
// The first block is the root block. When we continue, we create proxy blocks after that.
|
||||
if (js.lastContinuedPC == 0)
|
||||
js.initialBlockSize = js.numInstructions;
|
||||
else
|
||||
blocks.CreateProxyBlock(js.blockStart, js.lastContinuedPC, (GetCompilerPC() - js.lastContinuedPC) / sizeof(u32), GetCodePtr());
|
||||
js.lastContinuedPC = dest;
|
||||
b->originalSize = js.numInstructions;
|
||||
}
|
||||
|
||||
bool Jit::DescribeCodePtr(const u8 *ptr, std::string &name) {
|
||||
@@ -538,51 +523,6 @@ void Jit::UnlinkBlock(u8 *checkedEntry, u32 originalAddress) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Jit::ReplaceJalTo(u32 dest) {
|
||||
const ReplacementTableEntry *entry = nullptr;
|
||||
u32 funcSize = 0;
|
||||
if (!CanReplaceJalTo(dest, &entry, &funcSize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Warning - this might be bad if the code at the destination changes...
|
||||
if (entry->flags & REPFLAG_ALLOWINLINE) {
|
||||
// Jackpot! Just do it, no flushing. The code will be entirely inlined.
|
||||
|
||||
// First, compile the delay slot. It's unconditional so no issues.
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
// Technically, we should write the unused return address to RA, but meh.
|
||||
MIPSReplaceFunc repl = entry->jitReplaceFunc;
|
||||
int cycles = (this->*repl)();
|
||||
js.downcountAmount += cycles;
|
||||
} else {
|
||||
gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);
|
||||
CompileDelaySlot(DELAYSLOT_NICE);
|
||||
FlushAll();
|
||||
MOV(32, MIPSSTATE_VAR(pc), Imm32(GetCompilerPC()));
|
||||
RestoreRoundingMode();
|
||||
ABI_CallFunction(entry->replaceFunc);
|
||||
SUB(32, MIPSSTATE_VAR(downcount), R(EAX));
|
||||
ApplyRoundingMode();
|
||||
}
|
||||
|
||||
js.compilerPC += 4;
|
||||
// No writing exits, keep going!
|
||||
|
||||
if (g_breakpoints.HasMemChecks()) {
|
||||
// We could modify coreState, so we need to write PC and check.
|
||||
// Otherwise, PC may end up on the jal. We add 4 to skip the delay slot.
|
||||
MOV(32, MIPSSTATE_VAR(pc), Imm32(GetCompilerPC() + 4));
|
||||
js.afterOp |= JitState::AFTER_CORE_STATE;
|
||||
}
|
||||
|
||||
// Add a trigger so that if the inlined code changes, we invalidate this block.
|
||||
blocks.CreateProxyBlock(js.blockStart, dest, funcSize / sizeof(u32), GetCodePtr());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Jit::Comp_ReplacementFunc(MIPSOpcode op) {
|
||||
// We get here if we execute the first instruction of a replaced function. This means
|
||||
// that we do need to return to RA.
|
||||
|
||||
@@ -187,7 +187,6 @@ private:
|
||||
void FlushAll();
|
||||
void FlushPrefixV();
|
||||
void WriteDowncount(int offset = 0);
|
||||
bool ReplaceJalTo(u32 dest);
|
||||
|
||||
u32 GetCompilerPC();
|
||||
// See CompileDelaySlotFlags for flags.
|
||||
@@ -196,7 +195,6 @@ private:
|
||||
CompileDelaySlot(flags, &state);
|
||||
}
|
||||
void EatInstruction(MIPSOpcode op);
|
||||
void AddContinuedBlock(u32 dest);
|
||||
MIPSOpcode GetOffsetInstruction(int offset);
|
||||
|
||||
void WriteExit(u32 destination, int exit_num);
|
||||
@@ -268,22 +266,6 @@ private:
|
||||
}
|
||||
|
||||
bool PredictTakeBranch(u32 targetAddr, bool likely);
|
||||
bool CanContinueJump(u32 targetAddr) {
|
||||
if (!jo.continueJumps || js.numInstructions >= jo.continueMaxInstructions) {
|
||||
return false;
|
||||
}
|
||||
if (!targetAddr) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool CanContinueImmBranch(u32 targetAddr) {
|
||||
if (!jo.immBranches || js.numInstructions >= jo.continueMaxInstructions) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsAtDispatchFetch(const u8 *codePtr) const override {
|
||||
return codePtr == dispatcherFetch;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user