Skip to content

Join a partial write to the earlier value only if the lanes it keeps are read. - #11

Open
glampert wants to merge 1 commit into
ps2dev:masterfrom
glampert:fix/partial-write-ranges
Open

glampert wants to merge 1 commit into
ps2dev:masterfrom
glampert:fix/partial-write-ranges

Conversation

@glampert

Copy link
Copy Markdown

Follow-up to #9.

Problem

A VF write to some lanes of a variable (add.xyz fC, ...) was joined to the variable's earlier value whenever any other lane had been written before, so that the kept lanes stay in the same register. BranchState accumulates the lane flags along a path, so on a loop's second trip the write was joined to the previous trip's value even when every lane it kept was written again before being read (add.xyz fC then add.w fC, then sq fC). Values rewritten lane by lane on every trip then stayed live for the whole program and pinned a register each.

Tyra's stapip_cull_td_vu1 builds its three output colours this way; SCE vcl compiles it, openvcl failed with "ran out of VF registers (31 available) for outputColor3" (its exact live ranges need 29).

Fix

Before the branch-state walk, collectLanesLiveAfterWrites works out, for each partial write, which of the lanes it keeps are read before they are written again (a block's out_vf outputs count as read at its exit). BranchState::writeFloat joins the write to the earlier value only when one of those lanes is live. A read that needs lanes from several writes is still joined to all of them by completeLiveRanges from #9.

Tests

test_loop_liveness.cpp gains "a partial write starts a new value when the lanes it keeps are dead": a loop that writes fC xyz then w before storing it, with four VF registers; current master runs out of registers, this passes.

Effect on generated code

Checked against the ps2gl fixtures, Tyra's 15 programs and quake2-ps2's, each assembled with dvp-as and checked with vu-checker, master (17cbadb) against this branch:

  • Tyra stapip_cull_td_vu1 now compiles.
  • 25 programs change (register numbering and the scheduling that follows from it; all 12 fixtures, scei, billboard_renderer, 10 Tyra programs, quake2 textured_triangles), 21 are byte-identical.
  • The regalloc finding vu-checker reports on master for general_tri, general_pv_diff_tri, general_quad and general_pv_diff_quad ("instruction 171 (move.xyzw) reads VF30, which holds any of intensity66, normal depending on the path") is gone, and nothing new is reported.
  • The test suite and an ASan/UBSan build of the suite and all those programs are clean.

…are read

A VF write to some lanes of its variable joined the variable's earlier
value whenever another lane had been written before, to keep those
lanes in the same register. Lane flags accumulate along a path, so on a
loop's second trip a write was joined to the last trip's value even when
every lane it kept was written again before any read. Values rewritten
lane by lane on each trip then stayed live for the whole program, and
Tyra's stapip_cull_td_vu1, which SCE vcl compiles, ran out of VF
registers for outputColor3.

Work out which lanes are read after each partial write before they are
written again, with a block's outputs read at its exit, and join only
when one of the kept lanes is. completeLiveRanges already joins a read
that needs lanes from several writes to all of them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant