Merge pull request #22150 from hrydgard/interpreter-review

Claude review of the interpreter
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2026-08-29 00:20:17 +02:00
commit 10edf68b00
7 files changed
+190 -128

No files matched your search

+2 -83
View File
@@ -3,10 +3,6 @@
#include "ppsspp_config.h"
#if PPSSPP_PLATFORM(WINDOWS) && PPSSPP_ARCH(ARM64)
#include <arm64intr.h>
#endif
#include "Common/BitSet.h"
#include "Common/BitScan.h"
#include "Common/Common.h"
@@ -28,32 +24,6 @@
#include "Core/System.h"
#include "Core/MIPS/MIPSTracer.h"
#if PPSSPP_ARCH(ARM64)
// TODO: This should be put in some common header.
static inline u64 ARM64ReadFPCR() {
#if PPSSPP_PLATFORM(WINDOWS)
return _ReadStatusReg(ARM64_FPCR);
#else
// TODO: Try __builtin_arm_get_fpcr()
u64 fpcr; // not really 64-bit, just to match the register size.
asm volatile ("mrs %0, fpcr" : "=r" (fpcr));
return fpcr;
#endif
}
static inline void ARM64WriteFPCR(u64 fpcr) {
#if PPSSPP_PLATFORM(WINDOWS)
_WriteStatusReg(ARM64_FPCR, fpcr);
#else
// TODO: Try __builtin_arm_set_fpcr()
// Write back the modified FPCR
asm volatile ("msr fpcr, %0" : : "r" (fpcr));
#endif
}
#endif
#ifdef mips
// Why do MIPS compilers define something so generic? Try to keep defined, at least...
#undef mips
@@ -110,57 +80,6 @@ u32 IRRunMemCheck(u32 pc, u32 addr) {
return coreState != CORE_RUNNING_CPU ? 1 : 0;
}
void IRApplyRounding(MIPSState *mips) {
u32 fcr1Bits = mips->fcr31 & 0x01000003;
// If these are 0, we just leave things as they are.
if (fcr1Bits) {
int rmode = fcr1Bits & 3;
bool ftz = (fcr1Bits & 0x01000000) != 0;
#if PPSSPP_ARCH(SSE2)
u32 csr = _mm_getcsr() & ~0x6000;
// Translate the rounding mode bits to X86, the same way as in Asm.cpp.
if (rmode & 1) {
rmode ^= 2;
}
csr |= rmode << 13;
if (ftz) {
// Flush to zero
csr |= 0x8000;
}
_mm_setcsr(csr);
#elif PPSSPP_ARCH(ARM64)
u64 fpcr = ARM64ReadFPCR();
// Translate MIPS to ARM rounding mode
static const u8 lookup[4] = {0, 3, 1, 2};
fpcr &= ~(3 << 22); // Clear bits [23:22]
fpcr |= ((u64)lookup[rmode] << 22);
if (ftz) {
fpcr |= 1 << 24;
}
ARM64WriteFPCR(fpcr);
#endif
}
}
void IRRestoreRounding() {
#if PPSSPP_ARCH(SSE2)
// TODO: We should avoid this if we didn't apply rounding in the first place.
// In the meantime, clear out FTZ and rounding mode bits.
u32 csr = _mm_getcsr();
csr &= ~(7 << 13);
_mm_setcsr(csr);
#elif PPSSPP_ARCH(ARM64)
u64 fpcr = ARM64ReadFPCR(); // not really 64-bit, just to match the regsiter size.
fpcr &= ~(7 << 22); // Clear bits [23:22] for rounding, 24 for FTZ
// Write back the modified FPCR
ARM64WriteFPCR(fpcr);
#endif
}
u32 IRInterpret(MIPSState *mips, const IRInst *inst) {
while (true) {
switch (inst->op) {
@@ -1257,10 +1176,10 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst) {
break;
case IROp::ApplyRoundingMode:
IRApplyRounding(mips);
ApplyHostRoundingMode(mips);
break;
case IROp::RestoreRoundingMode:
IRRestoreRounding();
RestoreHostRoundingMode();
break;
case IROp::UpdateRoundingMode:
// TODO: Implement
-3
View File
@@ -11,9 +11,6 @@ u32 IRRunBreakpoint(u32 pc);
u32 IRRunMemCheck(u32 pc, u32 addr);
u32 IRInterpret(MIPSState *ms, const IRInst *inst);
void IRApplyRounding();
void IRRestoreRounding();
template <uint32_t alignment>
u32 RunValidateAddress(u32 pc, u32 addr, u32 isWrite) {
static_assert(alignment <= 16);
+69 -29
View File
@@ -82,6 +82,8 @@ int MIPS_InterpretSingleStep(MIPSState *mips) {
return 0;
}
MIPSOpcode op = Memory::Read_Opcode_JIT(mips->pc); // now unchecked
// Same reason as the run loop in MIPSInterpret_RunUntil - see ApplyHostRoundingMode.
ApplyHostRoundingMode(mips);
if (mips->inDelaySlot) {
MIPSInterpret(mips, op);
if (mips->inDelaySlot) {
@@ -91,6 +93,7 @@ int MIPS_InterpretSingleStep(MIPSState *mips) {
} else {
MIPSInterpret(mips, op);
}
RestoreHostRoundingMode();
return 1;
}
@@ -113,7 +116,7 @@ static u8 ReadMMIO_U8(MIPSState *mips, u32 addr) {
static u16 ReadMMIO_U16(MIPSState *mips, u32 addr) {
if (!Memory::IsKernelCodeAddress(mips->pc)) {
Core_MemoryException(addr, 2, mips->pc, MemoryExceptionType::READ_WORD, "Kernel mode only");
return (u8)UNKNOWN_MMIO_POISON;
return (u16)UNKNOWN_MMIO_POISON;
}
WARN_LOG(Log::CPU, "Unhandled MMIO Read16 at %08x", addr);
return (u16)UNKNOWN_MMIO_POISON;
@@ -122,7 +125,7 @@ static u16 ReadMMIO_U16(MIPSState *mips, u32 addr) {
static u32 ReadMMIO_U32(MIPSState *mips, u32 addr) {
if (!Memory::IsKernelCodeAddress(mips->pc)) {
Core_MemoryException(addr, 4, mips->pc, MemoryExceptionType::READ_WORD, "Kernel mode only");
return (u8)UNKNOWN_MMIO_POISON;
return UNKNOWN_MMIO_POISON;
}
if (GpioMMIO::IsGpioAddress(addr)) {
return GpioMMIO::Read32(addr);
@@ -235,7 +238,10 @@ namespace MIPSInt {
mips->pc += 4;
}
mips->inDelaySlot = false;
// HLE code is host code - it must not run under the guest's rounding mode.
RestoreHostRoundingMode();
CallSyscallWithPC(op, syscallPC);
ApplyHostRoundingMode(mips);
}
void Int_Sync(MIPSState *mips, MIPSOpcode op) {
@@ -414,9 +420,10 @@ namespace MIPSInt {
if (rt != 0) {
if (!Memory::IsValid4AlignedAddress(addr)) {
Core_MemoryException(addr, 4, PC, MemoryExceptionType::READ_WORD, "ll");
return;
R(rt) = 0;
} else {
R(rt) = Memory::ReadUnchecked_U32(addr);
}
R(rt) = Memory::ReadUnchecked_U32(addr);
}
mips->llBit = 1;
break;
@@ -424,9 +431,11 @@ namespace MIPSInt {
if (mips->llBit) {
if (!Memory::IsValid4AlignedAddress(addr)) {
Core_MemoryException(addr, 4, PC, MemoryExceptionType::WRITE_WORD, "sc");
return;
} else {
Memory::WriteUnchecked_U32(R(rt), addr);
}
Memory::WriteUnchecked_U32(R(rt), addr);
// Report success even if the store got dropped - reporting failure just makes
// the usual retry loop spin on the same bad address forever.
if (rt != 0) {
R(rt) = 1;
}
@@ -474,6 +483,11 @@ namespace MIPSInt {
PC += 4;
}
// On a bad access that's set to be ignored (the default), we mirror what
// Memory::ReadOrException_*/WriteOrException_* do, which is also what the JIT's slow
// path calls: loads produce zero, stores are dropped, and PC advances either way.
// Returning without advancing PC instead would just re-execute the same instruction forever.
// When the access is set to break, Core_MemoryException has already stopped the core.
void Int_ITypeMem(MIPSState *mips, MIPSOpcode op) {
int imm = (signed short)(op&0xFFFF);
int rt = _RT;
@@ -495,7 +509,8 @@ namespace MIPSInt {
break;
}
Core_MemoryException(addr, 1, PC, MemoryExceptionType::READ_WORD, "lb");
return;
R(rt) = 0;
break;
}
R(rt) = SignExtend8ToU32(Memory::ReadUnchecked_U8(addr));
break; //lb
@@ -507,7 +522,8 @@ namespace MIPSInt {
break;
}
Core_MemoryException(addr, 2, PC, MemoryExceptionType::READ_WORD, "lh");
return;
R(rt) = 0;
break;
}
R(rt) = SignExtend16ToU32(Memory::ReadUnchecked_U16(addr));
break; //lh
@@ -519,7 +535,8 @@ namespace MIPSInt {
break;
}
Core_MemoryException(addr, 4, PC, MemoryExceptionType::READ_WORD, "lw");
return;
R(rt) = 0;
break;
}
R(rt) = Memory::ReadUnchecked_U32(addr);
break; //lw
@@ -531,7 +548,8 @@ namespace MIPSInt {
break;
}
Core_MemoryException(addr, 1, PC, MemoryExceptionType::READ_WORD, "lbu");
return;
R(rt) = 0;
break;
}
R(rt) = Memory::ReadUnchecked_U8(addr);
break; //lbu
@@ -543,7 +561,8 @@ namespace MIPSInt {
break;
}
Core_MemoryException(addr, 2, PC, MemoryExceptionType::READ_WORD, "lhu");
return;
R(rt) = 0;
break;
}
R(rt) = Memory::ReadUnchecked_U16(addr);
break; //lhu
@@ -555,7 +574,7 @@ namespace MIPSInt {
break;
}
Core_MemoryException(addr, 1, PC, MemoryExceptionType::WRITE_WORD, "sb");
return;
break;
}
Memory::WriteUnchecked_U8(R(rt), addr);
break; //sb
@@ -567,7 +586,7 @@ namespace MIPSInt {
break;
}
Core_MemoryException(addr, 2, PC, MemoryExceptionType::WRITE_WORD, "sh");
return;
break;
}
Memory::WriteUnchecked_U16(R(rt), addr);
break; //sh
@@ -579,7 +598,7 @@ namespace MIPSInt {
break;
}
Core_MemoryException(addr, 4, PC, MemoryExceptionType::WRITE_WORD, "sw");
return;
break;
}
Memory::WriteUnchecked_U32(R(rt), addr);
break; //sw
@@ -589,12 +608,13 @@ namespace MIPSInt {
case 34: //lwl
{
// Not checking for alignment here - the actual read will be aligned.
if (!Memory::IsValidAddress(addr)) {
u32 mem = 0;
if (Memory::IsValidAddress(addr)) {
mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc);
} else {
Core_MemoryException(addr, 4, PC, MemoryExceptionType::READ_WORD, "lwl");
return;
}
u32 shift = (addr & 3) * 8;
u32 mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc);
u32 result = ( u32(R(rt)) & (0x00ffffff >> shift) ) | ( mem << (24 - shift) );
R(rt) = result;
}
@@ -603,12 +623,13 @@ namespace MIPSInt {
case 38: //lwr
{
// Not checking for alignment here - the actual read will be aligned.
if (!Memory::IsValidAddress(addr)) {
u32 mem = 0;
if (Memory::IsValidAddress(addr)) {
mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc);
} else {
Core_MemoryException(addr, 4, PC, MemoryExceptionType::READ_WORD, "lwr");
return;
}
u32 shift = (addr & 3) * 8;
u32 mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc);
u32 regval = R(rt);
u32 result = ( regval & (0xffffff00 << (24 - shift)) ) | ( mem >> shift );
R(rt) = result;
@@ -620,7 +641,7 @@ namespace MIPSInt {
// Not checking for alignment here - the actual read/write will be aligned.
if (!Memory::IsValidAddress(addr)) {
Core_MemoryException(addr, 4, PC, MemoryExceptionType::WRITE_WORD, "swl");
return;
break;
}
u32 shift = (addr & 3) * 8;
u32 mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc);
@@ -634,7 +655,7 @@ namespace MIPSInt {
// Not checking for alignment here - the actual read/write will be aligned.
if (!Memory::IsValidAddress(addr)) {
Core_MemoryException(addr, 4, PC, MemoryExceptionType::WRITE_WORD, "swr");
return;
break;
}
u32 shift = (addr & 3) << 3;
u32 mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc);
@@ -660,14 +681,15 @@ namespace MIPSInt {
case 49:
if (!Memory::IsValid4AlignedAddress(addr)) {
Core_MemoryException(addr, 4, PC, MemoryExceptionType::READ_WORD, "lwc1");
return;
FI(ft) = 0;
break;
}
FI(ft) = Memory::ReadUnchecked_U32(addr);
break; //lwc1
case 57:
if (!Memory::IsValid4AlignedAddress(addr)) {
Core_MemoryException(addr, 4, PC, MemoryExceptionType::WRITE_WORD, "swc1");
return;
break;
}
Memory::WriteUnchecked_U32(FI(ft), addr);
break; //swc1
@@ -718,6 +740,13 @@ namespace MIPSInt {
if (MIPSComp::jit) {
// In case of DISABLE, we need to tell jit we updated FCR31.
MIPSComp::jit->UpdateFCR31();
} else {
// The interpreter emulates the rounding mode by putting the host FPU in it,
// so it has to switch right here rather than at the next block boundary.
// Restore first: the new value may be back to the default, which Apply
// deliberately doesn't write.
RestoreHostRoundingMode();
ApplyHostRoundingMode(mips);
}
} else {
WARN_LOG_REPORT(Log::CPU, "WriteFCR: Unexpected reg %d (value %08x)", fs, value);
@@ -1068,10 +1097,16 @@ namespace MIPSInt {
break;
case 0x4: //ins
{
int size = (_SIZE + 1) - pos;
u32 sourcemask = 0xFFFFFFFFUL >> (32 - size);
u32 destmask = sourcemask << pos;
R(rt) = (R(rt) & ~destmask) | ((R(rs)&sourcemask) << pos);
// The size field actually holds msb (= pos + size - 1), so build the mask from
// that and shift it down, the way the JITs do. Computing the width as
// (_SIZE + 1) - pos instead would shift by 32 or more when msb < pos - undefined
// behavior, and on x86 it yields an all-ones mask that writes bits the JITs leave
// alone. Hardware calls that encoding unpredictable, so all we need is to be
// consistent and not invoke UB.
const u32 mask = 0xFFFFFFFFUL >> (31 - _SIZE);
const u32 sourcemask = mask >> pos;
const u32 destmask = sourcemask << pos;
R(rt) = (R(rt) & ~destmask) | ((R(rs) & sourcemask) << pos);
}
break;
}
@@ -1100,7 +1135,9 @@ namespace MIPSInt {
}
switch (op & 0x3f)
{
case 12: FsI(fd) = (int)floorf(F(fs)+0.5f); break; //round.w.s
// round.w.s is round-half-to-even, not half-away-from-zero - and its mode is fixed,
// so unlike cvt.w.s below it must not follow fcr31. round_ieee_754 is both.
case 12: FsI(fd) = (int)round_ieee_754(F(fs)); break; //round.w.s
case 13: //trunc.w.s
if (F(fs) >= 0.0f) {
FsI(fd) = (int)floorf(F(fs));
@@ -1247,7 +1284,10 @@ namespace MIPSInt {
int index = op.encoding & 0xFFFFFF;
const ReplacementTableEntry *entry = GetReplacementFunc(index);
if (entry && entry->replaceFunc && (entry->flags & REPFLAG_DISABLED) == 0) {
// Like a syscall, a replacement function is host code - see Int_Syscall.
RestoreHostRoundingMode();
int cycles = entry->replaceFunc();
ApplyHostRoundingMode(mips);
if (entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) {
// Interpret the original instruction under the hook.
+20 -13
View File
@@ -216,23 +216,22 @@ namespace MIPSInt
float d[4];
ReadVector(mips, d, V_Quad, vt);
int offset = (addr >> 2) & 3;
const bool valid = Memory::IsValid4AlignedAddress(addr);
if ((op & 2) == 0) {
if (!Memory::IsValid4AlignedAddress(addr)) {
if (!valid) {
Core_MemoryException(addr, 16, PC, MemoryExceptionType::READ_WORD, "lvl.q");
return;
}
// It's an LVL
for (int i = 0; i < offset + 1; i++) {
d[3 - i] = Memory::ReadUnchecked_Float(addr - 4 * i);
d[3 - i] = valid ? Memory::ReadUnchecked_Float(addr - 4 * i) : 0.0f;
}
} else {
if (!Memory::IsValid4AlignedAddress(addr)) {
if (!valid) {
Core_MemoryException(addr, 16, PC, MemoryExceptionType::READ_WORD, "lvr.q");
return;
}
// It's an LVR
for (int i = 0; i < (3 - offset) + 1; i++) {
d[i] = Memory::ReadUnchecked_Float(addr + 4 * i);
d[i] = valid ? Memory::ReadUnchecked_Float(addr + 4 * i) : 0.0f;
}
}
WriteVector(mips, d, V_Quad, vt);
@@ -240,14 +239,18 @@ namespace MIPSInt
break;
case 54: //lv.q
// A quadword access has to be 16-byte aligned, so a misaligned one is simply
// rejected - we don't try to carry it out anyway, same as every other path here.
if ((addr & 0xF) || !Memory::IsValid4AlignedAddress(addr)) {
Core_MemoryException(addr, 16, PC, MemoryExceptionType::READ_WORD, "lv.q");
const float zero[4]{};
WriteVector(mips, zero, V_Quad, vt);
break;
}
#ifndef COMMON_BIG_ENDIAN
cf = reinterpret_cast<const float *>(Memory::GetPointerUnchecked(addr));
if (cf)
WriteVector(mips, cf, V_Quad, vt);
WriteVector(mips, cf, V_Quad, vt);
#else
float lvqd[4];
@@ -268,7 +271,7 @@ namespace MIPSInt
if ((op & 2) == 0) {
if (!Memory::IsValid4AlignedAddress(addr)) {
Core_MemoryException(addr, 16, PC, MemoryExceptionType::WRITE_WORD, "svl.q");
return;
break;
}
// It's an SVL
for (int i = 0; i < offset + 1; i++)
@@ -278,7 +281,7 @@ namespace MIPSInt
} else {
if (!Memory::IsValid4AlignedAddress(addr)) {
Core_MemoryException(addr, 16, PC, MemoryExceptionType::WRITE_WORD, "svr.q");
return;
break;
}
// It's an SVR
for (int i = 0; i < (3 - offset) + 1; i++) {
@@ -289,8 +292,10 @@ namespace MIPSInt
}
case 62: //sv.q
// See lv.q above.
if ((addr & 0xF) || !Memory::IsValid4AlignedAddress(addr)) {
Core_MemoryException(addr, 16, PC, MemoryExceptionType::WRITE_WORD, "sv.q");
break;
}
#ifndef COMMON_BIG_ENDIAN
f = reinterpret_cast<float *>(Memory::GetPointerWriteUnchecked(addr));
@@ -1642,7 +1647,8 @@ namespace MIPSInt
}
// D prefix works, just not for the cosine lane.
uint32_t dprefixRemove = (3 << cosineLane) | (1 << (8 + cosineLane));
// The saturation field is two bits per element (see ApplyPrefixD), the mask field one.
uint32_t dprefixRemove = (3 << (cosineLane * 2)) | (1 << (8 + cosineLane));
mips->vfpuCtrl[VFPU_CTRL_DPREFIX] &= 0xFFFFF ^ dprefixRemove;
ApplyPrefixD(mips, d, sz);
WriteVector(mips, d, sz, vd);
@@ -1757,14 +1763,15 @@ namespace MIPSInt
case 50: //lv.s
if (!Memory::IsValid4AlignedAddress(addr)) {
Core_MemoryException(addr, 4, PC, MemoryExceptionType::READ_WORD, "lv.s");
return;
VI(vt) = 0;
break;
}
VI(vt) = Memory::ReadUnchecked_U32(addr);
break;
case 58: //sv.s
if (!Memory::IsValid4AlignedAddress(addr)) {
Core_MemoryException(addr, 4, PC, MemoryExceptionType::WRITE_WORD, "sv.s");
return;
break;
}
Memory::WriteUnchecked_U32(VI(vt), addr);
break;
+82
View File
@@ -20,8 +20,14 @@
#include <mutex>
#include <utility>
#include "ppsspp_config.h"
#if PPSSPP_PLATFORM(WINDOWS) && PPSSPP_ARCH(ARM64)
#include <arm64intr.h>
#endif
#include "Common/CommonTypes.h"
#include "Common/Math/SIMDHeaders.h"
#include "Common/Serialize/Serializer.h"
#include "Common/Serialize/SerializeFuncs.h"
#include "Core/ConfigValues.h"
@@ -42,6 +48,82 @@ MIPSState *currentMIPS = &mipsr4k;
MIPSDebugInterface debugr4k(&mipsr4k);
MIPSDebugInterface *currentDebugMIPS = &debugr4k;
#if PPSSPP_ARCH(ARM64)
static inline u64 ARM64ReadFPCR() {
#if PPSSPP_PLATFORM(WINDOWS)
return _ReadStatusReg(ARM64_FPCR);
#else
// TODO: Try __builtin_arm_get_fpcr()
u64 fpcr; // not really 64-bit, just to match the register size.
asm volatile ("mrs %0, fpcr" : "=r" (fpcr));
return fpcr;
#endif
}
static inline void ARM64WriteFPCR(u64 fpcr) {
#if PPSSPP_PLATFORM(WINDOWS)
_WriteStatusReg(ARM64_FPCR, fpcr);
#else
// TODO: Try __builtin_arm_set_fpcr()
// Write back the modified FPCR
asm volatile ("msr fpcr, %0" : : "r" (fpcr));
#endif
}
#endif
void ApplyHostRoundingMode(const MIPSState *mips) {
u32 fcr1Bits = mips->fcr31 & 0x01000003;
// If these are 0, we just leave things as they are.
if (fcr1Bits) {
int rmode = fcr1Bits & 3;
bool ftz = (fcr1Bits & 0x01000000) != 0;
#if PPSSPP_ARCH(SSE2)
u32 csr = _mm_getcsr() & ~0x6000;
// Translate the rounding mode bits to X86, the same way as in Asm.cpp.
if (rmode & 1) {
rmode ^= 2;
}
csr |= rmode << 13;
if (ftz) {
// Flush to zero
csr |= 0x8000;
}
_mm_setcsr(csr);
#elif PPSSPP_ARCH(ARM64)
u64 fpcr = ARM64ReadFPCR();
// Translate MIPS to ARM rounding mode
static const u8 lookup[4] = {0, 3, 1, 2};
fpcr &= ~(3 << 22); // Clear bits [23:22]
fpcr |= ((u64)lookup[rmode] << 22);
if (ftz) {
fpcr |= 1 << 24;
}
ARM64WriteFPCR(fpcr);
#endif
}
}
void RestoreHostRoundingMode() {
// TODO: We should avoid this if we didn't apply rounding in the first place.
// In the meantime, clear out FTZ and rounding mode bits.
#if PPSSPP_ARCH(SSE2)
u32 csr = _mm_getcsr();
csr &= ~(7 << 13);
_mm_setcsr(csr);
#elif PPSSPP_ARCH(ARM64)
u64 fpcr = ARM64ReadFPCR(); // not really 64-bit, just to match the register size.
fpcr &= ~(7 << 22); // Clear bits [23:22] for rounding, 24 for FTZ
// Write back the modified FPCR
ARM64WriteFPCR(fpcr);
#endif
}
u8 voffset[128];
u8 fromvoffset[128];
+9
View File
@@ -293,3 +293,12 @@ extern MIPSDebugInterface *currentDebugMIPS;
extern MIPSState mipsr4k;
extern const float cst_constants[32];
// The guest's rounding mode and flush-to-zero flag (fcr31 bits 0-1 and 24) are emulated by putting
// the *host* FPU into the matching mode, since we do the arithmetic with plain host float ops.
// That mode must not be left on while running anything that isn't emulating a guest instruction -
// HLE syscalls, replacement functions, the GPU - so an emulation loop applies it on entry and
// restores it before calling out, the way the JITs do (see Jit::ApplyRoundingMode).
// Apply is cheap when the guest is in the default mode (by far the common case): it does nothing.
void ApplyHostRoundingMode(const MIPSState *mips);
void RestoreHostRoundingMode();
+8
View File
@@ -1272,6 +1272,12 @@ int MIPSInterpret_RunUntil(MIPSState *mips, u64 globalTicks) {
while (coreState == CORE_RUNNING_CPU) {
CoreTiming::Advance(mips);
// The emulated instructions below do their float math with plain host float ops, so the
// host FPU has to be in the guest's rounding mode while they run - and back in the normal
// one whenever we're not running them, which is what the JITs do too. Calls out from
// inside the run loops (syscalls, replacement functions) restore it themselves.
ApplyHostRoundingMode(mips);
uint64_t ticksLeft = globalTicks - CoreTiming::GetTicks(mips);
if (g_breakpoints.HasBreakPoints() || g_breakpoints.HasMemChecks() || g_breakpoints.HasRegBreakpoints() || ticksLeft <= mips->downcount) {
RunUntilDowncountZeroWithChecks(mips, globalTicks);
@@ -1279,6 +1285,8 @@ int MIPSInterpret_RunUntil(MIPSState *mips, u64 globalTicks) {
RunUntilDowncountZeroFast(mips);
}
RestoreHostRoundingMode();
if (CoreTiming::GetTicks(mips) > globalTicks) {
// DEBUG_LOG(Log::CPU, "Hit the max ticks, bailing 1 : %llu, %llu", globalTicks, CoreTiming::GetTicks(mips));
return 1;