docs(phase-32): T4b hand pass — func_80032A74 PROVED at 1 by producer census (§501-M); ghost_census.py + cc1_dumps.sh repair; backlog tie-break repair

- main:func_80032A74 (422 ins, closeness 1): the residual is ONE reload-time slot at sp+0x48 (u16 draft = 422/422 code, DIFF 22 frame
  rows; s16 draft = DIFF 1 at idx 244 lh vs lhu). Every post-parameter slot producer enumerated from gcc-2.7.2 (reload1.c:658 ghost
  alter_reg / caller-save.c:249 area / reload1.c:879 invalid-equiv / reload1.c:3499 spill_stack_slot) and refuted on the bytes: combine's
  newi2pat ghosts re-derive a narrow load and the site is lhu (no lb, no double load); a save area without sw/lw needs sched.c:4962
  staleness and no register-only insn shares a block with a call; $t0 holds no pseudo; LO mult results retry into GR_REGS. Verdict
  PROVED at 1 (pin kept with the verdict; ledger WALL-PROVED).
- NEW mechanism measured: local-alloc.c optimize_reg_copy_2 (tmp = x; tmp op= c; x = tmp) mints a ghost with stale refs, but after
  regclass -> GR_REGS, allocated, no slot (P13/P14). 18 isolated reproducers, 0 draft variants; cookbook §501-M; accelerators (12).
- tools/ghost_census.py (new): ghosts in a .lreg dump with their class (ST_REGS => slot). tools/cc1_dumps.sh: prints the .frame line,
  ins count, spill lines and the census; the under-counting standalone-(use) grep is gone. SETUP rows (R21).
- tools/backlog.py: load_best kept the EARLIEST record at equal closeness (docstring said latest) — the S84 row never rendered; fixed.
- CURRENT_PHASE.md: S84 log + refreshed 🛑 checkpoint (rows (b)(c)(d) next, then T5). No src/config/carve change; fleet check-all
  218/218 rc 0 at the S84 preflight.
