From bb36198eeab6a50aa0d984436bfaf49bf7115b09 Mon Sep 17 00:00:00 2001 From: Drew T <50529377+Druthulu@users.noreply.github.com> Date: Thu, 3 Sep 2026 16:00:05 -0600 Subject: [PATCH] =?UTF-8?q?docs(cookbook):=20=C2=A7462=20=E2=80=94=20four?= =?UTF-8?q?=20levers=20from=20func=5F80024054=20(74/53/32=20->=204)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the S76 agent, none previously recorded: 1. array[var-K] folds K into the symbol LO16/lhu displacement, and naming an intermediate idx does NOT stop it (the fold is front-end/combine, before any steerable register choice). A zero-byte opacity barrier on idx, one per use site, is what defeats it. 2. The fused sll 16 / sra 15 sign-extend-scale needs the index declared s16 — confirms §241's recipe reproduces on a fresh case. 3. A mask-then-compare LOCAL cross-jump-merged two case tails and flipped branch polarity to bne; switching on the expression directly fixed both and matched the target's forward-beq. The temporary was the defect — §461 from the other direction. 4. A pointer parameter's SIGNEDNESS decides how -1 is materialized: s16* gives addiu -1, u16* gives ori 0xffff, because gcc-2.7.2 canonicalizes the RHS constant against the lvalue's signedness when picking the load-immediate opcode. Invisible in the C, one instruction in the asm. Residual is one permuter-class DELAY-SLOT diff two prior attempts also hit. --- docs/matching-cookbook.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/docs/matching-cookbook.md b/docs/matching-cookbook.md index 628774a89..969c03b9a 100644 --- a/docs/matching-cookbook.md +++ b/docs/matching-cookbook.md @@ -35280,3 +35280,40 @@ drift). The lever is real for one op and a dead end for a chain; do not spend tu **Still open (permuter-class, Law 3):** the `$v0`/`$v1` tie above, and a redundant `D_80073140[i]` re-read the target schedules early to fill a load-delay slot while cc1 schedules it at its use. Every C-level attempt to move it either CSE'd the two reads into one (−1 ins) or added a move (+1). + +#### §462 — FOUR LEVERS FROM `main:func_80024054` (91 ins, 74/53/32 → 4) + +**Source: the S76 agent on `func_80024054`.** Four independent unlocks, none previously recorded; +the last one is the kind of rule that silently costs a whole attempt. + +**1. `array[var - K]` folds K into the symbol's LO16 / `lhu` displacement.** Naming an intermediate +`idx = var - K` does NOT stop it — the fold happens at the front end / in combine, before any +register assignment you could steer. The only thing that defeated it was a zero-byte opacity barrier +immediately after computing the index, one per use site: +`__asm__ __volatile__("" : "=r"(idx) : "0"(idx));` +Reach for this whenever the target loads from `sym+0` with a computed index and your build folds the +constant into the displacement instead. + +**2. The fused `sll 16; sra 15` sign-extend-and-scale wants the index declared `s16`, not `s32`.** +This confirms §241's recipe on a fresh case — worth knowing it reproduces rather than being a +one-off of that function. + +**3. A mask-then-compare LOCAL causes a cross-jump merge AND flips branch polarity.** Writing +`bits = val & 0xC000;` then `if (bits == …) else if …` merged two case tails into one shared block +and emitted `bne`-polarity branches where the target has `beq`. Dropping the local and switching on +the expression directly — `switch (val & 0xC000) { case 0x8000: … case 0xC000: … default: … }` — +fixed both at once and reproduced the target's forward-`beq` shape. **The temporary was the defect**; +this is the same family as §461's "laundering can be the defect", from the opposite direction. + +**4. 🔴 A POINTER PARAMETER'S SIGNEDNESS DECIDES HOW `-1` IS MATERIALIZED.** Declaring `arg1` as +`s16 *` rather than `u16 *` flips the fail-path constant from `ori $x, 0xffff` to `addiu $x, -1`, +matching the target — because gcc-2.7.2 canonicalizes the RHS constant against the **lvalue's** +signedness before choosing the load-immediate opcode. Nothing about the store's *value* changes, so +this is invisible in the C and shows up only as a one-instruction opcode difference. If a residual is +a lone `ori 0xffff` vs `addiu -1`, check the signedness of the pointer being written through before +touching anything else. + +**Left open:** one `DELAY-SLOT` residual — `addu $a3,$zero,$zero` is insn #0 in the target and lands +in the branch delay slot in every C variant. Two independent prior attempts hit the same wall; +five further variants (statement reorder, register pin, barriers either side of the load) each left +it unchanged or traded it for an equal residual elsewhere. Permuter-class, Law 3.