JitRegCache: New interface

This commit is contained in:
MerryMage 2018-10-15 21:00:47 +01:00
parent 6fef683e14
commit ae1bd7a6b0
4 changed files with 540 additions and 2 deletions

View File

@ -2,11 +2,14 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#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;
};

View File

@ -8,19 +8,252 @@
#include <cinttypes>
#include <cmath>
#include <limits>
#include <utility>
#include <variant>
#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<preg_t>(&contents))
{
rc->Realize(*preg);
}
}
OpArg RCOpArg::Location() const
{
if (const preg_t* preg = std::get_if<preg_t>(&contents))
{
ASSERT(rc->IsRealized(*preg));
return rc->R(*preg);
}
else if (const X64Reg* xr = std::get_if<X64Reg>(&contents))
{
return Gen::R(*xr);
}
else if (const u32* imm = std::get_if<u32>(&contents))
{
return Gen::Imm32(*imm);
}
ASSERT(false);
return {};
}
void RCOpArg::Unlock()
{
if (const preg_t* preg = std::get_if<preg_t>(&contents))
{
ASSERT(rc);
rc->NewUnlock(*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);
}
else
{
ASSERT(!rc);
}
rc = nullptr;
contents = std::monostate{};
}
bool RCOpArg::IsImm() const
{
if (const preg_t* preg = std::get_if<preg_t>(&contents))
{
return rc->R(*preg).IsImm();
}
else if (std::holds_alternative<u32>(contents))
{
return true;
}
return false;
}
s32 RCOpArg::SImm32() const
{
if (const preg_t* preg = std::get_if<preg_t>(&contents))
{
return rc->R(*preg).SImm32();
}
else if (const u32* imm = std::get_if<u32>(&contents))
{
return static_cast<s32>(*imm);
}
ASSERT(false);
return 0;
}
u32 RCOpArg::Imm32() const
{
if (const preg_t* preg = std::get_if<preg_t>(&contents))
{
return rc->R(*preg).Imm32();
}
else if (const u32* imm = std::get_if<u32>(&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<preg_t>(&contents))
{
rc->Realize(*preg);
}
}
RCX64Reg::operator X64Reg() const &
{
if (const preg_t* preg = std::get_if<preg_t>(&contents))
{
ASSERT(rc->IsRealized(*preg));
return rc->RX(*preg);
}
else if (const X64Reg* xr = std::get_if<X64Reg>(&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<preg_t>(&contents))
{
ASSERT(rc);
rc->NewUnlock(*preg);
}
else if (const X64Reg* xr = std::get_if<X64Reg>(&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();
}

View File

@ -7,15 +7,93 @@
#include <array>
#include <cinttypes>
#include <cstddef>
#include <type_traits>
#include <variant>
#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<std::monostate, Gen::X64Reg, u32, preg_t> 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<std::monostate, Gen::X64Reg, preg_t> contents;
};
class RegCache
{
public:
@ -57,7 +135,7 @@ public:
// these are powerpc reg indices
template <typename T>
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 <typename... Ts>
static void Realize(Ts&... rc)
{
static_assert(((std::is_same<Ts, RCOpArg>() || std::is_same<Ts, RCX64Reg>()) && ...));
(rc.Realize(), ...);
}
template <typename... Ts>
static void Unlock(Ts&... rc)
{
static_assert(((std::is_same<Ts, RCOpArg>() || std::is_same<Ts, RCX64Reg>()) && ...));
(rc.Unlock(), ...);
}
template <typename... Args>
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<PPCCachedReg, 32> m_regs;
std::array<X64CachedReg, NUM_XREGS> m_xregs;
std::array<RCConstraint, 32> m_constraints;
Gen::XEmitter* m_emitter = nullptr;
};

View File

@ -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,
};