From 3eea600c2aa0561ffc23db37390746558b5394f7 Mon Sep 17 00:00:00 2001 From: Drew T <50529377+Druthulu@users.noreply.github.com> Date: Thu, 3 Sep 2026 16:03:21 -0600 Subject: [PATCH] =?UTF-8?q?docs(cookbook):=20add=20=C2=A7464,=20which=20th?= =?UTF-8?q?e=20previous=20commit's=20message=20described=20but=20did=20not?= =?UTF-8?q?=20contain?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The §464 append was lost to a git index-lock race: the commit landed with a message documenting four levers from func_8005DE78 while the file held only §465 and §466. Caught by grepping the file for each section instead of trusting the commit I had just written. Content unchanged from the agent's report: a volatile QI/HI load preserves the zero-extend as its own andi; ||-vs-&& selects do_jump's drop-through arm; a volatile STORE can never be stolen into a delay slot (resource_conflicts_p returns 1 on any volatil resource), which is how to force a target nop after a j; and a "memory" clobber vs a volatile read are not interchangeable CSE-breakers — both reload the index, only the clobber leaves the addu operand order intact. --- docs/matching-cookbook.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/matching-cookbook.md b/docs/matching-cookbook.md index e4367531a5..ba69f284bc 100644 --- a/docs/matching-cookbook.md +++ b/docs/matching-cookbook.md @@ -35324,3 +35324,31 @@ is why this hides: the same expression is fine everywhere except under a `MEM`. * `(*(u16*)x)++` emits the extra `move` that `+= 1` omits. * `CatPrim`'s arg2 must be ` + D_80074778*4`: with a MEM as operand 0 the address is emitted first, then operand 1, then the load, so the `addu` comes out operand-1-first. + +#### §464 — FOUR VOLATILE/BARRIER LEVERS FROM `main:func_8005DE78` (141 ins → MATCH) + +*(Recorded after §465-§466: the original append was lost to a git index-lock race and the commit +message that claimed it landed before the text did. Content is unchanged from the agent's report.)* + +**Source: the S76 agent on `func_8005DE78`.** Structure came from the banked in-TU neighbours +(`func_8005DCA0`'s shape, `func_8005FBC8`'s body inlined as the loop test) — §2b "read a matched +neighbour first" paying out again. The four levers are new, and each names its mechanism. + +**1. A VOLATILE QI/HI LOAD PRESERVES THE ZERO-EXTEND AS ITS OWN INSTRUCTION.** Normally combine folds +a `u8 -> s32` promotion into the `lbu`. Make the load volatile and it cannot, so the extend survives +as a separate `andi $s2, $v0, 0xFF` — which is what the target has. Reach for it when your build is +one `andi` short around a byte load. + +**2. `if (A || B) { t } else { K }` VS `&&` SELECTS WHICH ARM IS `do_jump`'s DROP-THROUGH.** Same +truth table, different branch layout. When the residual is "right tests, wrong order", flip the +connective before touching registers. + +**3. 🔴 A VOLATILE STORE CAN NEVER BE STOLEN INTO A DELAY SLOT.** `reorg`'s `resource_conflicts_p` +returns 1 on ANY volatil resource, so the slot stays empty. **This is how you reproduce a target +`nop` after a `j`** — otherwise very hard to force, since every ordinary statement is a candidate. + +**4. A `"memory"` CLOBBER AND A VOLATILE READ ARE NOT INTERCHANGEABLE CSE-BREAKERS.** Both force the +`lbu 0x44` index to reload. But the volatile read lands the byte in `$v0` while the `"memory"` +clobber lands it in `$v1` **without flipping the `addu` operand order** — and that operand order was +the entire final 2-instruction `REGALLOC-PERM` residual. When a reload lever fixes the reload and +breaks the register, try the other spelling before calling the residual permuter-class.