fix(vu0): pin vf0 to (0,0,0,1) in every constructed R5900Context - #187
Draft
smmathews wants to merge 1 commit into
Draft
fix(vu0): pin vf0 to (0,0,0,1) in every constructed R5900Context#187smmathews wants to merge 1 commit into
smmathews wants to merge 1 commit into
Conversation
On real VU0 hardware vf0 is hardwired to (x,y,z,w) = (0,0,0,1), the vector-unit analogue of GPR r0. R5900Context's constructor memset the struct to zero and re-seeded only a short list of fields, leaving vf0 at (0,0,0,0) with w = 0. Pin vf0 in the constructor so every default-constructed context, including the callback, interrupt, and thread contexts built across the kernel, starts hardware-correct. The runtime's own top-level constructor needed a second fix. It default-constructs its embedded m_cpuContext, which runs the new constructor pin, then immediately memsets that same member back to zero and re-seeds only r0. That memset silently cleared the pin a second time, so the primary context that actually executes recompiled VU0 macro-mode code still started with vf0.w == 0, and stayed wrong until the first VU0 microprogram ran the state restore helper. Repin vf0 there too, right after the existing r0 re-seed, mirroring how r0 is already treated at that site. Adds two regression tests: one on a bare R5900Context asserting all four vf0 lanes, independent of any runtime, and one on a freshly constructed PS2Runtime asserting the same four lanes on its own m_cpuContext. The second test is the one that actually constrains the memset bypass; deleting the repin line makes only that test's w assertion fail while the bare-context test keeps passing, proving the two tests guard different sites.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix(vu0): pin vf0 to (0,0,0,1) in every constructed R5900Context
The gap
On real VU0 hardware, vf0 is hardwired to (x,y,z,w) = (0,0,0,1) and always
reads back that constant, the vector-unit analogue of GPR r0.
R5900Context's constructor memsets the whole struct to zero and thenre-seeds a short explicit list of fields (vu0_q, cop0_random, cop0_status,
cop0_prid, in_delay_slot, branch_pc).
vu0_vf[0]is not on that list, soevery default-constructed
R5900Contextstarts at (0,0,0,0), with w readas 0 instead of the hardware constant 1.
Nothing pins vf0 at construction today. The only existing vf0 pin in the
tree lives inside the VU0 state restore helper that runs at the end of a
VU0 microprogram execution; it repins vf0 after the fact but does not help
a context that has not yet run a VU0 microprogram. Several sites across
the runtime and kernel default-construct an
R5900Context(the runtime'sown CPU context, plus assorted callback, interrupt, and thread contexts),
and each one runs the same unpinned constructor and therefore starts with
a wrong vf0.
The runtime's own CPU context needs an extra fix beyond the constructor
pin.
PS2Runtime's top-level constructor default-constructs its embeddedm_cpuContext(running the constructor pin once), then immediatelymemsets that same member back to zero and re-seeds only GPR r0. Thatmemset silently clears the pin a second time, so the primary context that
actually executes recompiled VU0 macro-mode code starts with vf0.w == 0
again, and stays wrong until the first VU0 microprogram runs the restore
helper.
Why it matters
Recompiled VU0 macro-mode math that treats vf0 as the constant-1
homogeneous source (for example building a translation or bias term from
vf0.w) reads 0 instead of 1 until something else happens to repin vf0
first. The wrong result is silent: no crash, no assertion, just an
arithmetic answer that is off by whatever term depended on vf0.w being 1.
The fix
Two parts:
R5900Context's constructor, rightafter the existing vu0_q seed, so every default-constructed context
(callback, interrupt, and thread contexts included) starts
hardware-correct from the moment it exists.
PS2Runtime's own constructor, right afterthe existing r0 re-seed that follows its
memsetofm_cpuContext.That memset clears the pin the constructor just applied, so without
this second repin the runtime's primary CPU context, the one that
executes recompiled VU0 macro-mode code, would still start wrong. The
repin mirrors the r0 idiom sitting immediately above it.
Caveat: initialization only
This pins vf0 at construction time. It does not enforce the hardware
discard-on-write invariant for vf0's lifetime: the recompiler still emits
plain writes to vector register index 0 like any other register, so a
later VU0 macro-mode write can still clobber vf0 mid function. That
write-discard behavior is a recompiler/codegen concern and is explicitly
out of scope here. Read this change as "vf0 now starts hardware-correct,"
not "vf0 is now hardware-correct for the life of the context."
Scope
R5900Contextconstructorpin in
ps2_runtime.h, plus them_cpuContextrepin inps2_runtime.cpp), plus two new regression tests.untouched; it is not redundant with this change, since nothing else
pinned a freshly constructed context before now.
Test
Two regression tests:
R5900Context ctx;, independent of executing any VU0 microprogram orconstructing a runtime. On the unmodified constructor, the w assertion
fails (reads 0 instead of 1); after this change it passes. This test
guards the constructor pin only; it does not exercise
PS2Runtimeatall, so it cannot catch the memset bypass.
runtime.cpu().vu0_vf[0]on a freshly constructedPS2Runtime. This isthe test that actually constrains the memset bypass: on a tree with the
constructor pin but without the
ps2_runtime.cpprepin, its w assertionfails while the bare-context test still passes, which is exactly the
bypass this PR closes.
Repro:
Look for "R5900Context constructor pins vf0 to hardware (0,0,0,1)" and
"PS2Runtime's own CPU context has vf0 pinned to hardware (0,0,0,1)" in the
PS2RuntimeExpansion suite.
Rebase note
Both
ps2_runtime.handps2_runtime.cppare touched by other in-flightwork. Locate
R5900Contextand its constructor by name in the header,and locate the
m_cpuContextmemset/ r0 re-seed by name inPS2Runtime::PS2Runtime(), not by line number in either file.