diff --git a/Source/Core/Core/PowerPC/Jit64/FPURegCache.cpp b/Source/Core/Core/PowerPC/Jit64/FPURegCache.cpp index 449c3d6ebf..11decfbf98 100644 --- a/Source/Core/Core/PowerPC/Jit64/FPURegCache.cpp +++ b/Source/Core/Core/PowerPC/Jit64/FPURegCache.cpp @@ -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 diff --git a/Source/Core/Core/PowerPC/Jit64/GPRRegCache.cpp b/Source/Core/Core/PowerPC/Jit64/GPRRegCache.cpp index 22ccacca54..ed8079e0e3 100644 --- a/Source/Core/Core/PowerPC/Jit64/GPRRegCache.cpp +++ b/Source/Core/Core/PowerPC/Jit64/GPRRegCache.cpp @@ -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 diff --git a/Source/Core/Core/PowerPC/Jit64/JitRegCache.cpp b/Source/Core/Core/PowerPC/Jit64/JitRegCache.cpp index 5cdc71dd6b..67f04e9f5b 100644 --- a/Source/Core/Core/PowerPC/Jit64/JitRegCache.cpp +++ b/Source/Core/Core/PowerPC/Jit64/JitRegCache.cpp @@ -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(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,20 +123,22 @@ 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()) - { - Gen::X64Reg simple = m_regs[i].location.GetSimpleReg(); - if (m_xregs[simple].locked) - return 1; - if (m_xregs[simple].ppcReg != i) - return 2; - } - else if (m_regs[i].location.IsImm()) - { - return 3; - } + case PPCCachedReg::LocationType::Default: + case PPCCachedReg::LocationType::SpeculativeImmediate: + break; + case PPCCachedReg::LocationType::Bound: + { + Gen::X64Reg simple = m_regs[i].Location().GetSimpleReg(); + if (m_xregs[simple].locked) + return 1; + if (m_xregs[simple].ppcReg != i) + return 2; + break; + } + case PPCCachedReg::LocationType::Immediate: + return 3; } } return 0; @@ -166,18 +146,23 @@ int RegCache::SanityCheck() const 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()) - m_xregs[RX(preg)].dirty |= makeDirty; + 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,45 +199,45 @@ 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()) - { - X64Reg xr = RX(i); - doStore = m_xregs[xr].dirty; - if (mode == FlushMode::All) - { - m_xregs[xr].free = true; - m_xregs[xr].ppcReg = static_cast(INVALID_REG); - m_xregs[xr].dirty = false; - } - } - else - { - // must be immediate - do nothing - doStore = true; - } - OpArg newLoc = GetDefaultLocation(i); - if (doStore) - StoreRegister(i, newLoc); + case PPCCachedReg::LocationType::Default: + case PPCCachedReg::LocationType::SpeculativeImmediate: + return; + case PPCCachedReg::LocationType::Bound: + { + X64Reg xr = RX(i); + doStore = m_xregs[xr].dirty; if (mode == FlushMode::All) { - m_regs[i].location = newLoc; - m_regs[i].away = false; + m_xregs[xr].free = true; + m_xregs[xr].ppcReg = static_cast(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 { - 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) diff --git a/Source/Core/Core/PowerPC/Jit64/JitRegCache.h b/Source/Core/Core/PowerPC/Jit64/JitRegCache.h index ccd9ac4d95..eee6782513 100644 --- a/Source/Core/Core/PowerPC/Jit64/JitRegCache.h +++ b/Source/Core/Core/PowerPC/Jit64/JitRegCache.h @@ -7,16 +7,81 @@ #include #include +#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 void Lock(T p) { - m_regs[p].locked = true; + m_regs[p].Lock(); } template 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; diff --git a/Source/Core/Core/PowerPC/Jit64/Jit_FloatingPoint.cpp b/Source/Core/Core/PowerPC/Jit64/Jit_FloatingPoint.cpp index 175cfec058..77ed9196ce 100644 --- a/Source/Core/Core/PowerPC/Jit64/Jit_FloatingPoint.cpp +++ b/Source/Core/Core/PowerPC/Jit64/Jit_FloatingPoint.cpp @@ -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);