JitRegCache: Encapsulate behavior of PPCCachedReg
This commit is contained in:
parent
406159f7f0
commit
dd41bab365
|
@ -16,12 +16,12 @@ FPURegCache::FPURegCache(Jit64& jit) : RegCache{jit}
|
|||
|
||||
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)
|
||||
{
|
||||
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
|
||||
|
|
|
@ -16,12 +16,12 @@ GPRRegCache::GPRRegCache(Jit64& jit) : RegCache{jit}
|
|||
|
||||
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)
|
||||
{
|
||||
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
|
||||
|
@ -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
|
||||
// processing speculative constants.
|
||||
DiscardRegContentsIfCached(preg);
|
||||
m_regs[preg].away |= dirty;
|
||||
m_regs[preg].location = Imm32(imm_value);
|
||||
m_regs[preg].SetToImm32(imm_value, dirty);
|
||||
}
|
||||
|
||||
BitSet32 GPRRegCache::GetRegUtilization() const
|
||||
|
|
|
@ -35,38 +35,19 @@ void RegCache::Start()
|
|||
}
|
||||
for (size_t i = 0; i < m_regs.size(); i++)
|
||||
{
|
||||
m_regs[i].location = GetDefaultLocation(i);
|
||||
m_regs[i].away = false;
|
||||
m_regs[i].locked = false;
|
||||
m_regs[i] = PPCCachedReg{GetDefaultLocation(i)};
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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].dirty = false;
|
||||
m_xregs[xr].ppcReg = static_cast<size_t>(INVALID_REG);
|
||||
m_regs[preg].away = false;
|
||||
m_regs[preg].location = GetDefaultLocation(preg);
|
||||
m_regs[preg].Flushed();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,28 +66,25 @@ void RegCache::Flush(FlushMode mode, BitSet32 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));
|
||||
}
|
||||
|
||||
if (m_regs[i].away)
|
||||
{
|
||||
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())
|
||||
switch (m_regs[i].GetLocationType())
|
||||
{
|
||||
case PPCCachedReg::LocationType::Default:
|
||||
break;
|
||||
case PPCCachedReg::LocationType::SpeculativeImmediate:
|
||||
// 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,
|
||||
// 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,39 +123,46 @@ int RegCache::SanityCheck() const
|
|||
{
|
||||
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:
|
||||
break;
|
||||
case PPCCachedReg::LocationType::Bound:
|
||||
{
|
||||
Gen::X64Reg simple = m_regs[i].location.GetSimpleReg();
|
||||
Gen::X64Reg simple = m_regs[i].Location().GetSimpleReg();
|
||||
if (m_xregs[simple].locked)
|
||||
return 1;
|
||||
if (m_xregs[simple].ppcReg != i)
|
||||
return 2;
|
||||
break;
|
||||
}
|
||||
else if (m_regs[i].location.IsImm())
|
||||
{
|
||||
case PPCCachedReg::LocationType::Immediate:
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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())
|
||||
BindToRegister(preg, doLoad, makeDirty);
|
||||
else if (m_regs[preg].location.IsSimpleReg())
|
||||
case PPCCachedReg::LocationType::Default:
|
||||
case PPCCachedReg::LocationType::SpeculativeImmediate:
|
||||
break;
|
||||
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)
|
||||
{
|
||||
if (!m_regs[i].away || m_regs[i].location.IsImm())
|
||||
if (!m_regs[i].IsBound())
|
||||
{
|
||||
X64Reg xr = GetFreeXReg();
|
||||
if (m_xregs[xr].dirty)
|
||||
|
@ -186,18 +171,18 @@ void RegCache::BindToRegister(size_t i, bool doLoad, bool makeDirty)
|
|||
PanicAlert("GetFreeXReg returned locked register");
|
||||
m_xregs[xr].free = false;
|
||||
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)
|
||||
LoadRegister(i, xr);
|
||||
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();
|
||||
}
|
||||
}
|
||||
m_regs[i].away = true;
|
||||
m_regs[i].location = ::Gen::R(xr);
|
||||
m_regs[i].BoundTo(xr);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -214,10 +199,14 @@ void RegCache::BindToRegister(size_t i, bool doLoad, bool makeDirty)
|
|||
|
||||
void RegCache::StoreFromRegister(size_t i, FlushMode mode)
|
||||
{
|
||||
if (m_regs[i].away)
|
||||
bool doStore = false;
|
||||
|
||||
switch (m_regs[i].GetLocationType())
|
||||
{
|
||||
bool doStore;
|
||||
if (m_regs[i].location.IsSimpleReg())
|
||||
case PPCCachedReg::LocationType::Default:
|
||||
case PPCCachedReg::LocationType::SpeculativeImmediate:
|
||||
return;
|
||||
case PPCCachedReg::LocationType::Bound:
|
||||
{
|
||||
X64Reg xr = RX(i);
|
||||
doStore = m_xregs[xr].dirty;
|
||||
|
@ -227,32 +216,28 @@ void RegCache::StoreFromRegister(size_t i, FlushMode mode)
|
|||
m_xregs[xr].ppcReg = static_cast<size_t>(INVALID_REG);
|
||||
m_xregs[xr].dirty = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// must be immediate - do nothing
|
||||
case PPCCachedReg::LocationType::Immediate:
|
||||
doStore = true;
|
||||
break;
|
||||
}
|
||||
OpArg newLoc = GetDefaultLocation(i);
|
||||
|
||||
if (doStore)
|
||||
StoreRegister(i, newLoc);
|
||||
StoreRegister(i, GetDefaultLocation(i));
|
||||
if (mode == FlushMode::All)
|
||||
{
|
||||
m_regs[i].location = newLoc;
|
||||
m_regs[i].away = false;
|
||||
}
|
||||
}
|
||||
m_regs[i].Flushed();
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
if (IsBound(preg))
|
||||
return m_regs[preg].location.GetSimpleReg();
|
||||
if (m_regs[preg].IsBound())
|
||||
return m_regs[preg].Location().GetSimpleReg();
|
||||
|
||||
PanicAlert("Unbound register - %zu", preg);
|
||||
return Gen::INVALID_REG;
|
||||
|
@ -261,7 +246,7 @@ X64Reg RegCache::RX(size_t preg) const
|
|||
void RegCache::UnlockAll()
|
||||
{
|
||||
for (auto& reg : m_regs)
|
||||
reg.locked = false;
|
||||
reg.Unlock();
|
||||
}
|
||||
|
||||
void RegCache::UnlockAllX()
|
||||
|
@ -275,11 +260,6 @@ bool RegCache::IsFreeX(size_t xreg) const
|
|||
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()
|
||||
{
|
||||
size_t aCount;
|
||||
|
@ -302,7 +282,7 @@ X64Reg RegCache::GetFreeXReg()
|
|||
{
|
||||
X64Reg xreg = (X64Reg)aOrder[i];
|
||||
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;
|
||||
float score = ScoreRegister(xreg);
|
||||
if (score < min_score)
|
||||
|
|
|
@ -7,16 +7,81 @@
|
|||
#include <array>
|
||||
#include <cinttypes>
|
||||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/x64Emitter.h"
|
||||
#include "Core/PowerPC/PPCAnalyst.h"
|
||||
|
||||
class Jit64;
|
||||
|
||||
struct PPCCachedReg
|
||||
class PPCCachedReg
|
||||
{
|
||||
Gen::OpArg location;
|
||||
bool away; // value not in source register
|
||||
bool locked;
|
||||
public:
|
||||
enum class LocationType
|
||||
{
|
||||
/// 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
|
||||
|
@ -75,7 +140,7 @@ public:
|
|||
template <typename T>
|
||||
void Lock(T p)
|
||||
{
|
||||
m_regs[p].locked = true;
|
||||
m_regs[p].Lock();
|
||||
}
|
||||
template <typename T, typename... Args>
|
||||
void Lock(T first, Args... args)
|
||||
|
@ -117,7 +182,6 @@ public:
|
|||
void UnlockAllX();
|
||||
|
||||
bool IsFreeX(size_t xreg) const;
|
||||
bool IsBound(size_t preg) const;
|
||||
|
||||
Gen::X64Reg GetFreeXReg();
|
||||
int NumFreeRegisters() const;
|
||||
|
|
|
@ -460,7 +460,7 @@ void Jit64::fmrx(UGeckoInstruction inst)
|
|||
|
||||
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.
|
||||
fpr.BindToRegister(d);
|
||||
|
|
Loading…
Reference in New Issue