Jits: Fix interpreter fallback handling of discarded registers

When the interpreter writes to a discarded register, its type
must be changed so that it is no longer considered discarded.

Fixes a 62ce1c7 regression.
This commit is contained in:
JosJuice 2021-04-17 13:53:03 +02:00
parent 5322256065
commit b3b5016f54
8 changed files with 51 additions and 4 deletions

View File

@ -419,15 +419,23 @@ void Jit64::FallBackToInterpreter(UGeckoInstruction inst)
{ {
gpr.Flush(); gpr.Flush();
fpr.Flush(); fpr.Flush();
if (js.op->opinfo->flags & FL_ENDBLOCK) if (js.op->opinfo->flags & FL_ENDBLOCK)
{ {
MOV(32, PPCSTATE(pc), Imm32(js.compilerPC)); MOV(32, PPCSTATE(pc), Imm32(js.compilerPC));
MOV(32, PPCSTATE(npc), Imm32(js.compilerPC + 4)); MOV(32, PPCSTATE(npc), Imm32(js.compilerPC + 4));
} }
Interpreter::Instruction instr = PPCTables::GetInterpreterOp(inst); Interpreter::Instruction instr = PPCTables::GetInterpreterOp(inst);
ABI_PushRegistersAndAdjustStack({}, 0); ABI_PushRegistersAndAdjustStack({}, 0);
ABI_CallFunctionC(instr, inst.hex); ABI_CallFunctionC(instr, inst.hex);
ABI_PopRegistersAndAdjustStack({}, 0); ABI_PopRegistersAndAdjustStack({}, 0);
// If the instruction wrote to any registers which were marked as discarded,
// we must mark them as no longer discarded
gpr.Reset(js.op->regsOut);
fpr.Reset(js.op->GetFregsOut());
if (js.op->opinfo->flags & FL_ENDBLOCK) if (js.op->opinfo->flags & FL_ENDBLOCK)
{ {
if (js.isLastInstruction) if (js.isLastInstruction)

View File

@ -436,6 +436,16 @@ void RegCache::Flush(BitSet32 pregs)
} }
} }
void RegCache::Reset(BitSet32 pregs)
{
for (preg_t i : pregs)
{
ASSERT_MSG(DYNAREC, !m_regs[i].IsAway(),
"Attempted to reset a loaded register (did you mean to flush it?)");
m_regs[i].SetFlushed();
}
}
void RegCache::Revert() void RegCache::Revert()
{ {
ASSERT(IsAllUnlocked()); ASSERT(IsAllUnlocked());

View File

@ -171,6 +171,7 @@ 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));
void Reset(BitSet32 pregs);
void Revert(); void Revert();
void Commit(); void Commit();

View File

@ -161,6 +161,11 @@ void JitArm64::FallBackToInterpreter(UGeckoInstruction inst)
MOVI2R(ARM64Reg::W0, inst.hex); MOVI2R(ARM64Reg::W0, inst.hex);
BLR(ARM64Reg::X8); BLR(ARM64Reg::X8);
// If the instruction wrote to any registers which were marked as discarded,
// we must mark them as no longer discarded
gpr.ResetRegisters(js.op->regsOut);
fpr.ResetRegisters(js.op->GetFregsOut());
if (js.op->opinfo->flags & FL_ENDBLOCK) if (js.op->opinfo->flags & FL_ENDBLOCK)
{ {
if (js.isLastInstruction) if (js.isLastInstruction)

View File

@ -26,8 +26,21 @@ void Arm64RegCache::Init(ARM64XEmitter* emitter)
void Arm64RegCache::DiscardRegisters(BitSet32 regs) void Arm64RegCache::DiscardRegisters(BitSet32 regs)
{ {
for (int j : regs) for (int i : regs)
DiscardRegister(j); DiscardRegister(i);
}
void Arm64RegCache::ResetRegisters(BitSet32 regs)
{
for (int i : regs)
{
OpArg& reg = m_guest_registers[i];
ARM64Reg host_reg = reg.GetReg();
ASSERT_MSG(DYNAREC, host_reg == ARM64Reg::INVALID_REG,
"Attempted to reset a loaded register (did you mean to flush it?)");
reg.Flush();
}
} }
ARM64Reg Arm64RegCache::GetReg() ARM64Reg Arm64RegCache::GetReg()

View File

@ -154,6 +154,7 @@ public:
virtual void Start(PPCAnalyst::BlockRegStats& stats) {} virtual void Start(PPCAnalyst::BlockRegStats& stats) {}
void DiscardRegisters(BitSet32 regs); void DiscardRegisters(BitSet32 regs);
void ResetRegisters(BitSet32 regs);
// Flushes the register cache in different ways depending on the mode // Flushes the register cache in different ways depending on the mode
virtual void Flush(FlushMode mode, PPCAnalyst::CodeOp* op) = 0; virtual void Flush(FlushMode mode, PPCAnalyst::CodeOp* op) = 0;

View File

@ -957,8 +957,7 @@ u32 PPCAnalyzer::Analyze(u32 address, CodeBlock* block, CodeBuffer* buffer, std:
{ {
gprDiscardable |= op.regsOut; gprDiscardable |= op.regsOut;
gprDiscardable &= ~op.regsIn; gprDiscardable &= ~op.regsIn;
if (op.fregOut >= 0) fprDiscardable |= op.GetFregsOut();
fprDiscardable[op.fregOut] = true;
fprDiscardable &= ~op.fregsIn; fprDiscardable &= ~op.fregsIn;
} }
if (strncmp(op.opinfo->opname, "stfd", 4)) if (strncmp(op.opinfo->opname, "stfd", 4))

View File

@ -67,6 +67,16 @@ struct CodeOp // 16B
// (The reason why we can't always do this is because some games rely on the exact bits of // (The reason why we can't always do this is because some games rely on the exact bits of
// denormals and SNaNs being preserved as long as no arithmetic operation is performed on them.) // denormals and SNaNs being preserved as long as no arithmetic operation is performed on them.)
BitSet32 fprIsStoreSafe; BitSet32 fprIsStoreSafe;
BitSet32 GetFregsOut() const
{
BitSet32 result;
if (fregOut >= 0)
result[fregOut] = true;
return result;
}
}; };
struct BlockStats struct BlockStats