docs(cookbook): §462 — four levers from func_80024054 (74/53/32 -> 4)

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.
This commit is contained in:
Drew T
2026-09-03 16:00:05 -06:00
parent 371a18b6de
commit bb36198eea
+37
View File
@@ -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.