[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)
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue