[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) void Arm64RegCache::Init(ARM64XEmitter *emitter)
{ {
m_emit = emitter; m_emit = emitter;
m_float_emit.reset(new ARM64FloatEmitter(m_emit));
GetAllocationOrder(); GetAllocationOrder();
} }
@ -56,6 +57,23 @@ void Arm64RegCache::UnlockRegister(ARM64Reg host_reg)
reg->Unlock(); 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 // GPR Cache
void Arm64GPRCache::Start(PPCAnalyst::BlockRegStats &stats) void Arm64GPRCache::Start(PPCAnalyst::BlockRegStats &stats)
{ {
@ -212,23 +230,6 @@ void Arm64GPRCache::GetAllocationOrder()
m_host_registers.push_back(HostReg(reg)); 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 Arm64GPRCache::GetCallerSavedUsed()
{ {
BitSet32 registers(0); BitSet32 registers(0);
@ -254,35 +255,120 @@ void Arm64GPRCache::FlushByHost(ARM64Reg host_reg)
// FPR Cache // FPR Cache
void Arm64FPRCache::Flush(FlushMode mode, PPCAnalyst::CodeOp* op) 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) 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() void Arm64FPRCache::GetAllocationOrder()
{ {
const std::vector<ARM64Reg> allocation_order = const std::vector<ARM64Reg> allocation_order =
{ {
D0, D1, D2, D3, D4, D5, D6, D7, D8, D9, D10, // Callee saved
D11, D12, D13, D14, D15, D16, D17, D18, D19, Q8, Q9, Q10, Q11, Q12, Q13, Q14, Q15,
D20, D21, D22, D23, D24, D25, D26, D27, D28,
D29, D30, D31, // 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) for (ARM64Reg reg : allocation_order)
m_host_registers.push_back(HostReg(reg)); m_host_registers.push_back(HostReg(reg));
} }
void Arm64FPRCache::FlushMostStaleRegister()
{
// XXX: Flush a register
}
void Arm64FPRCache::FlushByHost(ARM64Reg host_reg) void Arm64FPRCache::FlushByHost(ARM64Reg host_reg)
{ {
// XXX: Scan guest registers and flush if found // 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 class Arm64RegCache
{ {
public: public:
Arm64RegCache() : m_emit(nullptr), m_reg_stats(nullptr) {}; Arm64RegCache() : m_emit(nullptr), m_float_emit(nullptr), m_reg_stats(nullptr) {};
virtual ~Arm64RegCache() {}; virtual ~Arm64RegCache() {};
void Init(ARM64XEmitter *emitter); void Init(ARM64XEmitter *emitter);
@ -133,10 +133,14 @@ public:
// Will dump an immediate to the host register as well // Will dump an immediate to the host register as well
virtual ARM64Reg R(u32 reg) = 0; virtual ARM64Reg R(u32 reg) = 0;
virtual BitSet32 GetCallerSavedUsed() = 0;
// Returns a temporary register for use // Returns a temporary register for use
// Requires unlocking after done // Requires unlocking after done
ARM64Reg GetReg(); ARM64Reg GetReg();
void StoreRegister(u32 preg) { FlushRegister(preg, false); }
// Locks a register so a cache cannot use it // Locks a register so a cache cannot use it
// Useful for function calls // Useful for function calls
template<typename T = ARM64Reg, typename... Args> template<typename T = ARM64Reg, typename... Args>
@ -166,7 +170,7 @@ protected:
virtual void GetAllocationOrder() = 0; virtual void GetAllocationOrder() = 0;
// Flushes the most stale register // Flushes the most stale register
virtual void FlushMostStaleRegister() = 0; void FlushMostStaleRegister();
// Lock a register // Lock a register
void LockRegister(ARM64Reg host_reg); void LockRegister(ARM64Reg host_reg);
@ -177,15 +181,31 @@ protected:
// Flushes a guest register by host provided // Flushes a guest register by host provided
virtual void FlushByHost(ARM64Reg host_reg) = 0; virtual void FlushByHost(ARM64Reg host_reg) = 0;
virtual void FlushRegister(u32 preg, bool maintain_state) = 0;
// Get available host registers // Get available host registers
u32 GetUnlockedRegisterCount(); u32 GetUnlockedRegisterCount();
void IncrementAllUsed()
{
for (auto& reg : m_guest_registers)
reg.IncrementLastUsed();
}
// Code emitter // Code emitter
ARM64XEmitter *m_emit; ARM64XEmitter *m_emit;
// Float emitter
std::unique_ptr<ARM64FloatEmitter> m_float_emit;
// Host side registers that hold the host registers in order of use // Host side registers that hold the host registers in order of use
std::vector<HostReg> m_host_registers; 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 // Register stats for the current block
PPCAnalyst::BlockRegStats *m_reg_stats; PPCAnalyst::BlockRegStats *m_reg_stats;
}; };
@ -215,34 +235,20 @@ public:
void BindToRegister(u32 preg, bool do_load); void BindToRegister(u32 preg, bool do_load);
void StoreRegister(u32 preg) { FlushRegister(preg, false); } BitSet32 GetCallerSavedUsed() override;
BitSet32 GetCallerSavedUsed();
protected: protected:
// Get the order of the host registers // Get the order of the host registers
void GetAllocationOrder(); void GetAllocationOrder();
// Flushes the most stale register
void FlushMostStaleRegister();
// Flushes a guest register by host provided // Flushes a guest register by host provided
void FlushByHost(ARM64Reg host_reg) override; void FlushByHost(ARM64Reg host_reg) override;
// Our guest GPRs void FlushRegister(u32 preg, bool maintain_state) override;
// PowerPC has 32 GPRs
OpArg m_guest_registers[32];
private: private:
bool IsCalleeSaved(ARM64Reg reg); 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 class Arm64FPRCache : public Arm64RegCache
@ -256,17 +262,19 @@ public:
// Will dump an immediate to the host register as well // Will dump an immediate to the host register as well
ARM64Reg R(u32 preg); ARM64Reg R(u32 preg);
void BindToRegister(u32 preg, bool do_load);
BitSet32 GetCallerSavedUsed() override;
protected: protected:
// Get the order of the host registers // Get the order of the host registers
void GetAllocationOrder(); void GetAllocationOrder();
// Flushes the most stale register
void FlushMostStaleRegister();
// Flushes a guest register by host provided // Flushes a guest register by host provided
void FlushByHost(ARM64Reg host_reg) override; void FlushByHost(ARM64Reg host_reg) override;
// Our guest FPRs void FlushRegister(u32 preg, bool maintain_state) override;
// Gekko has 32 paired registers(32x2)
OpArg m_guest_registers[32][2]; private:
bool IsCalleeSaved(ARM64Reg reg);
}; };