JitRegCache: Remove old interface

This commit is contained in:
MerryMage 2018-10-15 21:02:21 +01:00
parent 08c41090b2
commit 342067abfa
9 changed files with 186 additions and 297 deletions

View File

@ -920,10 +920,8 @@ u8* Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
fpr.Commit();
// If we have a register that will never be used again, flush it.
for (int j : ~op.gprInUse)
gpr.StoreFromRegister(j);
for (int j : ~op.fprInUse)
fpr.StoreFromRegister(j);
gpr.Flush(~op.gprInUse);
fpr.Flush(~op.fprInUse);
if (opinfo->flags & FL_LOADSTORE)
++js.numLoadStoreInst;

View File

@ -116,7 +116,7 @@ void Jit64::HandleNaNs(UGeckoInstruction inst, X64Reg xmm_out, X64Reg xmm, X64Re
else
{
// SSE2 fallback
RCX64Reg tmp = fpr.Scratch(fpr.GetFreeXReg());
RCX64Reg tmp = fpr.Scratch();
RegCache::Realize(tmp);
MOVAPD(clobber, R(xmm));
CMPPD(clobber, R(clobber), CMP_UNORD);

View File

@ -173,6 +173,7 @@ void Jit64::ComputeRC(preg_t preg, bool needs_test, bool needs_sext)
if (needs_test)
{
TEST(32, arg, arg);
arg.Unlock();
}
else
{
@ -180,11 +181,9 @@ void Jit64::ComputeRC(preg_t preg, bool needs_test, bool needs_sext)
// better to flush it here so that we don't have to flush it on both sides of the branch.
// We don't want to do this if a test is needed though, because it would interrupt macro-op
// fusion.
for (int j : ~js.op->gprInUse)
gpr.StoreFromRegister(j);
arg.Unlock();
gpr.Flush(~js.op->gprInUse);
}
arg.Unlock();
DoMergedBranchCondition();
}
}

View File

@ -271,7 +271,7 @@ void Jit64::dcbx(UGeckoInstruction inst)
X64Reg value = RSCRATCH2;
RCOpArg Ra = inst.RA ? gpr.Use(inst.RA, RCMode::Read) : RCOpArg::Imm32(0);
RCOpArg Rb = gpr.Use(inst.RB, RCMode::Read);
RCX64Reg tmp = gpr.Scratch(gpr.GetFreeXReg());
RCX64Reg tmp = gpr.Scratch();
RegCache::Realize(Ra, Rb, tmp);
MOV_sum(32, addr, Ra, Rb);

View File

@ -101,7 +101,6 @@ public:
ASSERT(IsLocked());
locked--;
}
void UnlockAll() { locked = 0; } // TODO: Remove from final version
private:
Gen::OpArg default_location{};
@ -142,7 +141,6 @@ public:
ASSERT(IsLocked());
locked--;
}
void UnlockAll() { locked = 0; } // TODO: Remove from final version
private:
preg_t ppcReg = static_cast<preg_t>(Gen::INVALID_REG);

View File

@ -12,9 +12,9 @@ class FPURegCache final : public RegCache
{
public:
explicit FPURegCache(Jit64& jit);
Gen::OpArg GetDefaultLocation(preg_t preg) const override;
protected:
Gen::OpArg GetDefaultLocation(preg_t preg) const override;
void StoreRegister(preg_t preg, const Gen::OpArg& newLoc) override;
void LoadRegister(preg_t preg, Gen::X64Reg newLoc) override;
const Gen::X64Reg* GetAllocationOrder(size_t* count) const override;

View File

@ -12,10 +12,10 @@ class GPRRegCache final : public RegCache
{
public:
explicit GPRRegCache(Jit64& jit);
Gen::OpArg GetDefaultLocation(preg_t preg) const override;
void SetImmediate32(preg_t preg, u32 imm_value, bool dirty = true);
protected:
Gen::OpArg GetDefaultLocation(preg_t preg) const override;
void StoreRegister(preg_t preg, const Gen::OpArg& new_loc) override;
void LoadRegister(preg_t preg, Gen::X64Reg new_loc) override;
const Gen::X64Reg* GetAllocationOrder(size_t* count) const override;

View File

@ -47,7 +47,7 @@ RCOpArg::RCOpArg(X64Reg xr) : rc(nullptr), contents(xr)
RCOpArg::RCOpArg(RegCache* rc_, preg_t preg) : rc(rc_), contents(preg)
{
rc->NewLock(preg);
rc->Lock(preg);
}
RCOpArg::~RCOpArg()
@ -128,14 +128,14 @@ void RCOpArg::Unlock()
if (const preg_t* preg = std::get_if<preg_t>(&contents))
{
ASSERT(rc);
rc->NewUnlock(*preg);
rc->Unlock(*preg);
}
else if (const X64Reg* xr = std::get_if<X64Reg>(&contents))
{
// If rc, we got this from an RCX64Reg.
// If !rc, we got this from RCOpArg::R.
if (rc)
rc->NewUnlockX(*xr);
rc->UnlockX(*xr);
}
else
{
@ -191,12 +191,12 @@ RCX64Reg::RCX64Reg() = default;
RCX64Reg::RCX64Reg(RegCache* rc_, preg_t preg) : rc(rc_), contents(preg)
{
rc->NewLock(preg);
rc->Lock(preg);
}
RCX64Reg::RCX64Reg(RegCache* rc_, X64Reg xr) : rc(rc_), contents(xr)
{
rc->NewLockX(xr);
rc->LockX(xr);
}
RCX64Reg::~RCX64Reg()
@ -251,12 +251,12 @@ void RCX64Reg::Unlock()
if (const preg_t* preg = std::get_if<preg_t>(&contents))
{
ASSERT(rc);
rc->NewUnlock(*preg);
rc->Unlock(*preg);
}
else if (const X64Reg* xr = std::get_if<X64Reg>(&contents))
{
ASSERT(rc);
rc->NewUnlockX(*xr);
rc->UnlockX(*xr);
}
else
{
@ -302,66 +302,11 @@ void RegCache::Start()
}
}
void RegCache::DiscardRegContentsIfCached(preg_t preg)
{
if (m_regs[preg].IsBound())
{
X64Reg xr = m_regs[preg].Location().GetSimpleReg();
m_xregs[xr].SetFlushed();
m_regs[preg].SetFlushed();
}
}
void RegCache::SetEmitter(XEmitter* emitter)
{
m_emitter = emitter;
}
void RegCache::Flush(FlushMode mode, BitSet32 regsToFlush)
{
ASSERT_MSG(
DYNA_REC,
std::none_of(m_xregs.begin(), m_xregs.end(), [](const auto& x) { return x.IsLocked(); }),
"Someone forgot to unlock a X64 reg");
for (unsigned int i : regsToFlush)
{
ASSERT_MSG(DYNA_REC, !m_regs[i].IsLocked(), "Someone forgot to unlock PPC reg %u (X64 reg %i).",
i, RX(i));
ASSERT_MSG(DYNA_REC, !m_regs[i].IsRevertable(), "Register transaction is in progress!");
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].SetFlushed();
break;
case PPCCachedReg::LocationType::Bound:
case PPCCachedReg::LocationType::Immediate:
StoreFromRegister(i, mode);
break;
}
}
}
void RegCache::FlushLockX(X64Reg reg)
{
FlushX(reg);
LockX(reg);
}
void RegCache::FlushLockX(X64Reg reg1, X64Reg reg2)
{
FlushX(reg1);
FlushX(reg2);
LockX(reg1);
LockX(reg2);
}
bool RegCache::SanityCheck() const
{
for (size_t i = 0; i < m_regs.size(); i++)
@ -389,20 +334,149 @@ bool RegCache::SanityCheck() const
return true;
}
void RegCache::KillImmediate(preg_t preg, bool doLoad, bool makeDirty)
RCOpArg RegCache::Use(preg_t preg, RCMode mode)
{
switch (m_regs[preg].GetLocationType())
m_constraints[preg].AddUse(mode);
return RCOpArg{this, preg};
}
RCOpArg RegCache::UseNoImm(preg_t preg, RCMode mode)
{
m_constraints[preg].AddUseNoImm(mode);
return RCOpArg{this, preg};
}
RCOpArg RegCache::BindOrImm(preg_t preg, RCMode mode)
{
m_constraints[preg].AddBindOrImm(mode);
return RCOpArg{this, preg};
}
RCX64Reg RegCache::Bind(preg_t preg, RCMode mode)
{
m_constraints[preg].AddBind(mode);
return RCX64Reg{this, preg};
}
RCX64Reg RegCache::RevertableBind(preg_t preg, RCMode mode)
{
m_constraints[preg].AddRevertableBind(mode);
return RCX64Reg{this, preg};
}
RCX64Reg RegCache::Scratch()
{
return Scratch(GetFreeXReg());
}
RCX64Reg RegCache::Scratch(X64Reg xr)
{
FlushX(xr);
return RCX64Reg{this, xr};
}
RCForkGuard RegCache::Fork()
{
return RCForkGuard{*this};
}
void RegCache::Flush(BitSet32 pregs)
{
ASSERT_MSG(
DYNA_REC,
std::none_of(m_xregs.begin(), m_xregs.end(), [](const auto& x) { return x.IsLocked(); }),
"Someone forgot to unlock a X64 reg");
for (preg_t i : pregs)
{
case PPCCachedReg::LocationType::Default:
case PPCCachedReg::LocationType::SpeculativeImmediate:
break;
case PPCCachedReg::LocationType::Bound:
if (makeDirty)
m_xregs[RX(preg)].MakeDirty();
break;
case PPCCachedReg::LocationType::Immediate:
BindToRegister(preg, doLoad, makeDirty);
break;
ASSERT_MSG(DYNA_REC, !m_regs[i].IsLocked(),
"Someone forgot to unlock PPC reg %zu (X64 reg %i).", i, RX(i));
ASSERT_MSG(DYNA_REC, !m_regs[i].IsRevertable(), "Register transaction is in progress!");
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].SetFlushed();
break;
case PPCCachedReg::LocationType::Bound:
case PPCCachedReg::LocationType::Immediate:
StoreFromRegister(i);
break;
}
}
}
void RegCache::Revert()
{
ASSERT(IsAllUnlocked());
for (auto& reg : m_regs)
{
if (reg.IsRevertable())
reg.SetRevert();
}
}
void RegCache::Commit()
{
ASSERT(IsAllUnlocked());
for (auto& reg : m_regs)
{
if (reg.IsRevertable())
reg.SetCommit();
}
}
bool RegCache::IsAllUnlocked() const
{
return std::none_of(m_regs.begin(), m_regs.end(), [](const auto& r) { return r.IsLocked(); }) &&
std::none_of(m_xregs.begin(), m_xregs.end(), [](const auto& x) { return x.IsLocked(); }) &&
!IsAnyConstraintActive();
}
void RegCache::PreloadRegisters(BitSet32 to_preload)
{
for (preg_t preg : to_preload)
{
if (NumFreeRegisters() < 2)
return;
if (!R(preg).IsImm())
BindToRegister(preg, true, false);
}
}
BitSet32 RegCache::RegistersInUse() const
{
BitSet32 result;
for (size_t i = 0; i < m_xregs.size(); i++)
{
if (!m_xregs[i].IsFree())
result[i] = true;
}
return result;
}
void RegCache::FlushX(X64Reg reg)
{
ASSERT_MSG(DYNA_REC, reg < m_xregs.size(), "Flushing non-existent reg %i", reg);
ASSERT(!m_xregs[reg].IsLocked());
if (!m_xregs[reg].IsFree())
{
StoreFromRegister(m_xregs[reg].Contents());
}
}
void RegCache::DiscardRegContentsIfCached(preg_t preg)
{
if (m_regs[preg].IsBound())
{
X64Reg xr = m_regs[preg].Location().GetSimpleReg();
m_xregs[xr].SetFlushed();
m_regs[preg].SetFlushed();
}
}
@ -457,7 +531,7 @@ void RegCache::StoreFromRegister(preg_t i, FlushMode mode)
{
X64Reg xr = RX(i);
doStore = m_xregs[xr].IsDirty();
if (mode == FlushMode::All)
if (mode == FlushMode::Full)
m_xregs[xr].SetFlushed();
break;
}
@ -468,39 +542,10 @@ void RegCache::StoreFromRegister(preg_t i, FlushMode mode)
if (doStore)
StoreRegister(i, GetDefaultLocation(i));
if (mode == FlushMode::All)
if (mode == FlushMode::Full)
m_regs[i].SetFlushed();
}
const OpArg& RegCache::R(preg_t preg) const
{
return m_regs[preg].Location();
}
X64Reg RegCache::RX(preg_t preg) const
{
ASSERT_MSG(DYNA_REC, m_regs[preg].IsBound(), "Unbound register - %zu", preg);
return m_regs[preg].Location().GetSimpleReg();
}
void RegCache::UnlockAll()
{
for (auto& reg : m_regs)
reg.UnlockAll();
m_constraints.fill({});
}
void RegCache::UnlockAllX()
{
for (auto& xreg : m_xregs)
xreg.UnlockAll();
}
bool RegCache::IsFreeX(size_t xreg) const
{
return m_xregs[xreg].IsFree();
}
X64Reg RegCache::GetFreeXReg()
{
size_t aCount;
@ -556,16 +601,6 @@ int RegCache::NumFreeRegisters() const
return count;
}
void RegCache::FlushX(X64Reg reg)
{
ASSERT_MSG(DYNA_REC, reg < m_xregs.size(), "Flushing non-existent reg %i", reg);
ASSERT(!m_xregs[reg].IsLocked());
if (!m_xregs[reg].IsFree())
{
StoreFromRegister(m_xregs[reg].Contents());
}
}
// Estimate roughly how bad it would be to de-allocate this register. Higher score
// means more bad.
float RegCache::ScoreRegister(X64Reg xreg) const
@ -598,102 +633,23 @@ float RegCache::ScoreRegister(X64Reg xreg) const
return score;
}
RCOpArg RegCache::Use(preg_t preg, RCMode mode)
const OpArg& RegCache::R(preg_t preg) const
{
m_constraints[preg].AddUse(mode);
return RCOpArg{this, preg};
return m_regs[preg].Location();
}
RCOpArg RegCache::UseNoImm(preg_t preg, RCMode mode)
X64Reg RegCache::RX(preg_t preg) const
{
m_constraints[preg].AddUseNoImm(mode);
return RCOpArg{this, preg};
ASSERT_MSG(DYNA_REC, m_regs[preg].IsBound(), "Unbound register - %zu", preg);
return m_regs[preg].Location().GetSimpleReg();
}
RCOpArg RegCache::BindOrImm(preg_t preg, RCMode mode)
{
m_constraints[preg].AddBindOrImm(mode);
return RCOpArg{this, preg};
}
RCX64Reg RegCache::Bind(preg_t preg, RCMode mode)
{
m_constraints[preg].AddBind(mode);
return RCX64Reg{this, preg};
}
RCX64Reg RegCache::RevertableBind(preg_t preg, RCMode mode)
{
m_constraints[preg].AddRevertableBind(mode);
return RCX64Reg{this, preg};
}
RCX64Reg RegCache::Scratch(X64Reg xr)
{
FlushX(xr);
return RCX64Reg{this, xr};
}
RCForkGuard RegCache::Fork()
{
return RCForkGuard{*this};
}
void RegCache::Revert()
{
ASSERT(IsAllUnlocked());
for (auto& reg : m_regs)
{
if (reg.IsRevertable())
reg.SetRevert();
}
}
void RegCache::Commit()
{
ASSERT(IsAllUnlocked());
for (auto& reg : m_regs)
{
if (reg.IsRevertable())
reg.SetCommit();
}
}
bool RegCache::IsAllUnlocked() const
{
return std::none_of(m_regs.begin(), m_regs.end(), [](const auto& r){ return r.IsLocked(); }) &&
std::none_of(m_xregs.begin(), m_xregs.end(), [](const auto& x){ return x.IsLocked(); }) &&
!IsAnyConstraintActive();
}
void RegCache::PreloadRegisters(BitSet32 to_preload)
{
for (preg_t preg : to_preload)
{
if (NumFreeRegisters() < 2)
return;
if (!R(preg).IsImm())
BindToRegister(preg, true, false);
}
}
BitSet32 RegCache::RegistersInUse() const
{
BitSet32 result;
for (size_t i = 0; i < m_xregs.size(); i++)
{
if (!m_xregs[i].IsFree())
result[i] = true;
}
return result;
}
void RegCache::NewLock(preg_t preg)
void RegCache::Lock(preg_t preg)
{
m_regs[preg].Lock();
}
void RegCache::NewUnlock(preg_t preg)
void RegCache::Unlock(preg_t preg)
{
m_regs[preg].Unlock();
if (!m_regs[preg].IsLocked())
@ -703,12 +659,12 @@ void RegCache::NewUnlock(preg_t preg)
}
}
void RegCache::NewLockX(X64Reg xr)
void RegCache::LockX(X64Reg xr)
{
m_xregs[xr].Lock();
}
void RegCache::NewUnlockX(X64Reg xr)
void RegCache::UnlockX(X64Reg xr)
{
m_xregs[xr].Unlock();
}

