mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-10 08:09:26 +01:00
[AArch64] Improves the register cache.
Removes the REG_AWAY nonsense I was doing. I've got to get the JIT more up to speed before thinking of insane register cache things. Also fixes a bug in immediate setting where if the register being set to an immediate already had a host register tied to it then it wouldn't free the register it had. Resulting in register exhaustion.
This commit is contained in:
parent
2c39d4044d
commit
c3c80e9440
@ -63,45 +63,6 @@ void Arm64RegCache::UnlockRegister(ARM64Reg host_reg)
|
||||
// GPR Cache
|
||||
void Arm64GPRCache::Start(PPCAnalyst::BlockRegStats &stats)
|
||||
{
|
||||
// To make this technique easy, let's just work on pairs of even/odd registers
|
||||
// We could do simple odd/even as well to get a few spare temporary registers
|
||||
// but it isn't really needed, we aren't starved for registers
|
||||
for (int reg = 0; reg < 32; reg += 2)
|
||||
{
|
||||
u32 regs_used = (stats.IsUsed(reg) << 1) | stats.IsUsed(reg + 1);
|
||||
switch (regs_used)
|
||||
{
|
||||
case 0x02: // Reg+0 used
|
||||
{
|
||||
ARM64Reg host_reg = GetReg();
|
||||
m_guest_registers[reg].LoadToReg(host_reg);
|
||||
m_emit->LDR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[reg]));
|
||||
}
|
||||
break;
|
||||
case 0x01: // Reg+1 used
|
||||
{
|
||||
ARM64Reg host_reg = GetReg();
|
||||
m_guest_registers[reg + 1].LoadToReg(host_reg);
|
||||
m_emit->LDR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[reg + 1]));
|
||||
}
|
||||
break;
|
||||
case 0x03: // Both registers used
|
||||
{
|
||||
// Get a 64bit host register
|
||||
ARM64Reg host_reg = EncodeRegTo64(GetReg());
|
||||
m_guest_registers[reg].LoadToAway(host_reg, REG_LOW);
|
||||
m_guest_registers[reg + 1].LoadToAway(host_reg, REG_HIGH);
|
||||
|
||||
// host_reg is 64bit here.
|
||||
// It'll load both guest_registers in one LDR
|
||||
m_emit->LDR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[reg]));
|
||||
}
|
||||
break;
|
||||
case 0x00: // Neither used
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Arm64GPRCache::IsCalleeSaved(ARM64Reg reg)
|
||||
@ -116,77 +77,34 @@ bool Arm64GPRCache::IsCalleeSaved(ARM64Reg reg)
|
||||
|
||||
void Arm64GPRCache::FlushRegister(u32 preg)
|
||||
{
|
||||
u32 base_reg = preg;
|
||||
OpArg& reg = m_guest_registers[preg];
|
||||
if (reg.GetType() == REG_REG)
|
||||
{
|
||||
ARM64Reg host_reg = reg.GetReg();
|
||||
|
||||
m_emit->STR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[preg]));
|
||||
Unlock(host_reg);
|
||||
UnlockRegister(host_reg);
|
||||
|
||||
reg.Flush();
|
||||
}
|
||||
else if (reg.GetType() == REG_IMM)
|
||||
{
|
||||
ARM64Reg host_reg = GetReg();
|
||||
|
||||
m_emit->MOVI2R(host_reg, reg.GetImm());
|
||||
m_emit->STR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[preg]));
|
||||
|
||||
Unlock(host_reg);
|
||||
|
||||
reg.Flush();
|
||||
}
|
||||
else if (reg.GetType() == REG_AWAY)
|
||||
{
|
||||
u32 next_reg = 0;
|
||||
if (reg.GetAwayLocation() == REG_LOW)
|
||||
next_reg = base_reg + 1;
|
||||
else
|
||||
next_reg = base_reg - 1;
|
||||
OpArg& reg2 = m_guest_registers[next_reg];
|
||||
ARM64Reg host_reg = reg.GetAwayReg();
|
||||
ARM64Reg host_reg_1 = reg.GetReg();
|
||||
ARM64Reg host_reg_2 = reg2.GetReg();
|
||||
// Flush if either of these shared registers are used.
|
||||
if (host_reg_1 == INVALID_REG)
|
||||
if (!reg.GetImm())
|
||||
{
|
||||
// We never loaded this register
|
||||
// We've got to test the state of our shared register
|
||||
// Currently it is always reg+1
|
||||
if (host_reg_2 == INVALID_REG)
|
||||
{
|
||||
// We didn't load either of these registers
|
||||
// This can happen in cases where we had to flush register state
|
||||
// or if we hit an interpreted instruction before we could use it
|
||||
// Dump the whole thing in one go and flush both registers
|
||||
|
||||
// 64bit host register will store 2 32bit store registers in one go
|
||||
if (reg.GetAwayLocation() == REG_LOW)
|
||||
m_emit->STR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[base_reg]));
|
||||
else
|
||||
m_emit->STR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[next_reg]));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Alright, bottom register isn't used, but top one is
|
||||
// Only store the top one
|
||||
m_emit->STR(INDEX_UNSIGNED, host_reg_2, X29, PPCSTATE_OFF(gpr[next_reg]));
|
||||
Unlock(host_reg_2);
|
||||
}
|
||||
m_emit->STR(INDEX_UNSIGNED, WSP, X29, PPCSTATE_OFF(gpr[preg]));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_emit->STR(INDEX_UNSIGNED, host_reg_1, X29, PPCSTATE_OFF(gpr[base_reg]));
|
||||
Unlock(host_reg_1);
|
||||
}
|
||||
// Flush both registers
|
||||
reg.Flush();
|
||||
reg2.Flush();
|
||||
Unlock(DecodeReg(host_reg));
|
||||
}
|
||||
ARM64Reg host_reg = GetReg();
|
||||
|
||||
m_emit->MOVI2R(host_reg, reg.GetImm());
|
||||
m_emit->STR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[preg]));
|
||||
|
||||
UnlockRegister(host_reg);
|
||||
}
|
||||
|
||||
reg.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64GPRCache::Flush(FlushMode mode, PPCAnalyst::CodeOp* op)
|
||||
@ -196,11 +114,7 @@ void Arm64GPRCache::Flush(FlushMode mode, PPCAnalyst::CodeOp* op)
|
||||
bool flush = true;
|
||||
if (mode == FLUSH_INTERPRETER)
|
||||
{
|
||||
if (!(op->regsOut[0] == i ||
|
||||
op->regsOut[1] == i ||
|
||||
op->regsIn[0] == i ||
|
||||
op->regsIn[1] == i ||
|
||||
op->regsIn[2] == i))
|
||||
if (!(op->regsOut[i] || op->regsIn[i]))
|
||||
{
|
||||
// This interpreted instruction doesn't use this register
|
||||
flush = false;
|
||||
@ -219,39 +133,6 @@ void Arm64GPRCache::Flush(FlushMode mode, PPCAnalyst::CodeOp* op)
|
||||
if (flush)
|
||||
FlushRegister(i);
|
||||
}
|
||||
else if (m_guest_registers[i].GetType() == REG_AWAY)
|
||||
{
|
||||
// We are away, that means that this register and the next are stored in a single 64bit register
|
||||
// There is a very good chance that both the registers are out in some "temp" register
|
||||
bool flush_2 = true;
|
||||
if (mode == FLUSH_INTERPRETER)
|
||||
{
|
||||
if (!(op->regsOut[0] == (i + 1) ||
|
||||
op->regsOut[1] == (i + 1) ||
|
||||
op->regsIn[0] == (i + 1) ||
|
||||
op->regsIn[1] == (i + 1) ||
|
||||
op->regsIn[2] == (i + 1)))
|
||||
{
|
||||
// This interpreted instruction doesn't use this register
|
||||
flush_2 = false;
|
||||
}
|
||||
}
|
||||
|
||||
ARM64Reg host_reg = m_guest_registers[i].GetAwayReg();
|
||||
ARM64Reg host_reg_1 = m_guest_registers[i].GetReg();
|
||||
ARM64Reg host_reg_2 = m_guest_registers[i + 1].GetReg();
|
||||
// Flush if either of these shared registers are used.
|
||||
if (flush ||
|
||||
flush_2 ||
|
||||
!IsCalleeSaved(host_reg) ||
|
||||
!IsCalleeSaved(host_reg_1) ||
|
||||
!IsCalleeSaved(host_reg_2))
|
||||
{
|
||||
FlushRegister(i); // Will flush both pairs of registers
|
||||
}
|
||||
// Skip the next register since we've handled it here
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,71 +155,6 @@ ARM64Reg Arm64GPRCache::R(u32 preg)
|
||||
return host_reg;
|
||||
}
|
||||
break;
|
||||
case REG_AWAY: // Register is away in a shared register
|
||||
{
|
||||
// Let's do the voodoo that we dodo
|
||||
if (reg.GetReg() == INVALID_REG)
|
||||
{
|
||||
// Alright, we need to extract from our away register
|
||||
// To our new 32bit register
|
||||
if (reg.GetAwayLocation() == REG_LOW)
|
||||
{
|
||||
OpArg& upper_reg = m_guest_registers[preg + 1];
|
||||
if (upper_reg.GetType() == REG_REG)
|
||||
{
|
||||
// If the upper reg is already moved away, just claim this one as ours now
|
||||
ARM64Reg host_reg = reg.GetAwayReg();
|
||||
reg.LoadToReg(DecodeReg(host_reg));
|
||||
return host_reg;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Top register is still loaded
|
||||
// Make sure to move to a new register
|
||||
ARM64Reg host_reg = GetReg();
|
||||
ARM64Reg current_reg = reg.GetAwayReg();
|
||||
reg.LoadToReg(host_reg);
|
||||
|
||||
// We are in the low bits
|
||||
// Just move it over to the low bits of the new register
|
||||
m_emit->UBFM(EncodeRegTo64(host_reg), current_reg, 0, 31);
|
||||
return host_reg;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OpArg& lower_reg = m_guest_registers[preg - 1];
|
||||
if (lower_reg.GetType() == REG_REG)
|
||||
{
|
||||
// If the lower register is moved away, claim this one as ours
|
||||
ARM64Reg host_reg = reg.GetAwayReg();
|
||||
reg.LoadToReg(DecodeReg(host_reg));
|
||||
|
||||
// Make sure to move our register from the high bits to the low bits
|
||||
m_emit->UBFM(EncodeRegTo64(host_reg), host_reg, 32, 63);
|
||||
return host_reg;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load this register in to the new low bits
|
||||
// We are no longer away
|
||||
ARM64Reg host_reg = GetReg();
|
||||
ARM64Reg current_reg = reg.GetAwayReg();
|
||||
reg.LoadToReg(host_reg);
|
||||
|
||||
// We are in the high bits
|
||||
m_emit->UBFM(EncodeRegTo64(host_reg), current_reg, 32, 63);
|
||||
return host_reg;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We've already moved to a valid place to work on
|
||||
return reg.GetReg();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case REG_NOTLOADED: // Register isn't loaded at /all/
|
||||
{
|
||||
// This is a bit annoying. We try to keep these preloaded as much as possible
|
||||
@ -357,6 +173,14 @@ ARM64Reg Arm64GPRCache::R(u32 preg)
|
||||
return INVALID_REG;
|
||||
}
|
||||
|
||||
void Arm64GPRCache::SetImmediate(u32 preg, u32 imm)
|
||||
{
|
||||
OpArg& reg = m_guest_registers[preg];
|
||||
if (reg.GetType() == REG_REG)
|
||||
Unlock(reg.GetReg());
|
||||
reg.LoadToImm(imm);
|
||||
}
|
||||
|
||||
void Arm64GPRCache::GetAllocationOrder()
|
||||
{
|
||||
// Callee saved registers first in hopes that we will keep everything stored there first
|
||||
@ -380,8 +204,7 @@ void Arm64GPRCache::FlushMostStaleRegister()
|
||||
{
|
||||
u32 last_used = m_guest_registers[i].GetLastUsed();
|
||||
if (last_used > most_stale_amount &&
|
||||
m_guest_registers[i].GetType() != REG_IMM &&
|
||||
m_guest_registers[i].GetType() != REG_NOTLOADED)
|
||||
m_guest_registers[i].GetType() == REG_REG)
|
||||
{
|
||||
most_stale_preg = i;
|
||||
most_stale_amount = last_used;
|
||||
|
@ -19,7 +19,6 @@ enum RegType
|
||||
REG_NOTLOADED = 0,
|
||||
REG_REG, // Reg type is register
|
||||
REG_IMM, // Reg is really a IMM
|
||||
REG_AWAY, // Reg is away
|
||||
};
|
||||
enum RegLocation
|
||||
{
|
||||
@ -56,14 +55,6 @@ public:
|
||||
{
|
||||
return m_reg;
|
||||
}
|
||||
ARM64Reg GetAwayReg()
|
||||
{
|
||||
return m_away_reg;
|
||||
}
|
||||
RegLocation GetAwayLocation()
|
||||
{
|
||||
return m_away_location;
|
||||
}
|
||||
u32 GetImm()
|
||||
{
|
||||
return m_value;
|
||||
@ -72,16 +63,6 @@ public:
|
||||
{
|
||||
m_type = REG_REG;
|
||||
m_reg = reg;
|
||||
|
||||
m_away_reg = INVALID_REG;
|
||||
}
|
||||
void LoadToAway(ARM64Reg reg, RegLocation location)
|
||||
{
|
||||
m_type = REG_AWAY;
|
||||
m_away_reg = reg;
|
||||
m_away_location = location;
|
||||
|
||||
m_reg = INVALID_REG;
|
||||
}
|
||||
void LoadToImm(u32 imm)
|
||||
{
|
||||
@ -89,14 +70,12 @@ public:
|
||||
m_value = imm;
|
||||
|
||||
m_reg = INVALID_REG;
|
||||
m_away_reg = INVALID_REG;
|
||||
}
|
||||
void Flush()
|
||||
{
|
||||
// Invalidate any previous information
|
||||
m_type = REG_NOTLOADED;
|
||||
m_reg = INVALID_REG;
|
||||
m_away_reg = INVALID_REG;
|
||||
|
||||
// Arbitrarily large value that won't roll over on a lot of increments
|
||||
m_last_used = 0xFFFF;
|
||||
@ -111,12 +90,6 @@ private:
|
||||
RegType m_type; // store type
|
||||
ARM64Reg m_reg; // host register we are in
|
||||
|
||||
// For REG_AWAY
|
||||
// Host register that we are away in
|
||||
// This is a 64bit register
|
||||
ARM64Reg m_away_reg;
|
||||
RegLocation m_away_location;
|
||||
|
||||
// For REG_IMM
|
||||
u32 m_value; // IMM value
|
||||
|
||||
@ -227,7 +200,7 @@ public:
|
||||
ARM64Reg R(u32 preg);
|
||||
|
||||
// Set a register to an immediate
|
||||
void SetImmediate(u32 reg, u32 imm) { m_guest_registers[reg].LoadToImm(imm); }
|
||||
void SetImmediate(u32 preg, u32 imm);
|
||||
|
||||
// Returns if a register is set as an immediate
|
||||
bool IsImm(u32 reg) { return m_guest_registers[reg].GetType() == REG_IMM; }
|
||||
|
Loading…
x
Reference in New Issue
Block a user