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:
Unknown W. Brackets 2014-12-07 21:09:08 -08:00
parent 487e175cf9
commit eeff110c0f
5 changed files with 109 additions and 39 deletions

View file

@ -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;

View file

@ -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() {

View file

@ -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;

View file

@ -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;

View file

@ -115,6 +115,7 @@ public:
private:
Gen::X64Reg GetFreeXReg();
Gen::X64Reg FindBestToSpill(bool unusedOnly);
const int *GetAllocationOrder(int &count);
MIPSCachedReg regs[NUM_MIPS_GPRS];