mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
jit: Improve and unify GPR spill logic.
Now the same logic on x86 and ARM, and handles HI/LO/etc. better.
This commit is contained in:
parent
487e175cf9
commit
eeff110c0f
5 changed files with 109 additions and 39 deletions
|
@ -197,26 +197,16 @@ ARMReg ArmRegCache::FindBestToSpill(bool unusedOnly) {
|
|||
int allocCount;
|
||||
const ARMReg *allocOrder = GetMIPSAllocationOrder(allocCount);
|
||||
|
||||
static const int UNUSED_LOOKAHEAD_OPS = 3;
|
||||
static const int UNUSED_LOOKAHEAD_OPS = 30;
|
||||
|
||||
for (int i = 0; i < allocCount; i++) {
|
||||
ARMReg reg = allocOrder[i];
|
||||
if (ar[reg].mipsReg != MIPS_REG_INVALID && mr[ar[reg].mipsReg].spillLock)
|
||||
continue;
|
||||
|
||||
if (unusedOnly) {
|
||||
bool unused = true;
|
||||
for (int ahead = 1; ahead <= UNUSED_LOOKAHEAD_OPS; ++ahead) {
|
||||
MIPSOpcode laterOp = Memory::Read_Instruction(compilerPC_ + ahead * sizeof(u32));
|
||||
// If read, it might need to be mapped again. If output, it might not need to be stored.
|
||||
if (MIPSAnalyst::ReadsFromGPReg(laterOp, ar[reg].mipsReg) || MIPSAnalyst::GetOutGPReg(laterOp) == ar[reg].mipsReg) {
|
||||
unused = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!unused) {
|
||||
continue;
|
||||
}
|
||||
// Not awesome. A used reg. Let's try to avoid spilling.
|
||||
if (unusedOnly && MIPSAnalyst::IsRegisterUsed(ar[reg].mipsReg, compilerPC_, UNUSED_LOOKAHEAD_OPS)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return reg;
|
||||
|
|
|
@ -680,25 +680,25 @@ namespace MIPSAnalyst {
|
|||
}
|
||||
}
|
||||
|
||||
bool IsRegisterUsed(MIPSGPReg reg, u32 addr, int instrs) {
|
||||
enum RegisterUsage {
|
||||
USAGE_CLOBBERED,
|
||||
USAGE_INPUT,
|
||||
USAGE_UNKNOWN,
|
||||
};
|
||||
|
||||
static RegisterUsage DetermineInOutUsage(u64 inFlag, u64 outFlag, u32 addr, int instrs) {
|
||||
u32 end = addr + instrs * sizeof(u32);
|
||||
while (addr < end) {
|
||||
const MIPSOpcode op = Memory::Read_Instruction(addr, true);
|
||||
const MIPSInfo info = MIPSGetInfo(op);
|
||||
|
||||
// Yes, used.
|
||||
if ((info & IN_RS) && (MIPS_GET_RS(op) == reg))
|
||||
return true;
|
||||
if ((info & IN_RT) && (MIPS_GET_RT(op) == reg))
|
||||
return true;
|
||||
if (info & inFlag)
|
||||
return USAGE_INPUT;
|
||||
|
||||
// Clobbered, so not used.
|
||||
if ((info & OUT_RT) && (MIPS_GET_RT(op) == reg))
|
||||
return false;
|
||||
if ((info & OUT_RD) && (MIPS_GET_RD(op) == reg))
|
||||
return false;
|
||||
if ((info & OUT_RA) && (reg == MIPS_REG_RA))
|
||||
return false;
|
||||
if (info & outFlag)
|
||||
return USAGE_CLOBBERED;
|
||||
|
||||
// Bail early if we hit a branch (could follow each path for continuing?)
|
||||
if ((info & IS_CONDBRANCH) || (info & IS_JUMP)) {
|
||||
|
@ -708,7 +708,63 @@ namespace MIPSAnalyst {
|
|||
}
|
||||
addr += 4;
|
||||
}
|
||||
return false;
|
||||
return USAGE_UNKNOWN;
|
||||
}
|
||||
|
||||
static RegisterUsage DetermineRegisterUsage(MIPSGPReg reg, u32 addr, int instrs) {
|
||||
switch (reg) {
|
||||
case MIPS_REG_HI:
|
||||
return DetermineInOutUsage(IN_HI, OUT_HI, addr, instrs);
|
||||
case MIPS_REG_LO:
|
||||
return DetermineInOutUsage(IN_LO, OUT_LO, addr, instrs);
|
||||
case MIPS_REG_FPCOND:
|
||||
return DetermineInOutUsage(IN_FPUFLAG, OUT_FPUFLAG, addr, instrs);
|
||||
case MIPS_REG_VFPUCC:
|
||||
return DetermineInOutUsage(IN_VFPU_CC, OUT_VFPU_CC, addr, instrs);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (reg > 32) {
|
||||
return USAGE_UNKNOWN;
|
||||
}
|
||||
|
||||
u32 end = addr + instrs * sizeof(u32);
|
||||
while (addr < end) {
|
||||
const MIPSOpcode op = Memory::Read_Instruction(addr, true);
|
||||
const MIPSInfo info = MIPSGetInfo(op);
|
||||
|
||||
// Yes, used.
|
||||
if ((info & IN_RS) && (MIPS_GET_RS(op) == reg))
|
||||
return USAGE_INPUT;
|
||||
if ((info & IN_RT) && (MIPS_GET_RT(op) == reg))
|
||||
return USAGE_INPUT;
|
||||
|
||||
// Clobbered, so not used.
|
||||
if ((info & OUT_RT) && (MIPS_GET_RT(op) == reg))
|
||||
return USAGE_CLOBBERED;
|
||||
if ((info & OUT_RD) && (MIPS_GET_RD(op) == reg))
|
||||
return USAGE_CLOBBERED;
|
||||
if ((info & OUT_RA) && (reg == MIPS_REG_RA))
|
||||
return USAGE_CLOBBERED;
|
||||
|
||||
// Bail early if we hit a branch (could follow each path for continuing?)
|
||||
if ((info & IS_CONDBRANCH) || (info & IS_JUMP)) {
|
||||
// Still need to check the delay slot (so end after it.)
|
||||
// We'll assume likely are taken.
|
||||
end = addr + 8;
|
||||
}
|
||||
addr += 4;
|
||||
}
|
||||
return USAGE_UNKNOWN;
|
||||
}
|
||||
|
||||
bool IsRegisterUsed(MIPSGPReg reg, u32 addr, int instrs) {
|
||||
return DetermineRegisterUsage(reg, addr, instrs) == USAGE_INPUT;
|
||||
}
|
||||
|
||||
bool IsRegisterClobbered(MIPSGPReg reg, u32 addr, int instrs) {
|
||||
return DetermineRegisterUsage(reg, addr, instrs) == USAGE_CLOBBERED;
|
||||
}
|
||||
|
||||
void HashFunctions() {
|
||||
|
|
|
@ -79,6 +79,8 @@ namespace MIPSAnalyst
|
|||
|
||||
// This tells us if the reg is used within intrs of addr (also includes likely delay slots.)
|
||||
bool IsRegisterUsed(MIPSGPReg reg, u32 addr, int instrs);
|
||||
// This tells us if the reg is clobbered within intrs of addr (e.g. it is surely not used.)
|
||||
bool IsRegisterClobbered(MIPSGPReg reg, u32 addr, int instrs);
|
||||
|
||||
struct AnalyzedFunction {
|
||||
u32 start;
|
||||
|
|
|
@ -122,6 +122,30 @@ void GPRRegCache::UnlockAllX() {
|
|||
xregs[i].allocLocked = false;
|
||||
}
|
||||
|
||||
X64Reg GPRRegCache::FindBestToSpill(bool unusedOnly) {
|
||||
int allocCount;
|
||||
const int *allocOrder = GetAllocationOrder(allocCount);
|
||||
|
||||
static const int UNUSED_LOOKAHEAD_OPS = 30;
|
||||
|
||||
for (int i = 0; i < allocCount; i++) {
|
||||
X64Reg reg = (X64Reg)allocOrder[i];
|
||||
if (xregs[reg].allocLocked)
|
||||
continue;
|
||||
if (xregs[reg].mipsReg != MIPS_REG_INVALID && regs[xregs[reg].mipsReg].locked)
|
||||
continue;
|
||||
|
||||
// Not awesome. A used reg. Let's try to avoid spilling.
|
||||
if (unusedOnly && MIPSAnalyst::IsRegisterUsed(xregs[reg].mipsReg, js_->compilerPC, UNUSED_LOOKAHEAD_OPS)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return reg;
|
||||
}
|
||||
|
||||
return INVALID_REG;
|
||||
}
|
||||
|
||||
X64Reg GPRRegCache::GetFreeXReg()
|
||||
{
|
||||
int aCount;
|
||||
|
@ -134,21 +158,18 @@ X64Reg GPRRegCache::GetFreeXReg()
|
|||
return (X64Reg)xr;
|
||||
}
|
||||
}
|
||||
//Okay, not found :( Force grab one
|
||||
|
||||
//TODO - add a pass to grab xregs whose mipsreg is not used in the next 3 instructions
|
||||
for (int i = 0; i < aCount; i++)
|
||||
{
|
||||
X64Reg xr = (X64Reg)aOrder[i];
|
||||
if (xregs[xr].allocLocked)
|
||||
continue;
|
||||
MIPSGPReg preg = xregs[xr].mipsReg;
|
||||
if (!regs[preg].locked)
|
||||
{
|
||||
StoreFromRegister(preg);
|
||||
return xr;
|
||||
}
|
||||
//Okay, not found :( Force grab one
|
||||
X64Reg bestToSpill = FindBestToSpill(true);
|
||||
if (bestToSpill == INVALID_REG) {
|
||||
bestToSpill = FindBestToSpill(false);
|
||||
}
|
||||
|
||||
if (bestToSpill != INVALID_REG) {
|
||||
StoreFromRegister(xregs[bestToSpill].mipsReg);
|
||||
return bestToSpill;
|
||||
}
|
||||
|
||||
//Still no dice? Die!
|
||||
_assert_msg_(JIT, 0, "Regcache ran out of regs");
|
||||
return (X64Reg) -1;
|
||||
|
|
|
@ -115,6 +115,7 @@ public:
|
|||
|
||||
private:
|
||||
Gen::X64Reg GetFreeXReg();
|
||||
Gen::X64Reg FindBestToSpill(bool unusedOnly);
|
||||
const int *GetAllocationOrder(int &count);
|
||||
|
||||
MIPSCachedReg regs[NUM_MIPS_GPRS];
|
||||
|
|
Loading…
Add table
Reference in a new issue