JitRegCache: Encapsulate behavior of PPCCachedReg

This commit is contained in:
MerryMage 2018-09-30 12:49:03 +01:00
parent 406159f7f0
commit dd41bab365
5 changed files with 149 additions and 106 deletions

View File

@ -16,12 +16,12 @@ FPURegCache::FPURegCache(Jit64& jit) : RegCache{jit}
void FPURegCache::StoreRegister(size_t preg, const OpArg& new_loc) void FPURegCache::StoreRegister(size_t preg, const OpArg& new_loc)
{ {
m_emitter->MOVAPD(new_loc, m_regs[preg].location.GetSimpleReg()); m_emitter->MOVAPD(new_loc, m_regs[preg].Location().GetSimpleReg());
} }
void FPURegCache::LoadRegister(size_t preg, X64Reg new_loc) void FPURegCache::LoadRegister(size_t preg, X64Reg new_loc)
{ {
m_emitter->MOVAPD(new_loc, m_regs[preg].location); m_emitter->MOVAPD(new_loc, m_regs[preg].Location());
} }
const X64Reg* FPURegCache::GetAllocationOrder(size_t* count) const const X64Reg* FPURegCache::GetAllocationOrder(size_t* count) const

View File

@ -16,12 +16,12 @@ GPRRegCache::GPRRegCache(Jit64& jit) : RegCache{jit}
void GPRRegCache::StoreRegister(size_t preg, const OpArg& new_loc) void GPRRegCache::StoreRegister(size_t preg, const OpArg& new_loc)
{ {
m_emitter->MOV(32, new_loc, m_regs[preg].location); m_emitter->MOV(32, new_loc, m_regs[preg].Location());
} }
void GPRRegCache::LoadRegister(size_t preg, X64Reg new_loc) void GPRRegCache::LoadRegister(size_t preg, X64Reg new_loc)
{ {
m_emitter->MOV(32, ::Gen::R(new_loc), m_regs[preg].location); m_emitter->MOV(32, ::Gen::R(new_loc), m_regs[preg].Location());
} }
OpArg GPRRegCache::GetDefaultLocation(size_t reg) const OpArg GPRRegCache::GetDefaultLocation(size_t reg) const
@ -51,8 +51,7 @@ void GPRRegCache::SetImmediate32(size_t preg, u32 imm_value, bool dirty)
// "dirty" can be false to avoid redundantly flushing an immediate when // "dirty" can be false to avoid redundantly flushing an immediate when
// processing speculative constants. // processing speculative constants.
DiscardRegContentsIfCached(preg); DiscardRegContentsIfCached(preg);
m_regs[preg].away |= dirty; m_regs[preg].SetToImm32(imm_value, dirty);
m_regs[preg].location = Imm32(imm_value);
} }
BitSet32 GPRRegCache::GetRegUtilization() const BitSet32 GPRRegCache::GetRegUtilization() const

View File

