From 1a34376f3a319fd83a24560ad21f028f6301c007 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 20 Dec 2016 21:59:53 -0500 Subject: [PATCH 1/8] JitRegCache: Separate FPURegCache and GPRRegCache into their own files Keeps them separated from one another and makes it less annoying to find member functions (which were previously spread out all over the place). --- Source/Core/Core/CMakeLists.txt | 2 + Source/Core/Core/Core.vcxproj | 4 + Source/Core/Core/Core.vcxproj.filters | 12 + .../Core/Core/PowerPC/Jit64/FPURegCache.cpp | 53 +++ Source/Core/Core/PowerPC/Jit64/FPURegCache.h | 18 + .../Core/Core/PowerPC/Jit64/GPRRegCache.cpp | 71 ++++ Source/Core/Core/PowerPC/Jit64/GPRRegCache.h | 19 + Source/Core/Core/PowerPC/Jit64/Jit.h | 2 + .../Core/Core/PowerPC/Jit64/JitRegCache.cpp | 325 ++++++------------ Source/Core/Core/PowerPC/Jit64/JitRegCache.h | 23 -- 10 files changed, 293 insertions(+), 236 deletions(-) create mode 100644 Source/Core/Core/PowerPC/Jit64/FPURegCache.cpp create mode 100644 Source/Core/Core/PowerPC/Jit64/FPURegCache.h create mode 100644 Source/Core/Core/PowerPC/Jit64/GPRRegCache.cpp create mode 100644 Source/Core/Core/PowerPC/Jit64/GPRRegCache.h diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 0d76fb8c47..a1bb58f238 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -196,6 +196,8 @@ if(_M_X86) PowerPC/Jit64IL/IR_X86.cpp PowerPC/Jit64IL/JitIL.cpp PowerPC/Jit64IL/JitIL_Tables.cpp + PowerPC/Jit64/FPURegCache.cpp + PowerPC/Jit64/GPRRegCache.cpp PowerPC/Jit64/Jit64_Tables.cpp PowerPC/Jit64/JitAsm.cpp PowerPC/Jit64/Jit_Branch.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 47845d7b3d..13492f654f 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -225,6 +225,8 @@ + + @@ -427,6 +429,8 @@ + + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index ea1040901e..00023e98f4 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -666,6 +666,12 @@ PowerPC\JitIL + + PowerPC\Jit64 + + + PowerPC\Jit64 + PowerPC\Jit64 @@ -1262,6 +1268,12 @@ PowerPC\JitIL + + PowerPC\Jit64 + + + PowerPC\Jit64 + PowerPC\Jit64 diff --git a/Source/Core/Core/PowerPC/Jit64/FPURegCache.cpp b/Source/Core/Core/PowerPC/Jit64/FPURegCache.cpp new file mode 100644 index 0000000000..698f7da640 --- /dev/null +++ b/Source/Core/Core/PowerPC/Jit64/FPURegCache.cpp @@ -0,0 +1,53 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Core/PowerPC/Jit64/FPURegCache.h" + +#include "Core/PowerPC/Jit64Common/Jit64Base.h" +#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h" + +using namespace Gen; + +void FPURegCache::StoreRegister(size_t preg, const OpArg& new_loc) +{ + emit->MOVAPD(new_loc, regs[preg].location.GetSimpleReg()); +} + +void FPURegCache::LoadRegister(size_t preg, X64Reg new_loc) +{ + emit->MOVAPD(new_loc, regs[preg].location); +} + +const X64Reg* FPURegCache::GetAllocationOrder(size_t* count) +{ + static const X64Reg allocation_order[] = {XMM6, XMM7, XMM8, XMM9, XMM10, XMM11, XMM12, + XMM13, XMM14, XMM15, XMM2, XMM3, XMM4, XMM5}; + *count = sizeof(allocation_order) / sizeof(X64Reg); + return allocation_order; +} + +OpArg FPURegCache::GetDefaultLocation(size_t reg) const +{ + return PPCSTATE(ps[reg][0]); +} + +BitSet32 FPURegCache::GetRegUtilization() +{ + return jit->js.op->gprInReg; +} + +BitSet32 FPURegCache::CountRegsIn(size_t preg, u32 lookahead) +{ + BitSet32 regs_used; + + for (u32 i = 1; i < lookahead; i++) + { + BitSet32 regs_in = jit->js.op[i].fregsIn; + regs_used |= regs_in; + if (regs_in[preg]) + return regs_used; + } + + return regs_used; +} diff --git a/Source/Core/Core/PowerPC/Jit64/FPURegCache.h b/Source/Core/Core/PowerPC/Jit64/FPURegCache.h new file mode 100644 index 0000000000..0ea2faf2f8 --- /dev/null +++ b/Source/Core/Core/PowerPC/Jit64/FPURegCache.h @@ -0,0 +1,18 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Core/PowerPC/Jit64/JitRegCache.h" + +class FPURegCache final : public RegCache +{ +public: + void StoreRegister(size_t preg, const Gen::OpArg& newLoc) override; + void LoadRegister(size_t preg, Gen::X64Reg newLoc) override; + const Gen::X64Reg* GetAllocationOrder(size_t* count) override; + Gen::OpArg GetDefaultLocation(size_t reg) const override; + BitSet32 GetRegUtilization() override; + BitSet32 CountRegsIn(size_t preg, u32 lookahead) override; +}; diff --git a/Source/Core/Core/PowerPC/Jit64/GPRRegCache.cpp b/Source/Core/Core/PowerPC/Jit64/GPRRegCache.cpp new file mode 100644 index 0000000000..c71e4471e8 --- /dev/null +++ b/Source/Core/Core/PowerPC/Jit64/GPRRegCache.cpp @@ -0,0 +1,71 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Core/PowerPC/Jit64/GPRRegCache.h" + +#include "Core/PowerPC/Jit64Common/Jit64Base.h" +#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h" + +using namespace Gen; + +void GPRRegCache::StoreRegister(size_t preg, const OpArg& new_loc) +{ + emit->MOV(32, new_loc, regs[preg].location); +} + +void GPRRegCache::LoadRegister(size_t preg, X64Reg new_loc) +{ + emit->MOV(32, ::Gen::R(new_loc), regs[preg].location); +} + +OpArg GPRRegCache::GetDefaultLocation(size_t reg) const +{ + return PPCSTATE(gpr[reg]); +} + +const X64Reg* GPRRegCache::GetAllocationOrder(size_t* count) +{ + static const X64Reg allocation_order[] = { +// R12, when used as base register, for example in a LEA, can generate bad code! Need to look into +// this. +#ifdef _WIN32 + RSI, RDI, R13, R14, R15, R8, + R9, R10, R11, R12, RCX +#else + R12, R13, R14, R15, RSI, RDI, + R8, R9, R10, R11, RCX +#endif + }; + *count = sizeof(allocation_order) / sizeof(X64Reg); + return allocation_order; +} + +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); + regs[preg].away |= dirty; + regs[preg].location = Imm32(imm_value); +} + +BitSet32 GPRRegCache::GetRegUtilization() +{ + return jit->js.op->gprInReg; +} + +BitSet32 GPRRegCache::CountRegsIn(size_t preg, u32 lookahead) +{ + BitSet32 regs_used; + + for (u32 i = 1; i < lookahead; i++) + { + BitSet32 regs_in = jit->js.op[i].regsIn; + regs_used |= regs_in; + if (regs_in[preg]) + return regs_used; + } + + return regs_used; +} diff --git a/Source/Core/Core/PowerPC/Jit64/GPRRegCache.h b/Source/Core/Core/PowerPC/Jit64/GPRRegCache.h new file mode 100644 index 0000000000..7032254611 --- /dev/null +++ b/Source/Core/Core/PowerPC/Jit64/GPRRegCache.h @@ -0,0 +1,19 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Core/PowerPC/Jit64/JitRegCache.h" + +class GPRRegCache final : public RegCache +{ +public: + void StoreRegister(size_t preg, const Gen::OpArg& new_loc) override; + void LoadRegister(size_t preg, Gen::X64Reg new_loc) override; + Gen::OpArg GetDefaultLocation(size_t reg) const override; + const Gen::X64Reg* GetAllocationOrder(size_t* count) override; + void SetImmediate32(size_t preg, u32 imm_value, bool dirty = true); + BitSet32 GetRegUtilization() override; + BitSet32 CountRegsIn(size_t preg, u32 lookahead) override; +}; diff --git a/Source/Core/Core/PowerPC/Jit64/Jit.h b/Source/Core/Core/PowerPC/Jit64/Jit.h index 588d776fab..f2318971c2 100644 --- a/Source/Core/Core/PowerPC/Jit64/Jit.h +++ b/Source/Core/Core/PowerPC/Jit64/Jit.h @@ -21,6 +21,8 @@ #include "Common/CommonTypes.h" #include "Common/x64ABI.h" #include "Common/x64Emitter.h" +#include "Core/PowerPC/Jit64/FPURegCache.h" +#include "Core/PowerPC/Jit64/GPRRegCache.h" #include "Core/PowerPC/Jit64/JitAsm.h" #include "Core/PowerPC/Jit64/JitRegCache.h" #include "Core/PowerPC/Jit64Common/Jit64Base.h" diff --git a/Source/Core/Core/PowerPC/Jit64/JitRegCache.cpp b/Source/Core/Core/PowerPC/Jit64/JitRegCache.cpp index 278d6a126c..c99f8511c7 100644 --- a/Source/Core/Core/PowerPC/Jit64/JitRegCache.cpp +++ b/Source/Core/Core/PowerPC/Jit64/JitRegCache.cpp @@ -14,7 +14,6 @@ #include "Common/x64Emitter.h" #include "Core/PowerPC/Jit64/Jit.h" #include "Core/PowerPC/Jit64/JitRegCache.h" -#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h" #include "Core/PowerPC/PowerPC.h" using namespace Gen; @@ -57,128 +56,17 @@ void RegCache::Start() // But only preload IF written OR reads >= 3 } -void RegCache::UnlockAll() +void RegCache::DiscardRegContentsIfCached(size_t preg) { - for (auto& reg : regs) - reg.locked = false; -} - -void RegCache::UnlockAllX() -{ - for (auto& xreg : xregs) - xreg.locked = false; -} - -BitSet32 GPRRegCache::GetRegUtilization() -{ - return jit->js.op->gprInReg; -} - -BitSet32 FPURegCache::GetRegUtilization() -{ - return jit->js.op->gprInReg; -} - -BitSet32 GPRRegCache::CountRegsIn(size_t preg, u32 lookahead) -{ - BitSet32 regsUsed; - for (u32 i = 1; i < lookahead; i++) + if (IsBound(preg)) { - BitSet32 regsIn = jit->js.op[i].regsIn; - regsUsed |= regsIn; - if (regsIn[preg]) - return regsUsed; + X64Reg xr = regs[preg].location.GetSimpleReg(); + xregs[xr].free = true; + xregs[xr].dirty = false; + xregs[xr].ppcReg = INVALID_REG; + regs[preg].away = false; + regs[preg].location = GetDefaultLocation(preg); } - return regsUsed; -} - -BitSet32 FPURegCache::CountRegsIn(size_t preg, u32 lookahead) -{ - BitSet32 regsUsed; - for (u32 i = 1; i < lookahead; i++) - { - BitSet32 regsIn = jit->js.op[i].fregsIn; - regsUsed |= regsIn; - if (regsIn[preg]) - return regsUsed; - } - return regsUsed; -} - -// Estimate roughly how bad it would be to de-allocate this register. Higher score -// means more bad. -float RegCache::ScoreRegister(X64Reg xr) -{ - size_t preg = xregs[xr].ppcReg; - float score = 0; - - // If it's not dirty, we don't need a store to write it back to the register file, so - // bias a bit against dirty registers. Testing shows that a bias of 2 seems roughly - // right: 3 causes too many extra clobbers, while 1 saves very few clobbers relative - // to the number of extra stores it causes. - if (xregs[xr].dirty) - score += 2; - - // If the register isn't actually needed in a physical register for a later instruction, - // writing it back to the register file isn't quite as bad. - if (GetRegUtilization()[preg]) - { - // Don't look too far ahead; we don't want to have quadratic compilation times for - // enormous block sizes! - // This actually improves register allocation a tiny bit; I'm not sure why. - u32 lookahead = std::min(jit->js.instructionsLeft, 64); - // Count how many other registers are going to be used before we need this one again. - u32 regs_in_count = CountRegsIn(preg, lookahead).Count(); - // Totally ad-hoc heuristic to bias based on how many other registers we'll need - // before this one gets used again. - score += 1 + 2 * (5 - log2f(1 + (float)regs_in_count)); - } - - return score; -} - -X64Reg RegCache::GetFreeXReg() -{ - size_t aCount; - const X64Reg* aOrder = GetAllocationOrder(&aCount); - for (size_t i = 0; i < aCount; i++) - { - X64Reg xr = aOrder[i]; - if (!xregs[xr].locked && xregs[xr].free) - { - return xr; - } - } - - // Okay, not found; run the register allocator heuristic and figure out which register we should - // clobber. - float min_score = std::numeric_limits::max(); - X64Reg best_xreg = INVALID_REG; - size_t best_preg = 0; - for (size_t i = 0; i < aCount; i++) - { - X64Reg xreg = (X64Reg)aOrder[i]; - size_t preg = xregs[xreg].ppcReg; - if (xregs[xreg].locked || regs[preg].locked) - continue; - float score = ScoreRegister(xreg); - if (score < min_score) - { - min_score = score; - best_xreg = xreg; - best_preg = preg; - } - } - - if (best_xreg != INVALID_REG) - { - StoreFromRegister(best_preg); - return best_xreg; - } - - // Still no dice? Die! - _assert_msg_(DYNA_REC, 0, "Regcache ran out of regs"); - return INVALID_REG; } void RegCache::FlushR(X64Reg reg) @@ -191,6 +79,35 @@ void RegCache::FlushR(X64Reg reg) } } +void RegCache::Flush(FlushMode mode, BitSet32 regsToFlush) +{ + for (size_t i = 0; i < xregs.size(); i++) + { + if (xregs[i].locked) + PanicAlert("Someone forgot to unlock X64 reg %zu", i); + } + + for (unsigned int i : regsToFlush) + { + if (regs[i].locked) + { + PanicAlert("Someone forgot to unlock PPC reg %u (X64 reg %i).", i, RX(i)); + } + + if (regs[i].away) + { + if (regs[i].location.IsSimpleReg() || regs[i].location.IsImm()) + { + StoreFromRegister(i, mode); + } + else + { + _assert_msg_(DYNA_REC, 0, "Jit64 - Flush unhandled case, reg %u PC: %08x", i, PC); + } + } + } +} + int RegCache::SanityCheck() const { for (size_t i = 0; i < regs.size(); i++) @@ -214,63 +131,6 @@ int RegCache::SanityCheck() const return 0; } -void RegCache::DiscardRegContentsIfCached(size_t preg) -{ - if (IsBound(preg)) - { - X64Reg xr = regs[preg].location.GetSimpleReg(); - xregs[xr].free = true; - xregs[xr].dirty = false; - xregs[xr].ppcReg = INVALID_REG; - regs[preg].away = false; - regs[preg].location = GetDefaultLocation(preg); - } -} - -void GPRRegCache::SetImmediate32(size_t preg, u32 immValue, bool dirty) -{ - // "dirty" can be false to avoid redundantly flushing an immediate when - // processing speculative constants. - DiscardRegContentsIfCached(preg); - regs[preg].away |= dirty; - regs[preg].location = Imm32(immValue); -} - -const X64Reg* GPRRegCache::GetAllocationOrder(size_t* count) -{ - static const X64Reg allocationOrder[] = { -// R12, when used as base register, for example in a LEA, can generate bad code! Need to look into -// this. -#ifdef _WIN32 - RSI, RDI, R13, R14, R15, R8, - R9, R10, R11, R12, RCX -#else - R12, R13, R14, R15, RSI, RDI, - R8, R9, R10, R11, RCX -#endif - }; - *count = sizeof(allocationOrder) / sizeof(X64Reg); - return allocationOrder; -} - -const X64Reg* FPURegCache::GetAllocationOrder(size_t* count) -{ - static const X64Reg allocationOrder[] = {XMM6, XMM7, XMM8, XMM9, XMM10, XMM11, XMM12, - XMM13, XMM14, XMM15, XMM2, XMM3, XMM4, XMM5}; - *count = sizeof(allocationOrder) / sizeof(X64Reg); - return allocationOrder; -} - -OpArg GPRRegCache::GetDefaultLocation(size_t reg) const -{ - return PPCSTATE(gpr[reg]); -} - -OpArg FPURegCache::GetDefaultLocation(size_t reg) const -{ - return PPCSTATE(ps[reg][0]); -} - void RegCache::KillImmediate(size_t preg, bool doLoad, bool makeDirty) { if (regs[preg].away) @@ -351,53 +211,60 @@ void RegCache::StoreFromRegister(size_t i, FlushMode mode) } } -void GPRRegCache::LoadRegister(size_t preg, X64Reg newLoc) +void RegCache::UnlockAll() { - emit->MOV(32, ::Gen::R(newLoc), regs[preg].location); + for (auto& reg : regs) + reg.locked = false; } -void GPRRegCache::StoreRegister(size_t preg, const OpArg& newLoc) +void RegCache::UnlockAllX() { - emit->MOV(32, newLoc, regs[preg].location); + for (auto& xreg : xregs) + xreg.locked = false; } -void FPURegCache::LoadRegister(size_t preg, X64Reg newLoc) +X64Reg RegCache::GetFreeXReg() { - emit->MOVAPD(newLoc, regs[preg].location); -} - -void FPURegCache::StoreRegister(size_t preg, const OpArg& newLoc) -{ - emit->MOVAPD(newLoc, regs[preg].location.GetSimpleReg()); -} - -void RegCache::Flush(FlushMode mode, BitSet32 regsToFlush) -{ - for (size_t i = 0; i < xregs.size(); i++) + size_t aCount; + const X64Reg* aOrder = GetAllocationOrder(&aCount); + for (size_t i = 0; i < aCount; i++) { - if (xregs[i].locked) - PanicAlert("Someone forgot to unlock X64 reg %zu", i); - } - - for (unsigned int i : regsToFlush) - { - if (regs[i].locked) + X64Reg xr = aOrder[i]; + if (!xregs[xr].locked && xregs[xr].free) { - PanicAlert("Someone forgot to unlock PPC reg %u (X64 reg %i).", i, RX(i)); - } - - if (regs[i].away) - { - if (regs[i].location.IsSimpleReg() || regs[i].location.IsImm()) - { - StoreFromRegister(i, mode); - } - else - { - _assert_msg_(DYNA_REC, 0, "Jit64 - Flush unhandled case, reg %u PC: %08x", i, PC); - } + return xr; } } + + // Okay, not found; run the register allocator heuristic and figure out which register we should + // clobber. + float min_score = std::numeric_limits::max(); + X64Reg best_xreg = INVALID_REG; + size_t best_preg = 0; + for (size_t i = 0; i < aCount; i++) + { + X64Reg xreg = (X64Reg)aOrder[i]; + size_t preg = xregs[xreg].ppcReg; + if (xregs[xreg].locked || regs[preg].locked) + continue; + float score = ScoreRegister(xreg); + if (score < min_score) + { + min_score = score; + best_xreg = xreg; + best_preg = preg; + } + } + + if (best_xreg != INVALID_REG) + { + StoreFromRegister(best_preg); + return best_xreg; + } + + // Still no dice? Die! + _assert_msg_(DYNA_REC, 0, "Regcache ran out of regs"); + return INVALID_REG; } int RegCache::NumFreeRegisters() @@ -410,3 +277,35 @@ int RegCache::NumFreeRegisters() count++; return count; } + +// Estimate roughly how bad it would be to de-allocate this register. Higher score +// means more bad. +float RegCache::ScoreRegister(X64Reg xr) +{ + size_t preg = xregs[xr].ppcReg; + float score = 0; + + // If it's not dirty, we don't need a store to write it back to the register file, so + // bias a bit against dirty registers. Testing shows that a bias of 2 seems roughly + // right: 3 causes too many extra clobbers, while 1 saves very few clobbers relative + // to the number of extra stores it causes. + if (xregs[xr].dirty) + score += 2; + + // If the register isn't actually needed in a physical register for a later instruction, + // writing it back to the register file isn't quite as bad. + if (GetRegUtilization()[preg]) + { + // Don't look too far ahead; we don't want to have quadratic compilation times for + // enormous block sizes! + // This actually improves register allocation a tiny bit; I'm not sure why. + u32 lookahead = std::min(jit->js.instructionsLeft, 64); + // Count how many other registers are going to be used before we need this one again. + u32 regs_in_count = CountRegsIn(preg, lookahead).Count(); + // Totally ad-hoc heuristic to bias based on how many other registers we'll need + // before this one gets used again. + score += 1 + 2 * (5 - log2f(1 + (float)regs_in_count)); + } + + return score; +} diff --git a/Source/Core/Core/PowerPC/Jit64/JitRegCache.h b/Source/Core/Core/PowerPC/Jit64/JitRegCache.h index f47e57e377..41fab3d79e 100644 --- a/Source/Core/Core/PowerPC/Jit64/JitRegCache.h +++ b/Source/Core/Core/PowerPC/Jit64/JitRegCache.h @@ -153,26 +153,3 @@ public: Gen::X64Reg GetFreeXReg(); int NumFreeRegisters(); }; - -class GPRRegCache final : public RegCache -{ -public: - void StoreRegister(size_t preg, const Gen::OpArg& newLoc) override; - void LoadRegister(size_t preg, Gen::X64Reg newLoc) override; - Gen::OpArg GetDefaultLocation(size_t reg) const override; - const Gen::X64Reg* GetAllocationOrder(size_t* count) override; - void SetImmediate32(size_t preg, u32 immValue, bool dirty = true); - BitSet32 GetRegUtilization() override; - BitSet32 CountRegsIn(size_t preg, u32 lookahead) override; -}; - -class FPURegCache final : public RegCache -{ -public: - void StoreRegister(size_t preg, const Gen::OpArg& newLoc) override; - void LoadRegister(size_t preg, Gen::X64Reg newLoc) override; - const Gen::X64Reg* GetAllocationOrder(size_t* count) override; - Gen::OpArg GetDefaultLocation(size_t reg) const override; - BitSet32 GetRegUtilization() override; - BitSet32 CountRegsIn(size_t preg, u32 lookahead) override; -}; From d1fc694c02bf805895c53f031684b85de75edbd9 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 20 Dec 2016 22:20:03 -0500 Subject: [PATCH 2/8] JitRegCache: Move protected interface below public interface --- Source/Core/Core/PowerPC/Jit64/JitRegCache.h | 25 ++++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/Source/Core/Core/PowerPC/Jit64/JitRegCache.h b/Source/Core/Core/PowerPC/Jit64/JitRegCache.h index 41fab3d79e..ef0d03864e 100644 --- a/Source/Core/Core/PowerPC/Jit64/JitRegCache.h +++ b/Source/Core/Core/PowerPC/Jit64/JitRegCache.h @@ -38,19 +38,6 @@ typedef int PReg; class RegCache { -protected: - std::array regs; - std::array xregs; - - virtual const Gen::X64Reg* GetAllocationOrder(size_t* count) = 0; - - virtual BitSet32 GetRegUtilization() = 0; - virtual BitSet32 CountRegsIn(size_t preg, u32 lookahead) = 0; - - Gen::XEmitter* emit; - - float ScoreRegister(Gen::X64Reg xreg); - public: RegCache(); virtual ~RegCache() {} @@ -152,4 +139,16 @@ public: bool IsBound(size_t preg) const { return regs[preg].away && regs[preg].location.IsSimpleReg(); } Gen::X64Reg GetFreeXReg(); int NumFreeRegisters(); + +protected: + virtual const Gen::X64Reg* GetAllocationOrder(size_t* count) = 0; + + virtual BitSet32 GetRegUtilization() = 0; + virtual BitSet32 CountRegsIn(size_t preg, u32 lookahead) = 0; + + float ScoreRegister(Gen::X64Reg xreg); + + std::array regs; + std::array xregs; + Gen::XEmitter* emit; }; From 41d47dda932bd5432c8db89d53c806aa5ea4ee01 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 20 Dec 2016 22:27:22 -0500 Subject: [PATCH 3/8] JitRegCache: Move implementation details into the cpp file where applicable Any future changes to these register cache functions won't require everything that includes the register cache header to also be recompiled. --- .../Core/Core/PowerPC/Jit64/JitRegCache.cpp | 63 ++++++++++++++++--- Source/Core/Core/PowerPC/Jit64/JitRegCache.h | 54 ++++++---------- 2 files changed, 75 insertions(+), 42 deletions(-) diff --git a/Source/Core/Core/PowerPC/Jit64/JitRegCache.cpp b/Source/Core/Core/PowerPC/Jit64/JitRegCache.cpp index c99f8511c7..5176fc330a 100644 --- a/Source/Core/Core/PowerPC/Jit64/JitRegCache.cpp +++ b/Source/Core/Core/PowerPC/Jit64/JitRegCache.cpp @@ -69,14 +69,9 @@ void RegCache::DiscardRegContentsIfCached(size_t preg) } } -void RegCache::FlushR(X64Reg reg) +void RegCache::SetEmitter(XEmitter* emitter) { - if (reg >= xregs.size()) - PanicAlert("Flushing non existent reg"); - if (!xregs[reg].free) - { - StoreFromRegister(xregs[reg].ppcReg); - } + emit = emitter; } void RegCache::Flush(FlushMode mode, BitSet32 regsToFlush) @@ -108,6 +103,36 @@ void RegCache::Flush(FlushMode mode, BitSet32 regsToFlush) } } +void RegCache::FlushR(X64Reg reg) +{ + if (reg >= xregs.size()) + PanicAlert("Flushing non existent reg"); + if (!xregs[reg].free) + { + StoreFromRegister(xregs[reg].ppcReg); + } +} + +void RegCache::FlushR(X64Reg reg, X64Reg reg2) +{ + FlushR(reg); + FlushR(reg2); +} + +void RegCache::FlushLockX(X64Reg reg) +{ + FlushR(reg); + LockX(reg); +} + +void RegCache::FlushLockX(X64Reg reg1, X64Reg reg2) +{ + FlushR(reg1); + FlushR(reg2); + LockX(reg1); + LockX(reg2); +} + int RegCache::SanityCheck() const { for (size_t i = 0; i < regs.size(); i++) @@ -211,6 +236,20 @@ void RegCache::StoreFromRegister(size_t i, FlushMode mode) } } +const OpArg& RegCache::R(size_t preg) const +{ + return regs[preg].location; +} + +X64Reg RegCache::RX(size_t preg) const +{ + if (IsBound(preg)) + return regs[preg].location.GetSimpleReg(); + + PanicAlert("Unbound register - %zu", preg); + return Gen::INVALID_REG; +} + void RegCache::UnlockAll() { for (auto& reg : regs) @@ -223,6 +262,16 @@ void RegCache::UnlockAllX() xreg.locked = false; } +bool RegCache::IsFreeX(size_t xreg) const +{ + return xregs[xreg].free && !xregs[xreg].locked; +} + +bool RegCache::IsBound(size_t preg) const +{ + return regs[preg].away && regs[preg].location.IsSimpleReg(); +} + X64Reg RegCache::GetFreeXReg() { size_t aCount; diff --git a/Source/Core/Core/PowerPC/Jit64/JitRegCache.h b/Source/Core/Core/PowerPC/Jit64/JitRegCache.h index ef0d03864e..e576e8d873 100644 --- a/Source/Core/Core/PowerPC/Jit64/JitRegCache.h +++ b/Source/Core/Core/PowerPC/Jit64/JitRegCache.h @@ -40,33 +40,26 @@ class RegCache { public: RegCache(); - virtual ~RegCache() {} + virtual ~RegCache() = default; + + virtual void StoreRegister(size_t preg, const Gen::OpArg& new_loc) = 0; + virtual void LoadRegister(size_t preg, Gen::X64Reg new_loc) = 0; + virtual Gen::OpArg GetDefaultLocation(size_t reg) const = 0; + void Start(); void DiscardRegContentsIfCached(size_t preg); - void SetEmitter(Gen::XEmitter* emitter) { emit = emitter; } - void FlushR(Gen::X64Reg reg); - void FlushR(Gen::X64Reg reg, Gen::X64Reg reg2) - { - FlushR(reg); - FlushR(reg2); - } - - void FlushLockX(Gen::X64Reg reg) - { - FlushR(reg); - LockX(reg); - } - void FlushLockX(Gen::X64Reg reg1, Gen::X64Reg reg2) - { - FlushR(reg1); - FlushR(reg2); - LockX(reg1); - LockX(reg2); - } + void SetEmitter(Gen::XEmitter* emitter); void Flush(FlushMode mode = FLUSH_ALL, BitSet32 regsToFlush = BitSet32::AllTrue(32)); void Flush(PPCAnalyst::CodeOp* op) { Flush(); } + + void FlushR(Gen::X64Reg reg); + void FlushR(Gen::X64Reg reg, Gen::X64Reg reg2); + + void FlushLockX(Gen::X64Reg reg); + void FlushLockX(Gen::X64Reg reg1, Gen::X64Reg reg2); + int SanityCheck() const; void KillImmediate(size_t preg, bool doLoad, bool makeDirty); @@ -74,19 +67,9 @@ public: // read only will not set dirty flag void BindToRegister(size_t preg, bool doLoad = true, bool makeDirty = true); void StoreFromRegister(size_t preg, FlushMode mode = FLUSH_ALL); - virtual void StoreRegister(size_t preg, const Gen::OpArg& newLoc) = 0; - virtual void LoadRegister(size_t preg, Gen::X64Reg newLoc) = 0; - const Gen::OpArg& R(size_t preg) const { return regs[preg].location; } - Gen::X64Reg RX(size_t preg) const - { - if (IsBound(preg)) - return regs[preg].location.GetSimpleReg(); - - PanicAlert("Unbound register - %zu", preg); - return Gen::INVALID_REG; - } - virtual Gen::OpArg GetDefaultLocation(size_t reg) const = 0; + const Gen::OpArg& R(size_t preg) const; + Gen::X64Reg RX(size_t preg) const; // Register locking. @@ -135,8 +118,9 @@ public: void UnlockAll(); void UnlockAllX(); - bool IsFreeX(size_t xreg) const { return xregs[xreg].free && !xregs[xreg].locked; } - bool IsBound(size_t preg) const { return regs[preg].away && regs[preg].location.IsSimpleReg(); } + bool IsFreeX(size_t xreg) const; + bool IsBound(size_t preg) const; + Gen::X64Reg GetFreeXReg(); int NumFreeRegisters(); From b629eb300a9362e4d36c6ce32c5460c0ad08719b Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 20 Dec 2016 22:35:10 -0500 Subject: [PATCH 4/8] JitRegCache: Remove unused Flush function This was kind of a pointless function, considering the parameter wasn't used at all, so the other Flush() function could have been just directly used instead. --- Source/Core/Core/PowerPC/Jit64/JitRegCache.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Core/Core/PowerPC/Jit64/JitRegCache.h b/Source/Core/Core/PowerPC/Jit64/JitRegCache.h index e576e8d873..76738490d7 100644 --- a/Source/Core/Core/PowerPC/Jit64/JitRegCache.h +++ b/Source/Core/Core/PowerPC/Jit64/JitRegCache.h @@ -52,7 +52,6 @@ public: void SetEmitter(Gen::XEmitter* emitter); void Flush(FlushMode mode = FLUSH_ALL, BitSet32 regsToFlush = BitSet32::AllTrue(32)); - void Flush(PPCAnalyst::CodeOp* op) { Flush(); } void FlushR(Gen::X64Reg reg); void FlushR(Gen::X64Reg reg, Gen::X64Reg reg2); From 4ae23fc45cafba35d6350235a29d19cf63583e70 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 20 Dec 2016 22:37:27 -0500 Subject: [PATCH 5/8] JitRegCache: Remove unused typedefs --- Source/Core/Core/PowerPC/Jit64/JitRegCache.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/Source/Core/Core/PowerPC/Jit64/JitRegCache.h b/Source/Core/Core/PowerPC/Jit64/JitRegCache.h index 76738490d7..6fa704b1d8 100644 --- a/Source/Core/Core/PowerPC/Jit64/JitRegCache.h +++ b/Source/Core/Core/PowerPC/Jit64/JitRegCache.h @@ -31,9 +31,6 @@ struct X64CachedReg bool locked; }; -typedef int XReg; -typedef int PReg; - #define NUMXREGS 16 class RegCache From e291e8f1a7cf98d14a9390b112d08d74586776c8 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 20 Dec 2016 22:43:02 -0500 Subject: [PATCH 6/8] JitRegCache: Convert #define macro into a typed constant --- Source/Core/Core/PowerPC/Jit64/Jit.cpp | 2 +- Source/Core/Core/PowerPC/Jit64/JitRegCache.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Core/Core/PowerPC/Jit64/Jit.cpp b/Source/Core/Core/PowerPC/Jit64/Jit.cpp index 53a0c49998..0c10f85c02 100644 --- a/Source/Core/Core/PowerPC/Jit64/Jit.cpp +++ b/Source/Core/Core/PowerPC/Jit64/Jit.cpp @@ -950,7 +950,7 @@ BitSet8 Jit64::ComputeStaticGQRs(const PPCAnalyst::CodeBlock& cb) const BitSet32 Jit64::CallerSavedRegistersInUse() const { BitSet32 result; - for (int i = 0; i < NUMXREGS; i++) + for (size_t i = 0; i < RegCache::NUM_XREGS; i++) { if (!gpr.IsFreeX(i)) result[i] = true; diff --git a/Source/Core/Core/PowerPC/Jit64/JitRegCache.h b/Source/Core/Core/PowerPC/Jit64/JitRegCache.h index 6fa704b1d8..112d248e96 100644 --- a/Source/Core/Core/PowerPC/Jit64/JitRegCache.h +++ b/Source/Core/Core/PowerPC/Jit64/JitRegCache.h @@ -31,11 +31,11 @@ struct X64CachedReg bool locked; }; -#define NUMXREGS 16 - class RegCache { public: + static constexpr size_t NUM_XREGS = 16; + RegCache(); virtual ~RegCache() = default; @@ -129,6 +129,6 @@ protected: float ScoreRegister(Gen::X64Reg xreg); std::array regs; - std::array xregs; + std::array xregs; Gen::XEmitter* emit; }; From 8ece485a2f95bcd199a99fcf25a0df9833a6b851 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 20 Dec 2016 22:59:03 -0500 Subject: [PATCH 7/8] JitRegCache: Make member variables conform to our coding style --- .../Core/Core/PowerPC/Jit64/FPURegCache.cpp | 4 +- .../Core/Core/PowerPC/Jit64/GPRRegCache.cpp | 8 +- .../Core/Core/PowerPC/Jit64/JitRegCache.cpp | 128 +++++++++--------- Source/Core/Core/PowerPC/Jit64/JitRegCache.h | 16 +-- 4 files changed, 78 insertions(+), 78 deletions(-) diff --git a/Source/Core/Core/PowerPC/Jit64/FPURegCache.cpp b/Source/Core/Core/PowerPC/Jit64/FPURegCache.cpp index 698f7da640..f85cfce6a8 100644 --- a/Source/Core/Core/PowerPC/Jit64/FPURegCache.cpp +++ b/Source/Core/Core/PowerPC/Jit64/FPURegCache.cpp @@ -11,12 +11,12 @@ using namespace Gen; void FPURegCache::StoreRegister(size_t preg, const OpArg& new_loc) { - emit->MOVAPD(new_loc, regs[preg].location.GetSimpleReg()); + m_emitter->MOVAPD(new_loc, m_regs[preg].location.GetSimpleReg()); } void FPURegCache::LoadRegister(size_t preg, X64Reg new_loc) { - emit->MOVAPD(new_loc, regs[preg].location); + m_emitter->MOVAPD(new_loc, m_regs[preg].location); } const X64Reg* FPURegCache::GetAllocationOrder(size_t* count) diff --git a/Source/Core/Core/PowerPC/Jit64/GPRRegCache.cpp b/Source/Core/Core/PowerPC/Jit64/GPRRegCache.cpp index c71e4471e8..89094cc93d 100644 --- a/Source/Core/Core/PowerPC/Jit64/GPRRegCache.cpp +++ b/Source/Core/Core/PowerPC/Jit64/GPRRegCache.cpp @@ -11,12 +11,12 @@ using namespace Gen; void GPRRegCache::StoreRegister(size_t preg, const OpArg& new_loc) { - emit->MOV(32, new_loc, regs[preg].location); + m_emitter->MOV(32, new_loc, m_regs[preg].location); } void GPRRegCache::LoadRegister(size_t preg, X64Reg new_loc) { - emit->MOV(32, ::Gen::R(new_loc), regs[preg].location); + m_emitter->MOV(32, ::Gen::R(new_loc), m_regs[preg].location); } OpArg GPRRegCache::GetDefaultLocation(size_t reg) const @@ -46,8 +46,8 @@ 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); - regs[preg].away |= dirty; - regs[preg].location = Imm32(imm_value); + m_regs[preg].away |= dirty; + m_regs[preg].location = Imm32(imm_value); } BitSet32 GPRRegCache::GetRegUtilization() diff --git a/Source/Core/Core/PowerPC/Jit64/JitRegCache.cpp b/Source/Core/Core/PowerPC/Jit64/JitRegCache.cpp index 5176fc330a..cc179d73e0 100644 --- a/Source/Core/Core/PowerPC/Jit64/JitRegCache.cpp +++ b/Source/Core/Core/PowerPC/Jit64/JitRegCache.cpp @@ -19,24 +19,24 @@ using namespace Gen; using namespace PowerPC; -RegCache::RegCache() : emit(nullptr) +RegCache::RegCache() { } void RegCache::Start() { - for (auto& xreg : xregs) + for (auto& xreg : m_xregs) { xreg.free = true; xreg.dirty = false; xreg.locked = false; xreg.ppcReg = INVALID_REG; } - for (size_t i = 0; i < regs.size(); i++) + for (size_t i = 0; i < m_regs.size(); i++) { - regs[i].location = GetDefaultLocation(i); - regs[i].away = false; - regs[i].locked = false; + m_regs[i].location = GetDefaultLocation(i); + m_regs[i].away = false; + m_regs[i].locked = false; } // todo: sort to find the most popular regs @@ -60,38 +60,38 @@ void RegCache::DiscardRegContentsIfCached(size_t preg) { if (IsBound(preg)) { - X64Reg xr = regs[preg].location.GetSimpleReg(); - xregs[xr].free = true; - xregs[xr].dirty = false; - xregs[xr].ppcReg = INVALID_REG; - regs[preg].away = false; - regs[preg].location = GetDefaultLocation(preg); + X64Reg xr = m_regs[preg].location.GetSimpleReg(); + m_xregs[xr].free = true; + m_xregs[xr].dirty = false; + m_xregs[xr].ppcReg = INVALID_REG; + m_regs[preg].away = false; + m_regs[preg].location = GetDefaultLocation(preg); } } void RegCache::SetEmitter(XEmitter* emitter) { - emit = emitter; + m_emitter = emitter; } void RegCache::Flush(FlushMode mode, BitSet32 regsToFlush) { - for (size_t i = 0; i < xregs.size(); i++) + for (size_t i = 0; i < m_xregs.size(); i++) { - if (xregs[i].locked) + if (m_xregs[i].locked) PanicAlert("Someone forgot to unlock X64 reg %zu", i); } for (unsigned int i : regsToFlush) { - if (regs[i].locked) + if (m_regs[i].locked) { PanicAlert("Someone forgot to unlock PPC reg %u (X64 reg %i).", i, RX(i)); } - if (regs[i].away) + if (m_regs[i].away) { - if (regs[i].location.IsSimpleReg() || regs[i].location.IsImm()) + if (m_regs[i].location.IsSimpleReg() || m_regs[i].location.IsImm()) { StoreFromRegister(i, mode); } @@ -105,11 +105,11 @@ void RegCache::Flush(FlushMode mode, BitSet32 regsToFlush) void RegCache::FlushR(X64Reg reg) { - if (reg >= xregs.size()) + if (reg >= m_xregs.size()) PanicAlert("Flushing non existent reg"); - if (!xregs[reg].free) + if (!m_xregs[reg].free) { - StoreFromRegister(xregs[reg].ppcReg); + StoreFromRegister(m_xregs[reg].ppcReg); } } @@ -135,19 +135,19 @@ void RegCache::FlushLockX(X64Reg reg1, X64Reg reg2) int RegCache::SanityCheck() const { - for (size_t i = 0; i < regs.size(); i++) + for (size_t i = 0; i < m_regs.size(); i++) { - if (regs[i].away) + if (m_regs[i].away) { - if (regs[i].location.IsSimpleReg()) + if (m_regs[i].location.IsSimpleReg()) { - Gen::X64Reg simple = regs[i].location.GetSimpleReg(); - if (xregs[simple].locked) + Gen::X64Reg simple = m_regs[i].location.GetSimpleReg(); + if (m_xregs[simple].locked) return 1; - if (xregs[simple].ppcReg != i) + if (m_xregs[simple].ppcReg != i) return 2; } - else if (regs[i].location.IsImm()) + else if (m_regs[i].location.IsImm()) { return 3; } @@ -158,47 +158,47 @@ int RegCache::SanityCheck() const void RegCache::KillImmediate(size_t preg, bool doLoad, bool makeDirty) { - if (regs[preg].away) + if (m_regs[preg].away) { - if (regs[preg].location.IsImm()) + if (m_regs[preg].location.IsImm()) BindToRegister(preg, doLoad, makeDirty); - else if (regs[preg].location.IsSimpleReg()) - xregs[RX(preg)].dirty |= makeDirty; + else if (m_regs[preg].location.IsSimpleReg()) + m_xregs[RX(preg)].dirty |= makeDirty; } } void RegCache::BindToRegister(size_t i, bool doLoad, bool makeDirty) { - if (!regs[i].away || regs[i].location.IsImm()) + if (!m_regs[i].away || m_regs[i].location.IsImm()) { X64Reg xr = GetFreeXReg(); - if (xregs[xr].dirty) + if (m_xregs[xr].dirty) PanicAlert("Xreg already dirty"); - if (xregs[xr].locked) + if (m_xregs[xr].locked) PanicAlert("GetFreeXReg returned locked register"); - xregs[xr].free = false; - xregs[xr].ppcReg = i; - xregs[xr].dirty = makeDirty || regs[i].away; + m_xregs[xr].free = false; + m_xregs[xr].ppcReg = i; + m_xregs[xr].dirty = makeDirty || m_regs[i].away; if (doLoad) LoadRegister(i, xr); - for (size_t j = 0; j < regs.size(); j++) + for (size_t j = 0; j < m_regs.size(); j++) { - if (i != j && regs[j].location.IsSimpleReg(xr)) + if (i != j && m_regs[j].location.IsSimpleReg(xr)) { Crash(); } } - regs[i].away = true; - regs[i].location = ::Gen::R(xr); + m_regs[i].away = true; + m_regs[i].location = ::Gen::R(xr); } else { // reg location must be simplereg; memory locations // and immediates are taken care of above. - xregs[RX(i)].dirty |= makeDirty; + m_xregs[RX(i)].dirty |= makeDirty; } - if (xregs[RX(i)].locked) + if (m_xregs[RX(i)].locked) { PanicAlert("Seriously WTF, this reg should have been flushed"); } @@ -206,18 +206,18 @@ void RegCache::BindToRegister(size_t i, bool doLoad, bool makeDirty) void RegCache::StoreFromRegister(size_t i, FlushMode mode) { - if (regs[i].away) + if (m_regs[i].away) { bool doStore; - if (regs[i].location.IsSimpleReg()) + if (m_regs[i].location.IsSimpleReg()) { X64Reg xr = RX(i); - doStore = xregs[xr].dirty; + doStore = m_xregs[xr].dirty; if (mode == FLUSH_ALL) { - xregs[xr].free = true; - xregs[xr].ppcReg = INVALID_REG; - xregs[xr].dirty = false; + m_xregs[xr].free = true; + m_xregs[xr].ppcReg = INVALID_REG; + m_xregs[xr].dirty = false; } } else @@ -230,21 +230,21 @@ void RegCache::StoreFromRegister(size_t i, FlushMode mode) StoreRegister(i, newLoc); if (mode == FLUSH_ALL) { - regs[i].location = newLoc; - regs[i].away = false; + m_regs[i].location = newLoc; + m_regs[i].away = false; } } } const OpArg& RegCache::R(size_t preg) const { - return regs[preg].location; + return m_regs[preg].location; } X64Reg RegCache::RX(size_t preg) const { if (IsBound(preg)) - return regs[preg].location.GetSimpleReg(); + return m_regs[preg].location.GetSimpleReg(); PanicAlert("Unbound register - %zu", preg); return Gen::INVALID_REG; @@ -252,24 +252,24 @@ X64Reg RegCache::RX(size_t preg) const void RegCache::UnlockAll() { - for (auto& reg : regs) + for (auto& reg : m_regs) reg.locked = false; } void RegCache::UnlockAllX() { - for (auto& xreg : xregs) + for (auto& xreg : m_xregs) xreg.locked = false; } bool RegCache::IsFreeX(size_t xreg) const { - return xregs[xreg].free && !xregs[xreg].locked; + return m_xregs[xreg].free && !m_xregs[xreg].locked; } bool RegCache::IsBound(size_t preg) const { - return regs[preg].away && regs[preg].location.IsSimpleReg(); + return m_regs[preg].away && m_regs[preg].location.IsSimpleReg(); } X64Reg RegCache::GetFreeXReg() @@ -279,7 +279,7 @@ X64Reg RegCache::GetFreeXReg() for (size_t i = 0; i < aCount; i++) { X64Reg xr = aOrder[i]; - if (!xregs[xr].locked && xregs[xr].free) + if (!m_xregs[xr].locked && m_xregs[xr].free) { return xr; } @@ -293,8 +293,8 @@ X64Reg RegCache::GetFreeXReg() for (size_t i = 0; i < aCount; i++) { X64Reg xreg = (X64Reg)aOrder[i]; - size_t preg = xregs[xreg].ppcReg; - if (xregs[xreg].locked || regs[preg].locked) + size_t preg = m_xregs[xreg].ppcReg; + if (m_xregs[xreg].locked || m_regs[preg].locked) continue; float score = ScoreRegister(xreg); if (score < min_score) @@ -322,7 +322,7 @@ int RegCache::NumFreeRegisters() size_t aCount; const X64Reg* aOrder = GetAllocationOrder(&aCount); for (size_t i = 0; i < aCount; i++) - if (!xregs[aOrder[i]].locked && xregs[aOrder[i]].free) + if (!m_xregs[aOrder[i]].locked && m_xregs[aOrder[i]].free) count++; return count; } @@ -331,14 +331,14 @@ int RegCache::NumFreeRegisters() // means more bad. float RegCache::ScoreRegister(X64Reg xr) { - size_t preg = xregs[xr].ppcReg; + size_t preg = m_xregs[xr].ppcReg; float score = 0; // If it's not dirty, we don't need a store to write it back to the register file, so // bias a bit against dirty registers. Testing shows that a bias of 2 seems roughly // right: 3 causes too many extra clobbers, while 1 saves very few clobbers relative // to the number of extra stores it causes. - if (xregs[xr].dirty) + if (m_xregs[xr].dirty) score += 2; // If the register isn't actually needed in a physical register for a later instruction, diff --git a/Source/Core/Core/PowerPC/Jit64/JitRegCache.h b/Source/Core/Core/PowerPC/Jit64/JitRegCache.h index 112d248e96..de33405485 100644 --- a/Source/Core/Core/PowerPC/Jit64/JitRegCache.h +++ b/Source/Core/Core/PowerPC/Jit64/JitRegCache.h @@ -73,7 +73,7 @@ public: template void Lock(T p) { - regs[p].locked = true; + m_regs[p].locked = true; } template void Lock(T first, Args... args) @@ -86,9 +86,9 @@ public: template void LockX(T x) { - if (xregs[x].locked) + if (m_xregs[x].locked) PanicAlert("RegCache: x %i already locked!", x); - xregs[x].locked = true; + m_xregs[x].locked = true; } template void LockX(T first, Args... args) @@ -100,9 +100,9 @@ public: template void UnlockX(T x) { - if (!xregs[x].locked) + if (!m_xregs[x].locked) PanicAlert("RegCache: x %i already unlocked!", x); - xregs[x].locked = false; + m_xregs[x].locked = false; } template void UnlockX(T first, Args... args) @@ -128,7 +128,7 @@ protected: float ScoreRegister(Gen::X64Reg xreg); - std::array regs; - std::array xregs; - Gen::XEmitter* emit; + std::array m_regs; + std::array m_xregs; + Gen::XEmitter* m_emitter = nullptr; }; From 407f3af8c309b31d762d4ca46fa9f88c8db9806e Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 20 Dec 2016 23:09:57 -0500 Subject: [PATCH 8/8] JitRegCache: Get rid of reliance on the jit global variable --- Source/Core/Core/PowerPC/Jit64/FPURegCache.cpp | 9 +++++++-- Source/Core/Core/PowerPC/Jit64/FPURegCache.h | 4 ++++ Source/Core/Core/PowerPC/Jit64/GPRRegCache.cpp | 9 +++++++-- Source/Core/Core/PowerPC/Jit64/GPRRegCache.h | 4 ++++ Source/Core/Core/PowerPC/Jit64/Jit.h | 4 ++-- Source/Core/Core/PowerPC/Jit64/JitRegCache.cpp | 4 ++-- Source/Core/Core/PowerPC/Jit64/JitRegCache.h | 5 ++++- 7 files changed, 30 insertions(+), 9 deletions(-) diff --git a/Source/Core/Core/PowerPC/Jit64/FPURegCache.cpp b/Source/Core/Core/PowerPC/Jit64/FPURegCache.cpp index f85cfce6a8..73689377e1 100644 --- a/Source/Core/Core/PowerPC/Jit64/FPURegCache.cpp +++ b/Source/Core/Core/PowerPC/Jit64/FPURegCache.cpp @@ -4,11 +4,16 @@ #include "Core/PowerPC/Jit64/FPURegCache.h" +#include "Core/PowerPC/Jit64/Jit.h" #include "Core/PowerPC/Jit64Common/Jit64Base.h" #include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h" using namespace Gen; +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()); @@ -34,7 +39,7 @@ OpArg FPURegCache::GetDefaultLocation(size_t reg) const BitSet32 FPURegCache::GetRegUtilization() { - return jit->js.op->gprInReg; + return m_jit.js.op->gprInReg; } BitSet32 FPURegCache::CountRegsIn(size_t preg, u32 lookahead) @@ -43,7 +48,7 @@ BitSet32 FPURegCache::CountRegsIn(size_t preg, u32 lookahead) for (u32 i = 1; i < lookahead; i++) { - BitSet32 regs_in = jit->js.op[i].fregsIn; + BitSet32 regs_in = m_jit.js.op[i].fregsIn; regs_used |= regs_in; if (regs_in[preg]) return regs_used; diff --git a/Source/Core/Core/PowerPC/Jit64/FPURegCache.h b/Source/Core/Core/PowerPC/Jit64/FPURegCache.h index 0ea2faf2f8..5e7fb5e5b5 100644 --- a/Source/Core/Core/PowerPC/Jit64/FPURegCache.h +++ b/Source/Core/Core/PowerPC/Jit64/FPURegCache.h @@ -6,9 +6,13 @@ #include "Core/PowerPC/Jit64/JitRegCache.h" +class Jit64; + class FPURegCache final : public RegCache { public: + explicit FPURegCache(Jit64& jit); + void StoreRegister(size_t preg, const Gen::OpArg& newLoc) override; void LoadRegister(size_t preg, Gen::X64Reg newLoc) override; const Gen::X64Reg* GetAllocationOrder(size_t* count) override; diff --git a/Source/Core/Core/PowerPC/Jit64/GPRRegCache.cpp b/Source/Core/Core/PowerPC/Jit64/GPRRegCache.cpp index 89094cc93d..e94f585526 100644 --- a/Source/Core/Core/PowerPC/Jit64/GPRRegCache.cpp +++ b/Source/Core/Core/PowerPC/Jit64/GPRRegCache.cpp @@ -4,11 +4,16 @@ #include "Core/PowerPC/Jit64/GPRRegCache.h" +#include "Core/PowerPC/Jit64/Jit.h" #include "Core/PowerPC/Jit64Common/Jit64Base.h" #include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h" using namespace Gen; +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); @@ -52,7 +57,7 @@ void GPRRegCache::SetImmediate32(size_t preg, u32 imm_value, bool dirty) BitSet32 GPRRegCache::GetRegUtilization() { - return jit->js.op->gprInReg; + return m_jit.js.op->gprInReg; } BitSet32 GPRRegCache::CountRegsIn(size_t preg, u32 lookahead) @@ -61,7 +66,7 @@ BitSet32 GPRRegCache::CountRegsIn(size_t preg, u32 lookahead) for (u32 i = 1; i < lookahead; i++) { - BitSet32 regs_in = jit->js.op[i].regsIn; + BitSet32 regs_in = m_jit.js.op[i].regsIn; regs_used |= regs_in; if (regs_in[preg]) return regs_used; diff --git a/Source/Core/Core/PowerPC/Jit64/GPRRegCache.h b/Source/Core/Core/PowerPC/Jit64/GPRRegCache.h index 7032254611..4e23268ece 100644 --- a/Source/Core/Core/PowerPC/Jit64/GPRRegCache.h +++ b/Source/Core/Core/PowerPC/Jit64/GPRRegCache.h @@ -6,9 +6,13 @@ #include "Core/PowerPC/Jit64/JitRegCache.h" +class Jit64; + class GPRRegCache final : public RegCache { public: + explicit GPRRegCache(Jit64& jit); + void StoreRegister(size_t preg, const Gen::OpArg& new_loc) override; void LoadRegister(size_t preg, Gen::X64Reg new_loc) override; Gen::OpArg GetDefaultLocation(size_t reg) const override; diff --git a/Source/Core/Core/PowerPC/Jit64/Jit.h b/Source/Core/Core/PowerPC/Jit64/Jit.h index f2318971c2..fc7e5d3522 100644 --- a/Source/Core/Core/PowerPC/Jit64/Jit.h +++ b/Source/Core/Core/PowerPC/Jit64/Jit.h @@ -35,8 +35,8 @@ private: void AllocStack(); void FreeStack(); - GPRRegCache gpr; - FPURegCache fpr; + GPRRegCache gpr{*this}; + FPURegCache fpr{*this}; // The default code buffer. We keep it around to not have to alloc/dealloc a // large chunk of memory for each recompiled block. diff --git a/Source/Core/Core/PowerPC/Jit64/JitRegCache.cpp b/Source/Core/Core/PowerPC/Jit64/JitRegCache.cpp index cc179d73e0..071e6bebc9 100644 --- a/Source/Core/Core/PowerPC/Jit64/JitRegCache.cpp +++ b/Source/Core/Core/PowerPC/Jit64/JitRegCache.cpp @@ -19,7 +19,7 @@ using namespace Gen; using namespace PowerPC; -RegCache::RegCache() +RegCache::RegCache(Jit64& jit) : m_jit{jit} { } @@ -348,7 +348,7 @@ float RegCache::ScoreRegister(X64Reg xr) // Don't look too far ahead; we don't want to have quadratic compilation times for // enormous block sizes! // This actually improves register allocation a tiny bit; I'm not sure why. - u32 lookahead = std::min(jit->js.instructionsLeft, 64); + u32 lookahead = std::min(m_jit.js.instructionsLeft, 64); // Count how many other registers are going to be used before we need this one again. u32 regs_in_count = CountRegsIn(preg, lookahead).Count(); // Totally ad-hoc heuristic to bias based on how many other registers we'll need diff --git a/Source/Core/Core/PowerPC/Jit64/JitRegCache.h b/Source/Core/Core/PowerPC/Jit64/JitRegCache.h index de33405485..54608ff793 100644 --- a/Source/Core/Core/PowerPC/Jit64/JitRegCache.h +++ b/Source/Core/Core/PowerPC/Jit64/JitRegCache.h @@ -10,6 +10,8 @@ #include "Common/x64Emitter.h" #include "Core/PowerPC/PPCAnalyst.h" +class Jit64; + enum FlushMode { FLUSH_ALL, @@ -36,7 +38,7 @@ class RegCache public: static constexpr size_t NUM_XREGS = 16; - RegCache(); + explicit RegCache(Jit64& jit); virtual ~RegCache() = default; virtual void StoreRegister(size_t preg, const Gen::OpArg& new_loc) = 0; @@ -128,6 +130,7 @@ protected: float ScoreRegister(Gen::X64Reg xreg); + Jit64& m_jit; std::array m_regs; std::array m_xregs; Gen::XEmitter* m_emitter = nullptr;