Merge pull request #12682 from JosJuice/jit-fallback-discard-assert

Jit: Skip discarded registers when flushing for interpreter fallback
This commit is contained in:
JosJuice 2024-12-12 23:48:37 +01:00 committed by GitHub
commit ada646a795
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 58 additions and 24 deletions

View File

@ -350,8 +350,8 @@ void Jit64::Shutdown()
void Jit64::FallBackToInterpreter(UGeckoInstruction inst)
{
gpr.Flush();
fpr.Flush();
gpr.Flush(BitSet32(0xFFFFFFFF), RegCache::IgnoreDiscardedRegisters::Yes);
fpr.Flush(BitSet32(0xFFFFFFFF), RegCache::IgnoreDiscardedRegisters::Yes);
if (js.op->canEndBlock)
{

View File

@ -391,7 +391,7 @@ void RegCache::Discard(BitSet32 pregs)
}
}
void RegCache::Flush(BitSet32 pregs)
void RegCache::Flush(BitSet32 pregs, IgnoreDiscardedRegisters ignore_discarded_registers)
{
ASSERT_MSG(
DYNA_REC,
@ -410,7 +410,8 @@ void RegCache::Flush(BitSet32 pregs)
case PPCCachedReg::LocationType::Default:
break;
case PPCCachedReg::LocationType::Discarded:
ASSERT_MSG(DYNA_REC, false, "Attempted to flush discarded PPC reg {}", i);
ASSERT_MSG(DYNA_REC, ignore_discarded_registers != IgnoreDiscardedRegisters::No,
"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.

View File

@ -126,6 +126,12 @@ public:
MaintainState,
};
enum class IgnoreDiscardedRegisters
{
No,
Yes,
};
explicit RegCache(Jit64& jit);
virtual ~RegCache() = default;
@ -168,7 +174,8 @@ public:
RCForkGuard Fork();
void Discard(BitSet32 pregs);
void Flush(BitSet32 pregs = BitSet32::AllTrue(32));
void Flush(BitSet32 pregs = BitSet32::AllTrue(32),
IgnoreDiscardedRegisters ignore_discarded_registers = IgnoreDiscardedRegisters::No);
void Reset(BitSet32 pregs);
void Revert();
void Commit();

View File

@ -256,8 +256,8 @@ void JitArm64::Shutdown()
void JitArm64::FallBackToInterpreter(UGeckoInstruction inst)
{
FlushCarry();
gpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG);
fpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG);
gpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG, IgnoreDiscardedRegisters::Yes);
fpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG, IgnoreDiscardedRegisters::Yes);
if (js.op->canEndBlock)
{

View File

@ -241,12 +241,16 @@ void Arm64GPRCache::FlushRegister(size_t index, FlushMode mode, ARM64Reg tmp_reg
}
}
void Arm64GPRCache::FlushRegisters(BitSet32 regs, FlushMode mode, ARM64Reg tmp_reg)
void Arm64GPRCache::FlushRegisters(BitSet32 regs, FlushMode mode, ARM64Reg tmp_reg,
IgnoreDiscardedRegisters ignore_discarded_registers)
{
for (auto iter = regs.begin(); iter != regs.end(); ++iter)
{
const int i = *iter;
ASSERT_MSG(DYNA_REC, m_guest_registers[GUEST_GPR_OFFSET + i].GetType() != RegType::Discarded,
ASSERT_MSG(DYNA_REC,
ignore_discarded_registers != IgnoreDiscardedRegisters::No ||
m_guest_registers[GUEST_GPR_OFFSET + i].GetType() != RegType::Discarded,
"Attempted to flush discarded register");
if (i + 1 < int(GUEST_GPR_COUNT) && regs[i + 1])
@ -288,11 +292,14 @@ void Arm64GPRCache::FlushRegisters(BitSet32 regs, FlushMode mode, ARM64Reg tmp_r
}
}
void Arm64GPRCache::FlushCRRegisters(BitSet8 regs, FlushMode mode, ARM64Reg tmp_reg)
void Arm64GPRCache::FlushCRRegisters(BitSet8 regs, FlushMode mode, ARM64Reg tmp_reg,
IgnoreDiscardedRegisters ignore_discarded_registers)
{
for (int i : regs)
{
ASSERT_MSG(DYNA_REC, m_guest_registers[GUEST_CR_OFFSET + i].GetType() != RegType::Discarded,
ASSERT_MSG(DYNA_REC,
ignore_discarded_registers != IgnoreDiscardedRegisters::No ||
m_guest_registers[GUEST_CR_OFFSET + i].GetType() != RegType::Discarded,
"Attempted to flush discarded register");
FlushRegister(GUEST_CR_OFFSET + i, mode, tmp_reg);
@ -318,10 +325,11 @@ void Arm64GPRCache::ResetCRRegisters(BitSet8 regs)
}
}
void Arm64GPRCache::Flush(FlushMode mode, ARM64Reg tmp_reg)
void Arm64GPRCache::Flush(FlushMode mode, ARM64Reg tmp_reg,
IgnoreDiscardedRegisters ignore_discarded_registers)
{
FlushRegisters(BitSet32(0xFFFFFFFF), mode, tmp_reg);
FlushCRRegisters(BitSet8(0xFF), mode, tmp_reg);
FlushRegisters(BitSet32(0xFFFFFFFF), mode, tmp_reg, ignore_discarded_registers);
FlushCRRegisters(BitSet8(0xFF), mode, tmp_reg, ignore_discarded_registers);
}
ARM64Reg Arm64GPRCache::R(const GuestRegInfo& guest_reg)
@ -498,14 +506,19 @@ Arm64FPRCache::Arm64FPRCache() : Arm64RegCache(GUEST_FPR_COUNT)
{
}
void Arm64FPRCache::Flush(FlushMode mode, ARM64Reg tmp_reg)
void Arm64FPRCache::Flush(FlushMode mode, ARM64Reg tmp_reg,
IgnoreDiscardedRegisters ignore_discarded_registers)
{
for (size_t i = 0; i < m_guest_registers.size(); ++i)
{
const RegType reg_type = m_guest_registers[i].GetType();
if (reg_type != RegType::NotLoaded && reg_type != RegType::Discarded &&
reg_type != RegType::Immediate)
if (reg_type == RegType::Discarded)
{
ASSERT_MSG(DYNA_REC, ignore_discarded_registers != IgnoreDiscardedRegisters::No,
"Attempted to flush discarded register");
}
else if (reg_type != RegType::NotLoaded && reg_type != RegType::Immediate)
{
FlushRegister(i, mode, tmp_reg);
}

View File

@ -81,6 +81,12 @@ enum class FlushMode : bool
MaintainState,
};
enum class IgnoreDiscardedRegisters
{
No,
Yes,
};
class OpArg
{
public:
@ -169,7 +175,8 @@ public:
// Flushes the register cache in different ways depending on the mode.
// A temporary register must be supplied when flushing GPRs with FlushMode::MaintainState,
// but in other cases it can be set to ARM64Reg::INVALID_REG when convenient for the caller.
virtual void Flush(FlushMode mode, Arm64Gen::ARM64Reg tmp_reg) = 0;
virtual void Flush(FlushMode mode, Arm64Gen::ARM64Reg tmp_reg,
IgnoreDiscardedRegisters ignore_discarded_registers) = 0;
virtual BitSet32 GetCallerSavedUsed() const = 0;
@ -315,7 +322,9 @@ public:
// Flushes the register cache in different ways depending on the mode.
// A temporary register must be supplied when flushing GPRs with FlushMode::MaintainState,
// but in other cases it can be set to ARM64Reg::INVALID_REG when convenient for the caller.
void Flush(FlushMode mode, Arm64Gen::ARM64Reg tmp_reg) override;
void Flush(
FlushMode mode, Arm64Gen::ARM64Reg tmp_reg,
IgnoreDiscardedRegisters ignore_discarded_registers = IgnoreDiscardedRegisters::No) override;
// Returns a guest GPR inside of a host register.
// Will dump an immediate to the host register as well.
@ -381,12 +390,12 @@ public:
void StoreRegisters(BitSet32 regs, Arm64Gen::ARM64Reg tmp_reg = Arm64Gen::ARM64Reg::INVALID_REG)
{
FlushRegisters(regs, FlushMode::All, tmp_reg);
FlushRegisters(regs, FlushMode::All, tmp_reg, IgnoreDiscardedRegisters::No);
}
void StoreCRRegisters(BitSet8 regs, Arm64Gen::ARM64Reg tmp_reg = Arm64Gen::ARM64Reg::INVALID_REG)
{
FlushCRRegisters(regs, FlushMode::All, tmp_reg);
FlushCRRegisters(regs, FlushMode::All, tmp_reg, IgnoreDiscardedRegisters::No);
}
void DiscardCRRegisters(BitSet8 regs);
@ -421,8 +430,10 @@ private:
void SetImmediate(const GuestRegInfo& guest_reg, u32 imm, bool dirty);
void BindToRegister(const GuestRegInfo& guest_reg, bool will_read, bool will_write = true);
void FlushRegisters(BitSet32 regs, FlushMode mode, Arm64Gen::ARM64Reg tmp_reg);
void FlushCRRegisters(BitSet8 regs, FlushMode mode, Arm64Gen::ARM64Reg tmp_reg);
void FlushRegisters(BitSet32 regs, FlushMode mode, Arm64Gen::ARM64Reg tmp_reg,
IgnoreDiscardedRegisters ignore_discarded_registers);
void FlushCRRegisters(BitSet8 regs, FlushMode mode, Arm64Gen::ARM64Reg tmp_reg,
IgnoreDiscardedRegisters ignore_discarded_registers);
};
class Arm64FPRCache : public Arm64RegCache
@ -432,7 +443,9 @@ public:
// Flushes the register cache in different ways depending on the mode.
// The temporary register can be set to ARM64Reg::INVALID_REG when convenient for the caller.
void Flush(FlushMode mode, Arm64Gen::ARM64Reg tmp_reg) override;
void Flush(
FlushMode mode, Arm64Gen::ARM64Reg tmp_reg,
IgnoreDiscardedRegisters ignore_discarded_registers = IgnoreDiscardedRegisters::No) override;
// Returns a guest register inside of a host register
// Will dump an immediate to the host register as well