summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorRyan Houdek <Sonicadvance1@gmail.com>2014-09-12 05:55:06 -0500
committerRyan Houdek <Sonicadvance1@gmail.com>2014-09-12 06:39:15 -0500
commite883a6fb2e106bfecabaaa93c4f65d3ea021e899 (patch)
treecb620e008a0647e29e07a15a3d14290f8c789694 /Source/Core
parent64b21a4812eb4d6912746d6cacabacd96e6875a2 (diff)
Fix all the current issues with the AArch64 register cache.
Fixes all the current issues I've been experiencing. Scaled back the register cache idea for now so I can actually work on some real instructions. Tested this work with unit tests so I know it works. Unit tests are pretty great things.
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp233
-rw-r--r--Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.h69
2 files changed, 218 insertions, 84 deletions
diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp
index a165828037..052d0ea9bf 100644
--- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp
+++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp
@@ -13,8 +13,12 @@ void Arm64RegCache::Init(ARM64XEmitter *emitter)
GetAllocationOrder();
}
-ARM64Reg Arm64RegCache::GetReg(void)
+ARM64Reg Arm64RegCache::GetReg()
{
+ // If we have no registers left, dump the most stale register first
+ if (!GetUnlockedRegisterCount())
+ FlushMostStaleRegister();
+
for (auto& it : m_host_registers)
{
if (!it.IsLocked())
@@ -29,6 +33,15 @@ ARM64Reg Arm64RegCache::GetReg(void)
return INVALID_REG;
}
+u32 Arm64RegCache::GetUnlockedRegisterCount()
+{
+ u32 unlocked_registers = 0;
+ for (auto& it : m_host_registers)
+ if (!it.IsLocked())
+ ++unlocked_registers;
+ return unlocked_registers;
+}
+
void Arm64RegCache::LockRegister(ARM64Reg host_reg)
{
auto reg = std::find(m_host_registers.begin(), m_host_registers.end(), host_reg);
@@ -101,6 +114,81 @@ bool Arm64GPRCache::IsCalleeSaved(ARM64Reg reg)
return std::find(callee_regs.begin(), callee_regs.end(), EncodeRegTo64(reg)) != callee_regs.end();
}
+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);
+
+ 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)
+ {
+ // 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);
+ }
+ }
+ 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));
+ }
+
+}
+
void Arm64GPRCache::Flush(FlushMode mode, PPCAnalyst::CodeOp* op)
{
for (int i = 0; i < 32; ++i)
@@ -124,26 +212,12 @@ void Arm64GPRCache::Flush(FlushMode mode, PPCAnalyst::CodeOp* op)
// Has to be flushed if it isn't in a callee saved register
ARM64Reg host_reg = m_guest_registers[i].GetReg();
if (flush || !IsCalleeSaved(host_reg))
- {
- m_emit->STR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[i]));
- Unlock(host_reg);
-
- m_guest_registers[i].Flush();
- }
+ FlushRegister(i);
}
else if (m_guest_registers[i].GetType() == REG_IMM)
{
if (flush)
- {
- ARM64Reg host_reg = GetReg();
-
- m_emit->MOVI2R(host_reg, m_guest_registers[i].GetImm());
- m_emit->STR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[i]));
-
- Unlock(host_reg);
-
- m_guest_registers[i].Flush();
- }
+ FlushRegister(i);
}
else if (m_guest_registers[i].GetType() == REG_AWAY)
{
@@ -173,39 +247,7 @@ void Arm64GPRCache::Flush(FlushMode mode, PPCAnalyst::CodeOp* op)
!IsCalleeSaved(host_reg_1) ||
!IsCalleeSaved(host_reg_2))
{
-
- if (host_reg_1 == INVALID_REG)
- {
- // 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
- m_emit->STR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[i]));
- }
- 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[i + 1]));
- Unlock(host_reg_2);
- }
- }
- else
- {
- m_emit->STR(INDEX_UNSIGNED, host_reg_1, X29, PPCSTATE_OFF(gpr[i]));
- Unlock(host_reg_1);
- }
- // Flush both registers
- m_guest_registers[i].Flush();
- m_guest_registers[i + 1].Flush();
- Unlock(DecodeReg(host_reg));
+ FlushRegister(i); // Will flush both pairs of registers
}
// Skip the next register since we've handled it here
++i;
@@ -216,6 +258,9 @@ void Arm64GPRCache::Flush(FlushMode mode, PPCAnalyst::CodeOp* op)
ARM64Reg Arm64GPRCache::R(u32 preg)
{
OpArg& reg = m_guest_registers[preg];
+ IncrementAllUsed();
+ reg.ResetLastUsed();
+
switch (reg.GetType())
{
case REG_REG: // already in a reg
@@ -225,6 +270,8 @@ ARM64Reg Arm64GPRCache::R(u32 preg)
{
ARM64Reg host_reg = GetReg();
m_emit->MOVI2R(host_reg, reg.GetImm());
+ reg.LoadToReg(host_reg);
+ return host_reg;
}
break;
case REG_AWAY: // Register is away in a shared register
@@ -232,22 +279,57 @@ ARM64Reg Arm64GPRCache::R(u32 preg)
// Let's do the voodoo that we dodo
if (reg.GetReg() == INVALID_REG)
{
- // Alright, we need to move to a valid location
- ARM64Reg host_reg = GetReg();
- reg.LoadAwayToReg(host_reg);
-
// Alright, we need to extract from our away register
// To our new 32bit register
if (reg.GetAwayLocation() == REG_LOW)
{
- // We are in the low bits
- // Just move it over to the low bits of the new register
- m_emit->UBFM(EncodeRegTo64(host_reg), reg.GetAwayReg(), 0, 31);
+ 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
{
- // We are in the high bits
- m_emit->UBFM(EncodeRegTo64(host_reg), reg.GetAwayReg(), 32, 63);
+ 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
@@ -259,10 +341,8 @@ ARM64Reg Arm64GPRCache::R(u32 preg)
break;
case REG_NOTLOADED: // Register isn't loaded at /all/
{
- // This is kind of annoying, we shouldn't have gotten here
- // This can happen with instructions that use multiple registers(eg lmw)
- // The PPCAnalyst needs to be modified to handle these cases
- _dbg_assert_msg_(DYNA_REC, false, "Hit REG_NOTLOADED type oparg. Fix the PPCAnalyst");
+ // This is a bit annoying. We try to keep these preloaded as much as possible
+ // This can also happen on cases where PPCAnalyst isn't feeing us proper register usage statistics
ARM64Reg host_reg = GetReg();
reg.LoadToReg(host_reg);
m_emit->LDR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[preg]));
@@ -277,7 +357,7 @@ ARM64Reg Arm64GPRCache::R(u32 preg)
return INVALID_REG;
}
-void Arm64GPRCache::GetAllocationOrder(void)
+void Arm64GPRCache::GetAllocationOrder()
{
// Callee saved registers first in hopes that we will keep everything stored there first
const std::vector<ARM64Reg> allocation_order =
@@ -292,6 +372,24 @@ void Arm64GPRCache::GetAllocationOrder(void)
m_host_registers.push_back(HostReg(reg));
}
+void Arm64GPRCache::FlushMostStaleRegister()
+{
+ u32 most_stale_preg = 0;
+ u32 most_stale_amount = 0;
+ for (u32 i = 0; i < 32; ++i)
+ {
+ 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)
+ {
+ most_stale_preg = i;
+ most_stale_amount = last_used;
+ }
+ }
+ FlushRegister(most_stale_preg);
+}
+
// FPR Cache
void Arm64FPRCache::Flush(FlushMode mode, PPCAnalyst::CodeOp* op)
{
@@ -303,7 +401,7 @@ ARM64Reg Arm64FPRCache::R(u32 preg)
// XXX: return a host reg holding a guest register
}
-void Arm64FPRCache::GetAllocationOrder(void)
+void Arm64FPRCache::GetAllocationOrder()
{
const std::vector<ARM64Reg> allocation_order =
{
@@ -317,3 +415,8 @@ void Arm64FPRCache::GetAllocationOrder(void)
m_host_registers.push_back(HostReg(reg));
}
+void Arm64FPRCache::FlushMostStaleRegister()
+{
+ // XXX: Flush a register
+}
+
diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.h b/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.h
index 58c21c2ae1..1eb1afd3d2 100644
--- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.h
+++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.h
@@ -42,10 +42,9 @@ class OpArg
{
public:
OpArg()
+ : m_type(REG_NOTLOADED), m_reg(INVALID_REG),
+ m_value(0), m_last_used(0)
{
- m_type = REG_NOTLOADED;
- m_reg = INVALID_REG;
- m_value = 0;
}
RegType GetType()
@@ -73,30 +72,40 @@ public:
{
m_type = REG_REG;
m_reg = reg;
+
+ m_away_reg = INVALID_REG;
}
void LoadToAway(ARM64Reg reg, RegLocation location)
{
m_type = REG_AWAY;
- m_reg = INVALID_REG;
m_away_reg = reg;
m_away_location = location;
- }
- void LoadAwayToReg(ARM64Reg reg)
- {
- // We are still an away type
- // We just are also in another register
- m_reg = reg;
+
+ m_reg = INVALID_REG;
}
void LoadToImm(u32 imm)
{
m_type = REG_IMM;
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;
}
+ u32 GetLastUsed() { return m_last_used; }
+ void ResetLastUsed() { m_last_used = 0; }
+ void IncrementLastUsed() { ++m_last_used; }
+
private:
// For REG_REG
RegType m_type; // store type
@@ -110,6 +119,8 @@ private:
// For REG_IMM
u32 m_value; // IMM value
+
+ u32 m_last_used;
};
class HostReg
@@ -117,10 +128,10 @@ class HostReg
public:
HostReg() : m_reg(INVALID_REG), m_locked(false) {}
HostReg(ARM64Reg reg) : m_reg(reg), m_locked(false) {}
- bool IsLocked(void) { return m_locked; }
- void Lock(void) { m_locked = true; }
- void Unlock(void) { m_locked = false; }
- ARM64Reg GetReg(void) { return m_reg; }
+ bool IsLocked() { return m_locked; }
+ void Lock() { m_locked = true; }
+ void Unlock() { m_locked = false; }
+ ARM64Reg GetReg() { return m_reg; }
bool operator==(const ARM64Reg& reg)
{
@@ -135,7 +146,7 @@ private:
class Arm64RegCache
{
public:
- Arm64RegCache(void) : m_emit(nullptr), m_reg_stats(nullptr) {};
+ Arm64RegCache() : m_emit(nullptr), m_reg_stats(nullptr) {};
virtual ~Arm64RegCache() {};
void Init(ARM64XEmitter *emitter);
@@ -151,7 +162,7 @@ public:
// Returns a temporary register for use
// Requires unlocking after done
- ARM64Reg GetReg(void);
+ ARM64Reg GetReg();
// Locks a register so a cache cannot use it
// Useful for function calls
@@ -177,7 +188,10 @@ public:
protected:
// Get the order of the host registers
- virtual void GetAllocationOrder(void) = 0;
+ virtual void GetAllocationOrder() = 0;
+
+ // Flushes the most stale register
+ virtual void FlushMostStaleRegister() = 0;
// Lock a register
void LockRegister(ARM64Reg host_reg);
@@ -185,6 +199,9 @@ protected:
// Unlock a register
void UnlockRegister(ARM64Reg host_reg);
+ // Get available host registers
+ u32 GetUnlockedRegisterCount();
+
// Code emitter
ARM64XEmitter *m_emit;
@@ -220,7 +237,10 @@ public:
protected:
// Get the order of the host registers
- void GetAllocationOrder(void);
+ void GetAllocationOrder();
+
+ // Flushes the most stale register
+ void FlushMostStaleRegister();
// Our guest GPRs
// PowerPC has 32 GPRs
@@ -228,6 +248,14 @@ protected:
private:
bool IsCalleeSaved(ARM64Reg reg);
+
+ void IncrementAllUsed()
+ {
+ for (auto& reg : m_guest_registers)
+ reg.IncrementLastUsed();
+ }
+
+ void FlushRegister(u32 preg);
};
class Arm64FPRCache : public Arm64RegCache
@@ -243,7 +271,10 @@ public:
protected:
// Get the order of the host registers
- void GetAllocationOrder(void);
+ void GetAllocationOrder();
+
+ // Flushes the most stale register
+ void FlushMostStaleRegister();
// Our guest FPRs
// Gekko has 32 paired registers(32x2)