Skip to content

Commit

Permalink
Jit64: Don't store immediate values in register cache
Browse files Browse the repository at this point in the history
They're now stored in ConstantPropagation instead.

I've also removed the LocationType enum. The location of each guest
register is now tracked using three booleans: Whether it is in ppcState,
whether it is in a host register, and whether it is a known immediate.
The first two of these booleans are stored in the register cache, and
the last one is stored in ConstantPropagation. This new model allows us
to handle the combination of a value simultaneously being in a host
register and being a known immediate. It also keeps track of which
registers are dirty, which was previously kept track of in X64CachedReg.

The old model maps to the new model as follows:

                                default    host_reg    immediate

Default                         true       false       false
Discarded                       false      false       false
Bound                           (!dirty)   true        false
Immediate                       false      false       true
SpeculativeImmediate            true       false       true
[previously unrepresentable]    (!dirty)   true        true
  • Loading branch information
JosJuice committed Sep 1, 2024
1 parent c9871f7 commit 52e2652
Show file tree
Hide file tree
Showing 11 changed files with 219 additions and 202 deletions.
3 changes: 2 additions & 1 deletion Source/Core/Core/PowerPC/Jit64/Jit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1115,10 +1115,11 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
CompileInstruction(op);
}

m_constant_propagation.Apply(constant_propagation_result, op.regsOut);
m_constant_propagation.Apply(constant_propagation_result, {});

if (constant_propagation_result.gpr >= 0)
{
// Mark the GPR as dirty in the register cache
gpr.SetImmediate32(constant_propagation_result.gpr,
constant_propagation_result.gpr_value);
}
Expand Down
2 changes: 2 additions & 0 deletions Source/Core/Core/PowerPC/Jit64/Jit.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ class Jit64 : public JitBase, public QuantizedMemoryRoutines

void IntializeSpeculativeConstants();

JitCommon::ConstantPropagation& GetConstantPropagation() { return m_constant_propagation; }

JitBlockCache* GetBlockCache() override { return &blocks; }
void Trace();

