JitArm64_RegCache: Implement caching of cr_val

This commit is contained in:
MerryMage 2017-04-26 16:49:44 +00:00
parent 68ee4fc932
commit a9fbf69cad
2 changed files with 135 additions and 52 deletions

View File

@ -92,6 +92,15 @@ void Arm64RegCache::FlushMostStaleRegister()
} }
// GPR Cache // GPR Cache
constexpr size_t GUEST_GPR_COUNT = 32;
constexpr size_t GUEST_CR_COUNT = 8;
constexpr size_t GUEST_GPR_OFFSET = 0;
constexpr size_t GUEST_CR_OFFSET = GUEST_GPR_COUNT;
Arm64GPRCache::Arm64GPRCache() : Arm64RegCache(GUEST_GPR_COUNT + GUEST_CR_COUNT)
{
}
void Arm64GPRCache::Start(PPCAnalyst::BlockRegStats& stats) void Arm64GPRCache::Start(PPCAnalyst::BlockRegStats& stats)
{ {
} }
@ -105,18 +114,48 @@ bool Arm64GPRCache::IsCalleeSaved(ARM64Reg reg)
return std::find(callee_regs.begin(), callee_regs.end(), EncodeRegTo64(reg)) != callee_regs.end(); return std::find(callee_regs.begin(), callee_regs.end(), EncodeRegTo64(reg)) != callee_regs.end();
} }
void Arm64GPRCache::FlushRegister(size_t preg, bool maintain_state) const OpArg& Arm64GPRCache::GetGuestGPROpArg(size_t preg) const
{ {
OpArg& reg = m_guest_registers[preg]; _assert_(preg < GUEST_GPR_COUNT);
return m_guest_registers[preg];
}
Arm64GPRCache::GuestRegInfo Arm64GPRCache::GetGuestGPR(size_t preg)
{
_assert_(preg < GUEST_GPR_COUNT);
return {32, PPCSTATE_OFF(gpr[preg]), m_guest_registers[GUEST_GPR_OFFSET + preg]};
}
Arm64GPRCache::GuestRegInfo Arm64GPRCache::GetGuestCR(size_t preg)
{
_assert_(preg < GUEST_CR_COUNT);
return {64, PPCSTATE_OFF(cr_val[preg]), m_guest_registers[GUEST_CR_OFFSET + preg]};
}
Arm64GPRCache::GuestRegInfo Arm64GPRCache::GetGuestByIndex(size_t index)
{
if (index >= GUEST_GPR_OFFSET && index < GUEST_GPR_OFFSET + GUEST_GPR_COUNT)
return GetGuestGPR(index - GUEST_GPR_OFFSET);
if (index >= GUEST_CR_OFFSET && index < GUEST_CR_OFFSET + GUEST_CR_COUNT)
return GetGuestCR(index - GUEST_CR_OFFSET);
_assert_msg_(DYNA_REC, false, "Invalid index for guest register");
}
void Arm64GPRCache::FlushRegister(size_t index, bool maintain_state)
{
GuestRegInfo guest_reg = GetGuestByIndex(index);
OpArg& reg = guest_reg.reg;
size_t bitsize = guest_reg.bitsize;
if (reg.GetType() == REG_REG) if (reg.GetType() == REG_REG)
{ {
ARM64Reg host_reg = reg.GetReg(); ARM64Reg host_reg = reg.GetReg();
if (reg.IsDirty()) if (reg.IsDirty())
m_emit->STR(INDEX_UNSIGNED, host_reg, PPC_REG, PPCSTATE_OFF(gpr[preg])); m_emit->STR(INDEX_UNSIGNED, host_reg, PPC_REG, guest_reg.ppc_offset);
if (!maintain_state) if (!maintain_state)
{ {
UnlockRegister(host_reg); UnlockRegister(DecodeReg(host_reg));
reg.Flush(); reg.Flush();
} }
} }
@ -124,16 +163,16 @@ void Arm64GPRCache::FlushRegister(size_t preg, bool maintain_state)
{ {
if (!reg.GetImm()) if (!reg.GetImm())
{ {
m_emit->STR(INDEX_UNSIGNED, WSP, PPC_REG, PPCSTATE_OFF(gpr[preg])); m_emit->STR(INDEX_UNSIGNED, bitsize == 64 ? ZR : WZR, PPC_REG, guest_reg.ppc_offset);
} }
else else
{ {
ARM64Reg host_reg = GetReg(); ARM64Reg host_reg = bitsize != 64 ? GetReg() : EncodeRegTo64(GetReg());
m_emit->MOVI2R(host_reg, reg.GetImm()); m_emit->MOVI2R(host_reg, reg.GetImm());
m_emit->STR(INDEX_UNSIGNED, host_reg, PPC_REG, PPCSTATE_OFF(gpr[preg])); m_emit->STR(INDEX_UNSIGNED, host_reg, PPC_REG, guest_reg.ppc_offset);
UnlockRegister(host_reg); UnlockRegister(DecodeReg(host_reg));
} }
if (!maintain_state) if (!maintain_state)
@ -143,11 +182,11 @@ void Arm64GPRCache::FlushRegister(size_t preg, bool maintain_state)
void Arm64GPRCache::FlushRegisters(BitSet32 regs, bool maintain_state) void Arm64GPRCache::FlushRegisters(BitSet32 regs, bool maintain_state)
{ {
for (size_t i = 0; i < m_guest_registers.size(); ++i) for (size_t i = 0; i < GUEST_GPR_COUNT; ++i)
{ {
if (regs[i]) if (regs[i])
{ {
if (i < 31 && regs[i + 1]) if (i + 1 < GUEST_GPR_COUNT && regs[i + 1])
{ {
// We've got two guest registers in a row to store // We've got two guest registers in a row to store
OpArg& reg1 = m_guest_registers[i]; OpArg& reg1 = m_guest_registers[i];
@ -155,10 +194,10 @@ void Arm64GPRCache::FlushRegisters(BitSet32 regs, bool maintain_state)
if (reg1.IsDirty() && reg2.IsDirty() && reg1.GetType() == REG_REG && if (reg1.IsDirty() && reg2.IsDirty() && reg1.GetType() == REG_REG &&
reg2.GetType() == REG_REG) reg2.GetType() == REG_REG)
{ {
size_t ppc_offset = GetGuestByIndex(i).ppc_offset;
ARM64Reg RX1 = R(i); ARM64Reg RX1 = R(i);
ARM64Reg RX2 = R(i + 1); ARM64Reg RX2 = R(i + 1);
m_emit->STP(INDEX_SIGNED, RX1, RX2, PPC_REG, ppc_offset);
m_emit->STP(INDEX_SIGNED, RX1, RX2, PPC_REG, PPCSTATE_OFF(gpr[0]) + i * sizeof(u32));
if (!maintain_state) if (!maintain_state)
{ {
UnlockRegister(RX1); UnlockRegister(RX1);
@ -171,7 +210,18 @@ void Arm64GPRCache::FlushRegisters(BitSet32 regs, bool maintain_state)
} }
} }
FlushRegister(i, maintain_state); FlushRegister(GUEST_GPR_OFFSET + i, maintain_state);
}
}
}
void Arm64GPRCache::FlushCRRegisters(BitSet32 regs, bool maintain_state)
{
for (size_t i = 0; i < GUEST_CR_COUNT; ++i)
{
if (regs[i])
{
FlushRegister(GUEST_CR_OFFSET + i, maintain_state);
} }
} }
} }
@ -179,7 +229,7 @@ void Arm64GPRCache::FlushRegisters(BitSet32 regs, bool maintain_state)
void Arm64GPRCache::Flush(FlushMode mode, PPCAnalyst::CodeOp* op) void Arm64GPRCache::Flush(FlushMode mode, PPCAnalyst::CodeOp* op)
{ {
BitSet32 to_flush; BitSet32 to_flush;
for (size_t i = 0; i < m_guest_registers.size(); ++i) for (size_t i = 0; i < GUEST_GPR_COUNT; ++i)
{ {
bool flush = true; bool flush = true;
if (m_guest_registers[i].GetType() == REG_REG) if (m_guest_registers[i].GetType() == REG_REG)
@ -192,11 +242,14 @@ void Arm64GPRCache::Flush(FlushMode mode, PPCAnalyst::CodeOp* op)
to_flush[i] = flush; to_flush[i] = flush;
} }
FlushRegisters(to_flush, mode == FLUSH_MAINTAIN_STATE); FlushRegisters(to_flush, mode == FLUSH_MAINTAIN_STATE);
FlushCRRegisters(BitSet32(~0U), mode == FLUSH_MAINTAIN_STATE);
} }
ARM64Reg Arm64GPRCache::R(size_t preg) ARM64Reg Arm64GPRCache::R(const GuestRegInfo& guest_reg)
{ {
OpArg& reg = m_guest_registers[preg]; OpArg& reg = guest_reg.reg;
size_t bitsize = guest_reg.bitsize;
IncrementAllUsed(); IncrementAllUsed();
reg.ResetLastUsed(); reg.ResetLastUsed();
@ -207,7 +260,7 @@ ARM64Reg Arm64GPRCache::R(size_t preg)
break; break;
case REG_IMM: // Is an immediate case REG_IMM: // Is an immediate
{ {
ARM64Reg host_reg = GetReg(); ARM64Reg host_reg = bitsize != 64 ? GetReg() : EncodeRegTo64(GetReg());
m_emit->MOVI2R(host_reg, reg.GetImm()); m_emit->MOVI2R(host_reg, reg.GetImm());
reg.Load(host_reg); reg.Load(host_reg);
reg.SetDirty(true); reg.SetDirty(true);
@ -219,10 +272,10 @@ ARM64Reg Arm64GPRCache::R(size_t preg)
// This is a bit annoying. We try to keep these preloaded as much as possible // 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 // This can also happen on cases where PPCAnalyst isn't feeing us proper register usage
// statistics // statistics
ARM64Reg host_reg = GetReg(); ARM64Reg host_reg = bitsize != 64 ? GetReg() : EncodeRegTo64(GetReg());
reg.Load(host_reg); reg.Load(host_reg);
reg.SetDirty(false); reg.SetDirty(false);
m_emit->LDR(INDEX_UNSIGNED, host_reg, PPC_REG, PPCSTATE_OFF(gpr[preg])); m_emit->LDR(INDEX_UNSIGNED, host_reg, PPC_REG, guest_reg.ppc_offset);
return host_reg; return host_reg;
} }
break; break;
@ -234,27 +287,28 @@ ARM64Reg Arm64GPRCache::R(size_t preg)
return INVALID_REG; return INVALID_REG;
} }
void Arm64GPRCache::SetImmediate(size_t preg, u32 imm) void Arm64GPRCache::SetImmediate(const GuestRegInfo& guest_reg, u32 imm)
{ {
OpArg& reg = m_guest_registers[preg]; OpArg& reg = guest_reg.reg;
if (reg.GetType() == REG_REG) if (reg.GetType() == REG_REG)
UnlockRegister(reg.GetReg()); UnlockRegister(DecodeReg(reg.GetReg()));
reg.LoadToImm(imm); reg.LoadToImm(imm);
} }
void Arm64GPRCache::BindToRegister(size_t preg, bool do_load) void Arm64GPRCache::BindToRegister(const GuestRegInfo& guest_reg, bool do_load)
{ {
OpArg& reg = m_guest_registers[preg]; OpArg& reg = guest_reg.reg;
size_t bitsize = guest_reg.bitsize;
reg.ResetLastUsed(); reg.ResetLastUsed();
reg.SetDirty(true); reg.SetDirty(true);
if (reg.GetType() == REG_NOTLOADED) if (reg.GetType() == REG_NOTLOADED)
{ {
ARM64Reg host_reg = GetReg(); ARM64Reg host_reg = bitsize != 64 ? GetReg() : EncodeRegTo64(GetReg());
reg.Load(host_reg); reg.Load(host_reg);
if (do_load) if (do_load)
m_emit->LDR(INDEX_UNSIGNED, host_reg, PPC_REG, PPCSTATE_OFF(gpr[preg])); m_emit->LDR(INDEX_UNSIGNED, host_reg, PPC_REG, guest_reg.ppc_offset);
} }
} }
@ -278,16 +332,17 @@ BitSet32 Arm64GPRCache::GetCallerSavedUsed()
BitSet32 registers(0); BitSet32 registers(0);
for (auto& it : m_host_registers) for (auto& it : m_host_registers)
if (it.IsLocked() && !IsCalleeSaved(it.GetReg())) if (it.IsLocked() && !IsCalleeSaved(it.GetReg()))
registers[it.GetReg()] = 1; registers[DecodeReg(it.GetReg())] = 1;
return registers; return registers;
} }
void Arm64GPRCache::FlushByHost(ARM64Reg host_reg) void Arm64GPRCache::FlushByHost(ARM64Reg host_reg)
{ {
host_reg = DecodeReg(host_reg);
for (size_t i = 0; i < m_guest_registers.size(); ++i) for (size_t i = 0; i < m_guest_registers.size(); ++i)
{ {
const OpArg& reg = m_guest_registers[i]; const OpArg& reg = m_guest_registers[i];
if (reg.GetType() == REG_REG && reg.GetReg() == host_reg) if (reg.GetType() == REG_REG && DecodeReg(reg.GetReg()) == host_reg)
{ {
FlushRegister(i, false); FlushRegister(i, false);
return; return;
@ -296,6 +351,12 @@ void Arm64GPRCache::FlushByHost(ARM64Reg host_reg)
} }
// FPR Cache // FPR Cache
constexpr size_t GUEST_FPR_COUNT = 32;
Arm64FPRCache::Arm64FPRCache() : Arm64RegCache(GUEST_FPR_COUNT)
{
}
void Arm64FPRCache::Flush(FlushMode mode, PPCAnalyst::CodeOp* op) void Arm64FPRCache::Flush(FlushMode mode, PPCAnalyst::CodeOp* op)
{ {
for (size_t i = 0; i < m_guest_registers.size(); ++i) for (size_t i = 0; i < m_guest_registers.size(); ++i)

View File

@ -4,7 +4,6 @@
#pragma once #pragma once
#include <array>
#include <cstddef> #include <cstddef>
#include <memory> #include <memory>
#include <vector> #include <vector>
@ -118,7 +117,9 @@ private:
class Arm64RegCache class Arm64RegCache
{ {
public: public:
Arm64RegCache() : m_emit(nullptr), m_float_emit(nullptr), m_reg_stats(nullptr){}; explicit Arm64RegCache(size_t guest_reg_count)
: m_emit(nullptr), m_float_emit(nullptr), m_guest_registers(guest_reg_count),
m_reg_stats(nullptr){};
virtual ~Arm64RegCache(){}; virtual ~Arm64RegCache(){};
void Init(ARM64XEmitter* emitter); void Init(ARM64XEmitter* emitter);
@ -133,7 +134,6 @@ public:
// Requires unlocking after done // Requires unlocking after done
ARM64Reg GetReg(); ARM64Reg GetReg();
void StoreRegisters(BitSet32 regs) { FlushRegisters(regs, 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>
@ -176,8 +176,6 @@ protected:
virtual void FlushRegister(size_t preg, bool maintain_state) = 0; virtual void FlushRegister(size_t preg, bool maintain_state) = 0;
virtual void FlushRegisters(BitSet32 regs, bool maintain_state) = 0;
// Get available host registers // Get available host registers
u32 GetUnlockedRegisterCount(); u32 GetUnlockedRegisterCount();
@ -197,9 +195,9 @@ protected:
std::vector<HostReg> m_host_registers; std::vector<HostReg> m_host_registers;
// Our guest GPRs // Our guest GPRs
// PowerPC has 32 GPRs // PowerPC has 32 GPRs and 8 CRs
// PowerPC also has 32 paired FPRs // PowerPC also has 32 paired FPRs
std::array<OpArg, 32> m_guest_registers; std::vector<OpArg> m_guest_registers;
// Register stats for the current block // Register stats for the current block
PPCAnalyst::BlockRegStats* m_reg_stats; PPCAnalyst::BlockRegStats* m_reg_stats;
@ -208,27 +206,32 @@ protected:
class Arm64GPRCache : public Arm64RegCache class Arm64GPRCache : public Arm64RegCache
{ {
public: public:
Arm64GPRCache();
~Arm64GPRCache() {} ~Arm64GPRCache() {}
void Start(PPCAnalyst::BlockRegStats& stats) override; void Start(PPCAnalyst::BlockRegStats& stats) override;
// Flushes the register cache in different ways depending on the mode // Flushes the register cache in different ways depending on the mode
void Flush(FlushMode mode, PPCAnalyst::CodeOp* op = nullptr) override; void Flush(FlushMode mode, PPCAnalyst::CodeOp* op = nullptr) override;
// Returns a guest register inside of a host register // Returns a guest GPR inside of a host register
// Will dump an immediate to the host register as well // Will dump an immediate to the host register as well
ARM64Reg R(size_t preg); ARM64Reg R(size_t preg) { return R(GetGuestGPR(preg)); }
// Returns a guest CR inside of a host register
// Set a register to an immediate ARM64Reg CR(size_t preg) { return R(GetGuestCR(preg)); }
void SetImmediate(size_t preg, u32 imm); // Set a register to an immediate, only valid for guest GPRs
void SetImmediate(size_t preg, u32 imm) { SetImmediate(GetGuestGPR(preg), imm); }
// Returns if a register is set as an immediate // Returns if a register is set as an immediate, only valid for guest GPRs
bool IsImm(size_t reg) const { return m_guest_registers[reg].GetType() == REG_IMM; } bool IsImm(size_t preg) const { return GetGuestGPROpArg(preg).GetType() == REG_IMM; }
// Gets the immediate that a register is set to // Gets the immediate that a register is set to, only valid for guest GPRs
u32 GetImm(size_t reg) const { return m_guest_registers[reg].GetImm(); } u32 GetImm(size_t preg) const { return GetGuestGPROpArg(preg).GetImm(); }
void BindToRegister(size_t preg, bool do_load); // Binds a guest GPR to a host register, optionally loading its value
void BindToRegister(size_t preg, bool do_load) { BindToRegister(GetGuestGPR(preg), do_load); }
// Binds a guest CR to a host register, optionally loading its value
void BindCRToRegister(size_t preg, bool do_load) { BindToRegister(GetGuestCR(preg), do_load); }
BitSet32 GetCallerSavedUsed() override; BitSet32 GetCallerSavedUsed() override;
void StoreRegisters(BitSet32 regs) { FlushRegisters(regs, false); }
void StoreCRRegisters(BitSet32 regs) { FlushCRRegisters(regs, false); }
protected: protected:
// Get the order of the host registers // Get the order of the host registers
void GetAllocationOrder() override; void GetAllocationOrder() override;
@ -236,17 +239,35 @@ protected:
// 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;
void FlushRegister(size_t preg, bool maintain_state) override; void FlushRegister(size_t index, bool maintain_state) override;
void FlushRegisters(BitSet32 regs, bool maintain_state) override;
private: private:
bool IsCalleeSaved(ARM64Reg reg); bool IsCalleeSaved(ARM64Reg reg);
struct GuestRegInfo
{
size_t bitsize;
size_t ppc_offset;
OpArg& reg;
};
const OpArg& GetGuestGPROpArg(size_t preg) const;
GuestRegInfo GetGuestGPR(size_t preg);
GuestRegInfo GetGuestCR(size_t preg);
GuestRegInfo GetGuestByIndex(size_t index);
ARM64Reg R(const GuestRegInfo& guest_reg);
void SetImmediate(const GuestRegInfo& guest_reg, u32 imm);
void BindToRegister(const GuestRegInfo& guest_reg, bool do_load);
void FlushRegisters(BitSet32 regs, bool maintain_state);
void FlushCRRegisters(BitSet32 regs, bool maintain_state);
}; };
class Arm64FPRCache : public Arm64RegCache class Arm64FPRCache : public Arm64RegCache
{ {
public: public:
Arm64FPRCache();
~Arm64FPRCache() {} ~Arm64FPRCache() {}
// Flushes the register cache in different ways depending on the mode // Flushes the register cache in different ways depending on the mode
void Flush(FlushMode mode, PPCAnalyst::CodeOp* op = nullptr) override; void Flush(FlushMode mode, PPCAnalyst::CodeOp* op = nullptr) override;
@ -263,6 +284,7 @@ public:
void FixSinglePrecision(size_t preg); void FixSinglePrecision(size_t preg);
void StoreRegisters(BitSet32 regs) { FlushRegisters(regs, false); }
protected: protected:
// Get the order of the host registers // Get the order of the host registers
void GetAllocationOrder() override; void GetAllocationOrder() override;
@ -272,8 +294,8 @@ protected:
void FlushRegister(size_t preg, bool maintain_state) override; void FlushRegister(size_t preg, bool maintain_state) override;
void FlushRegisters(BitSet32 regs, bool maintain_state) override;
private: private:
bool IsCalleeSaved(ARM64Reg reg); bool IsCalleeSaved(ARM64Reg reg);
void FlushRegisters(BitSet32 regs, bool maintain_state);
}; };