@ -35,38 +35,19 @@ void RegCache::Start()
} }
for (size_t i = 0; i < m_regs.size(); i++) for (size_t i = 0; i < m_regs.size(); i++)
{ {
m_regs[i].location = GetDefaultLocation(i); m_regs[i] = PPCCachedReg{GetDefaultLocation(i)};
m_regs[i].away = false;
m_regs[i].locked = false;
} }
// todo: sort to find the most popular regs
/*
int maxPreload = 2;
for (int i = 0; i < 32; i++)
{
if (stats.numReads[i] > 2 || stats.numWrites[i] >= 2)
{
LoadToX64(i, true, false); //stats.firstRead[i] <= stats.firstWrite[i], false);
maxPreload--;
if (!maxPreload)
break;
}
}*/
// Find top regs - preload them (load bursts ain't bad)
// But only preload IF written OR reads >= 3
} }
void RegCache::DiscardRegContentsIfCached(size_t preg) void RegCache::DiscardRegContentsIfCached(size_t preg)
{ {
if (IsBound(preg)) if (m_regs[preg].IsBound())
{ {
X64Reg xr = m_regs[preg].location.GetSimpleReg(); X64Reg xr = m_regs[preg].Location().GetSimpleReg();
m_xregs[xr].free = true; m_xregs[xr].free = true;
m_xregs[xr].dirty = false; m_xregs[xr].dirty = false;
m_xregs[xr].ppcReg = static_cast<size_t>(INVALID_REG); m_xregs[xr].ppcReg = static_cast<size_t>(INVALID_REG);
m_regs[preg].away = false; m_regs[preg].Flushed();
m_regs[preg].location = GetDefaultLocation(preg);
} }
} }
@ -85,28 +66,25 @@ void RegCache::Flush(FlushMode mode, BitSet32 regsToFlush)
for (unsigned int i : regsToFlush) for (unsigned int i : regsToFlush)
{ {
if (m_regs[i].locked) if (m_regs[i].IsLocked())
{ {
PanicAlert("Someone forgot to unlock PPC reg %u (X64 reg %i).", i, RX(i)); PanicAlert("Someone forgot to unlock PPC reg %u (X64 reg %i).", i, RX(i));
} }
if (m_regs[i].away) switch (m_regs[i].GetLocationType())
{
if (m_regs[i].location.IsSimpleReg() || m_regs[i].location.IsImm())
{
StoreFromRegister(i, mode);
}
else
{
ASSERT_MSG(DYNA_REC, 0, "Jit64 - Flush unhandled case, reg %u PC: %08x", i, PC);
}
}
else if (m_regs[i].location.IsImm())
{ {
case PPCCachedReg::LocationType::Default:
break;
case PPCCachedReg::LocationType::SpeculativeImmediate:
// We can have a cached value without a host register through speculative constants. // We can have a cached value without a host register through speculative constants.
// It must be cleared when flushing, otherwise it may be out of sync with PPCSTATE, // It must be cleared when flushing, otherwise it may be out of sync with PPCSTATE,
// if PPCSTATE is modified externally (e.g. fallback to interpreter). // if PPCSTATE is modified externally (e.g. fallback to interpreter).
m_regs[i].location = GetDefaultLocation(i); m_regs[i].Flushed();
break;
case PPCCachedReg::LocationType::Bound:
case PPCCachedReg::LocationType::Immediate:
StoreFromRegister(i, mode);
break;
} }
} }
} }
@ -145,20 +123,22 @@ int RegCache::SanityCheck() const
{ {
for (size_t i = 0; i < m_regs.size(); i++) for (size_t i = 0; i < m_regs.size(); i++)
{ {
if (m_regs[i].away) switch (m_regs[i].GetLocationType())
{ {
if (m_regs[i].location.IsSimpleReg()) case PPCCachedReg::LocationType::Default:
{ case PPCCachedReg::LocationType::SpeculativeImmediate:
Gen::X64Reg simple = m_regs[i].location.GetSimpleReg(); break;
if (m_xregs[simple].locked) case PPCCachedReg::LocationType::Bound:
return 1; {
if (m_xregs[simple].ppcReg != i) Gen::X64Reg simple = m_regs[i].Location().GetSimpleReg();
return 2; if (m_xregs[simple].locked)
} return 1;
else if (m_regs[i].location.IsImm()) if (m_xregs[simple].ppcReg != i)
{ return 2;
return 3; break;
} }
case PPCCachedReg::LocationType::Immediate:
return 3;
} }
} }
return 0; return 0;
@ -166,18 +146,23 @@ int RegCache::SanityCheck() const
void RegCache::KillImmediate(size_t preg, bool doLoad, bool makeDirty) void RegCache::KillImmediate(size_t preg, bool doLoad, bool makeDirty)
{ {
if (m_regs[preg].away) switch (m_regs[preg].GetLocationType())
{ {
if (m_regs[preg].location.IsImm()) case PPCCachedReg::LocationType::Default:
BindToRegister(preg, doLoad, makeDirty); case PPCCachedReg::LocationType::SpeculativeImmediate:
else if (m_regs[preg].location.IsSimpleReg()) break;
m_xregs[RX(preg)].dirty |= makeDirty; case PPCCachedReg::LocationType::Bound:
m_xregs[RX(preg)].dirty |= makeDirty;
break;
case PPCCachedReg::LocationType::Immediate:
BindToRegister(preg, doLoad, makeDirty);
break;
} }
} }
void RegCache::BindToRegister(size_t i, bool doLoad, bool makeDirty) void RegCache::BindToRegister(size_t i, bool doLoad, bool makeDirty)
{ {
if (!m_regs[i].away || m_regs[i].location.IsImm()) if (!m_regs[i].IsBound())
{ {
X64Reg xr = GetFreeXReg(); X64Reg xr = GetFreeXReg();
if (m_xregs[xr].dirty) if (m_xregs[xr].dirty)
@ -186,18 +171,18 @@ void RegCache::BindToRegister(size_t i, bool doLoad, bool makeDirty)
PanicAlert("GetFreeXReg returned locked register"); PanicAlert("GetFreeXReg returned locked register");
m_xregs[xr].free = false; m_xregs[xr].free = false;
m_xregs[xr].ppcReg = i; m_xregs[xr].ppcReg = i;
m_xregs[xr].dirty = makeDirty || m_regs[i].away; m_xregs[xr].dirty = makeDirty || m_regs[i].IsAway();
if (doLoad) if (doLoad)
LoadRegister(i, xr); LoadRegister(i, xr);
for (size_t j = 0; j < m_regs.size(); j++) for (size_t j = 0; j < m_regs.size(); j++)
{ {
if (i != j && m_regs[j].location.IsSimpleReg(xr)) if (i != j && m_regs[j].Location().IsSimpleReg(xr))
{ {
Crash(); Crash();
} }
} }
m_regs[i].away = true; m_regs[i].BoundTo(xr);
m_regs[i].location = ::Gen::R(xr);
} }
else else
{ {
@ -214,45 +199,45 @@ void RegCache::BindToRegister(size_t i, bool doLoad, bool makeDirty)
void RegCache::StoreFromRegister(size_t i, FlushMode mode) void RegCache::StoreFromRegister(size_t i, FlushMode mode)
{ {
if (m_regs[i].away) bool doStore = false;
switch (m_regs[i].GetLocationType())
{ {
bool doStore; case PPCCachedReg::LocationType::Default:
if (m_regs[i].location.IsSimpleReg()) case PPCCachedReg::LocationType::SpeculativeImmediate:
{ return;
X64Reg xr = RX(i); case PPCCachedReg::LocationType::Bound:
doStore = m_xregs[xr].dirty; {
if (mode == FlushMode::All) X64Reg xr = RX(i);
{ doStore = m_xregs[xr].dirty;
m_xregs[xr].free = true;
m_xregs[xr].ppcReg = static_cast<size_t>(INVALID_REG);
m_xregs[xr].dirty = false;
}
}
else
{
// must be immediate - do nothing
doStore = true;
}
OpArg newLoc = GetDefaultLocation(i);
if (doStore)
StoreRegister(i, newLoc);
if (mode == FlushMode::All) if (mode == FlushMode::All)
{ {
m_regs[i].location = newLoc; m_xregs[xr].free = true;
m_regs[i].away = false; m_xregs[xr].ppcReg = static_cast<size_t>(INVALID_REG);
m_xregs[xr].dirty = false;
} }
break;
} }
case PPCCachedReg::LocationType::Immediate:
doStore = true;
break;
}
if (doStore)
StoreRegister(i, GetDefaultLocation(i));
if (mode == FlushMode::All)
m_regs[i].Flushed();
} }
const OpArg& RegCache::R(size_t preg) const const OpArg& RegCache::R(size_t preg) const
{ {
return m_regs[preg].location; return m_regs[preg].Location();
} }
X64Reg RegCache::RX(size_t preg) const X64Reg RegCache::RX(size_t preg) const
{ {
if (IsBound(preg)) if (m_regs[preg].IsBound())
return m_regs[preg].location.GetSimpleReg(); return m_regs[preg].Location().GetSimpleReg();
PanicAlert("Unbound register - %zu", preg); PanicAlert("Unbound register - %zu", preg);
return Gen::INVALID_REG; return Gen::INVALID_REG;
@ -261,7 +246,7 @@ X64Reg RegCache::RX(size_t preg) const
void RegCache::UnlockAll() void RegCache::UnlockAll()
{ {
for (auto& reg : m_regs) for (auto& reg : m_regs)
reg.locked = false; reg.Unlock();
} }
void RegCache::UnlockAllX() void RegCache::UnlockAllX()
@ -275,11 +260,6 @@ bool RegCache::IsFreeX(size_t xreg) const
return m_xregs[xreg].free && !m_xregs[xreg].locked; return m_xregs[xreg].free && !m_xregs[xreg].locked;
} }
bool RegCache::IsBound(size_t preg) const
{
return m_regs[preg].away && m_regs[preg].location.IsSimpleReg();
}
X64Reg RegCache::GetFreeXReg() X64Reg RegCache::GetFreeXReg()
{ {
size_t aCount; size_t aCount;
@ -302,7 +282,7 @@ X64Reg RegCache::GetFreeXReg()
{ {
X64Reg xreg = (X64Reg)aOrder[i]; X64Reg xreg = (X64Reg)aOrder[i];
size_t preg = m_xregs[xreg].ppcReg; size_t preg = m_xregs[xreg].ppcReg;
if (m_xregs[xreg].locked || m_regs[preg].locked) if (m_xregs[xreg].locked || m_regs[preg].IsLocked())
continue; continue;
float score = ScoreRegister(xreg); float score = ScoreRegister(xreg);
if (score < min_score) if (score < min_score)

View File

@ -7,16 +7,81 @@
#include <array> #include <array>
#include <cinttypes> #include <cinttypes>
#include "Common/Assert.h"
#include "Common/x64Emitter.h" #include "Common/x64Emitter.h"
#include "Core/PowerPC/PPCAnalyst.h" #include "Core/PowerPC/PPCAnalyst.h"
class Jit64; class Jit64;
struct PPCCachedReg class PPCCachedReg
{ {
Gen::OpArg location; public:
bool away; // value not in source register enum class LocationType
bool locked; {
/// Value is currently at its default location
Default,
/// Value is currently bound to a x64 register
Bound,
/// Value is known as an immediate and has not been written back to its default location
Immediate,
/// Value is known as an immediate and is already present at its default location
SpeculativeImmediate,
};
PPCCachedReg() = default;
explicit PPCCachedReg(Gen::OpArg default_location_)
: default_location(default_location_), location(default_location_)
{
}
const Gen::OpArg& Location() const { return location; }
LocationType GetLocationType() const
{
if (!away)
{
if (location.IsImm())
return LocationType::SpeculativeImmediate;
ASSERT(location == default_location);
return LocationType::Default;
}
ASSERT(location.IsImm() || location.IsSimpleReg());
return location.IsImm() ? LocationType::Immediate : LocationType::Bound;
}
bool IsAway() const { return away; }
bool IsBound() const { return GetLocationType() == LocationType::Bound; }
void BoundTo(Gen::X64Reg xreg)
{
away = true;
location = Gen::R(xreg);
}
void Flushed()
{
away = false;
location = default_location;
}
void SetToImm32(u32 imm32, bool dirty = true)
{
away |= dirty;
location = Gen::Imm32(imm32);
}
bool IsLocked() const { return locked; }
void Lock() { locked = true; }
void Unlock() { locked = false; }
private:
Gen::OpArg default_location{};
Gen::OpArg location{};
bool away = false; // value not in source register
bool locked = false;
}; };
struct X64CachedReg struct X64CachedReg
@ -75,7 +140,7 @@ public:
template <typename T> template <typename T>
void Lock(T p) void Lock(T p)
{ {
m_regs[p].locked = true; m_regs[p].Lock();
} }
template <typename T, typename... Args> template <typename T, typename... Args>
void Lock(T first, Args... args) void Lock(T first, Args... args)
@ -117,7 +182,6 @@ public:
void UnlockAllX(); void UnlockAllX();
bool IsFreeX(size_t xreg) const; bool IsFreeX(size_t xreg) const;
bool IsBound(size_t preg) const;
Gen::X64Reg GetFreeXReg(); Gen::X64Reg GetFreeXReg();
int NumFreeRegisters() const; int NumFreeRegisters() const;

View File

@ -460,7 +460,7 @@ void Jit64::fmrx(UGeckoInstruction inst)
fpr.Lock(b, d); fpr.Lock(b, d);
if (fpr.IsBound(d)) if (fpr.R(d).IsSimpleReg())
{ {
// We don't need to load d, but if it is loaded, we need to mark it as dirty. // We don't need to load d, but if it is loaded, we need to mark it as dirty.
fpr.BindToRegister(d); fpr.BindToRegister(d);