Jit64: Don't store immediate values in register cache
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
This commit is contained in:
parent
c9871f772e
commit
2454dbd6b8
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -75,6 +75,8 @@ public:
|
|||
|
||||
void IntializeSpeculativeConstants();
|
||||
|
||||
JitCommon::ConstantPropagation& GetConstantPropagation() { return m_constant_propagation; }
|
||||
|
||||
JitBlockCache* GetBlockCache() override { return &blocks; }
|
||||
void Trace();
|
||||
|
||||
|
|
|
@ -16,111 +16,79 @@ 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) {}
|
||||
|
||||
Gen::OpArg GetDefaultLocation() const { return m_default_location; }
|
||||
|
||||
Gen::X64Reg GetHostRegister() const
|
||||
{
|
||||
ASSERT(m_in_host_register);
|
||||
return m_host_register;
|
||||
}
|
||||
|
||||
const std::optional<Gen::OpArg>& Location() const { return location; }
|
||||
bool IsInDefaultLocation() const { return m_in_default_location; }
|
||||
bool IsInHostRegister() const { return m_in_host_register; }
|
||||
|
||||
LocationType GetLocationType() const
|
||||
void SetFlushed(bool maintain_host_register)
|
||||
{
|
||||
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_revertable);
|
||||
if (!maintain_host_register)
|
||||
m_in_host_register = false;
|
||||
m_in_default_location = true;
|
||||
}
|
||||
|
||||
bool IsAway() const { return away; }
|
||||
bool IsDiscarded() const { return !location.has_value(); }
|
||||
bool IsBound() const { return GetLocationType() == LocationType::Bound; }
|
||||
|
||||
void SetBoundTo(Gen::X64Reg xreg)
|
||||
void SetInHostRegister(Gen::X64Reg xreg, bool dirty)
|
||||
{
|
||||
away = true;
|
||||
location = Gen::R(xreg);
|
||||
if (dirty)
|
||||
m_in_default_location = false;
|
||||
m_in_host_register = true;
|
||||
m_host_register = xreg;
|
||||
}
|
||||
|
||||
void SetDirty() { m_in_default_location = false; }
|
||||
|
||||
void SetDiscarded()
|
||||
{
|
||||
ASSERT(!revertable);
|
||||
away = false;
|
||||
location = std::nullopt;
|
||||
ASSERT(!m_revertable);
|
||||
m_in_default_location = false;
|
||||
m_in_host_register = false;
|
||||
}
|
||||
|
||||
void SetFlushed()
|
||||
{
|
||||
ASSERT(!revertable);
|
||||
away = false;
|
||||
location = default_location;
|
||||
}
|
||||
|
||||
void SetToImm32(u32 imm32, bool dirty = true)
|
||||
{
|
||||
away |= dirty;
|
||||
location = Gen::Imm32(imm32);
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -128,25 +96,20 @@ 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()
|
||||
|
@ -158,7 +121,6 @@ public:
|
|||
private:
|
||||
preg_t ppcReg = static_cast<preg_t>(Gen::INVALID_REG);
|
||||
bool free = true;
|
||||
bool dirty = false;
|
||||
size_t locked = 0;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -136,7 +136,7 @@ bool RCOpArg::IsImm() const
|
|||
{
|
||||
if (const preg_t* preg = std::get_if<preg_t>(&contents))
|
||||
{
|
||||
return rc->R(*preg).IsImm();
|
||||
return rc->IsImm(*preg);
|
||||
}
|
||||
else if (std::holds_alternative<u32>(contents))
|
||||
{
|
||||
|
@ -149,7 +149,7 @@ s32 RCOpArg::SImm32() const
|
|||
{
|
||||
if (const preg_t* preg = std::get_if<preg_t>(&contents))
|
||||
{
|
||||
return rc->R(*preg).SImm32();
|
||||
return rc->SImm32(*preg);
|
||||
}
|
||||
else if (const u32* imm = std::get_if<u32>(&contents))
|
||||
{
|
||||
|
@ -163,7 +163,7 @@ u32 RCOpArg::Imm32() const
|
|||
{
|
||||
if (const preg_t* preg = std::get_if<preg_t>(&contents))
|
||||
{
|
||||
return rc->R(*preg).Imm32();
|
||||
return rc->Imm32(*preg);
|
||||
}
|
||||
else if (const u32* imm = std::get_if<u32>(&contents))
|
||||
{
|
||||
|
@ -297,25 +297,16 @@ bool RegCache::SanityCheck() const
|
|||
{
|
||||
for (size_t i = 0; i < m_regs.size(); i++)
|
||||
{
|
||||
switch (m_regs[i].GetLocationType())
|
||||
{
|
||||
case PPCCachedReg::LocationType::Default:
|
||||
case PPCCachedReg::LocationType::Discarded:
|
||||
case PPCCachedReg::LocationType::SpeculativeImmediate:
|
||||
case PPCCachedReg::LocationType::Immediate:
|
||||
break;
|
||||
case PPCCachedReg::LocationType::Bound:
|
||||
if (m_regs[i].IsInHostRegister())
|
||||
{
|
||||
if (m_regs[i].IsLocked() || m_regs[i].IsRevertable())
|
||||
return false;
|
||||
|
||||
Gen::X64Reg xr = m_regs[i].Location()->GetSimpleReg();
|
||||
Gen::X64Reg xr = m_regs[i].GetHostRegister();
|
||||
if (m_xregs[xr].IsLocked())
|
||||
return false;
|
||||
if (m_xregs[xr].Contents() != i)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -381,13 +372,7 @@ void RegCache::Discard(BitSet32 pregs)
|
|||
ASSERT_MSG(DYNA_REC, !m_regs[i].IsRevertable(), "Register transaction is in progress for {}!",
|
||||
i);
|
||||
|
||||
if (m_regs[i].IsBound())
|
||||
{
|
||||
X64Reg xr = RX(i);
|
||||
m_xregs[xr].Unbind();
|
||||
}
|
||||
|
||||
m_regs[i].SetDiscarded();
|
||||
DiscardRegister(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -405,24 +390,7 @@ void RegCache::Flush(BitSet32 pregs)
|
|||
ASSERT_MSG(DYNA_REC, !m_regs[i].IsRevertable(), "Register transaction is in progress for {}!",
|
||||
i);
|
||||
|
||||
switch (m_regs[i].GetLocationType())
|
||||
{
|
||||
case PPCCachedReg::LocationType::Default:
|
||||
break;
|
||||
case PPCCachedReg::LocationType::Discarded:
|
||||
ASSERT_MSG(DYNA_REC, false, "Attempted to flush discarded PPC reg {}", i);
|
||||
break;
|
||||
case PPCCachedReg::LocationType::SpeculativeImmediate:
|
||||
// We can have a cached value without a host register through speculative constants.
|
||||
// It must be cleared when flushing, otherwise it may be out of sync with PPCSTATE,
|
||||
// if PPCSTATE is modified externally (e.g. fallback to interpreter).
|
||||
m_regs[i].SetFlushed();
|
||||
break;
|
||||
case PPCCachedReg::LocationType::Bound:
|
||||
case PPCCachedReg::LocationType::Immediate:
|
||||
StoreFromRegister(i);
|
||||
break;
|
||||
}
|
||||
StoreFromRegister(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -430,9 +398,9 @@ void RegCache::Reset(BitSet32 pregs)
|
|||
{
|
||||
for (preg_t i : pregs)
|
||||
{
|
||||
ASSERT_MSG(DYNA_REC, !m_regs[i].IsAway(),
|
||||
ASSERT_MSG(DYNA_REC, !m_regs[i].IsInHostRegister(),
|
||||
"Attempted to reset a loaded register (did you mean to flush it?)");
|
||||
m_regs[i].SetFlushed();
|
||||
m_regs[i].SetFlushed(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -469,7 +437,7 @@ void RegCache::PreloadRegisters(BitSet32 to_preload)
|
|||
{
|
||||
if (NumFreeRegisters() < 2)
|
||||
return;
|
||||
if (!R(preg).IsImm())
|
||||
if (!IsImm(preg))
|
||||
BindToRegister(preg, true, false);
|
||||
}
|
||||
}
|
||||
|
@ -496,51 +464,51 @@ void RegCache::FlushX(X64Reg reg)
|
|||
}
|
||||
}
|
||||
|
||||
void RegCache::DiscardRegContentsIfCached(preg_t preg)
|
||||
void RegCache::DiscardRegister(preg_t preg)
|
||||
{
|
||||
if (m_regs[preg].IsBound())
|
||||
if (m_regs[preg].IsInHostRegister())
|
||||
{
|
||||
X64Reg xr = m_regs[preg].Location()->GetSimpleReg();
|
||||
X64Reg xr = m_regs[preg].GetHostRegister();
|
||||
m_xregs[xr].Unbind();
|
||||
m_regs[preg].SetFlushed();
|
||||
}
|
||||
|
||||
m_regs[preg].SetDiscarded();
|
||||
}
|
||||
|
||||
void RegCache::BindToRegister(preg_t i, bool doLoad, bool makeDirty)
|
||||
{
|
||||
if (!m_regs[i].IsBound())
|
||||
if (!m_regs[i].IsInHostRegister())
|
||||
{
|
||||
X64Reg xr = GetFreeXReg();
|
||||
|
||||
ASSERT_MSG(DYNA_REC, !m_xregs[xr].IsDirty(), "Xreg {} already dirty", Common::ToUnderlying(xr));
|
||||
ASSERT_MSG(DYNA_REC, !m_xregs[xr].IsLocked(), "GetFreeXReg returned locked register");
|
||||
ASSERT_MSG(DYNA_REC, !m_regs[i].IsRevertable(), "Invalid transaction state");
|
||||
|
||||
m_xregs[xr].SetBoundTo(i, makeDirty || m_regs[i].IsAway());
|
||||
m_xregs[xr].SetBoundTo(i);
|
||||
|
||||
if (doLoad)
|
||||
{
|
||||
ASSERT_MSG(DYNA_REC, !m_regs[i].IsDiscarded(), "Attempted to load a discarded value");
|
||||
LoadRegister(i, xr);
|
||||
}
|
||||
|
||||
ASSERT_MSG(DYNA_REC,
|
||||
std::none_of(m_regs.begin(), m_regs.end(),
|
||||
[xr](const auto& r) {
|
||||
return r.Location().has_value() && r.Location()->IsSimpleReg(xr);
|
||||
return r.IsInHostRegister() && r.GetHostRegister() == xr;
|
||||
}),
|
||||
"Xreg {} already bound", Common::ToUnderlying(xr));
|
||||
|
||||
m_regs[i].SetBoundTo(xr);
|
||||
m_regs[i].SetInHostRegister(xr, makeDirty);
|
||||
}
|
||||
else
|
||||
{
|
||||
// reg location must be simplereg; memory locations
|
||||
// and immediates are taken care of above.
|
||||
if (makeDirty)
|
||||
m_xregs[RX(i)].MakeDirty();
|
||||
m_regs[i].SetDirty();
|
||||
}
|
||||
|
||||
if (makeDirty)
|
||||
DiscardImm(i);
|
||||
|
||||
ASSERT_MSG(DYNA_REC, !m_xregs[RX(i)].IsLocked(),
|
||||
"WTF, this reg ({} -> {}) should have been flushed", i, Common::ToUnderlying(RX(i)));
|
||||
}
|
||||
|
@ -550,31 +518,13 @@ void RegCache::StoreFromRegister(preg_t i, FlushMode mode)
|
|||
// When a transaction is in progress, allowing the store would overwrite the old value.
|
||||
ASSERT_MSG(DYNA_REC, !m_regs[i].IsRevertable(), "Register transaction on {} is in progress!", i);
|
||||
|
||||
bool doStore = false;
|
||||
|
||||
switch (m_regs[i].GetLocationType())
|
||||
{
|
||||
case PPCCachedReg::LocationType::Default:
|
||||
case PPCCachedReg::LocationType::Discarded:
|
||||
case PPCCachedReg::LocationType::SpeculativeImmediate:
|
||||
return;
|
||||
case PPCCachedReg::LocationType::Bound:
|
||||
{
|
||||
X64Reg xr = RX(i);
|
||||
doStore = m_xregs[xr].IsDirty();
|
||||
if (mode == FlushMode::Full)
|
||||
m_xregs[xr].Unbind();
|
||||
break;
|
||||
}
|
||||
case PPCCachedReg::LocationType::Immediate:
|
||||
doStore = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (doStore)
|
||||
if (!m_regs[i].IsInDefaultLocation())
|
||||
StoreRegister(i, GetDefaultLocation(i));
|
||||
if (mode == FlushMode::Full)
|
||||
m_regs[i].SetFlushed();
|
||||
|
||||
if (mode == FlushMode::Full && m_regs[i].IsInHostRegister())
|
||||
m_xregs[m_regs[i].GetHostRegister()].Unbind();
|
||||
|
||||
m_regs[i].SetFlushed(mode != FlushMode::Full);
|
||||
}
|
||||
|
||||
X64Reg RegCache::GetFreeXReg()
|
||||
|
@ -639,7 +589,7 @@ float RegCache::ScoreRegister(X64Reg xreg) const
|
|||
// 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 (m_xregs[xreg].IsDirty())
|
||||
if (!m_regs[preg].IsInDefaultLocation())
|
||||
score += 2;
|
||||
|
||||
// If the register isn't actually needed in a physical register for a later instruction,
|
||||
|
@ -660,16 +610,10 @@ float RegCache::ScoreRegister(X64Reg xreg) const
|
|||
return score;
|
||||
}
|
||||
|
||||
const OpArg& RegCache::R(preg_t preg) const
|
||||
{
|
||||
ASSERT_MSG(DYNA_REC, !m_regs[preg].IsDiscarded(), "Discarded register - {}", preg);
|
||||
return m_regs[preg].Location().value();
|
||||
}
|
||||
|
||||
X64Reg RegCache::RX(preg_t preg) const
|
||||
{
|
||||
ASSERT_MSG(DYNA_REC, m_regs[preg].IsBound(), "Unbound register - {}", preg);
|
||||
return m_regs[preg].Location()->GetSimpleReg();
|
||||
ASSERT_MSG(DYNA_REC, m_regs[preg].IsInHostRegister(), "Not in host register - {}", preg);
|
||||
return m_regs[preg].GetHostRegister();
|
||||
}
|
||||
|
||||
void RegCache::Lock(preg_t preg)
|
||||
|
@ -725,29 +669,23 @@ void RegCache::Realize(preg_t preg)
|
|||
return;
|
||||
}
|
||||
|
||||
switch (m_regs[preg].GetLocationType())
|
||||
if (IsImm(preg))
|
||||
{
|
||||
case PPCCachedReg::LocationType::Default:
|
||||
if (kill_mem)
|
||||
{
|
||||
do_bind();
|
||||
return;
|
||||
}
|
||||
m_constraints[preg].Realized(RCConstraint::RealizedLoc::Mem);
|
||||
return;
|
||||
case PPCCachedReg::LocationType::Discarded:
|
||||
case PPCCachedReg::LocationType::Bound:
|
||||
do_bind();
|
||||
return;
|
||||
case PPCCachedReg::LocationType::Immediate:
|
||||
case PPCCachedReg::LocationType::SpeculativeImmediate:
|
||||
if (dirty || kill_imm)
|
||||
{
|
||||
do_bind();
|
||||
return;
|
||||
}
|
||||
m_constraints[preg].Realized(RCConstraint::RealizedLoc::Imm);
|
||||
break;
|
||||
else
|
||||
m_constraints[preg].Realized(RCConstraint::RealizedLoc::Imm);
|
||||
}
|
||||
else if (!m_regs[preg].IsInHostRegister())
|
||||
{
|
||||
if (kill_mem)
|
||||
do_bind();
|
||||
else
|
||||
m_constraints[preg].Realized(RCConstraint::RealizedLoc::Mem);
|
||||
}
|
||||
else
|
||||
{
|
||||
do_bind();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -151,12 +151,14 @@ public:
|
|||
bool IsImm(Args... pregs) const
|
||||
{
|
||||
static_assert(sizeof...(pregs) > 0);
|
||||
return (R(pregs).IsImm() && ...);
|
||||
return (IsImm(preg_t(pregs)) && ...);
|
||||
}
|
||||
u32 Imm32(preg_t preg) const { return R(preg).Imm32(); }
|
||||
s32 SImm32(preg_t preg) const { return R(preg).SImm32(); }
|
||||
|
||||
bool IsBound(preg_t preg) const { return m_regs[preg].IsBound(); }
|
||||
virtual bool IsImm(preg_t preg) const = 0;
|
||||
virtual u32 Imm32(preg_t preg) const = 0;
|
||||
virtual s32 SImm32(preg_t preg) const = 0;
|
||||
|
||||
bool IsBound(preg_t preg) const { return m_regs[preg].IsInHostRegister(); }
|
||||
|
||||
RCOpArg Use(preg_t preg, RCMode mode);
|
||||
RCOpArg UseNoImm(preg_t preg, RCMode mode);
|
||||
|
@ -186,6 +188,7 @@ protected:
|
|||
virtual Gen::OpArg GetDefaultLocation(preg_t preg) const = 0;
|
||||
virtual void StoreRegister(preg_t preg, const Gen::OpArg& new_loc) = 0;
|
||||
virtual void LoadRegister(preg_t preg, Gen::X64Reg new_loc) = 0;
|
||||
virtual void DiscardImm(preg_t preg) = 0;
|
||||
|
||||
virtual std::span<const Gen::X64Reg> GetAllocationOrder() const = 0;
|
||||
|
||||
|
@ -193,7 +196,7 @@ protected:
|
|||
virtual BitSet32 CountRegsIn(preg_t preg, u32 lookahead) const = 0;
|
||||
|
||||
void FlushX(Gen::X64Reg reg);
|
||||
void DiscardRegContentsIfCached(preg_t preg);
|
||||
void DiscardRegister(preg_t preg);
|
||||
void BindToRegister(preg_t preg, bool doLoad = true, bool makeDirty = true);
|
||||
void StoreFromRegister(preg_t preg, FlushMode mode = FlushMode::Full);
|
||||
|
||||
|
@ -202,7 +205,7 @@ protected:
|
|||
int NumFreeRegisters() const;
|
||||
float ScoreRegister(Gen::X64Reg xreg) const;
|
||||
|
||||
const Gen::OpArg& R(preg_t preg) const;
|
||||
virtual Gen::OpArg R(preg_t preg) const = 0;
|
||||
Gen::X64Reg RX(preg_t preg) const;
|
||||
|
||||
void Lock(preg_t preg);
|
||||
|
|
|
@ -65,6 +65,8 @@ public:
|
|||
m_gpr_values[gpr] = value;
|
||||
}
|
||||
|
||||
void ClearGPR(size_t gpr) { m_gpr_values_known[gpr] = false; }
|
||||
|
||||
void Clear() { m_gpr_values_known = BitSet32{}; }
|
||||
|
||||
private:
|
||||
|
|
Loading…
Reference in New Issue