JitRegCache: New interface
This commit is contained in:
parent
6fef683e14
commit
ae1bd7a6b0
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
Loading…
Reference in New Issue