From 920f8bac357a49513cc85ced54faa34e93a137c0 Mon Sep 17 00:00:00 2001 From: Drew T <50529377+Druthulu@users.noreply.github.com> Date: Thu, 3 Sep 2026 16:29:55 -0600 Subject: [PATCH] =?UTF-8?q?docs(cookbook):=20=C2=A7476=20=E2=80=94=20a=20h?= =?UTF-8?q?ard-register=20pin=20strips=20nonzero=5Fbits=20and=20reg=5Fn=5F?= =?UTF-8?q?sets=3D=3D1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the S76 Fable agent on func_800226C0 — 670 instructions, the largest function in the project, matched at closeness 0. Explains WHY pins so often hurt, completing the arc of §461/§462/§471: (a) A pinned hard register carries no nonzero_bits, so combine cannot fold sext(HImode t) into a copy — which is exactly what the target's 228E4 addu/beqz/addu chain is, with cse2 reusing it as the loop multiplier. The $18 pin that looked obvious was what prevented the fold; one plain uninitialised s16 t (mul left an unpinned pseudo) unlocked it. (b) A pin makes reg_n_sets != 1, so birthing_insn_p refuses the §199-A boost and the value is placed first — a whole-block schedule shift. Unpinning o/col/sh23/abr fixed the prologue order and two ties. Rule: if a residual involves a sign/zero-extend fold or a first-in-block placement, REMOVE pins before adding them. --- docs/matching-cookbook.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/matching-cookbook.md b/docs/matching-cookbook.md index ad3a0895c..c7842e497 100644 --- a/docs/matching-cookbook.md +++ b/docs/matching-cookbook.md @@ -35608,3 +35608,31 @@ accesses and costs **12 mismatches**. A fleet-consensus declaration is a strong *(Also load-bearing: `q = D_800A46CE` as a real pointer local, matching the target keeping `&D_800A46CE` in `$s4` and deriving the `D_800A4642` store as `addiu $v1,$s4,-0x96` / `addu $v1,$s0,$v1` / `sh $v0,0xA($v1)`; plus two §194-A AFTER-placement fences.)* + +#### §476 — 🔴 A HARD-REGISTER PIN DESTROYS TWO THINGS COMBINE AND SCHED1 NEED (`func_800226C0`, 670 ins → MATCH) + +**Source: the S76 Fable agent on `main:func_800226C0` — at 670 instructions the largest function in +the project, matched at closeness 0.** This completes the "a lever can be the defect" arc (§461, +§462, §461-addendum, §471) with the mechanism that explains *why* pins so often hurt. + +**A pinned hard register loses NONZERO-BITS information.** The target's `228E4` chain +(`addu $v0,$s2` / `beqz` / `addu $t2,$v0`) is **combine folding `sext(HImode t)` into a copy**, with +`cse2` then reusing it as the loop multiplier. That fold needs `nonzero_bits` on the pseudo — and +**hard registers do not carry nonzero-bits**. So the `$18` pin that looked like the obvious way to +place `t` was exactly what prevented the fold. The fix was **one plain uninitialised `s16 t`**, with +`mul` left as an unpinned pseudo. + +**A pin also breaks the §199-A birthing boost.** `birthing_insn_p` requires `reg_n_sets == 1`; a +pinned `$a1` mask has `reg_n_sets != 1`, gets no boost, and is therefore **placed first** — visible +as a whole-block schedule difference. Unpinning `o`/`col`/`sh23`/`abr` fixed the prologue order, a +`t`/flags density tie and an `or` destination tie. + +**So the rule is not "pins are risky" but a specific two-part mechanism:** +> A hard-register pin (a) strips `nonzero_bits`, disabling combine folds that depend on a value's +> known width, and (b) makes `reg_n_sets != 1`, disabling the sched1 birthing boost. +> **If a residual involves a sign/zero-extend fold or a first-in-block placement, remove pins before +> adding them.** + +*(Also: the 850 single-prim block written as an inline `addPrim` with block-local temps, and the +function defined with the TU's typed prototype `(Obj_80021D38*, u8*, DVec_80021D38*, u8*)` to avoid +the §41 declaration wall.)*