[AArch64] Implement FPR Cache.

This commit is contained in:
Ryan Houdek 2015-01-07 14:24:29 -06:00
parent c511ee763a
commit 52a532370a
2 changed files with 146 additions and 52 deletions

View File

@ -10,6 +10,7 @@ using namespace Arm64Gen;
void Arm64RegCache::Init(ARM64XEmitter *emitter)
{
m_emit = emitter;
m_float_emit.reset(new ARM64FloatEmitter(m_emit));
GetAllocationOrder();
}
@ -56,6 +57,23 @@ void Arm64RegCache::UnlockRegister(ARM64Reg host_reg)
reg->Unlock();
}
void Arm64RegCache::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_REG)
{
most_stale_preg = i;
most_stale_amount = last_used;
}
}
FlushRegister(most_stale_preg, false);
}
// GPR Cache
void Arm64GPRCache::Start(PPCAnalyst::BlockRegStats &stats)
{
@ -212,23 +230,6 @@ void Arm64GPRCache::GetAllocationOrder()
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_REG)
{
most_stale_preg = i;
most_stale_amount = last_used;
}
}
FlushRegister(most_stale_preg, false);
}
BitSet32 Arm64GPRCache::GetCallerSavedUsed()
{
BitSet32 registers(0);
@ -254,35 +255,120 @@ void Arm64GPRCache::FlushByHost(ARM64Reg host_reg)
// FPR Cache
void Arm64FPRCache::Flush(FlushMode mode, PPCAnalyst::CodeOp* op)
{
// XXX: Flush our stuff
for (int i = 0; i < 32; ++i)
{
bool flush = true;
if (mode == FLUSH_INTERPRETER)
{
if (!(op->regsOut[i] || op->regsIn[i]))
{
// This interpreted instruction doesn't use this register
flush = false;
}
}
if (m_guest_registers[i].GetType() == REG_REG)
{
// 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))
FlushRegister(i, mode == FLUSH_MAINTAIN_STATE);
}
}
}
ARM64Reg Arm64FPRCache::R(u32 preg)
{
// XXX: return a host reg holding a guest register
OpArg& reg = m_guest_registers[preg];
IncrementAllUsed();
reg.ResetLastUsed();
switch (reg.GetType())
{
case REG_REG: // already in a reg
return reg.GetReg();
break;
case REG_NOTLOADED: // Register isn't loaded at /all/
{
ARM64Reg host_reg = GetReg();
reg.LoadToReg(host_reg);
m_float_emit->LDR(128, INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(ps[preg][0]));
return host_reg;
}
break;
default:
_dbg_assert_msg_(DYNA_REC, false, "Invalid OpArg Type!");
break;
}
// We've got an issue if we end up here
return INVALID_REG;
}
void Arm64FPRCache::BindToRegister(u32 preg, bool do_load)
{
OpArg& reg = m_guest_registers[preg];
if (reg.GetType() == REG_NOTLOADED)
{
ARM64Reg host_reg = GetReg();
reg.LoadToReg(host_reg);
if (do_load)
m_float_emit->LDR(128, INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(ps[preg][0]));
}
}
void Arm64FPRCache::GetAllocationOrder()
{
const std::vector<ARM64Reg> allocation_order =
{
D0, D1, D2, D3, D4, D5, D6, D7, D8, D9, D10,
D11, D12, D13, D14, D15, D16, D17, D18, D19,
D20, D21, D22, D23, D24, D25, D26, D27, D28,
D29, D30, D31,
// Callee saved
Q8, Q9, Q10, Q11, Q12, Q13, Q14, Q15,
// Caller saved
Q16, Q17, Q18, Q19, Q20, Q21, Q22, Q23,
Q24, Q25, Q26, Q27, Q28, Q29, Q30, Q31,
Q7, Q6, Q5, Q4, Q3, Q2, Q1, Q0
};
for (ARM64Reg reg : allocation_order)
m_host_registers.push_back(HostReg(reg));
}
void Arm64FPRCache::FlushMostStaleRegister()
{
// XXX: Flush a register
}
void Arm64FPRCache::FlushByHost(ARM64Reg host_reg)
{
// XXX: Scan guest registers and flush if found
}
bool Arm64FPRCache::IsCalleeSaved(ARM64Reg reg)
{
static std::vector<ARM64Reg> callee_regs =
{
Q8, Q9, Q10, Q11, Q12, Q13, Q14, Q15, INVALID_REG,
};
return std::find(callee_regs.begin(), callee_regs.end(), EncodeRegTo64(reg)) != callee_regs.end();
}
void Arm64FPRCache::FlushRegister(u32 preg, bool maintain_state)
{
OpArg& reg = m_guest_registers[preg];
if (reg.GetType() == REG_REG)
{
ARM64Reg host_reg = reg.GetReg();
m_float_emit->STR(128, INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(ps[preg][0]));
if (!maintain_state)
{
UnlockRegister(host_reg);
reg.Flush();
}
}
}
BitSet32 Arm64FPRCache::GetCallerSavedUsed()
{
BitSet32 registers(0);
for (auto& it : m_host_registers)
if (it.IsLocked())
registers[Q0 - it.GetReg()] = 1;
return registers;
}

View File

@ -119,7 +119,7 @@ private:
class Arm64RegCache
{
public:
Arm64RegCache() : m_emit(nullptr), m_reg_stats(nullptr) {};
Arm64RegCache() : m_emit(nullptr), m_float_emit(nullptr), m_reg_stats(nullptr) {};
virtual ~Arm64RegCache() {};
void Init(ARM64XEmitter *emitter);
@ -133,10 +133,14 @@ public:
// Will dump an immediate to the host register as well
virtual ARM64Reg R(u32 reg) = 0;
virtual BitSet32 GetCallerSavedUsed() = 0;
// Returns a temporary register for use
// Requires unlocking after done
ARM64Reg GetReg();
void StoreRegister(u32 preg) { FlushRegister(preg, false); }
// Locks a register so a cache cannot use it
// Useful for function calls
template<typename T = ARM64Reg, typename... Args>
@ -166,7 +170,7 @@ protected:
virtual void GetAllocationOrder() = 0;
// Flushes the most stale register
virtual void FlushMostStaleRegister() = 0;
void FlushMostStaleRegister();
// Lock a register
void LockRegister(ARM64Reg host_reg);
@ -177,15 +181,31 @@ protected:
// Flushes a guest register by host provided
virtual void FlushByHost(ARM64Reg host_reg) = 0;
virtual void FlushRegister(u32 preg, bool maintain_state) = 0;
// Get available host registers
u32 GetUnlockedRegisterCount();
void IncrementAllUsed()
{
for (auto& reg : m_guest_registers)
reg.IncrementLastUsed();
}
// Code emitter
ARM64XEmitter *m_emit;
// Float emitter
std::unique_ptr<ARM64FloatEmitter> m_float_emit;
// Host side registers that hold the host registers in order of use
std::vector<HostReg> m_host_registers;
// Our guest GPRs
// PowerPC has 32 GPRs
// PowerPC also has 32 paired FPRs
OpArg m_guest_registers[32];
// Register stats for the current block
PPCAnalyst::BlockRegStats *m_reg_stats;
};
@ -215,34 +235,20 @@ public:
void BindToRegister(u32 preg, bool do_load);
void StoreRegister(u32 preg) { FlushRegister(preg, false); }
BitSet32 GetCallerSavedUsed();
BitSet32 GetCallerSavedUsed() override;
protected:
// Get the order of the host registers
void GetAllocationOrder();
// Flushes the most stale register
void FlushMostStaleRegister();
// Flushes a guest register by host provided
void FlushByHost(ARM64Reg host_reg) override;
// Our guest GPRs
// PowerPC has 32 GPRs
OpArg m_guest_registers[32];
void FlushRegister(u32 preg, bool maintain_state) override;
private:
bool IsCalleeSaved(ARM64Reg reg);
void IncrementAllUsed()
{
for (auto& reg : m_guest_registers)
reg.IncrementLastUsed();
}
void FlushRegister(u32 preg, bool maintain_state);
};
class Arm64FPRCache : public Arm64RegCache
@ -256,17 +262,19 @@ public:
// Will dump an immediate to the host register as well
ARM64Reg R(u32 preg);
void BindToRegister(u32 preg, bool do_load);
BitSet32 GetCallerSavedUsed() override;
protected:
// Get the order of the host registers
void GetAllocationOrder();
// Flushes the most stale register
void FlushMostStaleRegister();
// Flushes a guest register by host provided
void FlushByHost(ARM64Reg host_reg) override;
// Our guest FPRs
// Gekko has 32 paired registers(32x2)
OpArg m_guest_registers[32][2];
void FlushRegister(u32 preg, bool maintain_state) override;
private:
bool IsCalleeSaved(ARM64Reg reg);
};