This commit is contained in:
Drew T
2026-09-06 11:44:40 -06:00
parent bfeef0c3a1
commit b6fd3fff28
33 changed files with 1333 additions and 108 deletions
+12
View File
@@ -310,3 +310,15 @@ unsloth_compiled_cache/
!/.run/P32/t5x/reports/*.md
!/.run/P32/t5x/verdicts.jsonl
!/.run/P32/t5x/resume_queue.txt
# P32 T4b hand pass (S84, 2026-09-06): notes, mechanism probes, verdict drafts and the private dump script (not the dumps_*/ or rtu/ dirs)
!/.run/P32/t4c/
/.run/P32/t4c/*
!/.run/P32/t4c/*.sh
!/.run/P32/t4c/*.py
!/.run/P32/t4c/*.md
!/.run/P32/t4c/*.log
!/.run/P32/t4c/*.json
!/.run/P32/t4c/func_*/
/.run/P32/t4c/func_*/*
!/.run/P32/t4c/func_*/*.c
!/.run/P32/t4c/func_*/*.md
+220
View File
@@ -0,0 +1,220 @@
[ OK ] resident
[ OK ] ov_SC01_000
[ OK ] ov_SC01_005
[ OK ] ov_SC01_006
[ OK ] ov_SC01_001
[ OK ] ov_SC03_002
[ OK ] ov_SC02_000
[ OK ] ov_SC04_000
[ OK ] ov_SC02_003
[ OK ] ov_SC05_000
[ OK ] ov_SC03_006
[ OK ] ov_SC01_077
[ OK ] ov_SC03_001
[ OK ] ov_SC04_019
[ OK ] ov_SC04_018
[ OK ] ov_SC06_000
[ OK ] ov_SC01_008
[ OK ] ov_SC07_000
[ OK ] ov_SC01_004
[ OK ] ov_SC02_005
[ OK ] ov_SC02_004
[ OK ] ov_SC02_017
[ OK ] ov_SC01_080
[ OK ] ov_SC01_084
[ OK ] ov_SC02_021
[ OK ] ov_SC02_015
[ OK ] ov_SC02_011
[ OK ] ov_SC01_074
[ OK ] ov_SC02_016
[ OK ] ov_SC01_009
[ OK ] ov_SC02_026
[ OK ] ov_SC02_028
[ OK ] ov_SC02_031
[ OK ] ov_SC02_035
[ OK ] ov_SC02_027
[ OK ] ov_SC02_039
[ OK ] ov_SC03_007
[ OK ] ov_SC02_041
[ OK ] ov_SC03_003
[ OK ] ov_SC03_011
[ OK ] ov_SC03_012
[ OK ] ov_SC03_013
[ OK ] ov_SC03_010
[ OK ] ov_SC03_014
[ OK ] ov_SC03_015
[ OK ] ov_SC03_023
[ OK ] ov_SC03_028
[ OK ] ov_SC03_030
[ OK ] ov_SC03_024
[ OK ] ov_SC03_029
[ OK ] ov_SC03_089
[ OK ] ov_SC03_031
[ OK ] main
[ OK ] ov_SC03_090
[ OK ] ov_SC03_091
[ OK ] ov_SC03_095
[ OK ] ov_SC03_092
[ OK ] ov_SC03_096
[ OK ] ov_SC03_097
[ OK ] ov_SC03_093
[ OK ] ov_SC03_094
[ OK ] ov_SC03_098
[ OK ] ov_SC03_101
[ OK ] ov_SC03_099
[ OK ] ov_SC03_100
[ OK ] ov_SC03_102
[ OK ] ov_SC03_103
[ OK ] ov_SC03_105
[ OK ] ov_SC03_109
[ OK ] ov_SC03_108
[ OK ] ov_SC03_111
[ OK ] ov_SC03_104
[ OK ] ov_SC03_112
[ OK ] ov_SC03_114
[ OK ] ov_SC03_110
[ OK ] ov_SC03_115
[ OK ] ov_SC03_113
[ OK ] ov_SC03_117
[ OK ] ov_SC03_116
[ OK ] ov_SC03_119
[ OK ] ov_SC03_118
[ OK ] ov_SC03_121
[ OK ] ov_SC04_004
[ OK ] ov_SC03_125
[ OK ] ov_SC04_002
[ OK ] ov_SC04_003
[ OK ] ov_SC03_126
[ OK ] ov_SC04_005
[ OK ] ov_SC03_124
[ OK ] ov_SC04_006
[ OK ] ov_SC04_008
[ OK ] ov_SC04_007
[ OK ] ov_SC04_009
[ OK ] ov_SC04_010
[ OK ] ov_SC04_011
[ OK ] ov_SC04_012
[ OK ] ov_SC04_015
[ OK ] ov_SC04_016
[ OK ] ov_SC04_020
[ OK ] ov_SC05_001
[ OK ] ov_SC05_003
[ OK ] ov_SC05_004
[ OK ] ov_SC05_005
[ OK ] ov_SC04_021
[ OK ] ov_SC05_006
[ OK ] ov_SC05_008
[ OK ] ov_SC05_007
[ OK ] ov_SC05_002
[ OK ] ov_SC05_009
[ OK ] ov_SC05_011
[ OK ] ov_SC05_010
[ OK ] ov_SC05_017
[ OK ] ov_SC05_019
[ OK ] ov_SC06_011
[ OK ] ov_SC06_006
[ OK ] ov_SC05_018
[ OK ] ov_SC06_008
[ OK ] ov_SC06_013
[ OK ] ov_SC06_018
[ OK ] ov_SC06_015
[ OK ] ov_SC06_010
[ OK ] ov_SC06_020
[ OK ] ov_SC06_016
[ OK ] ov_SC06_014
[ OK ] ov_SC06_022
[ OK ] ov_SC06_024
[ OK ] ov_SC06_025
[ OK ] ov_SC06_027
[ OK ] ov_SC06_030
[ OK ] ov_SC06_033
[ OK ] ov_SC06_032
[ OK ] ov_SC06_029
[ OK ] ov_SC07_008
[ OK ] ov_SC07_011
[ OK ] ov_SC07_002
[ OK ] ov_SC07_001
[ OK ] ov_SC07_009
[ OK ] ov_SC07_007
[ OK ] ov_SC07_010
[ OK ] ov_SC07_006
[ OK ] ov_MAIN_012
[ OK ] ov_SC02_037
[ OK ] ov_SC03_107
[ OK ] md_MAIN_015
[ OK ] md_MAIN_013
[ OK ] md_MAIN_016
[ OK ] md_MAIN_018
[ OK ] md_MAIN_020
[ OK ] md_MAIN_014
[ OK ] md_MAIN_022
[ OK ] md_MAIN_021
[ OK ] md_MAIN_017
[ OK ] md_MAIN_019
[ OK ] md_MAIN_023
[ OK ] md_MAIN_024
[ OK ] md_MAIN_026
[ OK ] md_MAIN_025
[ OK ] md_MAIN_027
[ OK ] md_MAIN_030
[ OK ] md_MAIN_031
[ OK ] md_MAIN_036
[ OK ] md_MAIN_029
[ OK ] md_MAIN_028
[ OK ] md_MAIN_033
[ OK ] md_MAIN_038
[ OK ] md_MAIN_037
[ OK ] md_MAIN_035
[ OK ] md_MAIN_032
[ OK ] md_MAIN_040
[ OK ] md_MAIN_034
[ OK ] md_MAIN_041
[ OK ] md_MAIN_042
[ OK ] md_MAIN_039
[ OK ] md_MAIN_043
[ OK ] md_MAIN_046
[ OK ] md_MAIN_047
[ OK ] md_MAIN_044
[ OK ] md_MAIN_001
[ OK ] md_MAIN_045
[ OK ] md_SC07_004
[ OK ] md_MAIN_011
[ OK ] md_SC07_003
[ OK ] md_MAIN_008
[ OK ] md_SC03_073
[ OK ] md_SC03_075
[ OK ] md_SC03_074
[ OK ] md_MAIN_003
[ OK ] md_SC03_077
[ OK ] md_SC03_076
[ OK ] md_SC03_078
[ OK ] md_SC03_133
[ OK ] md_SC03_079
[ OK ] md_SC03_135
[ OK ] md_SC03_134
[ OK ] md_SC03_132
[ OK ] md_SC03_136
[ OK ] md_SC03_138
[ OK ] md_SC04_026
[ OK ] md_SC03_137
[ OK ] md_SC04_028
[ OK ] md_SC04_024
[ OK ] md_SC04_029
[ OK ] md_SC04_027
[ OK ] md_SC04_025
[ OK ] md_SC04_030
[ OK ] md_SC05_023
[ OK ] md_SC05_025
[ OK ] md_SC05_026
[ OK ] md_SC05_028
[ OK ] md_SC05_024
[ OK ] md_SC05_027
[ OK ] md_SC02_009
[ OK ] md_SC05_029
[ OK ] md_MAIN_009
[ OK ] md_SC03_056
[ OK ] md_SC03_053
[ OK ] md_SC03_054
[ OK ] md_MAIN_007
check-all: 218 passed, 0 failed of 218
EXIT=0
+21
View File
@@ -0,0 +1,21 @@
#!/bin/bash
# usage: dump.sh <draft.c> <tag> -> .run/P32/t4c/dumps_<tag>/ (private; frame line + ins count + ghost census)
cd /home/musashi/bfm-decomp
f="$1"; tag="$2"
d=.run/P32/t4c/dumps_$tag; rm -rf $d; mkdir -p $d
src=$d/$tag.c
if ! grep -q '#include "common.h"' "$f"; then echo '#include "common.h"' > $src; fi
cat "$f" >> $src
mipsel-linux-gnu-cpp -lang-c -Iinclude -undef -Wall -fno-builtin -Dmips -D__GNUC__=2 -D__OPTIMIZE__ -Dpsx -D_PSYQ -D_MIPSEL -D_LANGUAGE_C $src > $d/$tag.i
(cd $d && /home/musashi/bfm-decomp/tools/bin/gcc-2.7.2-psx/cc1 -quiet -O2 -G0 -mips1 -mcpu=3000 -mgas -msoft-float -fgnu-linker -dr -ds -dj -df -dc -dS -dl -dg $tag.i -o $tag.s 2>$tag.err)
echo "== $tag: $(grep -m1 '\.frame' $d/$tag.s | sed 's/\t/ /g') ins=$(grep -cE '^\s+[a-z]' $d/$tag.s)"
grep -n "Spilling reg\|now on stack" $d/$tag.i.greg | head -5
# ghost census: pseudos with 'or none' class and NO occurrence in the greg insn stream
python3 tools/ghost_census.py "$d/$tag.i.lreg"; : <<'PY'
import re,sys
lreg=open(sys.argv[1]).read(); greg=open(sys.argv[2]).read()
for m in re.finditer(r'^Register (\d+) used (\d+) times.*?; (\w+ or none|pref \w+, else \w+|\w+ or \w+)\.', lreg, re.M):
r=m.group(1)
if not re.search(r'\(reg[^ ]* '+r+r'\)', greg):
print(f" GHOST candidate: pseudo {r} refs={m.group(2)} class={m.group(3)} (no occurrence in greg)")
PY
+57
View File
@@ -0,0 +1,57 @@
# func_80032A74 (main / 800_b_2, 422 ins) — T4b HAND PASS, S84 (2026-09-06), Fable 5.1 at Max
## Verdict: PROVED at closeness 1 — the pass is named, every producer of the residual is enumerated and refuted on the bytes.
**State (re-verified this session, real TU, `rtu_match … --tu src/800_b_2.c --asm-subdir asm/nonmatchings/800_b_2`):**
* `.run/P32/t4/drafts/func_80032A74_tuclean.c` (`s16 u18`): **DIFF 1** — idx 244 `lh v0,0x18(s1)` vs target `lhu`; frame exact (0x78).
* `.run/P32/t4c/func_80032A74/lhu_tuclean.c` (the same draft with `u16 u18`, TU-clean): **DIFF 22** = all 22 frame rows
(`addiu sp,-0x70` vs `-0x78`, the 10 saved-register offsets ×2); **422/422 instructions otherwise identical.**
So the residual is EXACTLY one never-referenced stack slot at sp+0x48 (bytes: sp10 local 0x10–0x2F, the a0/a1/a2 reload
spills at 0x30/0x38/0x40 — 5/2/4 refs — nothing at 0x48, saved regs 0x50–0x74). vars 56 → 64: any slot of 1..8 bytes
allocated AFTER pseudo 74's slot lands there and rounds (`MIPS_STACK_ALIGN`) to the same frame; an expand-time slot cannot
(it precedes the reload slots and would push `sw $a0,0x30($sp)` to 0x38).
## The producer census (from tools/reference/gcc-2.7.2 — every stack-slot allocation site that runs after the parameters' slots)
| producer | source | requirement | refuted on THIS function by |
|---|---|---|---|
| 1. ghost pseudo: `alter_reg(i,-1)` in the initial loop | reload1.c:658 (8-byte slot, regno order → right after 72/73/74) | a pseudo with `reg_n_refs>0`, `reg_renumber<0`, no REG_EQUIV, i.e. minted with NO occurrences BEFORE regclass (class `ST_REGS or none` → unallocatable) | only combine mints those (below): path (a) forces `lh`/`lb`; path (b) needs a 3-insn chain whose 2-insn merge fails outright and whose fold is one insn — every reachable fold of a memory head is a narrow load (`lh`/`lb`/a duplicate `lhu`), the target has one load at the site, no `lb`, no double load, all nine `lh` single-use |
| 2. caller-save area: `setup_save_areas` | caller-save.c:249 (4-byte slot per call-used hard reg holding ANY pseudo with `reg_n_calls_crossed>0`, once `caller_save_needed` is set by the profitability path global.c:1085 / local-alloc.c:2209) | a call-used-reg pseudo with a STALE positive count and no real crossing (else `save_call_clobbered_regs` emits `sw/lw` around the call — the target has none) | the only staleness route is sched.c:4962 (a multi-block pseudo keeps flow's count when sched's is 0) after sched1 moved a register-only def/use across a call INSIDE the call's block; the seven call blocks contain only arg setups (fenced by the call's USEs), loads/stores (fenced by `flush_pending_lists`) and call-result copies — nothing can move. combine never crosses a call except with a constant source (combine.c:924). `update_equiv_regs` moves nothing (it deletes 2-ref multi-block inits and doubles `reg_live_length`) |
| 3. invalid-equivalent-address slot | reload1.c:879 (`reg_equiv_memory_loc` whose address eliminates to a spilled pseudo → new slot) | an UNALLOCATED pseudo with a REG_EQUIV MEM through `arg0`/`arg2` (both spilled) | such pseudos are single-block (`update_equiv_regs` requires `reg_basic_block>=0`) and local-alloc/global allocate them unless every GR conflicts — impossible over a 2-insn life |
| 4. `spill_stack_slot` | reload1.c:3499 (`spill_hard_reg` → `alter_reg(i, regno)`; no retry for local-alloc'd pseudos, `retry_global_alloc` for allocnos) | a pseudo evicted from `$t0` or `LO` that gets no new home | `$t0` holds no pseudo (else `order_regs_for_reload` makes `$t1` the spill register — every param reload is `lw $t0`); LO-pref mult results are global allocnos with alternate class `GR_REGS` (`pref LO_REG, else GR_REGS`) and retry into a free GR — the draft's 273→`$t1`, 316→`$v1` |
## The NEW ghost producer found and measured (cookbook §501-M) — and why it cannot slot
`local-alloc.c optimize_reg_copy_2`: `tmp = x; <use tmp>; tmp = tmp op c; <use tmp>; x = tmp;` in ONE block (x dead at
the head copy and live after the copy-back; the head copy survives combine when tmp's first use is not its last and no
3-insn chain passes through it (combine.c:904 guard); the copy-back survives when tmp has an intervening use) rewrites
every `tmp` into `x`, leaves the two copies as no-op self-moves (deleted by jump.c), and decrements `reg_n_refs[tmp]` once
per insn while flow counted the in-place insn TWICE → `tmp` = a ghost with stale refs (P13: refs 5; P14: refs 1, "dies in 0
places"). **But it is minted AFTER regclass**, so it keeps `GR_REGS` and has no conflicts → global allocates it → **vars=0**
(P14). Only combine-minted ghosts (pre-regclass → `ST_REGS or none`) take a slot.
## Probes (all isolated reproducers, `.run/P32/t4c/dump.sh <p>.c <tag>`; oracle = `vars=` + `tools/ghost_census.py`)
| tag | shape | vars | ghost? | what it showed |
|---|---|---|---|---|
| p1–p4 | `m = vol; m = (m<<7)-(m<<4) / m *= 0x70 / m += 3; m >>= 7; vol = m` | 0 | no | combine absorbs the head copy through the 3-insn chain (sll,subu,copy) and the copy-back into the srl |
| p5 | `a = m<<3; b = m<<4; m = a-b` (independent uses) | 0 | no | head copy SURVIVES (`move`) — combine.c:904 guard; copy-back merged |
| p6/p7 | `m = m * K` / `m = m * m` | 0 | no | as p2 |
| p8 | intervening `J = m & 0x7F` before the copy-back | 0 | no | copy-back survives (`move`), head copy absorbed |
| p9 | a call between | 0 | no | the scan breaks at a CALL unless src crosses calls elsewhere |
| p10 | store-protected both copies | 0 | no | both `move`s survive but sched sinks the store below the copy-back → death note moves → optimize_reg_copy_2 sees no `REG_DEAD` |
| p11 | as p10 without a later mention of m | 0 | no | cse canonicalizes m→vol (`make_regs_eqv`: the longer-lived reg wins) → head copy dead |
| p12 | `k = m & 0x7F` after the srl, stored after the copy-back | 0 | no | still a `move` |
| **p13** | `k = m & 3; m >>= 7; Q = k + m; vol = m` + later real uses of m | 0 | **YES (refs 5)** | `.lreg`: `(set 72 72)` ×2, every m rewritten to vol — optimize_reg_copy_2 FIRED; m allocated ($2) because it still had real uses |
| **p14** | p13 with a dead trailing `m = 0` (keeps m canonical for cse; flow deletes it) | 0 | **YES (refs 1, GR_REGS)** | the pure ghost — allocated by global, no slot: the post-regclass rule |
| p15 | p14 without the trailing mention | 0 | no | cse canonicalizes m away |
| p16–p18 | register-only 3-chains with a live head (`(n<<24)>>24`, sign chain, `+4,*3,-12`) | 0 | no | fold at cse/tree level or the middle temp is reused by `find_split_point` — combine path (b) never runs |
| lhu_tuclean | the draft with `u16 u18` in the real TU | 56 | — | DIFF 22 (frame only), 422/422 code |
## What the S83 briefs got right and wrong
* S83 hand pass (HYPOTHESIS.md): "caller-save area, transient at iteration 1" — WRONG in its `$t0` form (Fable F1: a
pseudo in `$t0` displaces the spill register) and wrong in general (no staleness route in this function; see producer 2).
* T4b Fable (402k tokens): "ghost pseudo of the SIGN_EXTEND split" — RIGHT about the species; its "next lever" (a ghost
from a non-memory 3-chain) is combine path (b) and is refuted here (p16–p18 + the target's chains are already folded).
## Files
`.run/P32/t4c/func_80032A74/{lhu_tuclean.c,p1..p18.c,NOTES.md}` · `.run/P32/t4c/dump.sh` (private dump + census) ·
`tools/ghost_census.py` (promoted) · `tools/cc1_dumps.sh` (repaired: frame line + census instead of the `(use)` grep) ·
dumps under `.run/P32/t4c/dumps_*/` (regenerable, ignored).
+538
View File
@@ -0,0 +1,538 @@
#include "common.h"
/* =====================================================================================
* S79 (this session) — RESULT UNCHANGED AT closeness 1/422; the residual is now EXPLAINED
* AND THE ORPHAN SEARCH IS CLOSED. ~200 byte-probes, all with the pinned cc1
* (cpp -Iinclude | cc1 -quiet -O2 -G0 -mips1 -mcpu=3000 -mgas -msoft-float -fgnu-linker),
* oracle = `vars=` off the .frame line (NOT the `(use (reg))` count — see INSTRUMENT below).
*
* THE FRAME ARITHMETIC IS EXACT AND LEAVES EXACTLY ONE ORPHAN TO BUY (§165-03):
* target .frame $sp,0x78 regs=10/0 args=16 => vars = 0x40 = 64
* 64 = 0x20 (the ONE declared local, sp10) + 8*3 (the a0/a1/a2 reload spills at
* 0x30/0x38/0x40, all referenced) + 8*1 (ONE never-referenced slot at 0x48).
* sp10 must be 0x1C..0x20 bytes: 0x18 puts the first spill at 0x28, 0x24 puts it at 0x38;
* only [0x1C,0x20] CEIL_ROUNDs frame_offset to the target's 0x30. A declared pad can
* therefore NEVER buy the 8 bytes here — any pad lands before the spills and moves
* `sw $a0,0x30($sp)`. (§162i1/§226/§333 are all inapplicable to this function.)
*
* WHY THE ORPHAN AND THE `lhu` ARE MUTUALLY EXCLUSIVE (the new, general result):
* `extendhisi2` in mips.md is an EXPAND that does force_not_mem at -O2, so EVERY
* `int x = <s16 mem>` is movhi + ashl16 + ashr16 and combine 3-way-merges it to `lh`.
* In a SINGLE-USE merge, newi2pat==0 so elim_i2/elim_i1 DROP both intermediates' death
* notes -> no orphan. An orphan needs the HImode load's reg to carry a SECOND use, which
* forces the i3_subst_into_i2 path (newi2pat != 0 -> elim_i2 == 0) and strands the ashift
* intermediate. `zero_extendhisi2` is a define_INSN that takes memory, so every unsigned
* promotion is one insn and can never orphan (14 u16/u32/s32/QI respellings measured: 0).
* => an orphan in this function REQUIRES an `lh`, and the target's idx-244 load is `lhu`.
*
* THE SITE CENSUS (why no other site can pay for it). The target has 8 `lh`s
* (D_800C5328, D_800C532A, D_800A4646, D_800A46A2 x2, sp10.unk16, D_800A4EFA,
* ch->unk48, D_800A4EF6) and every one is SINGLE-USE, so each is a note-dropping
* 3->1 merge. The only two values in the whole body with a free narrow second use are
* - `vo->unk18` (mask + a QImode `(s8)` use) -> orphan only in the `lh` spelling, and
* - `n` (int uses + the `sh $s2,0x10($s0)` HImode store) -> `s16 n` costs +4 ins and
* TWO orphans (426 ins, near 395); an s16 shadow of n (nh/n pair, both directions)
* is 2 orphans / near 387. Measured, not assumed.
*
* MEASURED-INERT THIS SESSION (do not re-try):
* * 100-variant local-retyping sweep (20 locals x 6 widths): only `n`->s16/s8 (2 orphans,
* near 395/397) and `b`->s8 (1 orphan, near 273) move `vars`; nothing is free.
* * splitting `(s8)u18` into its own s32 temp DOES buy the orphan and the exact 0x78 frame
* (vars=64, near 5) — but combine then re-derives the byte straight from memory as a
* second load `lb $v0,0x18($s1)`; 8 spellings (u32/s32/s16 base, <<1 vs *2, compound,
* `(u8)`/`&0xFFFF` launders) all keep the `lb`. Best of that family: near 5.
* * `__asm__ __volatile__("" ::: "memory")` between the load and the use DOES block the
* 3-way merge and restores `lhu` at zero instruction cost — near 22, frame 0x70, and
* the orphan dies with the merge. Same for §148-C's zero-emission ref slider
* `__asm__ ("" :: "r"(u18))` placed BEFORE the expression (near 22, vars 56).
* Placed AFTER, the slider keeps the orphan but costs a `move` (423 ins, near 184).
* This is the tension in its sharpest form: the extra HImode use that BLOCKS the merge
* (giving `lhu`) is the same use that must SURVIVE the merge to strand the intermediate.
* * `?:`-accumulator respellings of the four selects (ch->unk48, ch->unk24, ch->unk4D,
* vv), s16 temps at every single-use `lh` site, un-hoisting the clear loop (index /
* pointer / 4 forms), and 6 zero-emission ref sliders at other sites: all vars=56.
* * volatile s16 local: vars=64 but near 192 (it is a DECLARED local, wrong stratum).
*
* INSTRUMENT CORRECTION (worth banking): §172's "count standalone `(use (reg))` in the
* .combine dump" UNDER-COUNTS. A minimal §167-10 reproducer
* (`s16 c = A; if (c != 0) A = c - 1;`) emits `vars= 8` with ZERO `(use (reg))` insns —
* the stranded pseudo is simply absent from every post-combine dump while flow's stale
* `reg_n_refs` keeps it an allocno (`; ST_REGS or none` in -dl, §165-03). Also: the
* naive grep counts hard-reg return USEs (`(use (reg/i:SI 2 v0))`) — 54 hits across
* src/800_b_2.c collapse to 11 real pseudo orphans. USE `vars=` AS THE ORACLE.
*
* WHAT IS LEFT (for whoever picks this up): the 8 bytes are almost certainly NOT a combine
* orphan. §172's producer 3 — a caller-save area, `assign_stack_local(SImode,4,0)`,
* allocated inside reload's loop (reload1.c:1445) and therefore AFTER the alter_reg slots
* (reload1.c:658) — lands exactly at 0x48 and MIPS_STACK_ALIGN rounds vars 0x3C -> 0x40,
* reproducing 64 with no instruction anywhere. That is a register-allocation event, not a
* spelling one; the C axis for it (one more call-crossing value competing for the 9
* callee-saved regs this function already uses in full) was not found. Next probe worth
* running: an A/B that adds one genuine call-crossing value and reads `vars` + the
* `.greg` "Spilling reg" lines, rather than any further respelling of the 0x18 read.
* ===================================================================================== */
/* func_80032A74 - NEAR, closeness 1 / 422 ins (frame 0x78 exact, every immediate, every stack
* offset, every branch target and 421 of 422 registers exact). Residual: idx 244 `lh` vs `lhu`.
*
* ===================================================================================
* THE LEVER THAT TOOK THIS FROM 12 -> 1 (new; not in the cookbook as of S77):
* HOIST A GLOBAL ARRAY'S BASE INTO A FILE-SCOPE-TYPED POINTER LOCAL ASSIGNED *BEFORE*
* THE LOOP. That single move reproduces the target's `lui $t0/addiu $t0/addu` shape
* for three different symbols AND puts them all in $t0, with no asm launder and no
* register pin. Mechanism, read out of the gcc-2.7.2 source (tools/reference/gcc-2.7.2):
*
* 1. local-alloc.c:472 - a pseudo is a LOCAL-ALLOC CANDIDATE only if
* `reg_basic_block[i] >= 0 && reg_n_deaths[i] == 1`
* i.e. it lives in ONE basic block and dies ONCE. `&D_800A4C28[idx]` written inline
* makes a 2-ref, one-block pseudo -> local-alloc hands it the LOWEST free hard reg
* (find_free_reg scans regno 0..31; MIPS defines no REG_ALLOC_ORDER) -> $v1.
* 2. Assigning the base to a local BEFORE the `for(;;)` makes it multi-block, so
* local-alloc skips it; global-alloc cannot place it either (all ten callee-saved
* registers are already taken and update_equiv_regs doubled its live length), so
* `reg_renumber < 0`.
* 3. update_equiv_regs (local-alloc.c:1030) has already attached REG_EQUIV (symbol_ref)
* because the pseudo is set once from a constant -> reload1.c DELETES the initialising
* insn (zero cost, so the hoist is FREE) and reload.c substitutes the symbol at every
* use, reloading it into a SPILL register. $t0 is this function's first spill reg
* (it is also what carries `lw $t0,0x30($sp)`, `addiu $t0,$zero,1`, `mflo $t0`) - which
* is exactly why the target shows the symbols and the parm reloads sharing $t0, and why
* `register s32 x __asm__("$8")` can NEVER reproduce it: a hard-reg user variable makes
* reload move every spill to $t1 (measured: +30 rows).
* 4. `addu $s1,$v0,$t0` operand order comes from writing `idx * 0x48 + (s32)vB`, not
* `vB + idx * 0x48`.
* 5. `const` on the two tables is LOAD-BEARING (dropping it costs 14 rows of scheduling
* around the mult/mflo pair) - the original declared them const.
*
* OTHER LEVERS RETAINED FROM THE PREVIOUS 408->12 RUN:
* - `vol` and `m` are ONE variable (both live in $a0 over disjoint ranges).
* - `register s32 base __asm__("$2")` on the 0x18-lerp base: without it the addu/addiu/subu
* chain lands in $v1 (+3 rows).
* - a zero-byte `__asm__ __volatile__("")` after `vo->unk04 = 0x6009F` stops sched1 hoisting
* the D_800A4EF6 `lh` above the three stores (-7 rows), and one after `ch->unk14 = t32`.
* - use the PARAMETERS directly (no `e = arg0` copies) so the reload spill slots stay
* 0x30/0x38/0x40; the 8-byte clear loop is a POINTER loop with `i = 7;` FIRST (S211).
*
* ===================================================================================
* THE ONE REMAINING ROW, and why it is a genuine wall for this spelling
* idx 244 mine `lh $v0,0x18($s1)` target `lhu $v0,0x18($s1)`
*
* The target frame is 0x78: sp10 at 0x10-0x2F, the three parm spills at 0x30/0x38/0x40, and a
* NEVER-REFERENCED 8-byte slot at 0x48. The only producer of that slot reachable from C here
* is a S172 combine USE-orphan, and the only site in this function that orphans is
* `s16 u18 = vo->unk18` with BOTH an int (sign) promotion and a QImode use - which forces the
* load to be `extendhisi2_internal` = `lh`. Spelling the int use as `(u16)u18 & 0xFF00` gives
* the target's `lhu` and is byte-identical in all 422 instructions - but the orphan vanishes,
* the frame drops to 0x70 and 22 stack-offset rows break (that draft is kept at
* .run/S77w/opus/scratch_func_80032A74/v2.c). Measured dead ends for a substitute orphan
* (each checked by counting standalone `(insn N P X (use (reg ...)))` in the cc1 `-dc` dump):
* - every u16/s16/s8/QImode respelling of the vo->unk18 site (14 forms) -> 0 orphans;
* - `s16 n` (D_800C5328/D_800C532A) -> 2 orphans but a `lhu`+`lh` DOUBLE LOAD, +4 ins;
* - an s16 temp at that site whose only uses are promotions -> folds, 0 orphans;
* - an s16 local for the func_8003F144 return -> 0 orphans.
* Every other `lh` in the target (D_800A4646, D_800A46A2 x2, sp10.unk16, D_800A4EFA,
* ch->unk48, D_800A4EF6) has a SINGLE consumer, and S172's rule is that a single-use load
* 3-way-merges and orphans nothing. A declared 8-byte dead local cannot substitute: expand-time
* locals precede the reload spills and push $a0 from 0x30 to 0x38.
*/
/* func_80032A74 - NEAR, closeness 12 / 422 ins (length exact, frame exact).
*
* LEVERS PROVEN THIS SESSION (each byte-measured with tools/match_one.py; start 408 -> 12):
* 1. FRAME +8 (vars 56 -> 64) = ONE combine USE-orphan (cookbook S172 producer 2). The target's
* frame is 0x10..0x2F C24 local | 0x30/0x38/0x40 parm spills | 0x48 NEVER-REFERENCED. A
* declared dead local CANNOT do it (expand-time locals precede reload spills - measured: it
* pushes a0 from 0x30 to 0x38), and neither can `asm("":: "m"(pad))` (mark_addressable puts it
* in the locals region too). What does: an `s16` LOCAL read from memory and promoted to int
* TWICE, sited after a CODE_LABEL -> expand emits movhi + ashift/ashiftrt, combine merges them
* into one `lh` and orphans the HImode pseudo as `(insn (use (reg:SI N)))`, which alter_reg
* still gives an 8-byte slot. Instrument: count standalone `(use (reg` insns in the .combine
* dump (tools/cc1_dumps.sh). ZERO-extending (u16) temps merge cleanly and orphan NOTHING -
* the ashift/ashiftrt PAIR is the whole mechanism. Single-use s16 temps also orphan nothing.
* COST: the only site available here is `vo->unk18`, where the target loads `lhu` - so idx 244
* is `lh` vs `lhu`, the one structural row left. A 2-use `lh` site would be free; the only
* other one is `n` (D_800C5328) and typing it s16 costs +4 ins.
* 2. `vol` AND `m` ARE ONE VARIABLE. The target holds both in $a0 across disjoint ranges; two
* separate C variables give two allocnos ($v1 and $a1). Merging them is what puts the whole
* volume chain in $a0.
* 3. THE S153 LAUNDER'S REAL COST IS AN ALLOCNO, AND THE FIX IS A PIN ON A DEAD TEMP. Removing
* the launder (plain `D_8007319E[pan]`) fixes m/$a0, the `li 0x100` delay-slot schedule and the
* D_800A4EF6 hoist all at once - but folds the address back to the 3-insn $at macro form (-2
* ins). Keeping the launder, the symbol pseudo has priority log2(refs)*refs/live_length ~ 0.67
* and OUTRANKS the long-lived `vol` (~0.19), so it steals $a0 and pushes the whole chain to $a1
* (+17 rows). `register s32 bp __asm__("$10")` parks it on a register nothing else wants:
* $a0 goes back to vol/m and reload keeps $t0. Do NOT pin it to $8: reload then picks $t1 for
* every parm reload (measured +14 rows, net worse). $9 costs 2 rows (the mflo temp at idx 272
* moves $t1 -> $t2); $10 costs none.
* 4. A zero-byte `__asm__ __volatile__("")` after `vo->unk04 = 0x6009F;` stops sched1 hoisting the
* D_800A4EF6 `lh` above the three stores (-7 rows).
* 5. S219: `vol *= 0x70; vol >>= 7;` (compound) vs `vol = (vol*0x70)>>7;` decides whether the
* `<<4` intermediate lands in $a0 or $v0 (-2 rows).
*
* REMAINING RESIDUAL (12), all REGISTER-NAMING, nothing structural:
* - 3 rows: the D_800A4C28 base is $v1, target $t0.
* - 8 rows: the laundered table base is $t1/$t2, target $t0 - and $t0 is unreachable because the
* same $t0 is reload's spill register for the three parm reloads; a hard-reg var there evicts
* reload. In the target BOTH uses coexist, which means those symbol pseudos are NOT allocnos -
* they are reload rematerialisations of a reg_equiv_constant. Every C spelling tried
* (array[i], &array[i], scalar `extern u16 D;` + `&D + off`, S195-H's struct-cast force_reg,
* const u16* local, S239 integer-space, one-table-two-index) folds to the $at macro form; only
* the launder produces the 4-insn shape, and the launder always creates an allocno.
* - 1 row: idx 244 `lh` vs `lhu` (see lever 1).
*/
/* 0x0C */
/* 0x0C */
/* 0x20 */
/* 0x14 */
/* ---- views this function needs (new names, no TU collision) ---- */
/* the caller's 0x54 request slot (Slot54, seen past its declared tail) */
typedef struct {
/* 0x00 */ u16 unk00;
/* 0x02 */ u16 unk02;
/* 0x04 */ u8 pad04[6];
/* 0x0A */ u8 unk0A;
/* 0x0B */ u8 pad0B[1];
/* 0x0C */ u16 unk0C;
/* 0x0E */ u8 unk0E[8];
} Req32A74;
/* the 0x14 record walked by this loop (Rec14, byte-resolved) */
typedef struct {
/* 0x00 */ u16 unk00;
/* 0x02 */ u16 unk02;
/* 0x04 */ u16 unk04;
/* 0x06 */ u8 unk06;
/* 0x07 */ u8 unk07;
/* 0x08 */ u8 unk08;
/* 0x09 */ u8 unk09;
/* 0x0A */ u8 unk0A;
/* 0x0B */ u8 unk0B;
/* 0x0C */ u8 pad0C[4];
/* 0x10 */ s32 unk10;
} Rec32A74; /* 0x14 */
/* the 0x54 mixer channel at D_800A4988 (cf. Chan336A8) */
typedef struct {
/* 0x00 */ s32 unk00;
/* 0x04 */ s32 unk04;
/* 0x08 */ u16 unk08;
/* 0x0A */ u16 unk0A;
/* 0x0C */ u16 unk0C;
/* 0x0E */ u16 unk0E;
/* 0x10 */ s16 unk10;
/* 0x12 */ s16 unk12;
/* 0x14 */ s32 unk14;
/* 0x18 */ u8 pad18[0xC];
/* 0x24 */ s32 unk24;
/* 0x28 */ u8 pad28[0xC];
/* 0x34 */ u8 unk34;
/* 0x35 */ u8 unk35;
/* 0x36 */ u8 unk36;
/* 0x37 */ u8 pad37[9];
/* 0x40 */ s32 unk40;
/* 0x44 */ s32 unk44;
/* 0x48 */ s16 unk48;
/* 0x4A */ s16 unk4A;
/* 0x4C */ u8 unk4C;
/* 0x4D */ u8 unk4D;
/* 0x4E */ u8 unk4E;
/* 0x4F */ u8 unk4F;
/* 0x50 */ u8 pad50[2];
/* 0x52 */ u8 unk52;
/* 0x53 */ u8 unk53;
} Chan32A74; /* 0x54 */
/* the 0x48 voice at D_800A4C28 (Slot, byte-resolved) */
typedef struct {
/* 0x00 */ s32 unk00;
/* 0x04 */ s32 unk04;
/* 0x08 */ u16 unk08;
/* 0x0A */ u16 unk0A;
/* 0x0C */ u16 unk0C;
/* 0x0E */ u16 unk0E;
/* 0x10 */ u8 pad10[4];
/* 0x14 */ u16 unk14;
/* 0x16 */ u8 pad16[2];
/* 0x18 */ u16 unk18;
/* 0x1A */ u8 pad1A[2];
/* 0x1C */ s32 unk1C;
/* 0x20 */ u8 pad20[0x1A];
/* 0x3A */ u16 unk3A;
/* 0x3C */ u16 unk3C;
/* 0x3E */ u8 pad3E[2];
/* 0x40 */ s32 unk40;
/* 0x44 */ u8 unk44;
/* 0x45 */ u8 unk45;
/* 0x46 */ u8 pad46[2];
} Voice32A74; /* 0x48 */
extern s16 D_800C5328[];
extern s16 D_800C532A[];
extern s16 D_800A4646[];
extern A12 D_80064D44[];
extern B12 *D_8006A970[];
extern Slot D_800A4C28[];
extern u8 D_800A4988[];
extern s32 D_80073140[];
extern s16 D_800A46A2;
extern s16 D_800A4EF6;
extern s16 D_800A4EFA;
extern u8 D_800A4F19;
extern u8 D_800A4F1E;
extern u16 D_8006AA30[];
extern u16 D_8006AB30[];
extern u16 D_8006AB32[];
extern const u16 D_8007319E[];
extern const u16 D_800731A0[];
extern u16 D_8007321E;
extern u8 D_8006AED8[];
extern s16 func_8003F144(s32, s32, s32, C24 *);
extern s32 func_8003F380(s32, s32);
extern s32 func_80030CA4(u16);
extern void func_8002EFF8(s32, s32);
extern void func_8002F064(s32, s32);
extern void func_800316F8(s32);
extern void func_80033324(s32, s32);
void func_80032A74(Slot54 *arg0, s32 arg1, Rec14 *arg2, s32 flags) {
#define REQ ((Req32A74 *)arg0)
#define REC ((Rec32A74 *)arg2)
C24 sp10;
Chan32A74 *ch;
Voice32A74 *vo;
A12 *dd;
B12 *q;
u8 *cp;
const u16 *tA;
const u16 *tB;
u8 *vB;
s32 i2;
u16 v;
u32 vv;
s32 idx;
s32 w;
s32 n;
s32 flag;
s32 h;
s32 i;
s32 b;
s32 sub;
u8 pan;
u32 vol;
u32 mp;
u32 qq;
s32 tb;
u32 d;
register s32 base __asm__("$2");
u16 t16;
s32 t8;
s32 t32;
tA = D_8007319E;
tB = D_800731A0;
vB = (u8 *)D_800A4C28;
v = REQ->unk02;
i = 7;
cp = (u8 *)arg0 + 7;
do {
cp[0xE] = 0;
i--;
cp--;
} while (i >= 0);
REQ->unk0C = 0;
for (;;) {
b = REC->unk08;
flag = 0;
if ((b & 0x80) == 0) {
dd = &D_80064D44[b];
sub = REC->unk09;
if (dd->unk06 != 0) {
flag = (u32)sub < (u32)dd->unk07;
}
n = D_800C5328[b * 2];
if (n < 0) {
if (flag == 0) {
break;
}
n = D_800C532A[b * 2];
if (n < 0) {
break;
}
if (sub >= D_800A4646[n * 12]) {
break;
}
}
} else {
n = 4;
}
if ((flags & 0x1000) && (flags & 0x7F) < 0x30U) {
v >>= 1;
flags = (flags & 0xFF80) | (0x2F - ((0x2F - (flags & 0x7F)) >> 1));
}
idx = func_80030CA4(v);
if (idx != 0) {
idx--;
vo = (Voice32A74 *)(idx * 0x48 + (s32)vB);
if ((b & 0x80) == 0) {
q = &D_8006A970[n][REC->unk09];
vo->unk1C = q->unk00;
vo->unk18 = q->unk04;
vo->unk3A = q->unk06;
vo->unk3C = q->unk08;
} else {
if (func_8003F144(D_800A46A2, b & 0x7F, REC->unk09, &sp10) != 0) {
goto next;
}
h = func_8003F380(D_800A46A2, sp10.unk16);
if (h < 0) {
goto next;
}
tb = sp10.unk04;
vo->unk1C = h;
vo->unk18 = tb << 8;
vo->unk3A = sp10.unk10;
vo->unk3C = sp10.unk12;
}
w = idx + 0x10;
vo->unk00 = D_80073140[w];
ch = (Chan32A74 *)(D_800A4988 + idx * 0x54);
ch->unk4C = 0;
ch->unk4A = 0x7FFF;
if (REQ->unk00 & 0x80) {
ch->unk4D = 0;
} else {
ch->unk4D = 1;
}
t16 = REC->unk02;
ch->unk04 = 0;
ch->unk08 = v;
ch->unk0A = w;
ch->unk00 = t16;
t8 = REC->unk09;
ch->unk0E = b;
ch->unk36 = flag;
ch->unk10 = n;
ch->unk12 = 0;
ch->unk0C = t8;
t32 = REC->unk10;
ch->unk4F = 0;
ch->unk4E = 0x85;
ch->unk14 = t32;
__asm__ __volatile__("");
vol = REC->unk06;
if (D_800A4F1E != 0) {
vol *= 0x70;
vol >>= 7;
}
if (flags & 0x1000) {
ch->unk48 = flags & 0x7F;
} else {
ch->unk48 = 0x7F;
}
ch->unk34 = vol & 0x7F;
pan = REC->unk07;
ch->unk35 = pan;
if (flags & 0x8000) {
ch->unk24 = REC->unk04 - 0x80;
} else {
ch->unk24 = REC->unk04;
}
ch->unk40 = (s32)func_80033324;
ch->unk44 = arg1;
if ((flags & 0x2000) && pan != 0) {
if ((flags & 0x3000) == 0x3000) {
ch->unk53 = D_8006AED8[(u32)(flags & 0xF00) >> 8];
} else {
ch->unk53 = flags & 0x7F;
}
} else {
ch->unk53 = 0;
}
if (REC->unk02 == 0) {
func_800316F8(ch);
}
{
u16 u18 = vo->unk18;
base = (u18 & 0xFF00) + (s8)u18 * 2;
}
base -= 0x3C00;
d = ch->unk24;
d -= base;
if (d >= 0x5300) {
vo->unk14 = 0x3FFF;
} else {
qq = D_8006AB30[d >> 8];
qq = qq * (0x100 - (d & 0xFF));
vo->unk14 = (qq + D_8006AB32[d >> 8] * (d & 0xFF)) >> 8;
}
vol = D_8006AA30[ch->unk34];
mp = vol * D_800A4EFA;
vol = mp >> 7;
mp = vol * ch->unk48;
vol = mp >> 7;
if (pan != 0) {
if (ch->unk53 != 0) {
pan += ch->unk53;
if (pan >= 0x42) {
pan -= 0x40;
if (pan >= 0x80) {
pan = 0x7F;
}
} else {
pan = 1;
}
}
if (D_800A4F19 != 0) {
vv = (vol * tA[pan]) >> 14;
vo->unk0A = vv;
vv = (vol * tB[0x7F - pan]) >> 14;
vo->unk08 = vv;
} else {
vv = (vol * D_8007321E) >> 14;
vo->unk0A = vv;
vo->unk08 = vv;
}
} else {
vv = vol;
vo->unk08 = vv;
vo->unk0A = vv;
}
ch->unk52 = pan;
vo->unk0C = 0;
vo->unk0E = 0;
vo->unk04 = 0x6009F;
__asm__ __volatile__("");
if (D_800A4EF6 > REC->unk0A) {
func_8002F064(1, vo->unk00);
} else {
func_8002F064(0, vo->unk00);
}
t16 = ch->unk0A;
vo->unk45 = 0;
vo->unk44 = 1;
vo->unk40 = t16;
if (REC->unk02 == 0) {
func_8002EFF8(1, vo->unk00);
}
REQ->unk0C++;
REQ->unk0E[idx] = 1;
}
next:
if (REC->unk0B == 0) {
break;
}
arg2++;
v = REC->unk00;
}
if (REQ->unk0C == 0) {
REQ->unk00 = 0;
} else {
REQ->unk0A = 4;
}
}
#undef REQ
#undef REC
+15
View File
@@ -0,0 +1,15 @@
extern u32 G, H;
extern u8 F;
void g(u32 vol) {
u32 m;
G = vol;
if (F != 0) {
m = vol;
m = (m << 7) - (m << 4);
m >>= 7;
vol = m;
}
H = vol;
m = H * 3;
G = m >> 14;
}
+17
View File
@@ -0,0 +1,17 @@
extern u32 G, H, J, Q;
extern u8 F;
void g(u32 vol) {
u32 m;
G = vol;
if (F != 0) {
m = vol;
J = m;
m >>= 7;
Q = m;
vol = m;
}
H = vol;
m = H;
m++;
G = m;
}
+14
View File
@@ -0,0 +1,14 @@
extern u32 G, H, J, Q;
extern u8 F;
void g(u32 vol) {
u32 m;
G = vol;
if (F != 0) {
m = vol;
J = m;
m >>= 7;
Q = m;
vol = m;
}
H = vol;
}
+18
View File
@@ -0,0 +1,18 @@
extern u32 G, H, J, Q;
extern u8 F;
void g(u32 vol) {
u32 m, k;
G = vol;
if (F != 0) {
m = vol;
J = m;
m >>= 7;
k = m & 0x7F;
vol = m;
Q = k;
}
H = vol;
m = H;
m++;
G = m;
}
+17
View File
@@ -0,0 +1,17 @@
extern u32 G, H, J, Q;
extern u8 F;
void g(u32 vol) {
u32 m, k;
G = vol;
if (F != 0) {
m = vol;
k = m & 3;
m >>= 7;
Q = k + m;
vol = m;
}
H = vol;
m = H;
m++;
G = m;
}
+15
View File
@@ -0,0 +1,15 @@
extern u32 G, H, Q;
extern u8 F;
void g(u32 vol) {
u32 m, k;
G = vol;
if (F != 0) {
m = vol;
k = m & 3;
m >>= 7;
Q = k + m;
vol = m;
}
H = vol;
m = 0;
}
+14
View File
@@ -0,0 +1,14 @@
extern u32 G, H, Q;
extern u8 F;
void g(u32 vol) {
u32 m, k;
G = vol;
if (F != 0) {
m = vol;
k = m & 3;
m >>= 7;
Q = k + m;
vol = m;
}
H = vol;
}
+11
View File
@@ -0,0 +1,11 @@
/* path (b) attempt: head N live later, middle T single-use, composition folds to one insn */
extern u32 G, H, J;
u32 g(u32 r, u32 *p) {
u32 n, t, x;
n = r & 0xFF; /* I1: N = r & 0xFF, live later */
t = n << 24; /* I2: T = N << 24 */
x = t >> 24; /* I3: X = T >> 24 == r & 0xFF == n? (combine: (lshiftrt (ashift (and r 0xFF) 24) 24) -> (and r 0xFF)) */
G = x;
p[0] = n;
return n + 1;
}
+11
View File
@@ -0,0 +1,11 @@
/* path (b) attempt with a sign-extend chain on a register value: N = r & 0xFF (live), T = N << 24, X = T >>a 24 */
extern u32 G, H, J;
s32 g(u32 r, u32 *p) {
u32 n; s32 t, x;
n = r & 0xFF;
t = n << 24;
x = t >> 24;
G = x;
p[0] = n;
return n + 1;
}
+11
View File
@@ -0,0 +1,11 @@
/* path (b) attempt: N = r + 4 (live), T = N * 3, X = T - 12 (== r*3 : one mult) */
extern u32 G, H, J;
u32 g(u32 r, u32 *p) {
u32 n, t, x;
n = r + 4;
t = n * 3;
x = t - 12;
G = x;
p[0] = n;
return n + 1;
}
+15
View File
@@ -0,0 +1,15 @@
extern u32 G, H;
extern u8 F;
void g(u32 vol) {
u32 m;
G = vol;
if (F != 0) {
m = vol;
m *= 0x70;
m >>= 7;
vol = m;
}
H = vol;
m = H * 3;
G = m >> 14;
}
+13
View File
@@ -0,0 +1,13 @@
extern u32 G, H;
extern u8 F;
void g(u32 vol) {
u32 m;
G = vol;
if (F != 0) {
m = vol;
m = (m << 7) - (m << 4);
m >>= 7;
vol = m;
}
H = vol;
}
+15
View File
@@ -0,0 +1,15 @@
extern u32 G, H;
extern u8 F;
void g(u32 vol) {
u32 m;
G = vol;
if (F != 0) {
m = vol;
m += 3;
m >>= 7;
vol = m;
}
H = vol;
m = H * 3;
G = m >> 14;
}
+17
View File
@@ -0,0 +1,17 @@
extern u32 G, H;
extern u8 F;
void g(u32 vol) {
u32 m, a, b;
G = vol;
if (F != 0) {
m = vol;
a = m << 3;
b = m << 4;
m = a - b;
m >>= 7;
vol = m;
}
H = vol;
m = H * 3;
G = m >> 14;
}
+16
View File
@@ -0,0 +1,16 @@
extern u32 G, H;
extern u8 F;
extern u16 K;
void g(u32 vol) {
u32 m;
G = vol;
if (F != 0) {
m = vol;
m = m * K;
m >>= 7;
vol = m;
}
H = vol;
m = H * 3;
G = m >> 14;
}
+16
View File
@@ -0,0 +1,16 @@
extern u32 G, H;
extern u8 F;
extern u16 K;
void g(u32 vol) {
u32 m;
G = vol;
if (F != 0) {
m = vol;
m = m * m;
m >>= 7;
vol = m;
}
H = vol;
m = H * 3;
G = m >> 14;
}
+18
View File
@@ -0,0 +1,18 @@
extern u32 G, H, J;
extern u8 F;
void g(u32 vol) {
u32 m;
G = vol;
if (F != 0) {
m = vol;
m = (m << 3) - m;
m <<= 4;
m >>= 7;
J = m & 0x7F;
vol = m;
}
H = vol;
m = H;
m++;
G = m;
}
+21
View File
@@ -0,0 +1,21 @@
extern u32 G, H, J;
extern u8 F;
extern void ext(u32);
void g(u32 vol) {
u32 m;
G = vol;
ext(vol);
if (F != 0) {
m = vol;
m = (m << 3) - m;
m <<= 4;
m >>= 7;
ext(m);
vol = m;
}
H = vol;
ext(vol);
m = H;
m++;
G = m;
}
+1
View File
@@ -3,3 +3,4 @@
{"ts": "2026-09-05 19:09:22", "addr": "0x80185810", "name": "func_80185810", "reach": null, "klass": "SCHED", "nins": 489, "status": "near", "closeness": 13, "where_stuck": "S83 Fable: 35 -> 13 at exact length; 3 of 4 windows closed (P_TAG bitfield OT link + integer add for the addu operand order; sched1 flush_pending_lists at the 33rd memory op explains the load order -> HI temps; hard-reg destinations are not birthing-boosted -> pins uu $4 / mode $5 / ot16 $6 give the LUID order; shf pin $3). Residual ONE cause idx 363-380: `cl &= 0xFFFF` is an unboosted 2nd set \u2014 the fence after p[7]|= is needed (else its two reads float to the block head, 43/51) yet it blocks sched2 fillers crossing into the tpage/code window. NEXT: a spelling in which cl is single-set (its high half cleared at birth: cl = *(u16*)... or the shift form) so no fence is needed, or the two cl reads consume a fresh single-set copy that combine cannot fold (nonzero_bits defeats a plain andi copy; try a subreg/HI-mode temp)", "best_draft": ".run/P32/t5x/fable/func_80185810.c", "binary": "ov_SC03_105", "source": "P32-T4b S83 Fable agent (471k tokens, 26 min, ~3,400 compiles); report .run/P32/t5x/reports/func_80185810.md", "residual": null, "passes_tried": null}
{"ts": "2026-09-05 23:33:21", "addr": "0x800cf408", "name": "func_800CF408", "reach": null, "klass": "SCHED+REGALLOC", "nins": 178, "status": "near", "closeness": 3, "where_stuck": "S83 Fable: 49 -> 3, zero pins (the \u00a7501-H shape + a dead arg1 kill against cse re-association + the P_TAG OT write + a named mhi born before block 1). Residual idx 10-12 = \u00a7501-H verbatim (the unboosted tag load blocks one cycle behind the tpage sw; the empty cycle eats the highest-LUID floater ori $s5,0x96) COUPLED to the $t2/$t3 qty_compare contest (2389 vs 2400): every cure of one re-opens the other. NEXT: fill the OT-chain lhu gap with an UNBOOSTED `p & m24` as the target does (needs a 2-set a3 that combine does not re-merge \u2014 combine.c:2309 decrements reg_n_sets on the merge \u2014 e.g. a second set through a different width/mode or a volatile-qualified temp), or move the contest margin by one ref elsewhere (an extra ob use in a block that does not touch the tag load)", "best_draft": ".run/P32/t5x/fable/func_800CF408.c", "binary": "md_MAIN_007", "source": "P32-T4b S83 Fable agent (499k tokens, 21 min, 135-variant sweep); report .run/P32/t5x/reports/func_800CF408.md", "residual": null, "passes_tried": null}
{"ts": "2026-09-05 12:06:54", "addr": "0x80032a74", "name": "func_80032A74", "reach": null, "klass": "WALL-CANDIDATE", "nins": 422, "status": "near", "closeness": 1, "where_stuck": "WALL candidate CONFIRMED in the real TU (S83): 422/422, sole residual idx 244 `lh v0,0x18(s1)` vs target `lhu` \u2014 extendhisi2 is a force_not_mem EXPAND (the orphan frame slot is minted only at an lh; \u00a7172 producer 3 caller-save area, reload1.c:1445), so lhu loses the 8 frame bytes; ~200 byte-probes + 100-variant retyping sweep (S79) + permuter_ils 8x150s null (S80). Citation current (\u00a7172, reload1.c:1445). Draft synced to the TU (typedefs stripped via cdecl.strip_provided_typedefs; D_80064D44/D_8006A970/func_8003F144/func_800316F8 spelled as the TU)", "best_draft": ".run/P32/t4/drafts/func_80032A74_tuclean.c", "binary": "main", "source": "P32-T4 S83 re-probe (R40): the S79w Opus draft was a CC1 FAIL in src/800_b_2.c only for PLUMBING (7 typedefs the TU provides via 800_shared.h + 4 decl spellings); stripped/synced copy re-run in the REAL TU: DIFF 1 (idx 244 lh vs lhu) \u2014 the recorded residual, confirmed in TU context", "residual": null, "passes_tried": null}
{"ts": "2026-09-06 11:41:02", "addr": "0x80032a74", "name": "func_80032A74", "reach": null, "klass": "WALL-PROVED", "nins": 422, "status": "near", "closeness": 1, "where_stuck": "T4b HAND PASS S84 (2026-09-06, Fable Max): PROVED at 1 by producer census. Residual = ONE reload-time slot at sp+0x48 (the u16 lhu draft is 422/422 with DIFF 22 = frame rows only; the s16 lh draft is DIFF 1 at idx 244 with the frame exact). The four post-parameter slot producers (reload1.c:658 ghost alter_reg / caller-save.c:249 area / reload1.c:879 invalid-equiv address / reload1.c:3499 spill_stack_slot) each refuted on the bytes: the site loads lhu and the function has no lb and no double load (combine newi2pat ghosts re-derive a narrow load); no register-only insn shares a block with a call (no sched.c:4962 staleness, so no save area without sw/lw); no unallocated single-block equiv pseudo; $t0 holds no pseudo (else $t1 would be the spill reg) and LO mult results retry into GR_REGS. NEW mechanism measured: optimize_reg_copy_2 ghosts (tmp = x; tmp op= c; x = tmp) are minted AFTER regclass -> GR_REGS, allocated, vars=0 (P14). 18 isolated reproducers, 0 draft variants; cookbook \u00a7501-M; notes .run/P32/t4c/func_80032A74/NOTES.md", "best_draft": ".run/P32/t4/drafts/func_80032A74_tuclean.c", "binary": "main", "source": null, "residual": null, "passes_tried": null}
+1 -1
View File
@@ -2,4 +2,4 @@
# 1 still-valid of 2; 1 dropped as stale (banked / linked / blocker-since-fixed).
# An exclude list records what the TOOLING could not do — regenerate it as
# part of every tool fix, or it becomes a list of work you decided not to do.
main:func_80032A74 # WALL: candidate: 422/422, frame/offsets/27 symbols exact, sole residual idx 244 `lh` vs `lhu` — extendhisi2 is a force_not_mem EXPAND (an orphan frame slot is minted only at an lh), the target's 8 extra frame bytes are §172 producer 3 (caller-save area, reload1.c:1445); ~200 byte-probes incl. a 100-variant retyping sweep (S79 Opus) + permuter_ils 8x150s null (S80); closeness 1 | T4 S83: the S79w draft was a CC1 FAIL only for PLUMBING (7 header typedefs + 4 decl spellings); synced copy .run/P32/t4/drafts/func_80032A74_tuclean.c re-run in the real TU DIFF 1 (idx 244 lh vs lhu) — CANDIDATE, unchanged | T4b S83 Fable (402k tokens): the 0x48 slot is a GHOST pseudo (combine.c:2306-2313 stranded temp → alter_reg 8-byte slot); caller-save route REFUTED; the only ghost species from a memory value is the SIGN_EXTEND narrow-load split = lh; closeness 1 STANDS — CANDIDATE→near-PROVED
main:func_80032A74 # WALL: candidate: 422/422, frame/offsets/27 symbols exact, sole residual idx 244 `lh` vs `lhu` — extendhisi2 is a force_not_mem EXPAND (an orphan frame slot is minted only at an lh), the target's 8 extra frame bytes are §172 producer 3 (caller-save area, reload1.c:1445); ~200 byte-probes incl. a 100-variant retyping sweep (S79 Opus) + permuter_ils 8x150s null (S80); closeness 1 | T4 S83: the S79w draft was a CC1 FAIL only for PLUMBING (7 header typedefs + 4 decl spellings); synced copy .run/P32/t4/drafts/func_80032A74_tuclean.c re-run in the real TU DIFF 1 (idx 244 lh vs lhu) — CANDIDATE, unchanged | T4b S83 Fable (402k tokens): the 0x48 slot is a GHOST pseudo (combine.c:2306-2313 stranded temp → alter_reg 8-byte slot); caller-save route REFUTED; the only ghost species from a memory value is the SIGN_EXTEND narrow-load split = lh; closeness 1 STANDS — CANDIDATE→near-PROVED | T4b HAND PASS S84 (2026-09-06): PROVED at 1 — producer census (§501-M): the 0x48 slot can only be a combine-minted ghost (newi2pat split), which re-derives a narrow load (lh/lb); the site is lhu, no lb, no double load; caller-save area needs sched staleness and no register-only insn shares a block with a call; invalid-equiv and spill_stack_slot producers refuted; optimize_reg_copy_2 ghosts are post-regclass and allocatable (measured). Best drafts: s16 = DIFF 1 (frame exact), u16 = 422/422 code with DIFF 22 frame rows
+3 -1
View File
@@ -808,7 +808,7 @@ Every script under `tools/` (plus the two report make-targets), grouped by purpo
| | `tools/recover_integration.py` | **(Phase 24 T6; extended Phase 29 SESSION-16/17, cookbook §65/§66)** The stranded-draft recovery DRIVER — consumes a wave dir (`--draft-dir`, repeatable; the backlog is the wrong source: unreliable `closeness`, overlay-specific drafts), runs the declared `--stages` (`demacroize` T1 / `arity` T2), then gates in TWO passes (**gate all → exact snapshot-restore → re-stage winners only**), so a non-bank never leaves an edit behind. `--run-id` puts all scratch + `verified_out`/`failed_out` under `.run/recover/<id>/` (closes §55b trap 4); bank truth is `banked_from_source()` (the stub is GONE from src), never a gate report; `stub_map` derives from `corpus.stubs` (R33). **Blast-radius tiers are ENFORCED** (`--max-tier`, write-set assertion), and **propagation is itself fleet-tier**: it needs `--max-tier fleet` AND `--r22`, and is refused outright after `demacroize` (those banks are ×1 by construction; `--auto-from` would re-macroize and undo them) — both refusals negative-control-tested, exit 1. `--probe-only` / `--report`. Success path verified end-to-end by the §66 free re-bank test. |
| | `tools/lift_types.py` · `tools/uniquify_type.py` | **(Phase 29 SESSION-14, cookbook §64/§64a)** Fleet-wide type lift into `src/shared/engine_types.h`, and the camp-uniquify that must precede it for VARIANT names (same identifier, different layouts in different TUs — reconciling them merges two layouts and breaks the minority camp). Both are **T2**: dry-run by default, and **R22 clean-fleet is the arbiter, not the per-binary gate**. |
| | `tools/fix_header_decl.py` | **(Phase 29 SESSION-13, cookbook §63 + its UPDATE)** Rewrites a shared `engine_core.h` caller decl to a draft's byte-true sig. **Effectively retired**: its `[SAFE]` verdict inspects one caller and is structurally blind to the other ~137 overlays the shared decl reaches — 3/3 banked per-binary, then R22 **139/140**. Use `demacroize.py` (T1) instead. |
| | `tools/backlog.py` | Near-miss ledger (`.run/backlog.jsonl` + `docs/backlog.md`); **fleet-aware** `load_best` (a 077-matched-but-stuck-local fn surfaces via its overlay record; Phase 23). |
| | `tools/backlog.py` | Near-miss ledger (`.run/backlog.jsonl` + `docs/backlog.md`); **fleet-aware** `load_best` (a 077-matched-but-stuck-local fn surfaces via its overlay record; Phase 23). | **S84 repair (2026-09-06):** `load_best` now keeps the LATEST record at equal closeness (its docstring's contract; the `(cscore, ts) <= …` compare had kept the EARLIEST, so a re-verdict at the same closeness never rendered — func_80032A74's PROVED row).
| | `tools/lora_grind.py` | Mass-run driver: rotate binaries → draft open ≤N-ins stubs with the served model → gate → propagate. `--min-reach N` (Phase 23) targets shared fns (sig-reach oracle == `dedup_propagate`). |
| | `tools/bulk_harvest.py` | **(Phase 23 / T10)** The phase-separated + parallel-gate harvester (throughput rebuild of `lora_grind`): **(A)** bulk-draft K fresh ≤N-ins stubs (GPU) → **(B)** `ProcessPoolExecutor --workers` byte-gate over DISTINCT binaries (`build/<bin>/**` isolated; `run_gate` per-binary lock + per-worker scratch, `propagate=False`/`commit=False`/`compute_fleet=False`) → **(C)** dedupe-once + ONE commit. Round-robin fuel spread; STOP-sentinel; on-demand/bounded. Measured 2026-07-01: gate **0.4s/fn** (8 workers, ~75× the serial gate) ⇒ drafting is the bottleneck (→ vLLM next). Run: `API_BASE=… MODEL=bfm-match-7b-v3 tools/bulk_harvest.py --binary-glob 'ov_SC03_*' --count 80 --workers 8 --measure`. `lora_grind` kept as the serial fallback. |
| | `tools/grinder.py` | Token-free decomp-permuter daemon on the backlog near-misses; **per-binary** (Phase-23 fix). `auto_supervisor.sh`/`auto_stop.sh` keep-alive + STOP sentinel. **Phase-24 T5:** auto-threads the residual `klass`/`where_stuck` into `p16_permute.setup` (§31-directed weights) + **input-changed idle gating** (`draft_sig`) replacing the blind `tried.clear()` churn. **Phase-29 T13A TARGETING:** `candidates()` filters on the MEASURED residual bucket from `autopsy.verdicts()` (1,303 → 78) and takes its directed profile from the measured class, not the logged label (91% of records have none, so the search silently ran on gcc defaults). Measured: of the 972 records this filter admitted, only **75 (7.7%) were permuter-shaped** — ~92% of the daemon's CPU was going where a search-closer provably cannot win, which is why it banked 0 after Phase 21. Degrades to undirected if the corpus is absent and says which mode it is in; `--no-targeting` A/Bs it. |
@@ -1021,6 +1021,8 @@ fills fast). Nothing is leaking — but the host does not get the memory back on
| `tools/agent_drafts_restore.py` (P32 T3, 2026-09-05) | REBUILDS a subagent's final deliverable draft from its transcript by replaying every Write/Edit/heredoc/cp/mv op (`<subagents-dir> --out <dir> [--pattern REGEX]`); one line per agent, NO-DELIVERABLE for agents with none (R32), and a count of unreplayable ops (an edit applied after a shell-side change → the rebuilt file may be STALE: verify with `rtu_match`, prefer an on-disk copy). Written when one agent's `find … -exec mv` swept 11 sibling deliverables out of the shared `.run/P32/t3/opus/` (cookbook §500-E; playbook §S80 addendum-2) | when a wave's deliverable file is missing but its transcript exists — the recovery route, never the primary |
| `tools/agent_reports.py` (P32 T3, 2026-09-05) | writes each Agent-tool subagent's FINAL message — the full prose report (closers, INERT levers, residual mechanism, TU-plumbing warnings) — to `<out>/<fn>__<arm>__<agentId>.md`; `agent_verdicts.py` keeps only the JSON line. T3's 31 reports live in `.run/P32/t3/reports/` (tracked) | after any single-agent drafting pass, before routing NEARs or banking MATCHes; the successor's first step after a dead session |
| `tools/transcript_dump.py` (P32 T3, 2026-09-05) | condenses a session transcript (`~/.claude/projects/<proj>/<session>.jsonl`, MBs of JSON) into readable text — assistant text, every tool call (Bash in full), truncated results, timestamps — so a successor can find the last checkpoint/commit and what was in flight | when a session died without a checkpoint; pair with agent_verdicts / agent_reports / agent_drafts_restore |
| `tools/cc1_dumps.sh` (P30 §172; repaired P32 T4b hand pass, 2026-09-06) | `cc1_dumps.sh <self-contained draft.c> <tag>` → `.run/c294/dumps_<tag>/<tag>.i.{rtl,jump,cse,loop,flow,combine,sched,lreg,greg}` + `<tag>.s` with the pinned cc1 (`-dr -ds -dj -df -dc -dS -dl -dg`); prints the `.frame` line (`vars=` is the frame-residue oracle), the instruction count, reload's `Spilling reg` / `now in` lines and the ghost census. The old standalone-`(use)` grep it printed UNDER-COUNTED (§172 note) and is gone | the first artefact of any residual: attribute it to a pass and a dump line before the first probe (accelerators (11)); edit the cc1 line for `-O0` modules |
| `tools/ghost_census.py` (P32 T4b hand pass, 2026-09-06; cookbook §501-M) | `ghost_census.py <tag>.i.lreg …` — every `Register N used …` header whose pseudo has NO occurrence in the post-sched insn stream (a ghost with stale `reg_n_refs`), with its class: `ST_REGS or none` ⇒ reload gives it an 8-byte `alter_reg` slot (combine-minted, pre-regclass); `GR_REGS …` ⇒ allocatable, no slot (`optimize_reg_copy_2`-minted, post-regclass). A census, exit 0 | frame-residue diagnosis (`vars=` off by 8·k); run by `cc1_dumps.sh` |
| `tools/parallel_gate.py` (S80, task #10) | **banked-but-not-merged is loud**: the worker's raw `git status` + scope ride in each result, a binary whose bank oracle fired but whose files were neither adopted nor refused prints `!! [pgate] BANKED-BUT-NOT-MERGED` and the run exits 2 (S80: `ov_SC03_107` banked 1 / merged 0 / exit 0, the bank died with the worktree — cause unrecovered because the fixed-path results JSON was overwritten); every run now also writes `.run/pgate_runs/<ts>.json`. Recovery = `rtu_match` MATCH → in-tree splice → `make build` (exit code) → commit | every `parallel_gate` run |
| `tools/verbatim_to_stub.py` (S80 usage note) | refuses to GUESS the asm subdir when the TU has no sibling `INCLUDE_ASM` left (a fully-decompiled overlay): pass `--asm-subdir asm/<bin>/nonmatchings/<subseg>` (main: `asm/nonmatchings/<subseg>`); `--apply --gate` re-extracts, rebuilds and compares the SHA. Used in #10 to revert S79 #7's assembly "bank" (cookbook §495) | any verbatim → stub conversion |
| `tools/p16_permute.py` + `tools/permuter_ils.py` (S80, task #9c) | **Pinned seeds are permutable now** (cookbook §493 S80 correction): `hide_asm` carries `asm(`/`__asm(` as well as `__asm__(` into the b64 pragma (keyword must be followed by `(`/`volatile`, so `INCLUDE_ASM("asm/…")` path strings are not statements — R39-controlled over 5,311 drafts); `permuter_ils` RE-HIDES every warm-restart waypoint (the permuter serializes pins raw), asserts `defines_fn` survived, aborts exit-2 on a refusal (R61a), and flushes its log (R55); `defines_fn` accepts K&R-style definitions. Any pre-S80 "permuter plateau" on a pinned seed was 1 cycle + silent no-ops — re-measure before citing it | every `permuter_ils` / `permuter_sweep` run; the S79 NEAR ledger re-run |
+10
View File
@@ -792,3 +792,13 @@ measured nothing. The cracks came from one dump each: the `7f000001` birthing bo
"not desirable" line (§501), the `-dl` quantity priorities (§501-B/E), the `.greg` "Register N in M" (§501-B/E), the `-dR`
hazard walk (§501-G). Accelerator: a residual's first artefact is the dump line that owns it — `tools/cc1_dumps.sh` gives all of
them in one run; a wall verdict without a pass and a dump line is a hypothesis, not a proof (extends (9) and R40).
**(12) Enumerate the artefact's PRODUCERS from the compiler source before probing a single spelling (P32 T4b hand pass,
`func_80032A74`, S84).** Two sessions (S79 ~200 byte-probes + a 100-variant sweep, S83 22 spellings, a 402k-token Fable agent)
had chased the 8 phantom frame bytes as a spelling problem. Reading every stack-slot allocation site in reload1.c /
caller-save.c / combine.c / local-alloc.c gave a four-row producer census, and each row died on a fact already in the bytes
or a dump — the site's `lhu`, the call blocks' contents, the spill register's identity (`lw $t0` ⇒ no pseudo lived in `$t0`),
the mult results' alternate class — without compiling a variant of the draft (18 isolated reproducers, 0 draft variants).
It also found a new ghost producer (`optimize_reg_copy_2`, §501-M) and measured why it cannot slot. Accelerator: a frame
residual gets a producer table first (`tools/cc1_dumps.sh` + `tools/ghost_census.py`), a spelling sweep last — and a
"PROVED" verdict is the table with every row refuted, not a sweep that came back empty (extends (9), (11), R40).
+2 -2
View File
@@ -2,11 +2,11 @@
> Generated by `tools/backlog.py render` from `.run/backlog.jsonl`. These are functions the Phase-21 automation got **close** on but did NOT byte-match. The whole-binary byte-gate is the sole arbiter (G3/P9): **byte-matches bank and are NOT listed here** — only genuine near-misses/blockers are. Ranked by hand-session priority: **reach** (×N propagation leverage) → **closeness** (match_one mismatch count, lower = closer) → **size**. Each row's `best_draft` is the closest C the machine reached — resume from there.
**Open near-misses:** 5 · by status {'near': 4, 'failed': 1} · by class {'WALL-CANDIDATE': 1, 'SCHED+REGALLOC': 1, 'FRAME+SCHED': 1, 'SCHED': 1, None: 1}
**Open near-misses:** 5 · by status {'near': 4, 'failed': 1} · by class {'WALL-PROVED': 1, 'SCHED+REGALLOC': 1, 'FRAME+SCHED': 1, 'SCHED': 1, None: 1}
| # | addr | reach | class | nins | status | closeness | where it stuck | best draft |
|--:|------|------:|-------|-----:|--------|----------:|----------------|------------|
| 1 | func_80032A74 | None | WALL-CANDIDATE | 422 | near | 1 | WALL candidate CONFIRMED in the real TU (S83): 422/422, sole residual idx 244 `lh v0,0x18(s1)` vs target `lhu` — extendhisi2 is a force_not_mem EXPAND (the orphan frame slot is minted only at an lh; §172 producer 3 caller-save area, reload1.c:1445), so lhu loses the 8 frame bytes; ~200 byte-probes + 100-variant retyping sweep (S79) + permuter_ils 8x150s null (S80). Citation current (§172, reload1.c:1445). Draft synced to the TU (typedefs stripped via cdecl.strip_provided_typedefs; D_80064D44/D_8006A970/func_8003F144/func_800316F8 spelled as the TU) | `.run/P32/t4/drafts/func_80032A74_tuclean.c` |
| 1 | func_80032A74 | None | WALL-PROVED | 422 | near | 1 | T4b HAND PASS S84 (2026-09-06, Fable Max): PROVED at 1 by producer census. Residual = ONE reload-time slot at sp+0x48 (the u16 lhu draft is 422/422 with DIFF 22 = frame rows only; the s16 lh draft is DIFF 1 at idx 244 with the frame exact). The four post-parameter slot producers (reload1.c:658 ghost alter_reg / caller-save.c:249 area / reload1.c:879 invalid-equiv address / reload1.c:3499 spill_stack_slot) each refuted on the bytes: the site loads lhu and the function has no lb and no double load (combine newi2pat ghosts re-derive a narrow load); no register-only insn shares a block with a call (no sched.c:4962 staleness, so no save area without sw/lw); no unallocated single-block equiv pseudo; $t0 holds no pseudo (else $t1 would be the spill reg) and LO mult results retry into GR_REGS. NEW mechanism measured: optimize_reg_copy_2 ghosts (tmp = x; tmp op= c; x = tmp) are minted AFTER regclass -> GR_REGS, allocated, vars=0 (P14). 18 isolated reproducers, 0 draft variants; cookbook §501-M; notes .run/P32/t4c/func_80032A74/NOTES.md | `.run/P32/t4/drafts/func_80032A74_tuclean.c` |
| 2 | func_800CF408 | None | SCHED+REGALLOC | 178 | near | 3 | S83 Fable: 49 -> 3, zero pins (the §501-H shape + a dead arg1 kill against cse re-association + the P_TAG OT write + a named mhi born before block 1). Residual idx 10-12 = §501-H verbatim (the unboosted tag load blocks one cycle behind the tpage sw; the empty cycle eats the highest-LUID floater ori $s5,0x96) COUPLED to the $t2/$t3 qty_compare contest (2389 vs 2400): every cure of one re-opens the other. NEXT: fill the OT-chain lhu gap with an UNBOOSTED `p & m24` as the target does (needs a 2-set a3 that combine does not re-merge — combine.c:2309 decrements reg_n_sets on the merge — e.g. a second set through a different width/mode or a volatile-qualified temp), or move the contest margin by one ref elsewhere (an extra ob use in a block that does not touch the tag load) | `.run/P32/t5x/fable/func_800CF408.c` |
| 3 | func_80039308 | None | FRAME+SCHED | 518 | near | 4 | S83 Fable: 17 -> 4 in the real TU. Closed the 11-row alias block (natural spelling; tail via a pointer so the li follows the addu in RTL) and rows 390/391 (p = r + a dead reset). Residual 4 = two causes: rows 49/50 the hoisted constant 2 vs the pinned vbase preheader order (move_movables splices after source preheader code); rows 412/415 a PHANTOM 8-byte frame slot with no traffic at sp+8 between the arg1 HImode spill (sp+0) and cnt (sp+0x10) — `lhu $s7` is reload's spill register; storing arg1 directly reproduces sh $a1/lhu $s7 but not the slot (frame 0x38 vs 0x40). NEXT: induce the phantom slot — a hard-reg spill_stack_slot (reload1.c spill_hard_reg on LO or $s7 during retry_global_alloc), cf. §501-E (pins forbid regs at retry) and the func_80032A74 ghost-pseudo finding (a stranded combine temp -> alter_reg 8-byte slot in regno order) | `.run/P32/t5x/fable/func_80039308.c` |
| 4 | func_80185810 | None | SCHED | 489 | near | 13 | S83 Fable: 35 -> 13 at exact length; 3 of 4 windows closed (P_TAG bitfield OT link + integer add for the addu operand order; sched1 flush_pending_lists at the 33rd memory op explains the load order -> HI temps; hard-reg destinations are not birthing-boosted -> pins uu $4 / mode $5 / ot16 $6 give the LUID order; shf pin $3). Residual ONE cause idx 363-380: `cl &= 0xFFFF` is an unboosted 2nd set — the fence after p[7]/= is needed (else its two reads float to the block head, 43/51) yet it blocks sched2 fillers crossing into the tpage/code window. NEXT: a spelling in which cl is single-set (its high half cleared at birth: cl = *(u16*)... or the shift form) so no fence is needed, or the two cl reads consume a fresh single-set copy that combine cannot fold (nonzero_bits defeats a plain andi copy; try a subreg/HI-mode temp) | `.run/P32/t5x/fable/func_80185810.c` |
+33
View File
@@ -37401,3 +37401,36 @@ nop (179 ins). **Law:** when the same ready-list slot decides both a scheduling
oscillate between two closeness floors (here 3 and 13/18); decouple by adding a filler that changes neither count (an unboosted
temp combine cannot re-merge — a different mode/width, or a `volatile` temp) or by moving the contest margin with a reference in a
block that does not touch the slot. 135-variant sweep floor 3 (×24). Next lever recorded in `docs/backlog.md`.
**§501-M — THE PHANTOM-SLOT PRODUCER CENSUS, and the ghost that cannot slot (P32 T4b hand pass, S84 2026-09-06;
`main:func_80032A74` PROVED at 1).** A never-referenced stack slot that sits AFTER the parameter spill slots (sp+0x48 here;
the params at 0x30/0x38/0x40 are `alter_reg` slots in regno order, so any expand-time local would displace them) can only
come from four reload-time sites, all read out of `tools/reference/gcc-2.7.2`: (1) `reload1.c:658` `alter_reg(i,-1)` for a
GHOST pseudo — `reg_n_refs>0`, no occurrence, no REG_EQUIV, class `ST_REGS or none` because regclass never saw it; (2)
`caller-save.c:249 setup_save_areas` — a 4-byte area per call-used hard reg holding ANY pseudo with `reg_n_calls_crossed>0`,
once `caller_save_needed` is set by the profitability retry (global.c:1085, local-alloc.c:2209; `4*calls < refs`); it leaves no
`sw/lw` only when the count is STALE-HIGH, and the sole staleness route is sched.c:4962 (a multi-block pseudo keeps flow's
count when sched's is 0 — the comment says why) after sched1 moved a register-only def/use across a call inside the call's
own block (combine never crosses a call except with a constant source, combine.c:924; `update_equiv_regs` moves nothing);
(3) `reload1.c:879` — a `reg_equiv_memory_loc` whose address eliminates to a SPILLED pseudo gets a fresh slot, but only an
UNALLOCATED pseudo qualifies and those equivalences are single-block (`update_equiv_regs`), so local-alloc takes them; (4)
`reload1.c:3499 spill_stack_slot` — a pseudo evicted from a spilled hard reg with no retry (local-alloc'd) or a failed
`retry_global_alloc`; `$t0` can hold no pseudo at all (`order_regs_for_reload` lists zero-use call-used regs first, so a
pseudo in `$t0` moves every param reload to `$t1`), and LO-pref mult results carry alternate class `GR_REGS` and re-home.
**The one producer reachable from C at zero code cost is combine's `newi2pat` split** (combine.c:1887 SIGN_EXTEND-of-narrow-
load, combine.c:1963 two-independent-SETs) whose `i2dest` vanishes with `reg_n_refs` kept (the zeroing at combine.c:2306 is
skipped whenever `newi2pat != 0`); both re-derive a NARROW LOAD (`lh`/`lb`, or a duplicate `lhu`) from the chain's memory
head — a register head folds at tree/cse level or has its middle temp re-used by `find_split_point`, so path (b) never runs
(18 reproducers). Hence a phantom slot whose site loads `lhu`, has no `lb` and no double load is unreachable: PROVED.
**The NEW ghost producer, measured, and why it does not slot:** `local-alloc.c optimize_reg_copy_2` on
`tmp = x; <use tmp>; tmp = tmp op c; <use tmp>; x = tmp;` (one block; x dead at the head copy, live after the copy-back;
the head copy survives combine when tmp's FIRST use is not its last and no 3-insn chain passes through it — combine.c:904;
the copy-back survives when tmp has an intervening use that sched keeps above it) rewrites every `tmp` to `x`, leaves two
no-op self-moves, and decrements `reg_n_refs[tmp]` once per insn while flow counted the in-place insn twice → a ghost with
stale refs (P13 refs 5, P14 refs 1). **It is minted AFTER regclass, keeps `GR_REGS` with no conflicts, and global simply
allocates it: vars=0.** Only pre-regclass (combine) ghosts take slots. **Instrument:** `tools/ghost_census.py <tag>.i.lreg`
(headers with no occurrence, class → SLOT / allocatable), now run by `tools/cc1_dumps.sh` in place of its `(use)` grep (which
under-counted, §172 note); `vars=` on the `.frame` line remains the arbiter. **Law:** before probing spellings for a frame
residual, enumerate the artefact's PRODUCERS from the source and refute each on the bytes — the site's load width (`lh` vs
`lhu`), the call blocks' contents, the spill register's identity and the mult results' alternate class each kill one
producer without a compile. Probes and notes: `.run/P32/t4c/func_80032A74/`.
+92 -86
View File
@@ -99,7 +99,7 @@ Scale estimate: 3–5 sessions.
`func_80011380` → `.run/m3/opus/func_80011380.c`; `func_801834A4` → `.run/S71_gate14/ov_SC03_105*/`),
`exclude_audit --write`, `backlog.py render`; a T3 idiom naming a wall's mechanism re-opens that row
(bounded: one attempt, permuter first). Wall table into this file; cookbook §496+; decision log; accelerators.
- [~] **T4b — crack and bank the final 15 (ADDED 2026-09-05 by Drew's directive; supersedes the kill gate: "anything that isn't the original hand-written asm or the PsyQ libs needs to be cracked and banked before we finish")** — **FIRST PASS COMPLETE 2026-09-05 (S83): 11 of 15 BANKED byte-identical, 4 carried to the NEXT session's HAND pass (Drew: no second agent round; hand-crack the remaining ones).** Hand pass first (22 spellings, 0 banks, every blocker refined to a mechanism in `.run/P32/t4b/<fn>/NOTES.md`), then one Fable agent per row (permission, not requirement — Drew), resumed 3-at-a-time through three usage-limit outages. Banked (each: coordinator `rtu_match` in the CURRENT real TU → `gate_main` / `bank.sh` byte-identical → one commit; pins dropped by `exclude_audit --write`; mechanism in cookbook §501–§501-L): `main:func_800391D4` `commit:3956` · `main:func_80039DEC` `commit:3959` · `md_MAIN_009:func_800CD674` `commit:3964` · `ov_SC06_022:func_8017DF28` `commit:3966` · `main:func_80020DA4` `commit:3969` · `ov_SC03_105:func_801834A4` `commit:3972` · `md_MAIN_003:func_800CF3E8` `commit:3976` · `md_MAIN_009:func_800CD92C` `commit:3979` · `ov_SC07_002:func_8017DC80` `commit:3983` · `main:func_80011380` `commit:3990` (the §474 "PROVED" wall) · `md_MAIN_007:func_800CF6D0` `commit:3992`. Ten of the eleven were T4 "walls" or long-standing NEARs. **Carried (4, all exact length, in `docs/backlog.md` with next levers):** `main:func_80032A74` 1 (the last pin; ghost pseudo — near-proved) · `md_MAIN_007:func_800CF408` 3 · `main:func_80039308` 4 · `ov_SC03_105:func_80185810` 13. Close: fleet R22 **218 passed / 0 failed, exits 0/0/0** (23:33–23:36 MDT, `.run/P32/t4b/r22_full.log`); `make report`: instr 13,486,890 / 13,488,497 = 100.0% · distinct 5,814,982 / 5,816,589 = 100.0% (90,982 / 90,984 unique) · fn-count 363,210 / 363,214 = 100.00% · **INCLUDE_ASM 4**; main REAL 787 · LINKED 1,256 · VERBATIM 3 · stubs 2 · 2,089 / 2,091 = 99.90% · `143dbb89…`; census `.run/P32/frontier_t4b_close.json` 4 stubs / 1,607 ins; decision-log P32 S83 T4b (R31); accelerators (10)–(11); tools-health OK (`.run/P32/t4b/tools_health.log`).
- [~] **T4b — crack and bank the final 15 (ADDED 2026-09-05 by Drew's directive; supersedes the kill gate: "anything that isn't the original hand-written asm or the PsyQ libs needs to be cracked and banked before we finish")** — **FIRST PASS COMPLETE 2026-09-05 (S83): 11 of 15 BANKED byte-identical, 4 carried to the NEXT session's HAND pass (Drew: no second agent round; hand-crack the remaining ones).** Hand pass first (22 spellings, 0 banks, every blocker refined to a mechanism in `.run/P32/t4b/<fn>/NOTES.md`), then one Fable agent per row (permission, not requirement — Drew), resumed 3-at-a-time through three usage-limit outages. Banked (each: coordinator `rtu_match` in the CURRENT real TU → `gate_main` / `bank.sh` byte-identical → one commit; pins dropped by `exclude_audit --write`; mechanism in cookbook §501–§501-L): `main:func_800391D4` `commit:3956` · `main:func_80039DEC` `commit:3959` · `md_MAIN_009:func_800CD674` `commit:3964` · `ov_SC06_022:func_8017DF28` `commit:3966` · `main:func_80020DA4` `commit:3969` · `ov_SC03_105:func_801834A4` `commit:3972` · `md_MAIN_003:func_800CF3E8` `commit:3976` · `md_MAIN_009:func_800CD92C` `commit:3979` · `ov_SC07_002:func_8017DC80` `commit:3983` · `main:func_80011380` `commit:3990` (the §474 "PROVED" wall) · `md_MAIN_007:func_800CF6D0` `commit:3992`. Ten of the eleven were T4 "walls" or long-standing NEARs. **HAND PASS S84 (2026-09-06, Fable Max, no agents): row (a) `main:func_80032A74` → PROVED at 1 (§501-M producer census: the 0x48 slot can only be a combine-minted ghost, which needs an `lh`/`lb`; the pin stays with its final verdict; ledger + `tools/ghost_census.py` + `cc1_dumps.sh` repair; rows (b)(c)(d) next).** Carried (4, all exact length, in `docs/backlog.md` with next levers): `main:func_80032A74` 1 (the last pin; ghost pseudo — near-proved) · `md_MAIN_007:func_800CF408` 3 · `main:func_80039308` 4 · `ov_SC03_105:func_80185810` 13. Close: fleet R22 **218 passed / 0 failed, exits 0/0/0** (23:33–23:36 MDT, `.run/P32/t4b/r22_full.log`); `make report`: instr 13,486,890 / 13,488,497 = 100.0% · distinct 5,814,982 / 5,816,589 = 100.0% (90,982 / 90,984 unique) · fn-count 363,210 / 363,214 = 100.00% · **INCLUDE_ASM 4**; main REAL 787 · LINKED 1,256 · VERBATIM 3 · stubs 2 · 2,089 / 2,091 = 99.90% · `143dbb89…`; census `.run/P32/frontier_t4b_close.json` 4 stubs / 1,607 ins; decision-log P32 S83 T4b (R31); accelerators (10)–(11); tools-health OK (`.run/P32/t4b/tools_health.log`).
- [ ] **T5 — PhaseEnd** (Max, Tier 1 — prompt R27): P7 checkbox walk; milestone demo (R22 fleet N/N, tools-health,
`verbatim_check --strict`, final census, wall ledger, parked-5 dispositions via `make audit-disc`,
`make report` all three metrics + main `143dbb89…` with/without SDK dirs, corrected denominators); **WAIT
@@ -135,6 +135,7 @@ cookbook before the next drafting step (R16/R30) · no `Co-Authored-By` trailer
needed for T0–T2b; if T2d needs it: `tools/ghidra_mcp_start.sh` → pause → Drew runs `/mcp` (R29) → G2 ping.
## Log
- 2026-09-06 (S84, session 72d8b4b1, Max, Fable 5.1) — **T4b hand pass, row (a) `main:func_80032A74` CLOSED: PROVED at 1 (verdict, not a bank).** Preflight: tree clean, `verbatim_check --strict` 5==5, `make check-all` 218 passed / 0 failed rc 0 (`.run/P32/t4c/check_all_preflight.log`). Re-verified in the real TU (`rtu_match … --tu src/800_b_2.c --asm-subdir asm/nonmatchings/800_b_2`): the `s16 u18` draft DIFF 1 (idx 244 `lh` vs `lhu`, frame exact); the `u16 u18` TU-clean variant DIFF 22 = the 22 frame rows only (422/422 code). The residual is ONE reload-time slot at sp+0x48. Instead of a spelling sweep, enumerated every post-parameter stack-slot producer from the 2.7.2 source (reload1.c:658 ghost `alter_reg` · caller-save.c:249 area · reload1.c:879 invalid-equiv address · reload1.c:3499 `spill_stack_slot`) and refuted each on the bytes/dumps: combine's `newi2pat` ghosts re-derive a narrow load (`lh`/`lb`) and the site is `lhu` with no `lb`/double load; a save area without `sw/lw` needs sched.c:4962 staleness and no register-only insn shares a block with any of the 7 calls; unallocated single-block equiv pseudos cannot exist; `$t0` holds no pseudo (`order_regs_for_reload`) and LO mult results retry into `GR_REGS`. Found and measured a NEW ghost producer — `local-alloc.c optimize_reg_copy_2` on `tmp = x; tmp op= c; x = tmp;` (P13 refs 5, P14 refs 1) — which cannot slot because it is minted after regclass (class `GR_REGS`, no conflicts → allocated, vars=0). 18 isolated reproducers, 0 draft variants. Deliverables: cookbook **§501-M**, `tools/ghost_census.py` (new) + `tools/cc1_dumps.sh` (repaired: frame line + census, the under-counting `(use)` grep gone) + SETUP rows (R21), accelerators (12), backlog row (WALL-PROVED) + `tools/backlog.py` tie-break repair (kept the EARLIEST record at equal closeness — the S84 row never rendered), `config/wave_exclude.txt` annotated (`exclude_audit --assert-fresh` 1/1), notes `.run/P32/t4c/func_80032A74/NOTES.md`. NEXT = row (b) `md_MAIN_007:func_800CF408` (3).
- 2026-09-05 13:05–23:40 MDT (S83, continued) — **T4b first pass COMPLETE: 11 of 15 banked, 4 carried.** Fifteen Fable agents launched from `.run/P32/t5x/` (BRIEF + packs + the hand-pass NOTES); three usage-limit outages killed every run (resumed each time via SendMessage with context intact; "write deliverables early" saved one crack from a dead run); from the second outage on, resumed 3 at a time (Drew). Verdicts: 11 MATCH (each re-verified by the coordinator in the CURRENT real TU and banked byte-identical — main rows via `gate_main` slates, overlays/modules via `SPLIT=… DRAFT_DIR=.run/P32/t5x/fable bank.sh`), 4 NEAR at exact length (1 / 3 / 4 / 13) with pass-attributed residuals and next levers ledgered. Every crack came from READING a pass dump against the 2.7.2 source; cookbook §501–§501-L (12 new laws: cascades, cross-jump-after-alloc, dying-input vs birthing boost, hard-reg sets count, pins forbid retry regs, CSE-quantity split, three-passes-three-dials, constants as floaters, manufactured orphans, a proved tree wall is not an RTL wall, sched2's /s exemption, coupled dials); decision-log P32 S83 T4b; accelerators (10)–(11). Instrument defects: bank.sh (empty fn list → built the unchanged tree, exit 0; `_jr_` TU split; draft dir) hardened, and two premature "banked" ledger messages corrected in the next commit (memory: write the message from the tool's output). Pins 7 → 1. Fleet R22 218/218 at the close; `make report` fleet 100.0/100.0/100.00, 4 stubs. **Drew: no second agent round; hand-crack the remaining four next session.** NEXT = the hand pass on the four (see the 🛑 block), then T5.
- 2026-09-05 12:05–12:40 MDT (S83, continued) — **T4 DONE.** Preflight: tree clean, verbatim 5==5, R22 218/218 (12:00), `exclude_audit --assert-fresh` 7/7. Every pinned wall's best draft re-run with `rtu_match` in its CURRENT real TU: `func_80011380` DIFF 6 (`--o0`), `func_80020DA4` DIFF 2, `func_8017DF28` DIFF 2, `func_801834A4` DIFF 6 ×3 variants; the three CC1-FAIL rows re-probed after their plumbing was understood — `func_80032A74` (7 TU-provided typedefs + 4 decl spellings → `cdecl.strip_provided_typedefs` + the TU's lines → DIFF 1 in the real TU), `func_80039DEC` and `func_800391D4` (a sandbox TU copy under `.run/P32/t4/tu/` with the declaration edited THERE → DIFF 2 / DIFF 3) — no `src/` edit, no byte-neutral commit spent on rows that will not bank. Leaf `match_one` re-measured all three (1 / 2 (permuter) / 3). **No verdict changed: 1 PROVED (§474) + 6 CANDIDATE**, citations current (§474, §172 reload1.c:1445, loop.md L4 2.7.2:1529, cse_expr.md [A23-2], the K&R promotion laws). Deliverables: the wall table (above), `config/wave_exclude.txt` per-row S83 lines, backlog rows for all 7 (+ the two path-less rows fixed, R62; `docs/backlog.md` 16 open), cookbook §500-I, accelerators (8), decision-log. NEXT = **T5 (Max, Tier 1 — prompt R27, WAIT for gate 2)**.
- 2026-09-05 11:30–12:30 MDT (S83, continued) — **T3 steps 8–9 DONE → T3 CLOSED.** Step 8: `permuter_ils` 8×150 s -j3 on the two REGALLOC-PERM seeds — `func_800CD674` plateau (best waypoint = the same 2-row `$a3↔$t1` pair; ledgered with cost), `func_8001BC6C` reached masked 1; the R63 read showed three mutations, one of them a WRONG-WIDTH `& 0xFF` (lhu→lbu); the two sound ones (idx after color; an early `tag`/`k` birth) = leaf MATCH, re-spelled well-defined as `k = 0; tag = (a1 << 8) | k;` (11 spellings measured), rtu MATCH in src/800.c, **gate_main BANKED 143dbb89** (`commit:3948`) — main 7 → 6 open. gate_main's first rebuild died on a concurrent agent's `src/.masked_diff_probe.<pid>.c` (present at parse, gone at compile) → **Makefile `C_SRCS` find now `-not -name '.*'`** (`commit:3949`, byte-neutral, control on `make -pn`). One bounded Opus second look at `func_800CF3E8` (245k tokens, 29 min): 27 holds; §500-D1's mechanism corrected to `cse.c find_best_addr` (fold_rtx MEM; COST pseudo 0 vs hard reg 1), the alias lever refuted 5/5, a new zero-byte pinned-pointer launder found (79 @ 470, structurally closer) — cookbook **§500-H**, backlog row updated. Step 9: `make report` (fleet instr 100.0% · distinct 99.9% · fn 100.00% · 15 stubs), `make report BINARY=main`, census `.run/P32/frontier_t3_close.json` (15 / 3,758), twin_rescan 0 free, cookbook §500-G/H + index, this file; R22 → see the 🛑 block. **Kill gate:** the session banked 29 and produced 3 new verdicts; the tail's three bounded attempts are spent — T3 closes on the evidence. NEXT = T4.
@@ -149,67 +150,61 @@ cookbook before the next drafting step (R16/R30) · no `Co-Authored-By` trailer
- 2026-09-05 — **T1a DONE — `resident:func_800D128C` BANKED (243 ins, byte-identical 8e17e02f, R22 213/213).** The stored S71 closeness-0 draft was byte-correct all along; the whole task was three instrument defects the resident (the fleet's one `common.h`-only, `--pre`-sandwich binary) exposed in overlay-only assumptions: (1) `jr_isolate_all` dropped a file-local typedef whose name engine_types.h also defines (§496 — fixed: provided types derived from the TU's own includes); (2) `jtbl_carve` regenerated `JTBL_INTERLEAVE` without the `--pre hdr.rodata.o` clause → extract refused → the gate linked a stale script and booked the byte-correct draft as DIFF; `harvest_verify` ignored that extract's rc (§498 — both fixed, R49/R61); (3) `interleave_check` read a `--pre` line as n=0 (false DRIFT; fixed). R38 then found two more stored MATCH bodies for T1b/T1c (see their rows). Effort stayed Max.
- 2026-09-05 — **T0 DONE.** Baseline reads all green (`.run/P32/t0_baseline.log`): `verbatim_check --strict` 5 bodies == 5 rows; `exclude_audit --assert-fresh` 8 entries, 8 WALL, 0 stale; `frontier_classify` → 21 rows = the S80 census exactly; `make tools-health` OK (sigs fresh, corpus(+resident), cdecl, audit-binaries 213/213, report lint+dedup, cookbook-index, split_indicator 213 OK); `make check-all` 213 passed / 0 failed, rc 0. Harness task list #1–#11 built (R28). NEXT = T1a.
## 🛑 SESSION CHECKPOINT — T4b FIRST PASS COMPLETE: 4 rows left, HAND-CRACK NEXT (no agents), then T5 (2026-09-05 23:45 MDT; written by session 491895ad "S83"; SUPERSEDES the 13:40/15:30 blocks)
## 🛑 SESSION CHECKPOINT — T4b HAND PASS IN PROGRESS: row (a) PROVED at 1, rows (b)(c)(d) NEXT, then T5 (2026-09-06 ~12:00 MDT; written by session 72d8b4b1 "S84"; SUPERSEDES the 2026-09-05 23:45 block)
### 0. How to use this block
You are a FRESH SESSION that has read `PROJECT_CONTEXT.md`, `phase-ends/DIGEST.md`, `PhaseEnd_Phase29/30/31.md` and this file, and
nothing else (CLAUDE.md protocol, R64 candidate). Replay this block verbatim into your chat, state phase / done / NEXT / effort,
list the rules from the digest, then WAIT for Drew. **NEXT is the HAND pass on the four remaining rows — Drew (2026-09-05 23:2x MDT):
"we will hand crack the remaining ones next session instead of using agents."** That is non-obvious root-cause work: recommend
**effort: Max** (R7/R27 — prompt and wait for the `/effort`). No Agent tool, no Workflow. After the four (banked or honestly
list the rules from the digest, then WAIT for Drew. **NEXT is the HAND pass on rows (b) → (c) → (d) — Drew (2026-09-05 23:2x MDT):
"we will hand crack the remaining ones next session instead of using agents."** Non-obvious root-cause work: recommend
**effort: Max** (R7/R27 — prompt and wait for the `/effort`). No Agent tool, no Workflow. After the three (banked or honestly
ledgered), T5 = the PhaseEnd (Tier 1, Max, WAIT for gate 2).
### 1. Where we are
**Phase 32 — the last 21 + the parked 5 (short, kill-gated) — extended by Drew's T4b directive: every function that is not original
hand-asm or a PsyQ object must be cracked and banked before the phase closes.** Gate 1 approved 2026-09-05; R44–R63 ratified then;
R64 candidate. Tasks: **T0 ✓ T1a ✓ T1b ✓ T1c ✓ T2a–c ✓ (T2d not needed) T3 ✓ T4 ✓** (commits in the task rows above) · **T4b first
pass ✓ — 11 of 15 banked** (`commit:3956` `commit:3959` `commit:3964` `commit:3966` `commit:3969` `commit:3972` `commit:3976` `commit:3979`
`commit:3983` `commit:3990` `commit:3992`), **4 carried** (below) · **T5 pending.** Harness tasks #1–#13 (#13 = T4b in_progress).
Fleet **218 binaries**. **Last fleet R22: `make clean && make extract-all && make check-all` → 217+main extracted, 218 passed /
0 failed, exits 0/0/0 at 23:36 MDT** (`.run/P32/t4b/r22_full.log`) — AFTER every bank of the session. `make report` at the close:
**instr 13,486,890 / 13,488,497 = 100.0% · distinct 5,814,982 / 5,816,589 = 100.0% (90,982 / 90,984 unique fns) · fn-count
363,210 / 363,214 = 100.00% · INCLUDE_ASM 4**; main REAL 787 · LINKED 1,256 · VERBATIM 3 · stubs 2 · byte-identical 2,089 /
2,091 = 99.90% · sha `143dbb89…`. `verbatim_check --strict` 5 == 5. `config/wave_exclude.txt`: **1 entry** (`main:func_80032A74`),
`exclude_audit --assert-fresh` OK. Census `.run/P32/frontier_t4b_close.json`: **4 stubs / 1,607 ins**. `make tools-health` → **OK** at the close (`.run/P32/t4b/tools_health.log`: sigs fresh; corpus(+resident) + cdecl + binaries +
report(lint+dedup) + cookbook-index green). Non-ghidra
tree clean at HEAD; the `ghidra/` churn is R23 restart-noise — never stage it. Claude commits, Drew pushes (R6; ~100 unpushed).
100% C after today: resident, md_SC03_053/054/056, md_MAIN_003, md_MAIN_009, ov_SC06_022, ov_SC07_002.
R64 candidate. Tasks: **T0 ✓ T1a ✓ T1b ✓ T1c ✓ T2a–c ✓ (T2d not needed) T3 ✓ T4 ✓ T4b first pass ✓ (11 of 15 banked)** ·
**T4b hand pass IN PROGRESS: row (a) `main:func_80032A74` CLOSED as PROVED at 1 (S84, a verdict — nothing banked this session);
rows (b)(c)(d) open** · **T5 pending.** Harness tasks #1–#13 (#13 = T4b in_progress).
Fleet **218 binaries**. **No `src/`, `config/` (except `wave_exclude.txt` annotation) or carve state changed in S84.** Last fleet R22:
`make clean && make extract-all && make check-all` → 217+main extracted, **218 passed / 0 failed, exits 0/0/0 at 2026-09-05 23:36 MDT**
(`.run/P32/t4b/r22_full.log`); S84 preflight `make check-all` → **218 passed / 0 failed, EXIT=0** (`.run/P32/t4c/check_all_preflight.log`).
`make report` (S83 close, still current): **instr 13,486,890 / 13,488,497 = 100.0% · distinct 5,814,982 / 5,816,589 = 100.0% (90,982 /
90,984 unique fns) · fn-count 363,210 / 363,214 = 100.00% · INCLUDE_ASM 4**; main REAL 787 · LINKED 1,256 · VERBATIM 3 · stubs 2 ·
byte-identical 2,089 / 2,091 = 99.90% · sha `143dbb89…`. `verbatim_check --strict` 5 == 5 (S84). `config/wave_exclude.txt`: **1 entry**
(`main:func_80032A74`, now carrying its S84 PROVED verdict), `exclude_audit --assert-fresh` 1/1 OK (S84). Census
`.run/P32/frontier_t4b_close.json`: **4 stubs / 1,607 ins**. `make tools-health` → OK at the S83 close (`.run/P32/t4b/tools_health.log`);
`cookbook_index.py --check` OK (S84). Non-ghidra tree clean at HEAD after the S84 commit; the `ghidra/` churn is R23 restart-noise —
never stage it. Claude commits, Drew pushes (R6; ~100 unpushed). 100% C: resident, md_SC03_053/054/056, md_MAIN_003, md_MAIN_009,
ov_SC06_022, ov_SC07_002.
### 2. What S83 did (session 491895ad, 2026-09-05 10:10 → 23:45 MDT)
T3 steps 0–9 (28 banks; §500-F/G/H), T4 (the walls' ledger; §500-I), then **T4b**: the hand pass (22 spellings, 0 banks, blockers
named in `.run/P32/t4b/<fn>/NOTES.md`), 15 Fable agents (one per row, ~2 h budget, brief `.run/P32/t5x/BRIEF.md`, packs, the NOTES),
three usage-limit outages (every agent resumed via SendMessage with its context; from the second outage on, 3 at a time — Drew),
**11 MATCH banked + 4 NEAR ledgered**. The T4b cracks are cookbook **§501–§501-L**; the doctrine is in `docs/decision-log.md` (P32
S83 T4b) and `docs/accelerators.md` (10)–(11): every crack came from reading a pass dump (`-dS` ready lists, `.loop` desirability,
`-dl` priorities, `.greg` dispositions, `-dR` hazard walks) against `tools/reference/gcc-2.7.2/` BEFORE the first probe; six of the
seven T4 "wall" citations named the wrong pass. Instrument fixes: `.run/P32/t3s3/bank.sh` (refuses an empty fn list; `SPLIT=` for
`_jr_` TUs; `DRAFT_DIR=`; verbatim check = instruction-bearing asm only; propagates a failed commit); Makefile `C_SRCS` dotfile
guard; `jtbl_carve --probe` names §303 for `md_*`. Two premature "banked" ledger messages (`commit:3962`, `commit:3963`) were corrected
in `commit:3964`'s message. **Nothing under `config/` changed except `wave_exclude.txt` (pins 7 → 1) and the T4b plumbing in
`src/800_c.c` (`commit:3953`, byte-neutral).**
### 2. What S84 did (session 72d8b4b1, 2026-09-06, Max, Fable 5.1, no agents)
Row (a) only. Re-verified both best drafts in the real TU; then, instead of a spelling sweep, read every post-parameter stack-slot
producer out of `tools/reference/gcc-2.7.2` and refuted each on the bytes/dumps — the producer census is cookbook **§501-M** and
`.run/P32/t4c/func_80032A74/NOTES.md`. Found a NEW ghost producer (`local-alloc.c optimize_reg_copy_2`) and measured why it cannot
slot (post-regclass → allocatable). Tooling: **`tools/ghost_census.py`** (new; ghosts with stale refs and their class), **`tools/cc1_dumps.sh`
repaired** (prints the `.frame` line + ins count + spill lines + the census; its `(use)` grep under-counted), **`tools/backlog.py`
tie-break repaired** (kept the EARLIEST record at equal closeness — the S84 PROVED row never rendered), SETUP rows (R21), accelerators
(12), backlog row `WALL-PROVED`, the pin annotated. 18 isolated reproducers (`.run/P32/t4c/func_80032A74/p1..p18.c`), 0 draft
variants. **Verdict for (a): PROVED at 1** — the honest final disposition the plan allows for this row (P9; nothing redefined).
### 3. THE FOUR REMAINING ROWS — the hand-crack briefs (everything a fresh session needs; all at EXACT length, all rtu-clean)
Verify any draft with `rtu_match` in the REAL TU (main: `--tu src/<sub>.c`); bank via `gate_main` (main) or
`SPLIT=<tu-basename> DRAFT_DIR=<dir> .run/P32/t3s3/bank.sh <binary> <tu> <asmdir> <arm> "<title>" <fn>` (it refuses on red). RTL
dumps: `tools/cc1_dumps.sh <self-contained draft> <tag>` → `.run/c294/dumps_<tag>/<tag>.i.{rtl,cse,loop,combine,lreg,greg,sched,...}`
(the draft must carry its own typedefs/externs; add `-O0` by editing the script's cc1 line for boot). Each row's Fable report
### 3. THE THREE REMAINING ROWS — the hand-crack briefs (all at EXACT length, all rtu-clean; unchanged from the 23:45 block)
Verify any draft with `rtu_match` in the REAL TU (main: `--tu src/<sub>.c --asm-subdir asm/nonmatchings/<sub>`; overlays/modules:
`--split <tu-basename> --source <binary>`); bank via `gate_main` (main) or `SPLIT=<tu-basename> DRAFT_DIR=<dir> .run/P32/t3s3/bank.sh
<binary> <tu> <asmdir> <arm> "<title>" <fn>` (it refuses on red). RTL dumps: `tools/cc1_dumps.sh <self-contained draft> <tag>` →
`.run/c294/dumps_<tag>/<tag>.i.{rtl,jump,cse,loop,flow,combine,sched,lreg,greg}` + the `.frame`/ins/spill/ghost summary (the draft
must carry its own typedefs/externs; add `-O0` by editing the script's cc1 line for boot). Each row's Fable report
(`.run/P32/t5x/reports/<fn>.md`) carries the dump citations and the measured-inert list — READ IT FIRST, do not repeat its sweeps.
**Do the producer census BEFORE any spelling probe (§501-M, accelerators (12)): name the pass and the dump line that owns the residual.**
**(a) `main:func_80032A74` — 422 ins, closeness 1, the LAST pin.** TU `src/800_b_2.c`, sub `800_b_2`, asm `asm/nonmatchings/800_b_2/`.
Best drafts: `.run/P32/t4/drafts/func_80032A74_tuclean.c` (TU-clean, rtu DIFF 1: idx 244 `lh` vs target `lhu`) and the code-exact
`lhu` respelling `.run/P32/t4b/func_80032A74/lhu.c` (422/422 identical EXCEPT the frame: vars 56 vs 64 → 22 offset rows); the
self-contained ancestor `.run/S79w/opus/func_80032A74.c` for dumps. **Mechanism (Fable, 402k tokens, `.run/P32/t5x/reports/
func_80032A74.md`):** the target's never-referenced 8-byte slot at 0x48 is a GHOST pseudo — a stranded middle temp of a 3-insn
combine whose refs-zeroing is skipped when `newi2pat != 0` (combine.c:2306–2313); zero occurrences → regclass "ST_REGS or none" →
reload1.c:658 `alter_reg` 8-byte slot in regno order = right after the three param slots (reproducer `ghost1`: vars=8, no code).
The S83 caller-save-area hypothesis is REFUTED (order_regs_for_reload picks zero-use regs first; an area needs `caller_save_needed`
and saves at every live call). The only ghost species from a memory value is the SIGN_EXTEND narrow-load split (combine.c:1887–
1930) whose signature IS `lh`; a jump-target second promotion is folded by cse follow-jumps; a fall-through one needs a register
sign_extend MIPS lacks; the generic two-SETs split (combine.c:1963–2020) has no candidate (cse pre-folds constant offsets). **Next
lever:** a ghost from a NON-memory value — a 3-insn combine over a register-only expression whose middle temp is stranded
(`newi2pat != 0` path) — e.g. a shift/mask/add chain on an already-loaded value whose intermediate has a second use in a LATER
block; enumerate the target's `and/sll/addu` triples on register operands and test each for the `(use)` orphan in `.combine`
(count vars= in the `.frame` line). If none, the row is PROVED at 1 (write the proof: "no C spelling mints an orphan without an lh").
**(a) `main:func_80032A74` — CLOSED: PROVED at 1 (S84).** Best drafts: `.run/P32/t4/drafts/func_80032A74_tuclean.c` (`s16 u18`, rtu
DIFF 1 = idx 244 `lh` vs `lhu`, frame exact) and `.run/P32/t4c/func_80032A74/lhu_tuclean.c` (`u16 u18`, 422/422 code, DIFF 22 = the
frame rows: vars 56 vs 64). Proof: the residual is one reload-time slot at sp+0x48; its only zero-code producer is a combine-minted
ghost (`newi2pat` split, combine.c:1887/1963 → reload1.c:658), which re-derives a narrow signed load; the site is `lhu`, the function
has no `lb` and no double load, all nine `lh` are single-use. Caller-save area (caller-save.c:249) needs sched.c:4962 staleness and no
register-only insn shares a block with any call; reload1.c:879 needs an unallocated single-block equiv pseudo; `spill_stack_slot`
needs a pseudo in `$t0` (impossible: `lw $t0` param reloads) or a failed LO retry (mult results have alternate `GR_REGS`).
`optimize_reg_copy_2` ghosts are post-regclass and allocatable (P14 vars=0). Do NOT reopen without a new producer.
**(b) `md_MAIN_007:func_800CF408` — 178 ins, closeness 3, zero pins.** TU `src/md_MAIN_007/md_MAIN_007.c`, sub `md_MAIN_007`, asm
`asm/md_MAIN_007/nonmatchings/md_MAIN_007/`. Draft `.run/P32/t5x/fable/func_800CF408.c`; report `.run/P32/t5x/reports/func_800CF408.md`;
@@ -228,11 +223,12 @@ asm `asm/nonmatchings/800_c/`. Draft `.run/P32/t5x/fable/func_80039308.c`; repor
two causes:** rows 49/50 — the hoisted constant `2` vs the pinned `vbase` preheader order (move_movables splices after source
preheader code; unpinning/moving vbase → 515/495; a named `cst2` → 520/286); rows 412/415 — `lhu $s7` is reload's SPILL REGISTER:
storing `arg1` directly spills the HImode parameter and reproduces `sh $a1 / lhu $s7` exactly, but the target frame is [arg1 spill
@0][8-byte slot with NO traffic @8][cnt @0x10] and the phantom slot could not be induced (frame 0x38 vs 0x40). **Next lever:** the
phantom slot is a hard-reg `spill_stack_slot` (reload1.c `spill_hard_reg` on LO or `$s7` during `retry_global_alloc`) or a ghost
pseudo (as in (a)) — read the target's frame layout against `.greg` "Spilling reg N" lines; a pseudo that global parks in LO and
reload respills (as `func_80020DA4`'s m13 did) mints the 8 bytes; cf. §501-E. Corrected law from this row: at an equal-priority
load/store tie sched2 issues the STORE via `potential_hazard`, not LUID (sched.c:2616–2680).
@0][8-byte slot with NO traffic @8][cnt @0x10] and the phantom slot could not be induced (frame 0x38 vs 0x40). **Next lever:** run the
§501-M producer census on THIS frame first — the phantom slot sits BETWEEN the arg1 spill (regno-lowest) and `cnt`, i.e. it is an
`alter_reg` slot of a pseudo numbered between them (or a `spill_stack_slot`): read the `.greg` "Spilling reg N" / "now on stack" lines
and the `.lreg` headers (`tools/ghost_census.py`) before any spelling; the S83 hypotheses (a hard-reg `spill_stack_slot` on LO or `$s7`
during `retry_global_alloc`, or a ghost as in (a)) are now testable with the census; cf. §501-E. Corrected law from this row: at an
equal-priority load/store tie sched2 issues the STORE via `potential_hazard`, not LUID (sched.c:2616–2680).
**(d) `ov_SC03_105:func_80185810` — 489 ins, closeness 13.** TU `src/ov_SC03_105/ov_SC03_105_jr_80181C84.c`, sub
`ov_SC03_105_jr_80181C84`, asm `asm/ov_SC03_105/nonmatchings/ov_SC03_105_jr_80181C84/`. Draft `.run/P32/t5x/fable/func_80185810.c`;
@@ -250,39 +246,47 @@ temp defeats `nonzero_bits`). 3,360-variant region-2 sweep best 14 — do not re
### 4. NEXT — in order
0. **Preflight:** `git status --short | grep -v ghidra/` (empty) · `verbatim_check --strict` (5 == 5) · `make check-all` → 218/218 (R56 baseline). Prompt `/effort max` (R27) and WAIT.
1. **The hand pass on (a)–(d), one row at a time, closest first (a → b → c → d):** read the report, reproduce the residual with
`rtu_match`, dump the RTL (`cc1_dumps.sh`), attribute the residual to a PASS and a dump line before the first probe (§501,
accelerators (11)), then the row's next lever; ≤ ~10 probes per row before writing the verdict. A MATCH → bank + commit + pin
drop (`exclude_audit --write config/wave_exclude.txt config/wave_exclude.txt`) + `backlog.py render` + cookbook §501-M…; a
plateau → `backlog.py log` with closeness, class, best draft, cost; for (a) at 1, a written PROOF that names the pass is an
acceptable final verdict (P9 — never redefine).
1. **The hand pass on (b) → (c) → (d), one row at a time:** read the report, reproduce the residual with `rtu_match`, dump the RTL
(`cc1_dumps.sh`), run the producer census where the residual is a frame/slot (§501-M), attribute the residual to a PASS and a dump
line before the first probe (§501, accelerators (11)/(12)), then the row's next lever; ≤ ~10 draft probes per row before writing the
verdict. A MATCH → bank + commit + `backlog.py render` + cookbook §501-N…; a plateau → `backlog.py log` (closeness, class, best draft,
cost) — write "banked" only from the tool's printed success line; a PROVED verdict names the pass and the refuted producers (P9).
2. **T4b close:** `make clean && make extract-all && make check-all` (quote 218/218) · `make report` + `make report BINARY=main` ·
`frontier_classify --json .run/P32/frontier_t4b_final.json` · refresh this block · commit · harness #13 done.
3. **T5 (Max, Tier 1 — prompt R27; WAIT for gate 2):** P7 walk (T0–T4b) → milestone demo (R22 218/218 · tools-health · verbatim 5==5 ·
the final census · the wall ledger — now ONE pin or none · `make audit-disc` UNCLAIMED 0 of 220 · `make report` ×3 metrics + main
the final census · the wall ledger — ONE pin, PROVED · `make audit-disc` UNCLAIMED 0 of 220 · `make report` ×3 metrics + main
`143dbb89…` WITH and WITHOUT the SDK object dirs · denominators) → WAIT → `PhaseEnd_Phase32.md` (Build Log · Deviations incl.
the T4b directive and the 4-row remainder stated plainly · Commit Message · Rules Added — R64 to RATIFY + the S83 candidates
the T4b directive and the remainder stated plainly · Commit Message · Rules Added — R64 to RATIFY + the S83/S84 candidates
below · Changelog v1.30.0 → v1.31.0 · Roadmap delta: P33 = verify + public flip · Plain-English Recap · believed/failed/sooner ·
🛑) → DIGEST.md §2/§3 (step 3b) → `git mv phase-ends/CURRENT_PHASE.md phase-ends/logs/Phase32.md` (R19) → R23 → leave both
uncommitted for Drew (R6) → "PhaseEnd file created. Commit the file and start a new Claude Code session for the next phase." →
HARD STOP (P8). **Rule candidates from S83 for the PhaseEnd table:** (i) a wall verdict must be PASS-attributed with a dump line
HARD STOP (P8). **Rule candidates for the PhaseEnd table:** (i) a wall verdict must be PASS-attributed with a dump line
(R40 → "exonerate the instrument, then name the pass"); (ii) write "banked" only from the tool's printed success line; (iii) agents
write deliverables early (a dead run's draft banked); (iv) a helper must refuse an empty work list (R43 restated).
write deliverables early (a dead run's draft banked); (iv) a helper must refuse an empty work list (R43 restated); **(v) S84: a
residual gets a PRODUCER CENSUS from the compiler source before a spelling sweep, and "PROVED" means every producer refuted on the
bytes (accelerators (12)); (vi) S84: a ledger's tie-break is part of the instrument — a re-verdict that cannot surface is a silent
skip (backlog.py kept the earliest record at equal closeness; R43/R61 family).**
### 5. Files, tools, exact invocations, gotchas
- **`.run/P32/t5x/`** (tracked: BRIEF.md, targets.json, `fable/func_*.c` (15 drafts), `reports/*.md` (15), `verdicts.jsonl` (16 rows),
resume_queue.txt; untracked: packs/, work/, rtu/). **`.run/P32/t4b/`** (tracked: `<fn>/NOTES.md|HYPOTHESIS.md`, `<fn>/*.c`
variants, `*.log`; gate slates under `gate/` are ignored — recreate). **`.run/P32/t3s3/`**: `bank.sh` (env `SPLIT=`, `DRAFT_DIR=`),
`splice.py`, logs. **`.run/c294/dumps_*`**: cc1 dump dirs (regenerable).
- **Transcripts of the 15 Fable agents:** `~/.claude/projects/-home-musashi-bfm-decomp/491895ad-3c84-4037-b04f-bf7e5ee16a0c/subagents/
agent-<id>.jsonl`; `tools/agent_verdicts.py <tasks>/a*.output` rebuilds verdicts (the S83 Haiku agents are in the same dir — filter by
`fn` against `.run/P32/t5x/targets.json`); `tools/agent_reports.py` saves full reports.
- **Gotchas that bit today:** `gate_main --assert-baseline --allow-dirty` RESTORES the working tree's TUs before building (its GREEN
measured the committed tree — use an in-tree `make extract BINARY=main && make build BINARY=main` for an uncommitted TU edit) ·
`make extract BINARY=main` rewrites main's `asm/` — never while something reads those `.s` files · a bank helper called with no
function name built the unchanged tree and exited 0 (fixed, R43) · backticks inside a double-quoted `--where` argument are
command substitution (log rows were mangled twice; use single quotes) · `.run/P32/**` allowlists are per-subdir — an ignored
path silently breaks a `git add … &&` chain · an agent's masked_diff probe in `src/` is build input (Makefile guard `commit:3949`).
- **`.run/P32/t4c/`** (S84, tracked: `dump.sh` (private dump + census, writes `dumps_<tag>/` under t4c), `check_all_preflight.log`,
`func_80032A74/{NOTES.md,lhu_tuclean.c,p1..p18.c}`; ignored: `dumps_*/`, `rtu/`). **`.run/P32/t5x/`** (tracked: BRIEF.md, targets.json,
`fable/func_*.c` (15), `reports/*.md` (15), `verdicts.jsonl`; untracked: packs/, work/, rtu/). **`.run/P32/t4b/`** (tracked:
`<fn>/NOTES.md|HYPOTHESIS.md`, `<fn>/*.c`, `*.log`). **`.run/P32/t3s3/`**: `bank.sh` (env `SPLIT=`, `DRAFT_DIR=`), `splice.py`, logs.
**`.run/c294/dumps_*`**: `cc1_dumps.sh` output (regenerable).
- **`tools/ghost_census.py <tag>.i.lreg`** — headers with no occurrence in the post-sched stream: `ST_REGS or none` ⇒ an 8-byte slot
(combine-minted); `GR_REGS …` ⇒ allocatable, no slot. `tools/cc1_dumps.sh` runs it and prints `vars=` (the arbiter).
- **Gotchas that bit in S84:** `rtu_match` on a MAIN function needs `--asm-subdir asm/nonmatchings/<sub>` (its default is the overlay
layout: "No such file … asm/ov_SC01_077/nonmatchings/…") · `.run/P32/t4b/func_80032A74/lhu.c` carries the TU-provided typedefs
(`Rec14`, `Slot54` …) and FAILS cc1 in the real TU — use the `*_tuclean.c` drafts (`cdecl.strip_provided_typedefs`) · a mechanism probe
must keep the temp's LAST mention after the variable's (cse `make_regs_eqv` canonicalizes the shorter-lived reg away) and sched can
sink a protecting store below a copy · `backlog.py` ties are now latest-wins (repaired) · the cookbook index does not index the bold
`§501-x` sub-entries (only `## §` headings) — `--check` stays green after appending one.
- **Gotchas from S83 (still live):** `gate_main --assert-baseline --allow-dirty` RESTORES the working tree's TUs before building (use an
in-tree `make extract BINARY=main && make build BINARY=main` for an uncommitted TU edit) · `make extract BINARY=main` rewrites main's
`asm/` — never while something reads those `.s` files · a bank helper called with no function name built the unchanged tree and
exited 0 (fixed, R43) · backticks inside a double-quoted `--where` argument are command substitution (use single quotes) ·
`.run/P32/**` allowlists are per-subdir — an ignored path silently breaks a `git add … &&` chain · an agent's masked_diff probe in
`src/` is build input (Makefile guard `commit:3949`).
### 6. Environment
Fleet 218; shas: main `143dbb89f34491258bbc27810d0a12ec8b43a8dd` · md_MAIN_007 `2ff702b605ab5cfc18474c464c4c07e5f8ffd48c` · ov_SC03_105
@@ -294,13 +298,15 @@ Drew pushes.
Every bank commits before the next command that can touch `src/` (R42) · count banks from the SOURCE and quote the fleet's green count
at every close (R58) · verify a build from its exit code (R53) · a masked/permuter score is not a closeness until its diff is read
(R63) · an `__asm__` body that reproduces instructions is a verbatim, not a bank (R62; zero-byte fences/launders are dials) · re-verify in
the CURRENT TU before splicing (§500-F) · attribute a residual to a PASS before sweeping levers (§501, accelerators (11)) · write
"banked" only from the tool's printed success line · rules check every 4 tasks (P6) · harvest into the cookbook before the next
drafting step (R16/R30) · no `Co-Authored-By` (R5) · never stage `ghidra/` (R23).
the CURRENT TU before splicing (§500-F) · attribute a residual to a PASS before sweeping levers (§501, accelerators (11)) · census the
PRODUCERS of a frame residual before any spelling (§501-M, accelerators (12)) · write "banked" only from the tool's printed success
line · rules check every 4 tasks (P6) · harvest into the cookbook before the next drafting step (R16/R30) · no `Co-Authored-By` (R5) ·
never stage `ghidra/` (R23).
**Plain-English recap (R18).** Today the crack pass banked twenty-eight functions, the walls got their written verdicts, and then the
owner moved the finish line: nothing may remain but the original hand-written assembly and Sony's library. A hand pass named every
blocker; fifteen deep agents then took one function each, survived three quota outages, and banked eleven of them — ten of which
had been declared unmatchable, one with a written proof — each by reading the compiler's own pass dumps rather than by guessing.
Every rebuild of all 218 files is still identical, and by instruction count the game is at 100.0%. Four functions remain, each within
one to thirteen instructions with its cause written down; the next session cracks them by hand, then the phase closes.
**Plain-English recap (R18).** This session took the first of the four leftover functions — the one that was a single instruction
away — and, instead of trying more rewordings, listed every way the 1995 compiler can leave an unused slot in a function's stack
frame and struck each off against the bytes. The only way that could produce this slot at zero cost needs a signed 16-bit load, and
the original uses an unsigned one, so the function is now recorded as proved-unreachable by re-spelling, with the compiler pass named.
Along the way a genuinely new compiler quirk was found and measured (a temporary copied in and out of a variable leaves a ghost
register behind), a small measuring tool was written, an old one repaired, and a ledger bug fixed that had been hiding updated
verdicts. Three functions remain, each with its cause written down; nothing in the game's rebuilt code changed.
+6 -2
View File
@@ -205,8 +205,12 @@ def load_best():
cur = best.get((key, subkey))
c = r.get("closeness")
cscore = c if isinstance(c, int) else 10 ** 9
if cur is None or (cscore, r.get("ts", "")) <= (cur[0], cur[1]):
best[(key, subkey)] = (cscore, r.get("ts", ""), r)
# Lower closeness wins; at EQUAL closeness the LATEST record wins (the docstring's contract).
# The old `(cscore, ts) <= (cur…)` kept the EARLIEST at a tie, so a re-verdict at the same
# closeness (S84: func_80032A74 CANDIDATE -> PROVED, both 1) never reached render (R43/R61).
ts = r.get("ts", "")
if cur is None or cscore < cur[0] or (cscore == cur[0] and ts >= cur[1]):
best[(key, subkey)] = (cscore, ts, r)
# fold each addr's unknown-nins record into its body when that body is unambiguous
sized = collections.defaultdict(list)
+8 -16
View File
@@ -1,7 +1,10 @@
#!/bin/bash
# usage: dump.sh <draft.c> <tag> -> .run/c294/<tag>.{s,greg,lreg,combine} + orphan-slot report
# usage: cc1_dumps.sh <draft.c> <tag> -> .run/c294/dumps_<tag>/<tag>.i.{rtl,jump,cse,loop,flow,combine,sched,lreg,greg} + <tag>.s
# gcc-2.7.2 cc1 writes dump files as <inputname>.<pass> in its CWD; we cd into a private dir so
# the repo root never collects gccdump.* droppings again.
# The draft must be self-contained (its own typedefs/externs); edit the cc1 line for -O0 (boot) modules.
# Prints: the .frame line (vars= is THE frame-residue oracle, §172/§501-M), the instruction count, the reload
# spill lines, and the ghost census (tools/ghost_census.py — the old standalone-`(use)` grep UNDER-COUNTS, §172 note).
cd /home/musashi/bfm-decomp
f="$1"; tag="$2"
d=.run/c294/dumps_$tag; rm -rf $d; mkdir -p $d
@@ -9,18 +12,7 @@ src=$d/$tag.c
if ! grep -q '#include "common.h"' "$f"; then echo '#include "common.h"' > $src; fi
cat "$f" >> $src
mipsel-linux-gnu-cpp -lang-c -Iinclude -undef -Wall -fno-builtin -Dmips -D__GNUC__=2 -D__OPTIMIZE__ -Dpsx -D_PSYQ -D_MIPSEL -D_LANGUAGE_C $src > $d/$tag.i
(cd $d && ../../../tools/bin/gcc-2.7.2-psx/cc1 -quiet -O2 -G0 -mips1 -mcpu=3000 -mgas -msoft-float -fgnu-linker -dr -ds -dj -dc -dl -dg $tag.i -o $tag.s 2>$tag.err)
echo "== $tag: dumps in $d"
grep -c "^(insn" $d/$tag.i.greg 2>/dev/null | sed 's/^/greg insns: /'
# orphan slots: pseudos whose ONLY appearance after reload is inside a (use ...) — count stack slots
python3 - "$d/$tag.i.greg" <<'PY'
import re, sys
t = open(sys.argv[1]).read()
# every "Register N ... in stack slot" style line differs by gcc version; instead pull the
# reload-era equivalences: (reg N) replaced by (mem (plus (reg 29) (const_int X)))
slots = {}
for m in re.finditer(r'\(insn [0-9]+ [0-9]+ [0-9]+[^\n]*\n?[^(]*\(use \(reg[^ ]* ([0-9]+)\)', t):
slots.setdefault(m.group(1), 0)
uses = re.findall(r'\(use \(reg[:A-Z]* ([0-9]+)[ )]', t)
print("USE-referenced pseudos in greg:", sorted(set(uses), key=int))
PY
(cd $d && ../../../tools/bin/gcc-2.7.2-psx/cc1 -quiet -O2 -G0 -mips1 -mcpu=3000 -mgas -msoft-float -fgnu-linker -dr -ds -dj -df -dc -dS -dl -dg $tag.i -o $tag.s 2>$tag.err)
echo "== $tag: $(grep -m1 '\.frame' $d/$tag.s | sed 's/\t/ /g') ins=$(grep -cE '^\s+[a-z]' $d/$tag.s) (dumps in $d)"
grep -n "Spilling reg\|now on stack\|now in" $d/$tag.i.greg | head -8
python3 tools/ghost_census.py "$d/$tag.i.lreg"
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env python3
"""ghost_census.py — the frame-residue oracle for gcc-2.7.2 dumps (P32 T4b hand pass, 2026-09-06; cookbook §501-M).
A GHOST is a pseudo that still carries flow's `reg_n_refs > 0` but has NO occurrence left in the insn stream.
Reload gives every ghost with `reg_renumber < 0` and no REG_EQUIV an 8-byte `alter_reg` slot (reload1.c:658,
align -1 = BIGGEST_ALIGNMENT) — the "never-referenced frame slot" residual (§172, §501-M). Whether a ghost
actually gets the slot depends on WHEN it was minted:
* minted by combine (the `newi2pat` split paths, combine.c:1887 / 1963) — BEFORE regclass, so regclass sees no
occurrence and gives it `ST_REGS or none`: unallocatable → slot. [these are the ones that matter]
* minted by local-alloc's `optimize_reg_copy_2` (`tmp = x; tmp = tmp op c; x = tmp;`) — AFTER regclass, so it
keeps `GR_REGS` and no conflicts: global allocates it → NO slot. (byte-measured, P14 in
.run/P32/t4c/func_80032A74/)
So the census reads the `.lreg` dump (post-sched, pre-alloc: the register headers are flow's counts as updated by
combine/sched/update_equiv_regs) and reports every header whose pseudo does not occur in the insn stream, with its
class — `ST_REGS or none` ⇒ this ghost WILL take a slot; `GR_REGS …` ⇒ it will not. Use `vars=` on the `.frame`
line as the final arbiter (tools/cc1_dumps.sh prints both).
usage: ghost_census.py <tag>.i.lreg [<tag>.i.lreg ...]
exit 0 always (a census, not a gate); prints one line per ghost, nothing when there are none.
"""
import re
import sys
def census(path):
text = open(path, errors="replace").read()
first_insn = text.find("\n(")
headers, body = (text[:first_insn], text[first_insn:]) if first_insn >= 0 else (text, "")
regs = re.findall(r"^Register (\d+) used (\d+) times[^\n]*?;\s*([^\n]*)$", headers, re.M)
occurrences = set(re.findall(r"\(reg[^ ]* (\d+)\)", body))
out = []
for regno, refs, tail in regs:
if regno not in occurrences:
cls = tail.strip().rstrip(".")
slot = "SLOT" if "ST_REGS" in cls or "NO_REGS" in cls else "allocatable, no slot"
out.append((int(regno), int(refs), cls, slot))
return out
def main(argv):
if len(argv) < 2 or argv[1] in ("-h", "--help"):
print(__doc__)
return 0
for path in argv[1:]:
rows = census(path)
tag = path if len(argv) > 2 else ""
for regno, refs, cls, slot in rows:
print(f" GHOST{(' ' + tag) if tag else ''}: pseudo {regno} refs={refs} [{cls}] -> {slot}")
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv))