diff --git a/Source/Core/Core/PowerPC/Jit64/RegCache/CachedReg.h b/Source/Core/Core/PowerPC/Jit64/RegCache/CachedReg.h index 08f387f0a9..b9a8b38c43 100644 --- a/Source/Core/Core/PowerPC/Jit64/RegCache/CachedReg.h +++ b/Source/Core/Core/PowerPC/Jit64/RegCache/CachedReg.h @@ -2,11 +2,14 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#pragma once + #include #include "Common/Assert.h" #include "Common/CommonTypes.h" #include "Common/x64Emitter.h" +#include "Core/PowerPC/Jit64/RegCache/RCMode.h" using preg_t = size_t; @@ -125,3 +128,79 @@ private: bool dirty = false; size_t locked = 0; }; + +class RCConstraint +{ +public: + bool IsRealized() const { return realized; } + + bool ShouldBind() const { return bind; } + bool ShouldLoad() const { return read; } + bool ShouldDirty() const { return write; } + bool ShouldKillImmediate() const { return kill_imm; } + + void Realized() { realized = true; } + void RealizedBound() + { + realized = true; + bind = true; + } + + void AddUse(RCMode mode) { AddConstraint(false, mode, false); } + void AddUseNoImm(RCMode mode) { AddConstraint(false, mode, true); } + void AddBind(RCMode mode) { AddConstraint(true, mode, false); } + +private: + void AddConstraint(bool should_bind, RCMode mode, bool should_kill_imm) + { + if (realized) + { + ASSERT(IsCompatible(should_bind, mode, should_kill_imm)); + return; + } + + if (should_bind) + bind = true; + + if (should_kill_imm) + kill_imm = true; + + switch (mode) + { + case RCMode::Read: + read = true; + break; + case RCMode::Write: + write = true; + break; + case RCMode::ReadWrite: + read = true; + write = true; + break; + } + } + + bool IsCompatible(bool should_bind, RCMode mode, bool should_kill_imm) + { + if (should_bind && !bind) + return false; + if (should_kill_imm && !kill_imm) + return false; + + switch (mode) + { + case RCMode::Read: + return read; + case RCMode::Write: + return write; + case RCMode::ReadWrite: + return read && write; + } + } + + bool realized = false; + bool bind = false; + bool write = false; + bool read = false; + bool kill_imm = false; +}; diff --git a/Source/Core/Core/PowerPC/Jit64/RegCache/JitRegCache.cpp b/Source/Core/Core/PowerPC/Jit64/RegCache/JitRegCache.cpp index 77275b5018..94aad4e9e1 100644 --- a/Source/Core/Core/PowerPC/Jit64/RegCache/JitRegCache.cpp +++ b/Source/Core/Core/PowerPC/Jit64/RegCache/JitRegCache.cpp @@ -8,19 +8,252 @@ #include #include #include +#include +#include #include "Common/Assert.h" #include "Common/BitSet.h" #include "Common/CommonTypes.h" #include "Common/MsgHandler.h" +#include "Common/VariantUtil.h" #include "Common/x64Emitter.h" #include "Core/PowerPC/Jit64/Jit.h" #include "Core/PowerPC/Jit64/RegCache/CachedReg.h" +#include "Core/PowerPC/Jit64/RegCache/RCMode.h" #include "Core/PowerPC/PowerPC.h" using namespace Gen; using namespace PowerPC; +RCOpArg RCOpArg::Imm32(u32 imm) +{ + return RCOpArg{imm}; +} + +RCOpArg RCOpArg::R(X64Reg xr) +{ + return RCOpArg{xr}; +} + +RCOpArg::RCOpArg() = default; + +RCOpArg::RCOpArg(u32 imm) : rc(nullptr), contents(imm) +{ +} + +RCOpArg::RCOpArg(X64Reg xr) : rc(nullptr), contents(xr) +{ +} + +RCOpArg::RCOpArg(RegCache* rc_, preg_t preg) : rc(rc_), contents(preg) +{ + rc->NewLock(preg); +} + +RCOpArg::~RCOpArg() +{ + Unlock(); +} + +RCOpArg::RCOpArg(RCOpArg&& other) noexcept + : rc(std::exchange(other.rc, nullptr)), + contents(std::exchange(other.contents, std::monostate{})) +{ +} + +RCOpArg& RCOpArg::operator=(RCOpArg&& other) noexcept +{ + Unlock(); + rc = std::exchange(other.rc, nullptr); + contents = std::exchange(other.contents, std::monostate{}); + return *this; +} + +RCOpArg::RCOpArg(RCX64Reg&& other) noexcept + : rc(std::exchange(other.rc, nullptr)), + contents(VariantCast(std::exchange(other.contents, std::monostate{}))) +{ +} + +RCOpArg& RCOpArg::operator=(RCX64Reg&& other) noexcept +{ + Unlock(); + rc = std::exchange(other.rc, nullptr); + contents = VariantCast(std::exchange(other.contents, std::monostate{})); + return *this; +} + +void RCOpArg::Realize() +{ + if (const preg_t* preg = std::get_if(&contents)) + { + rc->Realize(*preg); + } +} + +OpArg RCOpArg::Location() const +{ + if (const preg_t* preg = std::get_if(&contents)) + { + ASSERT(rc->IsRealized(*preg)); + return rc->R(*preg); + } + else if (const X64Reg* xr = std::get_if(&contents)) + { + return Gen::R(*xr); + } + else if (const u32* imm = std::get_if(&contents)) + { + return Gen::Imm32(*imm); + } + ASSERT(false); + return {}; +} + +void RCOpArg::Unlock() +{ + if (const preg_t* preg = std::get_if(&contents)) + { + ASSERT(rc); + rc->NewUnlock(*preg); + } + else if (const X64Reg* xr = std::get_if(&contents)) + { + // If rc, we got this from an RCX64Reg. + // If !rc, we got this from RCOpArg::R. + if (rc) + rc->NewUnlockX(*xr); + } + else + { + ASSERT(!rc); + } + + rc = nullptr; + contents = std::monostate{}; +} + +bool RCOpArg::IsImm() const +{ + if (const preg_t* preg = std::get_if(&contents)) + { + return rc->R(*preg).IsImm(); + } + else if (std::holds_alternative(contents)) + { + return true; + } + return false; +} + +s32 RCOpArg::SImm32() const +{ + if (const preg_t* preg = std::get_if(&contents)) + { + return rc->R(*preg).SImm32(); + } + else if (const u32* imm = std::get_if(&contents)) + { + return static_cast(*imm); + } + ASSERT(false); + return 0; +} + +u32 RCOpArg::Imm32() const +{ + if (const preg_t* preg = std::get_if(&contents)) + { + return rc->R(*preg).Imm32(); + } + else if (const u32* imm = std::get_if(&contents)) + { + return *imm; + } + ASSERT(false); + return 0; +} + +RCX64Reg::RCX64Reg() = default; + +RCX64Reg::RCX64Reg(RegCache* rc_, preg_t preg) : rc(rc_), contents(preg) +{ + rc->NewLock(preg); +} + +RCX64Reg::RCX64Reg(RegCache* rc_, X64Reg xr) : rc(rc_), contents(xr) +{ + rc->NewLockX(xr); +} + +RCX64Reg::~RCX64Reg() +{ + Unlock(); +} + +RCX64Reg::RCX64Reg(RCX64Reg&& other) noexcept + : rc(std::exchange(other.rc, nullptr)), + contents(std::exchange(other.contents, std::monostate{})) +{ +} + +RCX64Reg& RCX64Reg::operator=(RCX64Reg&& other) noexcept +{ + Unlock(); + rc = std::exchange(other.rc, nullptr); + contents = std::exchange(other.contents, std::monostate{}); + return *this; +} + +void RCX64Reg::Realize() +{ + if (const preg_t* preg = std::get_if(&contents)) + { + rc->Realize(*preg); + } +} + +RCX64Reg::operator X64Reg() const & +{ + if (const preg_t* preg = std::get_if(&contents)) + { + ASSERT(rc->IsRealized(*preg)); + return rc->RX(*preg); + } + else if (const X64Reg* xr = std::get_if(&contents)) + { + return *xr; + } + ASSERT(false); + return {}; +} + +RCX64Reg::operator OpArg() const & +{ + return Gen::R(RCX64Reg::operator X64Reg()); +} + +void RCX64Reg::Unlock() +{ + if (const preg_t* preg = std::get_if(&contents)) + { + ASSERT(rc); + rc->NewUnlock(*preg); + } + else if (const X64Reg* xr = std::get_if(&contents)) + { + ASSERT(rc); + rc->NewUnlockX(*xr); + } + else + { + ASSERT(!rc); + } + + rc = nullptr; + contents = std::monostate{}; +} + RegCache::RegCache(Jit64& jit) : m_jit{jit} { } @@ -214,6 +447,7 @@ void RegCache::UnlockAll() { for (auto& reg : m_regs) reg.UnlockAll(); + m_constraints.fill({}); } void RegCache::UnlockAllX() @@ -323,3 +557,96 @@ float RegCache::ScoreRegister(X64Reg xreg) const return score; } + +RCOpArg RegCache::Use(preg_t preg, RCMode mode) +{ + 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}; +} + +RCX64Reg RegCache::Bind(preg_t preg, RCMode mode) +{ + m_constraints[preg].AddBind(mode); + return RCX64Reg{this, preg}; +} + +RCX64Reg RegCache::Scratch(X64Reg xr) +{ + FlushX(xr); + return RCX64Reg{this, xr}; +} + +void RegCache::NewLock(preg_t preg) +{ + m_regs[preg].Lock(); +} + +void RegCache::NewUnlock(preg_t preg) +{ + m_regs[preg].Unlock(); + if (!m_regs[preg].IsLocked()) + { + // Fully unlocked, reset realization state. + m_constraints[preg] = {}; + } +} + +void RegCache::NewLockX(X64Reg xr) +{ + m_xregs[xr].Lock(); +} + +void RegCache::NewUnlockX(X64Reg xr) +{ + m_xregs[xr].Unlock(); +} + +bool RegCache::IsRealized(preg_t preg) const +{ + return m_constraints[preg].IsRealized(); +} + +void RegCache::Realize(preg_t preg) +{ + if (m_constraints[preg].IsRealized()) + return; + + const bool load = m_constraints[preg].ShouldLoad(); + const bool dirty = m_constraints[preg].ShouldDirty(); + const bool kill_imm = m_constraints[preg].ShouldKillImmediate(); + + const auto do_bind = [&] { + BindToRegister(preg, load, dirty); + m_constraints[preg].RealizedBound(); + }; + + if (m_constraints[preg].ShouldBind()) + { + do_bind(); + return; + } + + switch (m_regs[preg].GetLocationType()) + { + case PPCCachedReg::LocationType::Default: + break; + case PPCCachedReg::LocationType::Bound: + do_bind(); + break; + case PPCCachedReg::LocationType::Immediate: + case PPCCachedReg::LocationType::SpeculativeImmediate: + if (dirty || kill_imm) + { + do_bind(); + } + break; + } + + m_constraints[preg].Realized(); +} diff --git a/Source/Core/Core/PowerPC/Jit64/RegCache/JitRegCache.h b/Source/Core/Core/PowerPC/Jit64/RegCache/JitRegCache.h index 8c42a6947d..adf8eaa438 100644 --- a/Source/Core/Core/PowerPC/Jit64/RegCache/JitRegCache.h +++ b/Source/Core/Core/PowerPC/Jit64/RegCache/JitRegCache.h @@ -7,15 +7,93 @@ #include #include #include +#include +#include #include "Common/x64Emitter.h" -#include "Core/PowerPC/PPCAnalyst.h" #include "Core/PowerPC/Jit64/RegCache/CachedReg.h" +#include "Core/PowerPC/PPCAnalyst.h" class Jit64; +enum class RCMode; + +class RCOpArg; +class RCX64Reg; +class RegCache; using preg_t = size_t; +class RCOpArg +{ +public: + static RCOpArg Imm32(u32 imm); + static RCOpArg R(Gen::X64Reg xr); + RCOpArg(); + ~RCOpArg(); + RCOpArg(RCOpArg&&) noexcept; + RCOpArg& operator=(RCOpArg&&) noexcept; + + RCOpArg(RCX64Reg&&) noexcept; + RCOpArg& operator=(RCX64Reg&&) noexcept; + + RCOpArg(const RCOpArg&) = delete; + RCOpArg& operator=(const RCOpArg&) = delete; + + void Realize(); + Gen::OpArg Location() const; + operator Gen::OpArg() const & { return Location(); } + operator Gen::OpArg() const && = delete; + bool IsSimpleReg() const { return Location().IsSimpleReg(); } + bool IsSimpleReg(Gen::X64Reg reg) const { return Location().IsSimpleReg(reg); } + Gen::X64Reg GetSimpleReg() const { return Location().GetSimpleReg(); } + + void Unlock(); + + bool IsImm() const; + s32 SImm32() const; + u32 Imm32() const; + +private: + friend class RegCache; + + explicit RCOpArg(u32 imm); + explicit RCOpArg(Gen::X64Reg xr); + RCOpArg(RegCache* rc_, preg_t preg); + + RegCache* rc = nullptr; + std::variant contents; +}; + +class RCX64Reg +{ +public: + RCX64Reg(); + ~RCX64Reg(); + RCX64Reg(RCX64Reg&&) noexcept; + RCX64Reg& operator=(RCX64Reg&&) noexcept; + + RCX64Reg(const RCX64Reg&) = delete; + RCX64Reg& operator=(const RCX64Reg&) = delete; + + void Realize(); + operator Gen::OpArg() const &; + operator Gen::X64Reg() const &; + operator Gen::OpArg() const && = delete; + operator Gen::X64Reg() const && = delete; + + void Unlock(); + +private: + friend class RegCache; + friend class RCOpArg; + + RCX64Reg(RegCache* rc_, preg_t preg); + RCX64Reg(RegCache* rc_, Gen::X64Reg xr); + + RegCache* rc = nullptr; + std::variant contents; +}; + class RegCache { public: @@ -57,7 +135,7 @@ public: // these are powerpc reg indices template - void Lock(T p) + void Lock(T p) // TODO: Make private { m_regs[p].Lock(); } @@ -105,7 +183,40 @@ public: Gen::X64Reg GetFreeXReg(); int NumFreeRegisters() const; + // New interface + + template + static void Realize(Ts&... rc) + { + static_assert(((std::is_same() || std::is_same()) && ...)); + (rc.Realize(), ...); + } + + template + static void Unlock(Ts&... rc) + { + static_assert(((std::is_same() || std::is_same()) && ...)); + (rc.Unlock(), ...); + } + + template + bool IsImm(Args... pregs) const + { + static_assert(sizeof...(pregs) > 0); + return (R(pregs).IsImm() && ...); + } + u32 Imm32(preg_t preg) const { return R(preg).Imm32(); } + s32 SImm32(preg_t preg) const { return R(preg).SImm32(); } + + RCOpArg Use(preg_t preg, RCMode mode); + RCOpArg UseNoImm(preg_t preg, RCMode mode); + RCX64Reg Bind(preg_t preg, RCMode mode); + RCX64Reg Scratch(Gen::X64Reg xr); + protected: + friend class RCOpArg; + friend class RCX64Reg; + virtual void StoreRegister(preg_t preg, const Gen::OpArg& new_loc) = 0; virtual void LoadRegister(preg_t preg, Gen::X64Reg new_loc) = 0; @@ -118,8 +229,17 @@ protected: 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); + bool IsRealized(preg_t preg) const; + void Realize(preg_t preg); + Jit64& m_jit; std::array m_regs; std::array m_xregs; + std::array m_constraints; Gen::XEmitter* m_emitter = nullptr; }; diff --git a/Source/Core/Core/PowerPC/Jit64/RegCache/RCMode.h b/Source/Core/Core/PowerPC/Jit64/RegCache/RCMode.h new file mode 100644 index 0000000000..efe72ac4f5 --- /dev/null +++ b/Source/Core/Core/PowerPC/Jit64/RegCache/RCMode.h @@ -0,0 +1,12 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +enum class RCMode +{ + Read, + Write, + ReadWrite, +};