diff --git a/.run/P32/t3s3/bank.sh b/.run/P32/t3s3/bank.sh index 61e7a262b..b71f80902 100644 --- a/.run/P32/t3s3/bank.sh +++ b/.run/P32/t3s3/bank.sh @@ -9,7 +9,8 @@ want=$(cut -d' ' -f1 config/check.$bin.sha); log=.run/P32/t3s3/bank_${bin}_$(dat DD=${DRAFT_DIR:-.run/P32/t3/$arm} for fn in "${fns[@]}"; do d=$DD/$fn.c; [ -f "$d" ] || { echo "NO DRAFT $d"; exit 1; } n=$(grep -cE '\.ent|\.word|__asm__[^;]*"[^"]*\b[a-z]{2,5}[ \t]+\$' "$d" || true); [ "$n" = 0 ] || { echo "VERBATIM-SUSPECT $fn ($n): an asm carrying an instruction (zero-byte fences/launders are fine)"; exit 1; } - r=$(.venv/bin/python tools/rtu_match.py $fn --split $bin --source $bin --c "$d" --asm-subdir "$asmdir" --work .run/P32/t3s3/verify_bank/$fn 2>&1 | grep -m1 '^MATCH\|^DIFF\|^CC1'); echo "$fn: $r" + SPLIT=${SPLIT:-$bin} + out=$(.venv/bin/python tools/rtu_match.py $fn --split $SPLIT --source $bin --c "$d" --asm-subdir "$asmdir" --work .run/P32/t3s3/verify_bank/$fn 2>&1); r=$(echo "$out" | grep -m1 '^MATCH\|^DIFF\|^CC1'); echo "$fn: $r"; [ -n "$r" ] || echo "$out" | tail -3 case "$r" in MATCH*) ;; *) echo "STOP: $fn not MATCH"; exit 1;; esac; done for fn in "${fns[@]}"; do .venv/bin/python .run/P32/t3s3/splice.py "$tu" "$asmdir" $fn "$DD/$fn.c" || exit 1; done make build BINARY=$bin -j8 > "$log" 2>&1; rc=$?; echo "BUILD_RC=$rc" diff --git a/.run/P32/t5x/fable/func_8017DF28.c b/.run/P32/t5x/fable/func_8017DF28.c new file mode 100644 index 000000000..0a9a2396f --- /dev/null +++ b/.run/P32/t5x/fable/func_8017DF28.c @@ -0,0 +1,107 @@ +/* func_8017DF28 (ov_SC06_022, 119 ins) — MATCH (match_one + rtu_match, S83 Fable, 2026-09-05). + * + * The 2-insn residual of every prior draft (`addiu $s2,$sp,0x10` in the jal's + * delay slot vs the target's `bnez` slot) was NOT a cse / expand_block_move / + * reorg wall. It was sched1's REGISTER-BIRTH priority boost applied to the + * call insn, and the trigger was the number of $v0 SETS in the function: + * + * * sched.c:2469 birthing_insn_p — a SET of a live register with + * reg_n_sets[reg] == 1 is "birthing"; adjust_priority (sched.c:2507) then + * lifts its priority to max_priority (the 0x7f000001 entries in the + * `-dS` ready lists). regclass.c:1791 reg_scan counts reg_n_sets for HARD + * regs too, so a function whose ONLY value-returning call is + * `obj = func_8012C1B8()` has reg_n_sets[$v0] == 1 and that call_insn's + * `(set (reg v0) (call ...))` is birthing. + * * The block-move destination pseudo P = fp+16 (mips.c:2350 copy_addr_to_reg; + * cse later re-uses it for both `&mtx` args — that part of the prior + * analysis was right, and is NOT the lever) is also birthing. sched1 is a + * BACKWARD list scheduler; at T-3 the ready list is {P-def, call}: both at + * max priority → rank_for_schedule falls to the LUID tie-break + * (sched.c:2428) → the LATER insn (the call) is scheduled first = placed + * later → P-def lands ABOVE the jal. reorg's backward search for the jal + * (set/needed built with include_delayed_effects=0) then takes it. + * * With ANY second $v0 set in the function the call's priority is the plain + * 1 (as in the banked same-TU twin func_80180700, which has 3 $v0 sets and + * whose addiu sits in its beqz slot for exactly this reason): P-def wins + * the ready list, is placed right AFTER the call, and reorg's FORWARD search + * for the jal rejects it because mark_set_resources (reorg.c:542) marks + * every call_used reg — $sp INCLUDED (no `!fixed_regs` filter) — as set by + * the call, so an insn reading $sp cannot be pulled up into the call's + * slot. The bnez's backward search takes it instead → target bytes. + * + * The lever is therefore: one more value-returning call. libgte's real + * prototype is `MATRIX *RotMatrixY(long, MATRIX *)`, so calling it through + * its pointer-returning type is the faithful spelling; the TU's `void` + * declaration is kept (cast at the use, §500-F). Any of the six callees + * cast to a value-returning type gives the same 119/119 (all measured). + * + * Measured inert before this (S71/S79, do not repeat): statement placement of + * mptr, initialiser form, $18 pin, §194-K re-tie, §153 launder, §5a fence, + * word-wise / per-field copy, copy below the call, §H diamond. + */ + +#ifndef BFM_ENGINE_TYPES_H /* standalone (match_one) only; inert in the TU */ +typedef struct { s32 w[8]; } Blk20; +#endif + +extern Blk20 D_800AE620; +extern s32 D_801AAD00[]; +extern s32 D_801AAD08[]; +extern void func_8012C1B8(void); +extern void func_8012C218(void *a0); +extern void func_8001C214(s32 a0, s32 a1); +extern void func_8012B2CC(s32 a0); +extern void func_8002D4C8(s32 a0, s32 a1); +extern void RotMatrixY(s32 a0, void *a1); +extern void func_800484EC(s32 a0, s32 a1, s32 a2); + +typedef struct { s16 h0, h1, h2, h3; } Q_8017DF28; /* 8 bytes, align 2 -> lwl/lwr copy */ +typedef struct { s32 x, y, z; } VEC_8017DF28; + +void func_8017DF28(s32 param_1) +{ + Blk20 mtx; /* sp+0x10 */ + VEC_8017DF28 pos; /* sp+0x30 */ + VEC_8017DF28 *mptr; + s32 obj; + + mtx = D_800AE620; + + obj = ((s32 (*)(void))func_8012C1B8)(); + mptr = (VEC_8017DF28 *)&mtx; + if (obj == 0) { + func_8012C218((void *)param_1); + return; + } + + func_8001C214(obj, (s32)D_801AAD08); + + *(Q_8017DF28 *)(obj + 0x08) = *(Q_8017DF28 *)(*(s32 *)(param_1 + 0x20) + 0x08); + *(Q_8017DF28 *)(obj + 0x10) = *(Q_8017DF28 *)(*(s32 *)(param_1 + 0x20) + 0x10); + *(Q_8017DF28 *)(obj + 0x18) = *(Q_8017DF28 *)(*(s32 *)(param_1 + 0x20) + 0x18); + *(s32 *)(obj + 0x04) = *(s32 *)(*(s32 *)(param_1 + 0x20) + 0x04); + *(u16 *)(obj + 0x2C) = *(u16 *)(*(s32 *)(param_1 + 0x20) + 0x2C); + + *(s32 *)(param_1 + 0xCC) = obj; + *(s32 *)(*(s32 *)(param_1 + 0x20) + 0x24) = (s32)D_801AAD00; + *(s32 *)(param_1 + 0x48) = 0xC000; + + *(u16 *)(param_1 + 0xA) = *(u16 *)(param_1 + 0xA) - 0xB0; + *(u16 *)(*(s32 *)(param_1 + 0x20) + 0x12) = *(u16 *)(param_1 + 0x62); + func_8012B2CC(param_1); + + *(u16 *)(param_1 + 0xAE) = 0x2000; + + pos.y = -0xC0000; + pos.x = 0; + pos.z = -0x80000; + + /* libgte RotMatrixY returns MATRIX*: the value-returning call is the + * second $v0 set that keeps sched1 from boosting the func_8012C1B8 call + * (see header). The TU declares it void, so cast at the use. */ + ((void *(*)(s32, void *))RotMatrixY)(*(s16 *)(param_1 + 0x62), mptr); + func_800484EC((s32)mptr, (s32)&pos, param_1 + 0x10); + + func_8002D4C8(0x955, 0); + *(u16 *)(param_1 + 0x2) = *(u16 *)(param_1 + 0x2) + 1; +} diff --git a/.run/P32/t5x/reports/func_8017DF28.md b/.run/P32/t5x/reports/func_8017DF28.md new file mode 100644 index 000000000..ac81fd1c1 --- /dev/null +++ b/.run/P32/t5x/reports/func_8017DF28.md @@ -0,0 +1,101 @@ +# func_8017DF28 (ov_SC06_022, 119 ins) — MATCH — Fable, S83 (2026-09-05) + +**Verdict:** `match_one` MATCH (119/119) · `rtu_match` MATCH in the real TU +(`src/ov_SC06_022/ov_SC06_022_jr_8017BEBC.c`) · every relocation symbol of the target `.s` present in the draft. +Draft: `.run/P32/t5x/fable/func_8017DF28.c`. Scratch: `.run/P32/t5x/work/func_8017DF28/` (RTL dumps `dumps_d0` += prior best, `dumps_v2` = the fix, `dumps_nb` = the banked same-TU twin), `.run/P32/t5x/rtu/func_8017DF28/`. + +## What closed it (one line) +The residual was **sched1's register-birth priority boost on the call insn**, triggered by `reg_n_sets[$v0] == 1` +(the function had exactly ONE value-returning call). Adding a second `$v0` set — calling `RotMatrixY` through its +real libgte pointer-returning type `((void *(*)(s32, void *))RotMatrixY)(...)`, TU declaration untouched — drops the +call to priority 1, the backward list scheduler then places the block-move address pseudo AFTER the call, and reorg +hands it to the `bnez` slot exactly as the target does. + +## The residual, restated from the bytes +``` +idx 25 | MINE addiu s2,sp,16 (jal func_8012C1B8 slot) | TARGET nop +idx 28 | MINE nop (bnez s1 slot) | TARGET addiu $s2,$sp,0x10 +``` + +## Mechanism — read from the dumps and the source, not inferred + +### 1. The prior analysis was RIGHT about the insn's identity and WRONG about the lever +`-dc`: the `addiu $s2,$sp,0x10` is `(insn 11 (set (reg 75) (plus fp 16)))`, minted by `expand_block_move` +(`config/mips/mips.c:2350 dest_reg = copy_addr_to_reg(...)`) for `mtx = D_800AE620;`. cse folds the movstr MEM to +`(plus fp 16)` (find_best_addr prefers the higher-rtx-cost equal-address-cost form) and then substitutes reg 75 into +both later `&mtx` arg loads (`d0.i.cse` insns 144/153: `a1/a0 = reg 75`, REG_EQUAL `(plus fp 16)`). That is all true +and it is **also true in the TARGET's own source**: cse follows the `bnez` as TAKEN because its label is barrier-preceded +and single-use (`cse.c:8118`), so the table — and reg 75 — carries into the body. Nothing in C can stop that reuse +without changing bytes that already match; the §H diamond / §194-K / §153 levers were aimed at the wrong pass. + +### 2. Where the def lands is decided by sched1 (BACKWARD list scheduler), not by cse or reorg +`-dS` ready lists for basic block 0 (top → `bnez`), insn 11 = P-def, 14 = block move, 17 = the call, 19 = `s1 = v0`, 25 = jump: +``` +prior best (d0) fixed (v2) banked twin func_80180700 (nb) +T-2: 11 (7f000001) 19 (7f000001), now 19 11 T-2: 11 (7f000001) 19 (7f000001), now 19 11 T-2: 21 (7f000001) 39 (7f000001), now 39 21 +T-3: 11 (7f000001) 17 (7f000001), now 17 11 <-- T-3: 11 (7f000001) 17 (1), now 11 17 T-3: 21 (7f000001) 37 (1), now 21 37 +T-4: 11 (7f000001) 14 (1), now 11 14 T-4: 17 (1), now 17 T-4: 37 (1), now 37 +result: 14, 11, 17, 19, 25 (P-def ABOVE the call) result: 14, 17, 11, 19, 25 (P-def AFTER) result: ..., 37, 21, 39, 42 (AFTER) +``` +* `0x7f000001` = `max_priority` = the **birth boost**: `sched.c:2507 adjust_priority` → `:2539 if (birthing_insn_p (PATTERN (prev)))` + → `:2544 INSN_PRIORITY (prev) = max`. `sched.c:2469 birthing_insn_p` returns `:2490 (reg_n_sets[i] == 1)` for a SET whose + dest register is live — and it is evaluated for **hard** registers too (the call's `(set (reg:SI 2 v0) (call ...))` inside + the PARALLEL). `regclass.c:1791 reg_scan_mark_refs` counts `reg_n_sets[REGNO (dest)]++` for every REG dest, hard or pseudo. +* d0 has exactly ONE `(set (reg:SI 2 v0) ...)` in `.rtl` (`grep -c` = 1: the `obj = func_8012C1B8()` call; all other callees + are `void` in the TU). So that call is "birthing" and ties the P-def at max priority; `sched.c:2385 rank_for_schedule` breaks + the tie by `:2428 return INSN_LUID (tmp) - INSN_LUID (tmp2)` → the later insn (the call) is scheduled first = emitted + LATER in the backward schedule → the P-def is emitted above it. +* nb (banked, same TU, same shape `m = D_800AE620; self = call(); if (self) {... RotMatrixY(..,&m); func_800484EC(&m,..)}`) + has 3 `$v0` sets (`rand()` ×2 + `func_8012C658`) → its call has priority 1 → P-def wins → placed after the call → + `beq ...; addu $18,$sp,16` in the slot. This is the known-true case that exposed the mechanism. +* Pseudo 75's def is not pinned to either side of the call in sched1: `sched.c:1732` pins a pseudo def only when + `reg_n_calls_crossed[regno] == 0`, and reg 75 crosses calls. + +### 3. Why reorg then produces the TARGET bytes (and why it produced the residual before) +* P-def ABOVE the jal (d0): the jal's **backward** search (`reorg.c:2904-2905`, `include_delayed_effects = 0`) builds + `set`/`needed` from the call's own pattern only → `addiu s2,sp,16` conflicts with nothing → filled into the jal slot. +* P-def right AFTER the jal (v2 / target): the jal's **forward** search (`reorg.c:2999 mark_set_resources (insn,&set,0,1)`, + delayed effects ON) marks as SET every `call_used_regs[i] || global_regs[i]` (`reorg.c:542`) — **`$sp` is call_used + (fixed regs are) and there is NO `!fixed_regs` filter** — so the `addiu` *references* a set resource + (`reorg.c:3058 insn_references_resource_p (trial, &set, 1)`) and is refused. The `bnez`'s backward search then reaches + it (`s1 = v0` is skipped because the branch needs `$s1`) → the bnez slot. The jal keeps its `nop`. 119/119. + +## Levers measured this session (all `match_one`, `--asm-subdir asm/ov_SC06_022/nonmatchings/ov_SC06_022_jr_8017BEBC`) +| variant | change vs the S79 best | result | +|---|---|---| +| v1 | `((s32 (*)(s32, s32))func_8002D4C8)(0x955, 0);` | MATCH 119 | +| **v2** | `((void *(*)(s32, void *))RotMatrixY)(...)` — libgte's real return type; **chosen** | **MATCH 119; rtu MATCH** | +| v3 | `((s32 (*)(s32, s32))func_8001C214)(...)` | MATCH 119 | +| v4 | `((s32 (*)(s32))func_8012B2CC)(param_1);` | MATCH 119 | +| v5 | `((void *(*)(s32, s32, s32))func_800484EC)(...)` (ApplyMatrixLV, also pointer-returning in libgte) | MATCH 119 | +| v6 | `((s32 (*)(void *))func_8012C218)(...)` (the fall-through callee) | MATCH 119 | + +Any second `$v0` set anywhere in the function works; v2 was chosen because it is the spelling closest to the real SDK +prototype (`MATRIX *RotMatrixY(long, MATRIX *)`), keeps the TU's `void` declaration (cast at the use, §500-F), and the +draft already used the same call-through-cast idiom for `func_8012C1B8`. + +Inert (S71/S79, byte-measured, not repeated): mptr placement, initialiser form, `$18` pin, §194-K re-tie, §153 launder, +§5a fence, word-wise/per-field copy, copy below the call, §H diamond, permuter (waypoint was semantically divergent, R14). + +## Cookbook-ready entry (for the coordinator's harvest) +**§NEW — "The lone value-returning call": sched1's birth boost on `$v0` hoists an unrelated address def above the call.** +* **Tell:** exact length; the only diff is an `addiu $sN,$sp,K` (or any single-set pseudo def whose uses are all after a + call) sitting in a `jal`'s delay slot in yours vs the following branch's slot in the target; the draft has EXACTLY ONE + non-void callee (grep the `.rtl` dump: `grep -c "(set (reg:SI 2 v0)"` = 1). +* **Mechanism:** `sched.c:2469/2507` birthing_insn_p/adjust_priority treat the call's `(set v0 (call))` as a register + birth when `reg_n_sets[$v0] == 1` (regclass.c:1791 counts hard regs) → max priority → LUID tie-break (sched.c:2428) + places it after the def in the backward schedule → def above the call → reorg's backward search fills the jal slot. +* **Fix:** give the function a second `$v0` set: call one more callee through a value-returning type + (`((s32 (*)(...))f)(...)`, value discarded — expand_call still emits `(set v0 (call))`), preferring the callee whose real + SDK prototype returns a value (libgte `RotMatrixY`/`ApplyMatrixLV` return pointers). Zero bytes, no asm. +* **Corollary (recognition):** a target that has a single non-void call AND its address def below that call cannot be the + original's declaration set — some other callee returned a value in the original source (K&R implicit `int`, or the + SDK prototype). Conversely the same boost explains "why does my def sit above the call when the twin's sits below". +* **Tooling hook:** `harvest_verify`/`match_one` classifier: when class is SCHEDULE-REORDER with a jal-slot↔branch-slot + swap of a `$sp`-relative addiu, count `(set (reg:SI 2 v0)` in the `-dr` dump; ==1 → suggest this recipe before the permuter. + +## Files +* Draft: `/home/musashi/bfm-decomp/.run/P32/t5x/fable/func_8017DF28.c` +* Dumps: `/home/musashi/bfm-decomp/.run/P32/t5x/work/func_8017DF28/dumps_{d0,v2,nb}/` (`*.i.sched` ready lists, `*.i.cse`, `*.i.sched2`, `*.s`) +* Variants: `/home/musashi/bfm-decomp/.run/P32/t5x/work/func_8017DF28/v_v1.c … v_v6.c`, twin source `nb_80180700.c`, dumper `dump.sh` diff --git a/.run/P32/t5x/verdicts.jsonl b/.run/P32/t5x/verdicts.jsonl index c8fee05d2..19bd81dba 100644 --- a/.run/P32/t5x/verdicts.jsonl +++ b/.run/P32/t5x/verdicts.jsonl @@ -1,3 +1,4 @@ {"fn": "func_800391D4", "binary": "main", "arm": "fable", "status": "MATCH", "closeness": 0, "compiles": true, "draft_path": ".run/P32/t5x/fable/func_800391D4.c", "note": "explicit promotion `a1v = arg1` before `off = 0` (the extend becomes preheader source) + insn_count knife-edge re-padded 7 -> 9 __asm__(\"\") so loop.c:1631 keeps the D_800C6DD0 address un-hoisted (threshold 58 vs insn_count 59); coordinator rtu MATCH 75/75; gate_main BANKED 143dbb89", "session": "491895ad"} {"fn": "func_80039DEC", "binary": "main", "arm": "fable", "status": "MATCH", "closeness": 0, "compiles": true, "draft_path": ".run/P32/t5x/fable/func_80039DEC.c", "note": "natural 3-case switch(a2) with DUPLICATED case tails (cross-jump merges them post-reload) + block-scope u8 *p per case; a2-raw keeps 4 refs (pri 1951) and is allocated first -> $a3 (global.c allocno_compare/find_reg); refutes the argument-position pin theory and the local_reg_n_refs hypothesis; the permuter's 2 was R63-unsound (tmp uninitialised). Coordinator rtu MATCH 74/74; gate_main BANKED 143dbb89", "session": "491895ad"} {"fn": "func_800CD674", "binary": "md_MAIN_009", "arm": "fable", "status": "MATCH", "closeness": 0, "compiles": true, "draft_path": ".run/P32/t5x/fable/func_800CD674.c", "note": "prims 3/4 use different masked-p variables (pm3/pm2, single-death each) + a trailing `__asm__ volatile(\"\" : \"=r\"(pm3))` on pm3. Mechanism: a 2-death pseudo is excluded from local_alloc (local-alloc.c:471) -> global -> $a3; the target's $t1 is combine_regs' hard-reg branch (qty_phys_sugg from $9 dying as an input) which wins only for a pseudo BORN at that `and`; a fresh single-SET pseudo for prim 3 regresses to 31 because sched.c adjust_priority (2511-2545) boosts a birthing SET to max priority and glues the and to the or (the old '+1 pseudo displaces constants' was reg_n_sets/sched1, not regalloc); the asm's second set makes pm3 reg_n_sets=2 (no boost) and 2-death (global, $a3 as before). Coordinator rtu MATCH 174/174; bank.sh d270f695", "session": "491895ad"} +{"fn": "func_8017DF28", "binary": "ov_SC06_022", "arm": "fable", "status": "MATCH", "closeness": 0, "compiles": true, "draft_path": ".run/P32/t5x/fable/func_8017DF28.c", "note": "a second $v0 SET: RotMatrixY called through its real libgte pointer-returning type ((void *(*)(s32, void *))RotMatrixY)(...), TU void decl untouched. Mechanism: with ONE value-returning call reg_n_sets[$v0]==1 (regclass.c:1791) makes the call_insn birthing (sched.c:2469/2507 -> max priority) in sched1's backward list scheduler; tie with the block-move address def (mips.c:2350 copy_addr_to_reg, cse-reused — also in the original) broken by LUID put the addiu ABOVE the jal where reorg's backward search fills the jal slot (reorg.c:2904); with a second $v0 set the call is priority 1, the def lands after the call, reorg's forward search refuses it (mark_set_resources reorg.c:542 marks all call_used regs incl. $sp) and the bnez backward search takes it. Known-true: same-TU twin func_80180700 (3 $v0 sets). Coordinator rtu MATCH 119/119; bank.sh byte-identical", "session": "491895ad"} diff --git a/config/wave_exclude.txt b/config/wave_exclude.txt index b2480152c..eea6e94b3 100644 --- a/config/wave_exclude.txt +++ b/config/wave_exclude.txt @@ -1,9 +1,8 @@ # regenerated by tools/exclude_audit.py from config/wave_exclude.txt -# 5 still-valid of 6; 1 dropped as stale (banked / linked / blocker-since-fixed). +# 4 still-valid of 5; 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. ov_SC03_105:func_801834A4 # WALL: loop.c movable ordering, closeness 6 (S71) | T4 S83: re-probed in the real TU (3 stored variants) DIFF 6 — CANDIDATE, unchanged -ov_SC06_022:func_8017DF28 # WALL: expand_block_move copy_addr_to_reg pseudo cse-reused for both later &mtx args (cse_expr.md [A23-2]/§H) — the addiu $s2,sp,0x10 sits in the jal's delay slot vs the target's bnez slot; closeness 2 on five RTL-verified attempts (S71/S79); permuter_ils 8x150s (S80) reported 1 but its waypoint REPLACED the addiu with `sw zero,48(sp)` — a divergent rewrite, not a closer body: closeness stays 2 | T4 S83: re-probed in the real TU DIFF 2 (addiu/nop slot swap idx 25/28) — CANDIDATE, unchanged; citation [A23-2] current 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 main:func_80020DA4 # WALL: candidate: 100/100, phantom 16-byte frame reproduced (address-taken frame_pad[3]); residual = mflo destination $t0 vs $a2 (REGALLOC-PERM), pinning regresses to 79; 5 attempts 51→20→14→8→2 (S7x/S79) + permuter_ils 8x150s null (S80); closeness 2 | T4 S83: re-probed in the real TU DIFF 2 — CANDIDATE, unchanged main:func_80011380 # WALL: §474 PROVED C-level floor (closeness 6) — fold-const.c:882 split_tree merges MULT(MULT(i,2),2); the two escapes each cost one instruction (stupid.c:497 adjacency / expand_decl use-brackets); §388 -O0 colouring oracle. Pinned S79 #8. | T4 S83: re-probed in the real TU (rtu --o0) DIFF 6 — PROVED (§474), unchanged diff --git a/docs/backlog.md b/docs/backlog.md index 6592faf94..31329730a 100644 --- a/docs/backlog.md +++ b/docs/backlog.md @@ -2,20 +2,19 @@ > 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:** 13 · by status {'near': 12, 'failed': 1} · by class {'WALL-CANDIDATE': 4, 'WALL-PROVED': 1, 'SCHED': 4, 'REGALLOC': 1, 'ALIAS': 1, 'FRAME': 1, None: 1} +**Open near-misses:** 12 · by status {'near': 11, 'failed': 1} · by class {'WALL-CANDIDATE': 3, 'WALL-PROVED': 1, 'SCHED': 4, 'REGALLOC': 1, 'ALIAS': 1, 'FRAME': 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` | | 2 | func_80020DA4 | None | WALL-CANDIDATE | 100 | near | 2 | WALL candidate CONFIRMED (S83 rtu DIFF 2): 100/100, phantom 16-byte frame reproduced (address-taken frame_pad[3]); residual = mflo destination $t0 vs $a2 (REGALLOC-PERM), pinning regresses to 79; 5 attempts 51->20->14->8->2 + permuter_ils null (S80) | `.run/S79w/sonnet/func_80020DA4.c` | -| 3 | func_8017DF28 | None | WALL-CANDIDATE | 119 | near | 2 | WALL candidate CONFIRMED (S83 rtu DIFF 2): expand_block_move copy_addr_to_reg pseudo cse-reused for both later &mtx args (gcc-2.7.2-map cse_expr.md [A23-2]/§H); 119/119; the addiu $s2,sp,0x10 sits in the jal delay slot vs the target bnez slot; five RTL-verified attempts (S71/S79); permuter_ils (S80) "1" was a divergent rewrite (R63). Citation current ([A23-2] present in cse_expr.md) | `.run/S79w/sonnet/func_8017DF28.c` | -| 4 | func_801834A4 | None | WALL-CANDIDATE | 106 | near | 6 | WALL candidate: loop.c movable ordering, closeness 6 (S71); re-probed S83 in the real TU: DIFF 6 for all three stored variants (unchanged) | `.run/S71_gate14/ov_SC03_105-cn-cast-rc-sd/func_801834A4.c` | -| 5 | func_80011380 | None | WALL-PROVED | 192 | near | 6 | §474 PROVED C-level floor (boot -O0): fold-const.c:882 split_tree merges MULT(MULT(i,2),2); the two escapes each cost one instruction (stupid.c:497 adjacency / expand_decl use-brackets); §388 -O0 colouring oracle. Pinned S79 #8; re-probed S83 in the real TU: DIFF 6 (unchanged) | `.run/m3/opus/func_80011380.c` | -| 6 | func_800CD92C | None | SCHED | 247 | near | 15 | map §S7 prologue WEAVE: the {sw,lui,ori} groups for 0xE100008D/8F land after the 9-insn li block instead of before — the §17 pins reproduce the ALLOCATION but the hoist happens in sched2. Same SPRT family as func_800CD674 (§364 mirror levers applied) | `.run/P32/t3/opus/func_800CD92C.c` | -| 7 | func_80039308 | None | REGALLOC | 518 | near | 17 | sched2 + cross-block regalloc: preheader 49/50 swap, un-spellable addu $a2,$a0,$zero (every p=r form cse-propagated), a temp on $t0 vs $s7, and 11 insns of one alias fact (2nd D_80073140[j] load cannot schedule above the D_800C7D20 store from C; /s unlock costs the address allocation, net 20-24). 34->17 via s16 b4 widening copy + dead-local identity sweep (.run/P32/t3/restored/sweep_func_80039308.py) + $2 pin. permuter_ils --klass REGALLOC 2x150s: no gain | `.run/P32/t3/opus/func_80039308.c` | -| 8 | func_800CF3E8 | None | ALIAS | 469 | near | 27 | ONE cause: the pinned-base alias basin (§500-D1) in the p5/p6 tail; blocks 1-2 byte-exact (idx 0-361). 54->27 via blk2 constant birth order + birthing-boost local w60 + §194-A fence relocation. Inert: all 9 pins load-bearing (+5..+1409), asm position x7, h6 hoist 32x2, tag reshape, P_TAG ADDPRIM, array p6 stores, ~92k annealed variants. Untested: an unpinned alias of p6 for the tag load alone (ONE Opus second look allowed) | `.run/P32/t3/opus/func_800CF3E8.c` | -| 9 | func_80185810 | None | SCHED | 489 | near | 35 | [permuter] 4 emission windows (see report .run/P32/t3/reports/func_80185810__opus__*.md); exact length, rtu-clean | `.run/P32/t3/opus/func_80185810.c` | -| 10 | func_8017DC80 | None | FRAME | 346 | near | 46 | the historic -33 LENGTH wall CLOSED (GTE macros must be REAL macros — the TU house block; the splat Handwritten tag is wrong): 346/346, exact 0x70 frame + 9 callee-saved. Residual: reload-slot frame + the la $a0 slot; cse1 unifies OT index and n<4 across func_80010A08(8) (§500-D2 zero-byte asm retire) | `.run/P32/t3/opus/func_8017DC80.c` | -| 11 | func_800CF408 | None | SCHED | 178 | near | 49 | [permuter] 3 hunks: two prologue sched2 slots, an mlo/mhi allocno tie, a 3-insn block-2 head hoist. Two LENGTH-bearing pins found (tp $17 shared by 0xE1000087/97 = the 6th callee-saved; ob $10 fixes the $t1/$t2/$t3 rotation, 56->49). §351 family (func_8001212C -O0 / func_8017DD04 -O2 exemplars) | `.run/P32/t3/opus/func_800CF408.c` | -| 12 | func_800CF6D0 | None | SCHED | 249 | near | 137 | sched1 rank_for_schedule last-insn-CLASS tie (every store priority 2, equal refs; QImode stores grouped, loads floated, HImode after — 5 of 6 blocks) + $t1<->$t3 local-alloc swap of the two masks. 249/249 exact length only with tpage-before-len field order (19 swept). Inert at 137: pins on tpage constants/masks, asm re-ties, volatile/memory fences, /s-denial on any store subset, *0x4000 vs <<14, p++ vs p+0x18, / swap. decomp-permuter 122 was semantically wrong (R63) | `.run/P32/t3/opus/func_800CF6D0.c` | -| 13 | func_80062144 | None | | None | failed | | won't compile standalone (loose-typing / missing decl) | | +| 3 | func_801834A4 | None | WALL-CANDIDATE | 106 | near | 6 | WALL candidate: loop.c movable ordering, closeness 6 (S71); re-probed S83 in the real TU: DIFF 6 for all three stored variants (unchanged) | `.run/S71_gate14/ov_SC03_105-cn-cast-rc-sd/func_801834A4.c` | +| 4 | func_80011380 | None | WALL-PROVED | 192 | near | 6 | §474 PROVED C-level floor (boot -O0): fold-const.c:882 split_tree merges MULT(MULT(i,2),2); the two escapes each cost one instruction (stupid.c:497 adjacency / expand_decl use-brackets); §388 -O0 colouring oracle. Pinned S79 #8; re-probed S83 in the real TU: DIFF 6 (unchanged) | `.run/m3/opus/func_80011380.c` | +| 5 | func_800CD92C | None | SCHED | 247 | near | 15 | map §S7 prologue WEAVE: the {sw,lui,ori} groups for 0xE100008D/8F land after the 9-insn li block instead of before — the §17 pins reproduce the ALLOCATION but the hoist happens in sched2. Same SPRT family as func_800CD674 (§364 mirror levers applied) | `.run/P32/t3/opus/func_800CD92C.c` | +| 6 | func_80039308 | None | REGALLOC | 518 | near | 17 | sched2 + cross-block regalloc: preheader 49/50 swap, un-spellable addu $a2,$a0,$zero (every p=r form cse-propagated), a temp on $t0 vs $s7, and 11 insns of one alias fact (2nd D_80073140[j] load cannot schedule above the D_800C7D20 store from C; /s unlock costs the address allocation, net 20-24). 34->17 via s16 b4 widening copy + dead-local identity sweep (.run/P32/t3/restored/sweep_func_80039308.py) + $2 pin. permuter_ils --klass REGALLOC 2x150s: no gain | `.run/P32/t3/opus/func_80039308.c` | +| 7 | func_800CF3E8 | None | ALIAS | 469 | near | 27 | ONE cause: the pinned-base alias basin (§500-D1) in the p5/p6 tail; blocks 1-2 byte-exact (idx 0-361). 54->27 via blk2 constant birth order + birthing-boost local w60 + §194-A fence relocation. Inert: all 9 pins load-bearing (+5..+1409), asm position x7, h6 hoist 32x2, tag reshape, P_TAG ADDPRIM, array p6 stores, ~92k annealed variants. Untested: an unpinned alias of p6 for the tag load alone (ONE Opus second look allowed) | `.run/P32/t3/opus/func_800CF3E8.c` | +| 8 | func_80185810 | None | SCHED | 489 | near | 35 | [permuter] 4 emission windows (see report .run/P32/t3/reports/func_80185810__opus__*.md); exact length, rtu-clean | `.run/P32/t3/opus/func_80185810.c` | +| 9 | func_8017DC80 | None | FRAME | 346 | near | 46 | the historic -33 LENGTH wall CLOSED (GTE macros must be REAL macros — the TU house block; the splat Handwritten tag is wrong): 346/346, exact 0x70 frame + 9 callee-saved. Residual: reload-slot frame + the la $a0 slot; cse1 unifies OT index and n<4 across func_80010A08(8) (§500-D2 zero-byte asm retire) | `.run/P32/t3/opus/func_8017DC80.c` | +| 10 | func_800CF408 | None | SCHED | 178 | near | 49 | [permuter] 3 hunks: two prologue sched2 slots, an mlo/mhi allocno tie, a 3-insn block-2 head hoist. Two LENGTH-bearing pins found (tp $17 shared by 0xE1000087/97 = the 6th callee-saved; ob $10 fixes the $t1/$t2/$t3 rotation, 56->49). §351 family (func_8001212C -O0 / func_8017DD04 -O2 exemplars) | `.run/P32/t3/opus/func_800CF408.c` | +| 11 | func_800CF6D0 | None | SCHED | 249 | near | 137 | sched1 rank_for_schedule last-insn-CLASS tie (every store priority 2, equal refs; QImode stores grouped, loads floated, HImode after — 5 of 6 blocks) + $t1<->$t3 local-alloc swap of the two masks. 249/249 exact length only with tpage-before-len field order (19 swept). Inert at 137: pins on tpage constants/masks, asm re-ties, volatile/memory fences, /s-denial on any store subset, *0x4000 vs <<14, p++ vs p+0x18, / swap. decomp-permuter 122 was semantically wrong (R63) | `.run/P32/t3/opus/func_800CF6D0.c` | +| 12 | func_80062144 | None | | None | failed | | won't compile standalone (loose-typing / missing decl) | |