Expand Down
118 changes: 40 additions & 78 deletions Source/Core/Core/PowerPC/Jit64/RegCache/CachedReg.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,137 +16,100 @@ using preg_t = size_t;
class PPCCachedReg
{
public:
enum class LocationType
{
/// Value is currently at its default location
Default,
/// Value is not stored anywhere because we know it won't be read before the next write
Discarded,
/// 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_)
{
}
explicit PPCCachedReg(Gen::OpArg default_location) : m_default_location(default_location) {}

const std::optional<Gen::OpArg>& Location() const { return location; }
Gen::OpArg GetDefaultLocation() const { return m_default_location; }

LocationType GetLocationType() const
Gen::X64Reg GetHostRegister() const
{
if (!location.has_value())
return LocationType::Discarded;

if (!away)
{
ASSERT(!revertable);

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;
ASSERT(m_in_host_register);
return m_host_register;
}

bool IsAway() const { return away; }
bool IsDiscarded() const { return !location.has_value(); }
bool IsBound() const { return GetLocationType() == LocationType::Bound; }
bool IsInDefaultLocation() const { return m_in_default_location; }
bool IsInHostRegister() const { return m_in_host_register; }

void SetBoundTo(Gen::X64Reg xreg)
void SetFlushed(bool maintain_host_register)
{
away = true;
location = Gen::R(xreg);
ASSERT(!m_revertable);
if (!maintain_host_register)
m_in_host_register = false;
m_in_default_location = true;
}

void SetDiscarded()
void SetInHostRegister(Gen::X64Reg xreg, bool dirty)
{
ASSERT(!revertable);
away = false;
location = std::nullopt;
if (dirty)
m_in_default_location = false;
m_in_host_register = true;
m_host_register = xreg;
}

void SetFlushed()
{
ASSERT(!revertable);
away = false;
location = default_location;
}
void SetDirty() { m_in_default_location = false; }

void SetToImm32(u32 imm32, bool dirty = true)
void SetDiscarded()
{
away |= dirty;
location = Gen::Imm32(imm32);
ASSERT(!m_revertable);
m_in_default_location = false;
m_in_host_register = false;
}

bool IsRevertable() const { return revertable; }
bool IsRevertable() const { return m_revertable; }
void SetRevertable()
{
ASSERT(IsBound());
revertable = true;
ASSERT(m_in_host_register);
m_revertable = true;
}
void SetRevert()
{
ASSERT(revertable);
revertable = false;
SetFlushed();
ASSERT(m_revertable);
m_revertable = false;
SetFlushed(false);
}
void SetCommit()
{
ASSERT(revertable);
revertable = false;
ASSERT(m_revertable);
m_revertable = false;
}

bool IsLocked() const { return locked > 0; }
void Lock() { locked++; }
bool IsLocked() const { return m_locked > 0; }
void Lock() { m_locked++; }
void Unlock()
{
ASSERT(IsLocked());
locked--;
m_locked--;
}

private:
Gen::OpArg default_location{};
std::optional<Gen::OpArg> location{};
bool away = false; // value not in source register
bool revertable = false;
size_t locked = 0;
Gen::OpArg m_default_location{};
Gen::X64Reg m_host_register{};
bool m_in_default_location = true;
bool m_in_host_register = false;
bool m_revertable = false;
size_t m_locked = 0;
};

class X64CachedReg
{
public:
preg_t Contents() const { return ppcReg; }

void SetBoundTo(preg_t ppcReg_, bool dirty_)
void SetBoundTo(preg_t ppcReg_)
{
free = false;
ppcReg = ppcReg_;
dirty = dirty_;
}

void Unbind()
{
ppcReg = static_cast<preg_t>(Gen::INVALID_REG);
free = true;
dirty = false;
}

bool IsFree() const { return free && !locked; }

bool IsDirty() const { return dirty; }
void MakeDirty() { dirty = true; }

bool IsLocked() const { return locked > 0; }
void Lock() { locked++; }
void Unlock()
Expand All @@ -158,7 +121,6 @@ class X64CachedReg
private:
preg_t ppcReg = static_cast<preg_t>(Gen::INVALID_REG);
bool free = true;
bool dirty = false;
size_t locked = 0;
};

Expand Down
43 changes: 39 additions & 4 deletions Source/Core/Core/PowerPC/Jit64/RegCache/FPURegCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,51 @@ FPURegCache::FPURegCache(Jit64& jit) : RegCache{jit}
{
}

bool FPURegCache::IsImm(preg_t preg) const
{
return false;
}

u32 FPURegCache::Imm32(preg_t preg) const
{
ASSERT_MSG(DYNA_REC, false, "FPURegCache doesn't support immediates");
return 0;
}

s32 FPURegCache::SImm32(preg_t preg) const
{
ASSERT_MSG(DYNA_REC, false, "FPURegCache doesn't support immediates");
return 0;
}

OpArg FPURegCache::R(preg_t preg) const
{
if (m_regs[preg].IsInHostRegister())
{
return ::Gen::R(m_regs[preg].GetHostRegister());
}
else
{
ASSERT_MSG(DYNA_REC, m_regs[preg].IsInDefaultLocation(), "FPR {} missing!", preg);
return m_regs[preg].GetDefaultLocation();
}
}

void FPURegCache::StoreRegister(preg_t preg, const OpArg& new_loc)
{
ASSERT_MSG(DYNA_REC, m_regs[preg].IsBound(), "Unbound register - {}", preg);
m_emitter->MOVAPD(new_loc, m_regs[preg].Location()->GetSimpleReg());
ASSERT_MSG(DYNA_REC, m_regs[preg].IsInHostRegister(), "FPR {} not in host register", preg);
m_emitter->MOVAPD(new_loc, m_regs[preg].GetHostRegister());
}

void FPURegCache::LoadRegister(preg_t preg, X64Reg new_loc)
{
ASSERT_MSG(DYNA_REC, !m_regs[preg].IsDiscarded(), "Discarded register - {}", preg);
m_emitter->MOVAPD(new_loc, m_regs[preg].Location().value());
ASSERT_MSG(DYNA_REC, m_regs[preg].IsInDefaultLocation(), "FPR {} not in default location", preg);
m_emitter->MOVAPD(new_loc, m_regs[preg].GetDefaultLocation());
}

void FPURegCache::DiscardImm(preg_t preg)
{
// FPURegCache doesn't support immediates, so no need to do anything
}

std::span<const X64Reg> FPURegCache::GetAllocationOrder() const
Expand Down
6 changes: 6 additions & 0 deletions Source/Core/Core/PowerPC/Jit64/RegCache/FPURegCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,16 @@ class FPURegCache final : public RegCache
public:
explicit FPURegCache(Jit64& jit);

bool IsImm(preg_t preg) const override;
u32 Imm32(preg_t preg) const override;
s32 SImm32(preg_t preg) const override;

protected:
Gen::OpArg R(preg_t preg) const override;
Gen::OpArg GetDefaultLocation(preg_t preg) const override;
void StoreRegister(preg_t preg, const Gen::OpArg& newLoc) override;
void LoadRegister(preg_t preg, Gen::X64Reg newLoc) override;
void DiscardImm(preg_t preg) override;
std::span<const Gen::X64Reg> GetAllocationOrder() const override;
BitSet32 GetRegUtilization() const override;
BitSet32 CountRegsIn(preg_t preg, u32 lookahead) const override;
Expand Down
69 changes: 63 additions & 6 deletions Source/Core/Core/PowerPC/Jit64/RegCache/GPRRegCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,72 @@ GPRRegCache::GPRRegCache(Jit64& jit) : RegCache{jit}
{
}

bool GPRRegCache::IsImm(preg_t preg) const
{
return m_jit.GetConstantPropagation().HasGPR(preg);
}

u32 GPRRegCache::Imm32(preg_t preg) const
{
ASSERT(m_jit.GetConstantPropagation().HasGPR(preg));
return m_jit.GetConstantPropagation().GetGPR(preg);
}

s32 GPRRegCache::SImm32(preg_t preg) const
{
ASSERT(m_jit.GetConstantPropagation().HasGPR(preg));
return m_jit.GetConstantPropagation().GetGPR(preg);
}

OpArg GPRRegCache::R(preg_t preg) const
{
if (m_regs[preg].IsInHostRegister())
{
return ::Gen::R(m_regs[preg].GetHostRegister());
}
else if (m_jit.GetConstantPropagation().HasGPR(preg))
{
return ::Gen::Imm32(m_jit.GetConstantPropagation().GetGPR(preg));
}
else
{
ASSERT_MSG(DYNA_REC, m_regs[preg].IsInDefaultLocation(), "GPR {} missing!", preg);
return m_regs[preg].GetDefaultLocation();
}
}

void GPRRegCache::StoreRegister(preg_t preg, const OpArg& new_loc)
{
ASSERT_MSG(DYNA_REC, !m_regs[preg].IsDiscarded(), "Discarded register - {}", preg);
m_emitter->MOV(32, new_loc, m_regs[preg].Location().value());
if (m_regs[preg].IsInHostRegister())
{
m_emitter->MOV(32, new_loc, ::Gen::R(m_regs[preg].GetHostRegister()));
}
else
{
ASSERT_MSG(DYNA_REC, m_jit.GetConstantPropagation().HasGPR(preg),
"GPR {} not in host register or constant propagation", preg);
m_emitter->MOV(32, new_loc, ::Gen::Imm32(m_jit.GetConstantPropagation().GetGPR(preg)));
}
}

void GPRRegCache::LoadRegister(preg_t preg, X64Reg new_loc)
{
ASSERT_MSG(DYNA_REC, !m_regs[preg].IsDiscarded(), "Discarded register - {}", preg);
m_emitter->MOV(32, ::Gen::R(new_loc), m_regs[preg].Location().value());
const JitCommon::ConstantPropagation& constant_propagation = m_jit.GetConstantPropagation();
if (constant_propagation.HasGPR(preg))
{
m_emitter->MOV(32, ::Gen::R(new_loc), ::Gen::Imm32(constant_propagation.GetGPR(preg)));
}
else
{
ASSERT_MSG(DYNA_REC, m_regs[preg].IsInDefaultLocation(), "GPR {} not in default location",
preg);
m_emitter->MOV(32, ::Gen::R(new_loc), m_regs[preg].GetDefaultLocation());
}
}

void GPRRegCache::DiscardImm(preg_t preg)
{
m_jit.GetConstantPropagation().ClearGPR(preg);
}

OpArg GPRRegCache::GetDefaultLocation(preg_t preg) const
Expand All @@ -48,8 +104,9 @@ void GPRRegCache::SetImmediate32(preg_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].SetToImm32(imm_value, dirty);
m_jit.GetConstantPropagation().SetGPR(preg, imm_value);
if (dirty)
DiscardRegister(preg);
}

BitSet32 GPRRegCache::GetRegUtilization() const
Expand Down
9 changes: 9 additions & 0 deletions Source/Core/Core/PowerPC/Jit64/RegCache/GPRRegCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,21 @@ class GPRRegCache final : public RegCache
{
public:
explicit GPRRegCache(Jit64& jit);

using RegCache::IsImm;

bool IsImm(preg_t preg) const override;
u32 Imm32(preg_t preg) const override;
s32 SImm32(preg_t preg) const override;

void SetImmediate32(preg_t preg, u32 imm_value, bool dirty = true);

protected:
Gen::OpArg R(preg_t preg) const override;
Gen::OpArg GetDefaultLocation(preg_t preg) const override;
void StoreRegister(preg_t preg, const Gen::OpArg& new_loc) override;
void LoadRegister(preg_t preg, Gen::X64Reg new_loc) override;
void DiscardImm(preg_t preg) override;
std::span<const Gen::X64Reg> GetAllocationOrder() const override;
BitSet32 GetRegUtilization() const override;
BitSet32 CountRegsIn(preg_t preg, u32 lookahead) const override;
Expand Down
Loading

0 comments on commit 52e2652

Please sign in to comment.