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:
parent
5322256065
commit
b3b5016f54
|
@ -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)
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue