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();
fpr.Flush();
if (js.op->opinfo->flags & FL_ENDBLOCK)
{
MOV(32, PPCSTATE(pc), Imm32(js.compilerPC));
MOV(32, PPCSTATE(npc), Imm32(js.compilerPC + 4));
}
Interpreter::Instruction instr = PPCTables::GetInterpreterOp(inst);
ABI_PushRegistersAndAdjustStack({}, 0);
ABI_CallFunctionC(instr, inst.hex);
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.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()
{
ASSERT(IsAllUnlocked());

View File

@ -171,6 +171,7 @@ public:
RCForkGuard Fork();
void Discard(BitSet32 pregs);
void Flush(BitSet32 pregs = BitSet32::AllTrue(32));
void Reset(BitSet32 pregs);
void Revert();
void Commit();

View File

@ -161,6 +161,11 @@ void JitArm64::FallBackToInterpreter(UGeckoInstruction inst)
MOVI2R(ARM64Reg::W0, inst.hex);
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.isLastInstruction)

View File

@ -26,8 +26,21 @@ void Arm64RegCache::Init(ARM64XEmitter* emitter)
void Arm64RegCache::DiscardRegisters(BitSet32 regs)
{
for (int j : regs)
DiscardRegister(j);
for (int i : regs)
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()

View File

@ -154,6 +154,7 @@ public:
virtual void Start(PPCAnalyst::BlockRegStats& stats) {}
void DiscardRegisters(BitSet32 regs);
void ResetRegisters(BitSet32 regs);
// Flushes the register cache in different ways depending on the mode
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.regsIn;
if (op.fregOut >= 0)
fprDiscardable[op.fregOut] = true;
fprDiscardable |= op.GetFregsOut();
fprDiscardable &= ~op.fregsIn;
}
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
// denormals and SNaNs being preserved as long as no arithmetic operation is performed on them.)
BitSet32 fprIsStoreSafe;
BitSet32 GetFregsOut() const
{
BitSet32 result;
if (fregOut >= 0)
result[fregOut] = true;
return result;
}
};
struct BlockStats