[AArch64] Implement FPR Cache.
This commit is contained in:
parent
c511ee763a
commit
52a532370a
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue