From 6f9860c46a5ed206afe9307eec78ee25772bb1b5 Mon Sep 17 00:00:00 2001 From: Drew T <50529377+Druthulu@users.noreply.github.com> Date: Sat, 25 Jul 2026 12:28:14 -0600 Subject: [PATCH] =?UTF-8?q?docs(phase-29):=20=C2=A774=20=E2=80=94=20pin-sa?= =?UTF-8?q?fety=20audit;=20the=205-pin=20behemoth=20draft=20is=20SAFE=20(o?= =?UTF-8?q?pen=20action=20#3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AUDITED .run/giants/s18_func_8017D960_b2.c (pins $25 $17 $19 $20 $21) WITHOUT recompiling: the SESSION-18 match_one object survives and cmp proves its t.c is this draft. - VERDICT SAFE. The corrupting form of the §72 hazard is a CALLER-SAVED pin ($25=$t9) whose live range spans a jal — gcc-2.7.2 does not save/restore an explicit-register variable across a call. Byte-checked: exactly 3 jal, all at 0x2c-0x50; first pin write at 0x58 => NO call after the pins are established. Corroborated by a token census of the C (every call-shaped token after line 270 is a file-local macro: gte_*, BOXTEST, ATTEN, CLAMP80). - The observed excess writes (2/3/3/5/5 vs 2 assignments each + 1 epilogue lw) are the BENIGN §72 mode: gcc using the pinned reg as scratch before the pinned value lands (lui/lw/addiu on $20, with addu t9,s4,zero routing r1's value out through it). Nothing live was clobbered. - cookbook §74: the reusable audit (objdump the surviving object; compare jal addresses against the first pin write; expect writes == assignments + 1 epilogue restore) + the standing rule — prefer a callee-saved register for any pin outliving a call; a caller-saved pin across a jal is a real wall verdict, not a drafting slip. --- docs/matching-cookbook.md | 44 +++++++++++++++++++++++++++++++++++++ phase-ends/CURRENT_PHASE.md | 23 +++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/docs/matching-cookbook.md b/docs/matching-cookbook.md index d42d7da112..0cbf8af674 100644 --- a/docs/matching-cookbook.md +++ b/docs/matching-cookbook.md @@ -5797,3 +5797,47 @@ compile-time; the emitted code is unchanged; nothing outside the draft is touche 5. **Pick the isolation probe deliberately:** an overlay that instantiates the *return-casting* macros (`((s32 (*)(...))func_X)(...)`, §17a-1) is the only place a decl's return type could plausibly touch codegen. `ov_SC01_000` served that role for `func_8014F3E8`. + +## §74 — Auditing a pinned draft: the §72 hazard is CALLER-SAVED pins spanning a call, and only the disassembly can tell you (Phase 29 SESSION-19, `func_8017D960` b2, 5 pins) + +§72 established that a `register __asm__("$N")` pin is a **preference, not a reservation** — gcc will +happily put another value in `$N` while your variable is notionally live. The banked corpus is safe +from this by construction (the whole-binary byte-gate rejects any miscompile), but **un-gated drafts +are not**, and a behemoth draft can carry pins for days before it ever reaches a gate. This is the +cheap audit. + +**Two distinct failure modes, only one of which is real most of the time:** + +1. **CALLER-SAVED PIN SPANNING A CALL — the one that silently corrupts.** `$25` (`$t9`) and the + `$t0–$t9` range are call-clobbered. gcc-2.7.2 does **not** save/restore an explicit-register + variable across a call, so if the pinned variable's live range crosses a `jal`, the value is + destroyed with no diagnostic. **Callee-saved pins (`$16–$23` = `$s0–$s7`) are immune** — the + prologue/epilogue save/restore covers them. +2. **SCRATCH REUSE OF THE PINNED REGISTER — usually benign.** gcc will use `$N` as a temporary for an + unrelated value *before* the pinned variable's own value lands there. Observed here: `lui s4,..; + lw s4,0(s4); addiu s4,s4,-128` — `$20` (pinned `r1lo`) carried the raw global for two insns, and + `addu t9,s4,zero` copied that value out to `$25` (pinned `r1`) on the way. Self-consistent; nothing + live was clobbered. + +**The audit (no recompile needed if a `match_one` object survives — `.run/match/.//t.o`; +confirm it is the draft you think it is with `cmp t.c `):** +``` +mipsel-linux-gnu-objdump -d > dis.txt +# (a) does any call exist after the first pin write? +grep -nE '\bjalr?\b' dis.txt # compare addresses against the pin-write addresses +# (b) how many times is each pinned reg WRITTEN? (first operand, excluding sw/branch/jal/mult-class) +``` +Expect `writes == (assignments in the C) + 1 epilogue `lw` for each callee-saved pin`. Anything above +that is scratch reuse — read those sites before assuming they are benign. + +**Worked verdict (`.run/giants/s18_func_8017D960_b2.c`, pins `$25 $17 $19 $20 $21`):** 3 `jal`s total, +all at `0x2c–0x50`, and the **first pin write is at `0x58`** — *no call after the pins are +established*, so the caller-saved `$25` pin never spans one and mode (1) does not arise. Modes (2) +sightings on `$20`/`$21`/`$17` are the benign scratch pattern above. **Draft is safe to keep building +on.** (The C corroborates: everything after the three prologue calls is a macro — `gte_*`, `BOXTEST`, +`ATTEN`, `CLAMP80` — not a function call. Verify that with a token census, not by eye: a 636-line +behemoth hides a `jal` easily.) + +**Standing rule of thumb:** prefer a **callee-saved** register for any pin whose variable outlives a +call; if the target's register really is caller-saved and the value really does span a `jal`, the pin +cannot express it — that is a genuine wall verdict, not a drafting slip. diff --git a/phase-ends/CURRENT_PHASE.md b/phase-ends/CURRENT_PHASE.md index 4d2eb47880..eeb0d751a2 100644 --- a/phase-ends/CURRENT_PHASE.md +++ b/phase-ends/CURRENT_PHASE.md @@ -3524,6 +3524,29 @@ conditional) · main-EXE/B9 + GLM/B6 + resident's 14 walls (P30) · behemoths B7 propagation is worth its own gated pass (32+84 ins × 138 = 16,008 ins ≈ **+0.12pp** if it lands). Drafts: `.run/drafts-s18-widen/` (F3E8) + `.run/drafts-s18-widen2/` (D4C0, the param-axis variant). +- **✅ 2026-07-25 (SESSION-19) — §72 PIN-SAFETY AUDIT (open action #3): the 5-pin behemoth draft is + SAFE, and the audit produced the general test (§74).** `.run/giants/s18_func_8017D960_b2.c` carries + `register __asm__` pins on `$25 $17 $19 $20 $21`. **`$25` is `$t9` — CALLER-SAVED**, which is the + only genuinely corrupting form of the §72 hazard (gcc-2.7.2 does not save/restore an + explicit-register variable across a call, so a live range spanning a `jal` is destroyed silently). + **Audited without recompiling** — the SESSION-18 `match_one` object survives at + `.run/match/func_8017D960.2216347/func_8017D960/t.o` and `cmp` proves its `t.c` IS this draft. + **VERDICT — mode (1) does not arise:** the object contains **exactly 3 `jal`s, all at `0x2c–0x50`, + and the first pin write is at `0x58`** ⇒ *no call after the pins are established*. Corroborated in + the C: every call-shaped token after line 270 is a macro defined in the file (`gte_*`, `BOXTEST`, + `ATTEN`, `CLAMP80`) — checked by token census, not by eye, because a 636-line behemoth hides a + `jal` easily. + **Mode (2) IS present and is benign:** write-counts per pinned reg are 2/3/3/5/5 against 2 + assignments each (+1 epilogue restore for the callee-saved four). The excess is gcc using the pinned + register as a SCRATCH before the pinned variable's own value lands — `lui s4,..; lw s4,0(s4); + addiu s4,s4,-128` (`$20` carrying the raw `D_801CBC90` for two insns) with `addu t9,s4,zero` + routing `r1`'s value out through it. Self-consistent; nothing live was clobbered. + **⇒ the draft is safe to keep building on**, and the reusable test is cookbook **§74** (objdump the + surviving `match_one` object; compare `jal` addresses against the first pin write; expect + `writes == assignments + 1 epilogue lw`). **Standing rule:** prefer a **callee-saved** register for + any pin whose variable outlives a call; if the target genuinely wants a caller-saved reg across a + `jal`, the pin cannot express it — that is a real wall verdict, not a drafting slip. + > **🛑 SESSION-18 CLOSING CHECKPOINT (2026-07-25, Opus 5 @ High) — SUPERSEDES the earlier SESSION-18 > block, which was written mid-session and is STALE (it still says "two searches in flight"). > Fresh session safe here.**