Skip to content

Keep a pipelined loop's scratch registers off values live across it. - #8

Merged
fjtrujy merged 1 commit into
ps2dev:masterfrom
glampert:fix/pipeline-live-registers
Oct 23, 2026
Merged

fjtrujy merged 1 commit into
ps2dev:masterfrom
glampert:fix/pipeline-live-registers

Conversation

@glampert

Copy link
Copy Markdown

The generic software pipeliner picks scratch registers for the values it rotates from VF31 down, skipping only the registers the loop body names. A register that holds a value the body never names, but that is read after the loop or on the next trip of an enclosing loop, counts as free, and a rotation overwrites it.

ps2gl's general program hits this with the default pipelining. The directional-light vertex loop's rotations take the registers holding material_spec and vert_to_eye. Both were loaded before the loop and are read again for the next light and by the point-light loop. Those later passes then compute with garbage. general_pv_diff has the same problem.

Fix

findVuLoopCandidates now works out which VF lanes are live where each loop is entered and where it is left. It uses lane-level liveness over the whole program's control flow: labels, branches, bal/jr returns, --endexit, and out_vf read at the exit. The result is stored in VuLoopCandidate::liveVfRegisters. collectLoopVfBaseKeys adds those registers to the ones the body names, so all three scratch pools keep clear of them.

Effect

  • Of the 12 ps2gl fixtures, general and general_pv_diff change. Every vu-checker register-allocation finding they had is gone, and nothing new appears. The other 10 compile byte-identical.
  • In a local corpus of 46 programs from ps2gl, Tyra, quake2-ps2 and the openvcl examples, the remaining 44 are byte-identical.
  • The test suite and an ASan/UBSan run are clean.

Tests

test/integration/test_pipeline_live_registers.cpp adds two cases. Both fail on master without this fix:

  • a value read after a pipelined loop keeps its register;
  • a value read on each trip of an outer loop keeps its register.

Not covered

The env-gated generic kernel rewrite has its own rename pool, which this change doesn't touch. That path is not enabled by default.

The software pipeliner takes scratch registers for the values it rotates
from VF31 down, skipping only the registers the loop body names. A
register holding a value the body does not name, but that is read after
the loop or on the next trip of an enclosing loop, counted as free, and a
rotation overwrote it. In ps2gl's general program the directional light
loop took the registers of material_spec and vert_to_eye, so the next
light and the point lights read garbage.

Work out which VF lanes are live where each loop is entered and left,
over the whole program's control flow, and keep those registers out of
the scratch pool along with the ones the body names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@fjtrujy fjtrujy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good improvement!

@fjtrujy
fjtrujy merged commit 786f856 into ps2dev:master Oct 23, 2026
6 checks passed
@glampert
glampert deleted the fix/pipeline-live-registers branch October 23, 2026 12:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants