From 52a532370a7525f9c859990fa271d844d0b3387c Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Wed, 7 Jan 2015 14:24:29 -0600 Subject: [PATCH] [AArch64] Implement FPR Cache. --- .../PowerPC/JitArm64/JitArm64_RegCache.cpp | 142 ++++++++++++++---- .../Core/PowerPC/JitArm64/JitArm64_RegCache.h | 56 ++++--- 2 files changed, 146 insertions(+), 52 deletions(-) diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp index fc7fc952d6..0abcb29abb 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp @@ -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 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 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; +} diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.h b/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.h index d032a16f82..115fad0720 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.h +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.h @@ -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 @@ -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 m_float_emit; + // Host side registers that hold the host registers in order of use std::vector 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); };