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.