mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
EE-SRA 2 WS-B2: read-site pin merges for 128-bit/raw GPR readers
The JIT-internal readers that bypass armLoadEERegPtr's scalar-only pin substitution would observe a stale lower half under lazy-dirty: the NEON dual-residence plain fill, mmiLoadReg, recSQ's quad source, and QMTC2 get an Ins-lane-0 merge from the pin (armMergeEEPinIntoQuad — no-op in write-through, keeps laziness intact vs. flushing). CTC2's word read and PMADDW's SL[ss] lane reads convert to armLoadEERegPtr — correct under both modes and a free Ldr->Mov substitution under write-through; upper-half (SL[2]) reads fall through to memory, which is always canonical for the unmirrored half. The other seam-inventory readers were already safe: LWL/LWR/SWL/SWR/LDL/LDR/ SDL/SDR merge reads route through _eeMoveGPRtoR (pin-aware), and the NEON fill's const/dirty-scalar arms already Ins the newest value. recompiler_tests 1112/1112 with the flag off. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
39ce31186d
commit
b7fb5e6174
@@ -751,7 +751,8 @@ void recCOP2_QMTC2()
|
||||
}
|
||||
else
|
||||
{
|
||||
armAsm->Ldr(RQSCRATCH, armCpuRegMem(&cpuRegs.GPR.r[_Rt_]));
|
||||
armAsm->Ldr(RQSCRATCH, armCpuRegMem(&cpuRegs.GPR.r[_Rt_]));
|
||||
armMergeEEPinIntoQuad(RQSCRATCH, _Rt_); // lazy-dirty: stale lower half
|
||||
}
|
||||
armAsm->Str(RQSCRATCH, armVU0Mem(&VU0.VF[_Rd_]));
|
||||
}
|
||||
@@ -816,7 +817,9 @@ void recCOP2_CTC2()
|
||||
}
|
||||
else
|
||||
{
|
||||
armAsm->Ldr(RWSCRATCH, armCpuRegMem(&cpuRegs.GPR.r[_Rt_].UL[0]));
|
||||
// armLoadEERegPtr: substitutes a pinned reg's mirror — required under
|
||||
// lazy-dirty, a free Ldr->Mov under write-through.
|
||||
armLoadEERegPtr(RWSCRATCH, &cpuRegs.GPR.r[_Rt_].UL[0]);
|
||||
}
|
||||
|
||||
if (fs == REG_R)
|
||||
|
||||
@@ -844,6 +844,8 @@ int _allocGPRtoNEONreg(int gprreg, int mode)
|
||||
else
|
||||
{
|
||||
armLoadEERegPtr(armQRegister(neonreg), &cpuRegs.GPR.r[gprreg].UQ);
|
||||
// Lazy-dirty: a dirty pin makes the memory lower half stale.
|
||||
armMergeEEPinIntoQuad(armQRegister(neonreg), gprreg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,9 @@ static void mmiLoadReg(const a64::VRegister& qreg, int gpr)
|
||||
}
|
||||
else
|
||||
{
|
||||
armAsm->Ldr(qreg, armCpuRegMem(&cpuRegs.GPR.r[gpr].UQ));
|
||||
armAsm->Ldr(qreg, armCpuRegMem(&cpuRegs.GPR.r[gpr].UQ));
|
||||
// Lazy-dirty: merge the pin over the possibly-stale lower half.
|
||||
armMergeEEPinIntoQuad(qreg, gpr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -936,8 +938,12 @@ REC_FUNC(PDIVUW);
|
||||
// matches the interp's ordering under every Rd/Rs/Rt aliasing.
|
||||
static void recPMADDWLane(int dd, int ss, bool isSub)
|
||||
{
|
||||
armAsm->Ldr(a64::w8, armCpuRegMem(&cpuRegs.GPR.r[_Rs_].SL[ss]));
|
||||
armAsm->Ldr(a64::w9, armCpuRegMem(&cpuRegs.GPR.r[_Rt_].SL[ss]));
|
||||
// armLoadEERegPtr: lane 0 (SL[0]) substitutes a pinned reg's mirror —
|
||||
// required under lazy-dirty (memory lower half may be stale) and a free
|
||||
// Ldr→Mov under write-through; lane 2 (SL[2]) is upper-half → memory is
|
||||
// always canonical there and the helper falls through to the plain Ldr.
|
||||
armLoadEERegPtr(a64::w8, &cpuRegs.GPR.r[_Rs_].SL[ss]);
|
||||
armLoadEERegPtr(a64::w9, &cpuRegs.GPR.r[_Rt_].SL[ss]);
|
||||
|
||||
if (!isSub && ss == 0)
|
||||
{
|
||||
|
||||
@@ -286,6 +286,19 @@ static __fi void armFlushEEClobberedPins()
|
||||
}
|
||||
}
|
||||
|
||||
// Lazy-dirty read-site merge: a 128-bit guest-GPR read from canonical memory
|
||||
// (NEON dual-residence fill, MMI quad loads, SQ/QMTC2 sources) may see a
|
||||
// stale lower half when the pin is dirty — Ins the pin into lane 0 after the
|
||||
// load. The upper 64 bits are never mirrored, so memory is always right for
|
||||
// them. No-op in write-through mode (memory == pin by construction).
|
||||
static __fi void armMergeEEPinIntoQuad(const vixl::aarch64::VRegister& q, int gpr)
|
||||
{
|
||||
if (!EE_PIN_LAZY_DIRTY)
|
||||
return;
|
||||
if (const vixl::aarch64::Register* pin = armEEPinForGPR(gpr))
|
||||
armAsm->Ins(q.V2D(), 0, *pin);
|
||||
}
|
||||
|
||||
static __fi void armLoadEERegPtr(const vixl::aarch64::CPURegister& reg, const void* field)
|
||||
{
|
||||
// Pinned guest GPR: serve the read from the mirror register. The mirror
|
||||
|
||||
@@ -785,6 +785,7 @@ void recSQ()
|
||||
// preferring NEON for full-128-bit guest GPRs.
|
||||
iFlushCall(FLUSH_CONSTANT_REGS);
|
||||
armLoadEERegPtr(a64::q0, &cpuRegs.GPR.r[_Rt_].UQ);
|
||||
armMergeEEPinIntoQuad(a64::q0, _Rt_); // lazy-dirty: stale lower half
|
||||
|
||||
armLoadEERegPtr(a64::w9, &cpuRegs.GPR.r[_Rs_].UL[0]);
|
||||
if (_Imm_ != 0)
|
||||
|
||||
Reference in New Issue
Block a user