4.9 KiB
§215 — PIN ECONOMY: the twin's pins are NOT part of the shape, and §17's "pin every call-crossing value" is over-broad (P31 S58)
THE DEFAULT IS PLAIN LOCALS. Run that A/B first. Five cards in this harvest copied a twin's
register T v __asm__("$16"/"$17"/"$18") triple and were made worse by it:
| card | binary | with the twin's pins | with plain locals |
|---|---|---|---|
func_80182A30 |
ov_SC06_000 | every pin permutation left a 4-insn SCHEDULE-REORDER in bb0; statement order controlled which pairs permuted (i=0 last → s2,s1,s0; i=0 mid → s0,s2,s1) but never reached the target's s2,s0,s1 |
s32 i; s32 *ptr; s32 a0; → the exact target weave, first try; statement order became irrelevant |
func_8018B684 |
ov_SC03_006 | the ≥0.9 twin's __asm__ barrier gave gcc nothing to hoist; a fresh local + barrier gave 24 ins / wrong fills |
duplicate the call per arm with literal constants → cross_jump merges the tail, MATCH 25 |
func_80184840 |
ov_SC04_011 | the twin matched without any pin, so the twin was actively misleading on the regalloc axis | this card needed register s32 p __asm__("$4") — the opposite conclusion from the same family |
func_801AD5B4 |
md_SC07_004 | the twin's pins were entirely unnecessary for this relative | plain locals sufficed once statement order was right |
func_8017E424 |
ov_SC07_007 | the TU-neighbours' house hints (register pins, casted calls) were counterproductive for this body | typing the parameter plain s32 won — one pseudo, no pointer/int allocno split |
AND §17's ABSOLUTE IS WRONG. func_8018130C (ov_SC03_124, wave af): §17 prescribes pinning
EVERY call-crossing value. Pinning arg0 to $17 alone → near 21. Pinning only $s0 to $16,
leaving s2/s3 unpinned so gcc keeps its own density ranking → near 4. Pinning s2/s3 as well
→ near 47 LENGTH-DRIFT (the pins on the mult-chain temps broke gcc's negu/mflo scheduling).
Only the values that genuinely cross a jal boundary need pins.
WHEN THE PIN IS THE ANSWER — four shapes, all "pin exactly one thing":
- A caller-saved colour rotation where the target register IS the incoming-arg register.
func_801AE908(md_SC07_004): target colours p→$a0/ t→$v0/ test→$v1against first-fit's$v0/$v1/$a0. Tried and inert:s32-vs-u16on the temp; §13259/§15519's copy-preference respellings (bothvoid*ands32parameter — the$4preference did not survive); field-based spelling with no temp.register s32 p __asm__("$4")→ MATCH 34. This is the explicit complement of §2425's "noregister __asm__pins needed". - A lone
lwin the wrong GPR whose only use is a branch.func_801A2364(md_SC07_004): a plains32 tempput the0xD4field load in$v1;register s32 flag __asm__("$4")forcedlw $a0,0xD4($s0)+beqz $a0. MATCH 39. - Pin the CONSTANT interloper, not the pointer.
func_8018CAF0(ov_SC04_011): unpinned, the0x80000000constant landed in$a1and the pointer/value in$v0/$v1; target wants constant=$v1.register s32 mask __asm__("$3"), with the mask statement placed before the pointer load, closed all 5. (§176-B2's exemplar contested a load against a constant; this is the same fix on a different contested pair — a bareluiconstant feeding an OR mask.) - A zero-byte pin to raise a use count, placed BEFORE the branch.
func_801852C8(ov_SC03_006, 15 ins): ONE__asm__ __volatile__("" :: "r"(hw))immediately after the halfword's birth raised its use count enough for allocno priority to hand it$v1while the long-lived pointer kept$a1. Placement is load-bearing: inside thethen-block it did nothing.
AND THE DIRECTION IS OFTEN INVERTED FROM INTUITION. func_80188104 (ov_SC02_017): the residual
was two early loads reading the restored $a0 where the target reads $s0.
(a) Pinning the SAVED value to $16 fails — the allocator gives the plain parameter its own
callee-saved home ($s1), adds an sw/lw pair (+3 ins), and still serves the early loads off
$a0. (b) Pinning the TAIL web to $4 snaps it: since that value occupies $4 across the
call, the parameter must survive the call elsewhere → it stays in $s0, the early loads read $s0,
and the copy materialises as the target's addu $a0,$s0,$zero in the beqz slot. (c) Ordering
trap: writing the copy FIRST lets dbr thread it into the delay slot before the two loads, so cse
serves them from a fresh $a0 again — the copy must stay after the store pairs.
Read (a)/(b) as the rule: pin the register you want OCCUPIED, not the value you want MOVED.
PROVENANCE. Ten cards, six overlays, waves ab–ag. Also confirms §176-C practically: the $4 pin
scheduled fine around the call on func_80188104 despite the sched.c:1704 hard-reg anti-dep bug
(that bug only adds false deps, and first-fit had no better choice than $4 anyway).