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) void Jit64::FallBackToInterpreter(UGeckoInstruction inst)
{ {
gpr.Flush(); gpr.Flush(BitSet32(0xFFFFFFFF), RegCache::IgnoreDiscardedRegisters::Yes);
fpr.Flush(); fpr.Flush(BitSet32(0xFFFFFFFF), RegCache::IgnoreDiscardedRegisters::Yes);
if (js.op->canEndBlock) 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( ASSERT_MSG(
DYNA_REC, DYNA_REC,
@ -410,7 +410,8 @@ void RegCache::Flush(BitSet32 pregs)
case PPCCachedReg::LocationType::Default: case PPCCachedReg::LocationType::Default:
break; break;
case PPCCachedReg::LocationType::Discarded: 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; break;
case PPCCachedReg::LocationType::SpeculativeImmediate: case PPCCachedReg::LocationType::SpeculativeImmediate:
// We can have a cached value without a host register through speculative constants. // We can have a cached value without a host register through speculative constants.

View File

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

View File

@ -256,8 +256,8 @@ void JitArm64::Shutdown()
void JitArm64::FallBackToInterpreter(UGeckoInstruction inst) void JitArm64::FallBackToInterpreter(UGeckoInstruction inst)
{ {
FlushCarry(); FlushCarry();
gpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG); gpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG, IgnoreDiscardedRegisters::Yes);
fpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG); fpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG, IgnoreDiscardedRegisters::Yes);
if (js.op->canEndBlock) 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) for (auto iter = regs.begin(); iter != regs.end(); ++iter)
{ {
const int i = *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"); "Attempted to flush discarded register");
if (i + 1 < int(GUEST_GPR_COUNT) && regs[i + 1]) 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) 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"); "Attempted to flush discarded register");
FlushRegister(GUEST_CR_OFFSET + i, mode, tmp_reg); 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); FlushRegisters(BitSet32(0xFFFFFFFF), mode, tmp_reg, ignore_discarded_registers);
FlushCRRegisters(BitSet8(0xFF), mode, tmp_reg); FlushCRRegisters(BitSet8(0xFF), mode, tmp_reg, ignore_discarded_registers);
} }
ARM64Reg Arm64GPRCache::R(const GuestRegInfo& guest_reg) 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) for (size_t i = 0; i < m_guest_registers.size(); ++i)
{ {
const RegType reg_type = m_guest_registers[i].GetType(); const RegType reg_type = m_guest_registers[i].GetType();
if (reg_type != RegType::NotLoaded && reg_type != RegType::Discarded && if (reg_type == RegType::Discarded)
reg_type != RegType::Immediate) {
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); FlushRegister(i, mode, tmp_reg);
} }

View File

@ -81,6 +81,12 @@ enum class FlushMode : bool
MaintainState, MaintainState,
}; };
enum class IgnoreDiscardedRegisters
{
No,
Yes,
};
class OpArg class OpArg
{ {
public: public:
@ -169,7 +175,8 @@ public:
// Flushes the register cache in different ways depending on the mode. // Flushes the register cache in different ways depending on the mode.
// A temporary register must be supplied when flushing GPRs with FlushMode::MaintainState, // 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. // 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; virtual BitSet32 GetCallerSavedUsed() const = 0;
@ -315,7 +322,9 @@ public:
// Flushes the register cache in different ways depending on the mode. // Flushes the register cache in different ways depending on the mode.
// A temporary register must be supplied when flushing GPRs with FlushMode::MaintainState, // 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. // 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. // Returns a guest GPR inside of a host register.
// Will dump an immediate to the host register as well. // 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) 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) 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); void DiscardCRRegisters(BitSet8 regs);
@ -421,8 +430,10 @@ private:
void SetImmediate(const GuestRegInfo& guest_reg, u32 imm, bool dirty); void SetImmediate(const GuestRegInfo& guest_reg, u32 imm, bool dirty);
void BindToRegister(const GuestRegInfo& guest_reg, bool will_read, bool will_write = true); void BindToRegister(const GuestRegInfo& guest_reg, bool will_read, bool will_write = true);
void FlushRegisters(BitSet32 regs, FlushMode mode, Arm64Gen::ARM64Reg tmp_reg); void FlushRegisters(BitSet32 regs, FlushMode mode, Arm64Gen::ARM64Reg tmp_reg,
void FlushCRRegisters(BitSet8 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 class Arm64FPRCache : public Arm64RegCache
@ -432,7 +443,9 @@ public:
// Flushes the register cache in different ways depending on the mode. // 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. // 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 // Returns a guest register inside of a host register
// Will dump an immediate to the host register as well // Will dump an immediate to the host register as well