View File

@ -126,89 +126,16 @@ class RegCache
public:
enum class FlushMode
{
All,
Full,
MaintainState,
};
explicit RegCache(Jit64& jit);
virtual ~RegCache() = default;
virtual Gen::OpArg GetDefaultLocation(preg_t preg) const = 0;
void Start();
void DiscardRegContentsIfCached(preg_t preg);
void SetEmitter(Gen::XEmitter* emitter);
void Flush(FlushMode mode = FlushMode::All, BitSet32 regsToFlush = BitSet32::AllTrue(32));
void FlushLockX(Gen::X64Reg reg);
void FlushLockX(Gen::X64Reg reg1, Gen::X64Reg reg2);
bool SanityCheck() const;
void KillImmediate(preg_t preg, bool doLoad, bool makeDirty);
// TODO - instead of doload, use "read", "write"
// read only will not set dirty flag
void BindToRegister(preg_t preg, bool doLoad = true, bool makeDirty = true);
void StoreFromRegister(preg_t preg, FlushMode mode = FlushMode::All);
const Gen::OpArg& R(preg_t preg) const;
Gen::X64Reg RX(preg_t preg) const;
// Register locking.
// these are powerpc reg indices
template <typename T>
void Lock(T p) // TODO: Make private
{
m_regs[p].Lock();
}
template <typename T, typename... Args>
void Lock(T first, Args... args)
{
Lock(first);
Lock(args...);
}
// these are x64 reg indices
template <typename T>
void LockX(T x)
{
if (m_xregs[x].IsLocked())
PanicAlert("RegCache: x %i already locked!", x);
m_xregs[x].Lock();
}
template <typename T, typename... Args>
void LockX(T first, Args... args)
{
LockX(first);
LockX(args...);
}
template <typename T>
void UnlockX(T x)
{
if (!m_xregs[x].IsLocked())
PanicAlert("RegCache: x %i already unlocked!", x);
m_xregs[x].Unlock();
}
template <typename T, typename... Args>
void UnlockX(T first, Args... args)
{
UnlockX(first);
UnlockX(args...);
}
void UnlockAll();
void UnlockAllX();
bool IsFreeX(size_t xreg) const;
Gen::X64Reg GetFreeXReg();
int NumFreeRegisters() const;
// New interface
template <typename... Ts>
static void Realize(Ts&... rc)
@ -238,15 +165,17 @@ public:
RCOpArg BindOrImm(preg_t preg, RCMode mode);
RCX64Reg Bind(preg_t preg, RCMode mode);
RCX64Reg RevertableBind(preg_t preg, RCMode mode);
RCX64Reg Scratch();
RCX64Reg Scratch(Gen::X64Reg xr);
RCForkGuard Fork();
void Flush(BitSet32 pregs = BitSet32::AllTrue(32));
void Revert();
void Commit();
bool IsAllUnlocked() const;
void PreloadRegisters(BitSet32 regs);
void PreloadRegisters(BitSet32 pregs);
BitSet32 RegistersInUse() const;
protected:
@ -254,6 +183,7 @@ protected:
friend class RCX64Reg;
friend class RCForkGuard;
virtual Gen::OpArg GetDefaultLocation(preg_t preg) const = 0;
virtual void StoreRegister(preg_t preg, const Gen::OpArg& new_loc) = 0;
virtual void LoadRegister(preg_t preg, Gen::X64Reg new_loc) = 0;
@ -263,14 +193,22 @@ protected:
virtual BitSet32 CountRegsIn(preg_t preg, u32 lookahead) const = 0;
void FlushX(Gen::X64Reg reg);
void DiscardRegContentsIfCached(preg_t preg);
void BindToRegister(preg_t preg, bool doLoad = true, bool makeDirty = true);
void StoreFromRegister(preg_t preg, FlushMode mode = FlushMode::Full);
Gen::X64Reg GetFreeXReg();
int NumFreeRegisters() const;
float ScoreRegister(Gen::X64Reg xreg) const;
// New interface
void NewLock(preg_t preg);
void NewUnlock(preg_t preg);
void NewLockX(Gen::X64Reg xr);
void NewUnlockX(Gen::X64Reg xr);
const Gen::OpArg& R(preg_t preg) const;
Gen::X64Reg RX(preg_t preg) const;
void Lock(preg_t preg);
void Unlock(preg_t preg);
void LockX(Gen::X64Reg xr);
void UnlockX(Gen::X64Reg xr);
bool IsRealized(preg_t preg) const;
void Realize(preg_t preg);