From 194cce75701fb1ffb2bb2bcf43445948025881cf Mon Sep 17 00:00:00 2001 From: Drew T <50529377+Druthulu@users.noreply.github.com> Date: Wed, 26 Aug 2026 18:32:51 -0600 Subject: [PATCH] =?UTF-8?q?docs(phase-31):=20T4=20DONE=20=E2=80=94=20routi?= =?UTF-8?q?ng=20rule=20(<=3D50:=20Sonnet+DeepSeek=20parallel,=20Opus=20res?= =?UTF-8?q?idue;=2051-120:=20Sonnet,=20Opus=20escalation;=20>120:=20Opus;?= =?UTF-8?q?=20haiku=20retired;=20M-extend-tell=20->=20wall);=20frontier-s6?= =?UTF-8?q?1=20Addendum=205;=20judge=20artifacts=20(P31=20S62)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .run/t4/claude_banked.json | 45 ++ .run/t4/distill_out.json | 518 +++++++++++++++++++++ .run/t4/judge.json | 216 +++++++++ .run/t4/judge.py | 89 ++++ .run/t4/sample.json | 122 +++++ .run/t4/targets.json | 202 ++++++++ docs/tool-designs/frontier-analysis-s61.md | 9 + phase-ends/CURRENT_PHASE.md | 23 + 8 files changed, 1224 insertions(+) create mode 100644 .run/t4/claude_banked.json create mode 100644 .run/t4/distill_out.json create mode 100644 .run/t4/judge.json create mode 100644 .run/t4/judge.py create mode 100644 .run/t4/sample.json create mode 100644 .run/t4/targets.json diff --git a/.run/t4/claude_banked.json b/.run/t4/claude_banked.json new file mode 100644 index 000000000..e95a017ce --- /dev/null +++ b/.run/t4/claude_banked.json @@ -0,0 +1,45 @@ +{ + "haiku": [ + "func_80180F5C", + "func_8017F590", + "func_8017FF74", + "func_80184FB4", + "func_8017E014", + "func_8017EF90" + ], + "sonnet": [ + "func_8017FB38", + "func_80180F5C", + "func_8017F590", + "func_8017FF74", + "func_801810A4", + "func_8017E014", + "func_8017EF90", + "func_8017E54C", + "func_8017F3F8", + "func_80181894", + "func_800CAF9C", + "func_801804C0", + "func_8017E7D0", + "func_8017F608" + ], + "opus": [ + "func_8017FB38", + "func_80180F5C", + "func_8017F590", + "func_8017FF74", + "func_80184FB4", + "func_801810A4", + "func_8017E014", + "func_8017EF90", + "func_8017E54C", + "func_8017F3F8", + "func_80181894", + "func_800CAF9C", + "func_801804C0", + "func_8017E7D0", + "func_8017F608", + "func_800CB578", + "func_801808E8" + ] +} \ No newline at end of file diff --git a/.run/t4/distill_out.json b/.run/t4/distill_out.json new file mode 100644 index 000000000..d0193e624 --- /dev/null +++ b/.run/t4/distill_out.json @@ -0,0 +1,518 @@ +{ + "extract": [ + { + "fn": "func_8017F590", + "arm": "sonnet", + "title": "Trivial thunk: forward a0/{*(a0+8)}/a0+0x10 to sibling call", + "mechanism": "No compiler law involved \u2014 this is a plain 9-instruction marshalling wrapper. gcc-2.7.2 loads one struct field into $a1, computes a second field's address into $a2 in the jal's delay slot, and calls the callee with $ra saved/restored around it (not a true tail call, just a normal call-then-return in a tiny frame).", + "asm_tell": "addiu $sp,$sp,-0x18 / sw $ra,0x10($sp) / lw $a1,0x8($a0) / jal func_X / (delay slot) addiu $a2,$a0,0x10 / lw $ra,0x10($sp) / addiu $sp,$sp,0x18 / jr $ra / nop \u2014 i.e. a 3-arg forwarding call sandwiched in a minimal saved-$ra frame, with the third argument computed as a stack/struct-offset address in the branch-delay slot.", + "c_shape": "extern void func_8017F5B4(s32 a0, s32 a1, s32 a2);\nvoid func_8017F590(s32 a0) {\n func_8017F5B4(a0, *(s32 *)(a0 + 8), a0 + 0x10);\n}", + "evidence": "Only one match_one call in the whole transcript (step 24), and it went straight to MATCH: {\"status\": \"match\", \"closeness\": 0, \"nins\": 9, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", \"profile\": null, \"bucket\": \"integration\", \"closeness\": 0, \"nins_mine\": 9, \"nins_tgt\": 9, \"sig\": \"MATCH\", \"detail\": {}}}. There was never a nonzero closeness to drop from \u2014 first compile matched. The only wrinkle was a false lead: the pack's \"banked twin\" warm-start context (a 55-instruction gouraud-lighting function, ov_SC02_011:func_80182488 similarity) was actually the body of the callee itself (func_8017F5B4, already matched elsewhere in the same TU at line 6047) \u2014 not the 9-instruction target, which the agent correctly identified by counting target instructions (grep -c '/\\*' = 9) before drafting.", + "cookbook_refs_used": [], + "novel_claim": false, + "trivial": true + }, + { + "fn": "func_8017F590", + "arm": "opus", + "title": "Pack's neighbour/twin/prior-attempt body is a wrong-function decoy", + "mechanism": "Not a compiler codegen effect at all \u2014 closeness was 0 from the very first compile and never moved. The actual obstacle was harness/pack data integrity: the campaign pack bundled a \"warm-start\" previous-attempt body, a \"banked twin\" (func_80182488), and an in-TU \"neighbour\" (func_8017F724) that all belonged to a different function, and the task prompt's own --asm-subdir (ov_SC02_031_jr_8017BEBC) pointed at a path that doesn't exist on disk (real path: ov_SC02_031_jr_8017AE2C). match_one's own --help text warns that an omitted/wrong subdir silently diffs against a resident homonym and returns a confident verdict about the wrong target.", + "asm_tell": "No asm/codegen tell \u2014 the tell is on the metadata side: the bundled \"warm-start\" body has ~12 statements against a 9-instruction target (instruction-count mismatch, \u00a7238's fastest cheap check); the cited neighbour symbol (func_8017F724) does not appear anywhere when the destination TU is grepped; and the --asm-subdir path named in the task prompt does not exist on disk at all.", + "c_shape": "No C idiom reproduces this \u2014 the fix is procedural: grep the real destination TU directly for the true callee's definition and copy its signature verbatim rather than trusting the pack's pointers, e.g. the real callee turned out to be `void func_8017F5B4(s32 arg0, s32 arg1, s32 arg2)` defined later in the same TU (ov_SC02_031_jr_8017AE2C.c:6047), forward-declared since the wrapper precedes it:\n```c\nvoid func_8017F5B4(s32 arg0, s32 arg1, s32 arg2);\nvoid func_8017F590(s32 arg0) {\n func_8017F5B4(arg0, *(s32 *)(arg0 + 8), arg0 + 0x10);\n}\n```", + "evidence": "match_one was called exactly twice and both times returned MATCH at closeness 0 \u2014 there was never a nonzero reading to fix: `{\"status\": \"match\", \"closeness\": 0, \"nins\": 9, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", ...}}` (v1 draft, and again on the final submit-path draft). The only defect caught, and it was caught before ever compiling, was pack contamination: the prompt's --asm-subdir didn't exist on disk, the pack's cited \"neighbour\" (func_8017F724) and \"banked twin\" (func_80182488, ov_SC02_011) corresponded to nothing in the real destination TU, and the bundled \"previous attempt\" body was for a different, larger function entirely. The agent independently verified the sole relocation by hand-decoding the raw jal word `0x0C05FD6D -> 0x8017F5B4` rather than trusting any tool-masked symbol.", + "cookbook_refs_used": [ + "\u00a7238" + ], + "novel_claim": false, + "trivial": true + }, + { + "fn": "func_8017E7D0", + "arm": "sonnet", + "title": "Single lhu-vs-lh residual on a declared-unsigned array read", + "mechanism": "gcc-2.7.2 picks the halfword load opcode (lhu = zero-extend vs lh = sign-extend) from the signedness of the C expression actually performing the read, not merely from the memory location's own declared type. The array here is (correctly) declared `u16 D_80188A2A[]` \u2014 it's read unsigned elsewhere in the same TU \u2014 but this one access is stored straight into an s32 destination and the target wants that value sign-extended, so the source must have re-cast the element to signed at this specific read site, overriding the array's own declared unsignedness for just this one use.", + "asm_tell": "Target: `lh $v0, 0x0($s1)` (opcode 0x84\u2011class, sign-extend) where the naive draft compiled to `lhu $v0,0($s1)` (opcode 0x94\u2011class, zero-extend) \u2014 everything else in the instruction (register, offset, base) identical; match_one's masked diff reduces this to one residual pair and classifies it `klass: WIDTH, sig: \"WIDTH/lhu!=lh\"`.", + "c_shape": "extern u16 D_80188A2A[];\n...\n*(s32 *)(a0 + 0x1C) = (s16)D_80188A2A[0]; /* explicit (s16) cast forces the sign-extending lh\n despite the array's own declared-unsigned u16 type */", + "evidence": "match_one before the fix: `{\"status\": \"near\", \"closeness\": 1, \"nins\": 54, \"residual\": [[12, \"96220000 lhu\\tv0,0(s1)\", \"86220000 lh $v0, 0x0($s1)\"]], \"verdict\": {\"klass\": \"WIDTH\", ..., \"sig\": \"WIDTH/lhu!=lh\", \"detail\": {\"pairs\": [[12, \"lhu\", \"lh\"]], \"kinds\": {\"opcode\": 1}}}}` \u2014 then after the single-line Edit adding `(s16)` to the `D_80188A2A[0]` read: `{\"status\": \"match\", \"closeness\": 0, \"nins\": 54, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", ..., \"sig\": \"MATCH\", \"detail\": {}}}`. Closeness 1 -> 0, the only drop-to-zero moment in the transcript.", + "cookbook_refs_used": [ + "I3 (division by constant -> magic multiply)", + "I4 (runtime division -> divu + break)", + "\u00a7228 (reading-the-divide addenda, incl. \u00a7228.3 break7/break6 signed-divide-by-variable)", + "ADDENDUM to \u00a7172b-4 (plain cast-division already produces the round-toward-zero pattern)", + "\u00a7193-A (banked-twin lookup, from the task pack, not agent-grepped)" + ], + "novel_claim": false, + "trivial": true + }, + { + "fn": "func_8017F3F8", + "arm": "sonnet", + "title": "Bundled prior-attempt body can decompile a different function", + "mechanism": "unknown as a compiler mechanism \u2014 this isn't gcc-2.7.2 codegen behavior at all. The pack's \"previous attempt\" was Ghidra's decompile of a body that calls a completely different callee than the true target's own relocations name, i.e. a stale/mismatched artifact bundled with the card, not a residual worth patching.", + "asm_tell": "The true .s's jal/relocation targets (func_8012C0EC, then a jump-table load of D_80187988, then jal func_8017F2B4) share zero addresses with the callee and globals named in the bundled decoy C body (FUN_8012c1b8, PTR_DAT_801afcbc, DAT_80187088) \u2014 a wholesale identity mismatch across every symbol, not a localized one- or two-instruction diff. This is the failure mode law 1c warns about in reverse: match_one masks jal targets, so a body built around the WRONG callee would still show a clean MATCH shape-wise; the only way to catch it is comparing symbol names against the target .s's own relocation lines by hand, which is exactly what this transcript's closing note did.", + "c_shape": "n/a \u2014 no C idiom reproduces this; the fix is procedural triage, not a code shape: discard the decoy body wholesale (its structure was for a different address entirely) and rebuild straight from the .s, borrowing only the jump-table dispatch shape from two already-banked siblings in the same TU:\n```c\nextern void (*D_TABLE[])(void *);\nD_TABLE[*(u16 *)((s32)a0 + 0x2)](a0);\n```\nand preserving the natural clear-bit / call / set-bit sequence as three separate full re-dereferences of `*(s32*)(*(s32*)(a0+0x20)+4)` (no local caching across the intervening call), which is just normal C \u2014 the compiler must reload after an opaque call regardless.", + "evidence": "Only one match_one invocation exists in the whole transcript, and it hit closeness 0 on the very first try \u2014 there is no \"dropped from N to 0\" arc to report. Exact line: `{\"status\": \"match\", \"closeness\": 0, \"nins\": 38, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", ..., \"closeness\": 0, \"nins_mine\": 38, \"nins_tgt\": 38, \"sig\": \"MATCH\", ...}}`. The draft that produced this was built by discarding the pack's bundled decoy body entirely (agent's own closing text: \"The bundled 'previous attempt' body ... was a different function entirely (wrong callee, wrong offsets) \u2014 discarded per the pack's own warning, not reused\") and instead copying the jump-table dispatch idiom verbatim from two already-banked siblings in the same TU (func_80180B94 at src/ov_SC02_037/ov_SC02_037_jr_8017AE2C.c:7205 and a second at line 8215).", + "cookbook_refs_used": [], + "novel_claim": false, + "trivial": true + }, + { + "fn": "func_801810A4", + "arm": "opus", + "title": "Empty third case flips switch from chain to bisected tree", + "mechanism": "gcc-2.7.2's switch lowering (balance_case_nodes) only bisects the case-dispatch into a binary compare tree once there are more than 2 case nodes; with just two live values (case 0, case 1) it falls back to a straight-line chain of equality branches. Adding a third case label for a value the code can actually produce \u2014 even an empty, no-op one \u2014 pushes the node count over that threshold, so the balancer builds a real slti-based bisection tree instead, which is the shape the target asm has.", + "asm_tell": "Target has an slti-based bisection: `li v0,1 / beq v1,v0,L1 / slti v0,v1,2 / beqz v0,L2 / bnez v1,L2` (39\u219240 ins incl. one more than a chain); the wrong draft instead emitted a linear equality chain `beqz v1,L2 / li v0,1 / beq v1,v0,L1` \u2014 one instruction shorter, flagged by match_one as klass LENGTH-DRIFT, delta -1.", + "c_shape": "switch (code) {\ncase 0: func_80180A20(s0); break;\ncase 1: ((void(*)(void*))func_80180C30)(s0); break;\ncase 2: break; /* empty, but required so the switch has 3 case nodes, not 2 */\n}", + "evidence": "v1 (only `case 0`/`case 1`, no `case 2`): match_one returned {\"status\": \"near\", \"closeness\": 14, \"nins\": 39, ... \"verdict\": {\"klass\": \"LENGTH-DRIFT\", \"detail\": {\"delta\": -1, \"at\": 22}}}. v2 (identical draft plus an empty `case 2: break;`): match_one returned {\"status\": \"match\", \"closeness\": 0, \"nins\": 40, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", ...}} \u2014 closeness 14 -> 0 from that single added case label.", + "cookbook_refs_used": [ + "\u00a7193-G" + ], + "novel_claim": false, + "trivial": false + }, + { + "fn": "func_80184FB4", + "arm": "opus", + "title": "Independent store placed after the call misses its jal delay slot", + "mechanism": "gcc-2.7.2's reorg delay-slot filler picks the jal's single delay-slot instruction from candidates that are already scheduled ahead of the call in RTL order and have no dependency on the call's arguments or return value; it does not reach past the call into later code. So which independent store fills a given call's delay slot is decided purely by that store's position in the SOURCE relative to the call \u2014 write it after the call in C and it schedules after the call in asm too (missing the slot, which gets filled by argument setup or shifts everything by one instruction); write the exact same statement immediately before the call and the scheduler drops it straight into that jal's delay slot.", + "asm_tell": "Target: `jal func_XXXX` whose own delay slot holds an unrelated non-argument instruction (`sh $zero, 0x5C($s0)`) while the real argument-setup move (`addu $a0,$s0,$zero`) sits one slot earlier than in your draft; your draft instead has that argument move sitting where the delay-slot store should be, cascading a positional/opcode mismatch across the whole tail (lui/addiu/move/li/sh all off-by-one) even though every opcode individually \"looks right.\"", + "c_shape": "// closeness 5 (wrong \u2014 store sequenced AFTER the call):\nfunc_8012B14C((s32)a0, (s32)&D_801905CC);\nv0 = 5;\n*(u16 *)((s32)a0 + 0x5C) = 0;\n\n// closeness 0 / MATCH \u2014 sink the independent store to directly BEFORE the call:\n*(u16 *)((s32)a0 + 0x5C) = 0;\nfunc_8012B14C((s32)a0, (s32)&D_801905CC);\nv0 = 5;", + "evidence": "Draft1 (store after the 3rd call) via match_one --json: {\"status\": \"near\", \"closeness\": 5, \"nins\": 29, \"residual\": [[16, \"3c050000 lui a1,0x0\", \"02002021 addu $a0, $s0, $zero\"], [17, \"24a50000 addiu a1,a1,0\", \"3c058019 lui $a1, %hi(D_801905CC)\"], [19, \"02002021 move a0,s0\", \"0c04ac53 jal func_8012B14C\"], [20, \"24020005 li v0,5\", \"a600005c sh $zero, 0x5C($s0)\"], [21, \"a600005c sh zero,92(s0)\", \"24020005 addiu $v0, $zero, 0x5\"]], \"verdict\": {\"klass\": \"ADDRESSING\", \"sig\": \"ADDRESSING/lui!=addu\", ...}}. Draft2 (identical statement moved to sit immediately before that same call) via match_one: \"MATCH (29 ins) func_80184FB4\" / {\"status\": \"match\", \"closeness\": 0, \"nins\": 29, \"residual\": []}. Only change between drafts: hoisting `*(u16 *)((s32)a0 + 0x5C) = 0;` from after `func_8012B14C(...)` to before it.", + "cookbook_refs_used": [], + "novel_claim": false, + "trivial": false + }, + { + "fn": "func_8017EF90", + "arm": "sonnet", + "title": "Seed/twin callees absent from target relocations means wrong function", + "mechanism": "Not a gcc-2.7.2 code-generation law \u2014 a pack/pipeline defect. The card's \"PREVIOUS ATTEMPT\" warm-start body and its \"declarations already decided\" fleet rows (func_8017EFD8, func_80146994, func_80171990) belonged to some other address entirely; none of those symbols occur anywhere in this target's own .s. The transcript only shows the catch and the fix, not why the pack was mismatched, so the root cause of the mismatch itself is unknown.", + "asm_tell": "Before spending a single compile: grep the seed's callee/global symbols against the target .s's own relocation-comment lines. Zero hits for func_8017EFD8 / func_80146994 / func_80171990 anywhere in asm/ov_SC03_124/.../func_8017EF90.s is the tell that the whole card (seed body AND its \"authoritative\" declarations) is for a different function, not a near-miss of this one.", + "c_shape": "Discard the seed entirely and rebuild straight from the .s. The real body turned out to be a throttle-counter + gated-init pair, whose exact idiom/declaration conventions were copied from an already-MATCHED sibling sitting in the *same destination TU* (func_80184E08 for the counter, func_801803CC/func_8017EBEC for the D_800B99DA&3 init block) rather than from the cross-overlay \"banked twin\" the card pointed at:\n```c\nv0 = *(u16 *)(p + 0x84); /* lhu; nop */\nv0 = v0 + 1; /* addiu 1 */\n*(u16 *)(p + 0x84) = v0; /* sh */\nif ((s16)v0 >= 0x10) { /* sll 16; sra 16; slti 0x10; bnez */\n throttled_call(p, 0x6B5, 0);\n *(u16 *)(p + 0x84) = 0; /* reset on trip */\n}\n```", + "evidence": "Only one match_one call appears in the whole transcript, and it is a first-try MATCH \u2014 there is no nonzero-to-zero iteration to trace, because the wrong seed was never even run through match_one (it was ruled out by symbol-grep before any compile). Exact line: `{\"status\": \"match\", \"closeness\": 0, \"nins\": 49, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", \"profile\": null, \"bucket\": \"integration\", \"closeness\": 0, \"nins_mine\": 49, \"nins_tgt\": 49, \"sig\": \"MATCH\", \"detail\": {}}}`. Post-MATCH the agent re-swept the target .s for every func_/D_ symbol (`grep -oE '(func_[0-9A-F]+|D_[0-9A-F]+)'`) and confirmed all nine appear in the target's own relocations, per law 1c.", + "cookbook_refs_used": [], + "novel_claim": false, + "trivial": true + }, + { + "fn": "func_80180F5C", + "arm": "sonnet", + "title": "Warm-start seed was an unrelated overlay-homonym function", + "mechanism": "Not a gcc quirk \u2014 a project naming artifact. Every BFM overlay is an independent link unit loaded at overlapping VRAM, and unmatched functions are auto-named by address; so func_80180F5C in ov_SC02_005 and func_80180F5C in ov_SC02_031 are coincidentally same-named, unrelated bodies. The pack's \"PREVIOUS ATTEMPT ... keep what matches the .s, fix what does not\" framing assumes the seed is a near-miss of the SAME function, which fails here: the seed (single func_8001AAA0 call-through-pointer, field +0x214) shares zero structure with the target (five sequential calls gated by a field+0x28 countdown hitting -1, field+0x15 increment) \u2014 it is the ov_SC02_005 homonym's body, not a broken draft of this target.", + "asm_tell": "No single instruction pattern \u2014 the tell is a structural mismatch between the seed and the target .s: seed's implied instruction/call count and field offsets (+0x214, one conditional call) don't line up with the glabel header's actual instruction count (37, 0x94 bytes) or the target's own lw/sw offsets (+0x28, +0x15) and callee set (func_8002D4C8/func_8001BFD0/func_800D0C48/func_800D1E28) as listed in the target's own relocation lines.", + "c_shape": "Not a codegen law, so no reproducing C snippet. The correct move is procedural: discard the seed and rebuild from (a) the target asm's actual control flow (decrement counter, compare == -1, branch) and (b) the in-TU sibling idiom family sitting right next to the target in the same file \u2014 func_80180F1C / func_80181130 \u2014 which share the exact state-machine skeleton: `s32 func_X(s32 a0) { ...; *(s32*)(a0+0x28) = N; *(u8*)(a0+0x15) += 1; return 0; }`.", + "evidence": "Only one match_one call appears in the whole transcript, and it returned MATCH on the first compile: `{\"status\": \"match\", \"closeness\": 0, \"nins\": 37, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", ... \"closeness\": 0, \"nins_mine\": 37, \"nins_tgt\": 37, \"sig\": \"MATCH\", \"detail\": {}}}`. The agent never ran match_one on the stale seed at all \u2014 it was discarded by inspection (callee set and field offsets didn't match the target's own relocations) before any compile, then the fresh draft (built from the target .s plus the in-TU func_80180F1C/func_80181130 idiom) matched immediately with no failed/near iteration in between.", + "cookbook_refs_used": [ + "law 1 (SYS.md)", + "law 1c / \u00a7174", + "\u00a7193-A (banked-twin lookup)", + "\u00a7194-E (in-TU neighbor lookup)", + "\u00a7195-A (decl_prior authoritative signatures)" + ], + "novel_claim": false, + "trivial": true + }, + { + "fn": "func_8017FB38", + "arm": "opus", + "title": "Move+andi pair collapses to one andi unless arg pinned", + "mechanism": "Unknown at the RTL-pass level \u2014 the transcript shows only the empirical fix, not a traced cause in combine.c/cse.c. Empirically: an unconstrained-pseudo argument built as `reg = v0; reg = reg & 0xFF;` right before a call lets gcc-2.7.2 fold the copy and mask into one `andi` (and typing the intermediate as `u8` loses the mask instruction entirely \u2014 tried and rejected); pinning that argument to a fixed hardware register ($4) plus an empty `__asm__(\"\" : \"=r\" : \"0\")` barrier between the copy and the mask defeats the fold, forcing the copy to materialize as its own `addu`/move before the `andi`, matching the target's two-instruction form.", + "asm_tell": "Target has a redundant-looking `addu $a0,$v0,$zero` (or `move`) immediately followed by `andi $a0,$a0,0xNN` right before a `jal`; a naive draft instead emits just one `andi $a0,$v0,0xNN` (or, with a narrow-typed intermediate, loses the andi altogether) \u2014 shows up as a 1-instruction LENGTH-DRIFT in match_one.", + "c_shape": "register s32 a0r __asm__(\"$4\");\na0r = v0;\n__asm__(\"\" : \"=r\"(a0r) : \"0\"(a0r)); /* barrier: blocks the copy+mask fold */\na0r = a0r & 0xFF;\nfunc_80016450(a0r, 1);", + "evidence": "Path: closeness 35 (d1, no pins) \u2192 10 (d2, $v0/$v1 register pins + frame_pad[6]; residual [36] \"304400ff andi a0,v0,0xff\" vs target \"00402021 addu $a0, $v0, $zero\") \u2192 still 10 with a `u8 t = v0;` intermediate (variant A; residual [36] becomes \"00000000 nop\" vs target \"00402021 addu $a0, $v0, $zero\" \u2014 the andi vanishes entirely, mask lost) \u2192 0 on variant B ($4-pinned a0r + cse barrier between copy and mask): {\"status\": \"match\", \"closeness\": 0, \"nins\": 44, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", ...}}. Final submitted draft reproduces the same MATCH verdict.", + "cookbook_refs_used": [ + "docs/cookbook-index.md line 28 (negative-const/unsigned-local note, cited to confirm lhu-for-HImode-load-feeding-sh; \u00a7194-G and \u00a7241 were grepped for but not the operative fix)" + ], + "novel_claim": true, + "trivial": false + }, + { + "fn": "func_80180F5C", + "arm": "opus", + "title": "Dual -1 loads + bne + delay-slot store = guarded countdown", + "mechanism": "gcc-2.7.2 materialises the comparand -1 into its own register (addiu $vY,$zero,-0x1) rather than comparing the decremented value against zero after an offset, then compares the just-decremented value ($vX) against it with bne; the decremented value's store-back is scheduled into the branch's own delay slot, so it executes on BOTH branch paths regardless of outcome. This is the same \"decrement is unconditional, the test follows\" reading \u00a7252 already established elsewhere in the cookbook, here on a bare in-place s32 field with no temp.", + "asm_tell": "`lw $vX,OFF($sN)` ; `addiu $vY,$zero,-0x1` ; `addiu $vX,$vX,-0x1` ; `bne $vX,$vY,Lskip` ; delay slot `sw $vX,OFF($sN)` \u2014 two independently-materialised -1 registers, a bne (not beqz), and the store living in the branch's OWN delay slot rather than a prior beqz's delay slot.", + "c_shape": "if (--*(s32 *)(a0 + 0x28) == -1) {\n // guarded body \u2014 runs only on the tick the countdown reaches -1\n ...\n}", + "evidence": "This transcript is a one-shot MATCH, not an iterative narrowing: match_one returned closeness 0 on the very first (and only) compile of the draft using `if (--*(s32 *)(a0 + 0x28) == -1) {...}` \u2014 there is no nonzero-to-zero drop to show. Quoted verbatim (identical on both the scratch draft and the final submission): `{\"status\": \"match\", \"closeness\": 0, \"nins\": 37, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", \"profile\": null, \"bucket\": \"integration\", \"closeness\": 0, \"nins_mine\": 37, \"nins_tgt\": 37, \"sig\": \"MATCH\", \"detail\": {}}}`.", + "cookbook_refs_used": [ + "law 1c (post-MATCH relocation/symbol audit)", + "\u00a7193-A (banked cross-overlay twin, ov_SC01_077:func_8017E170)", + "\u00a7194-E (in-TU neighbour mining, func_80180F1C)", + "\u00a7195-A (declarations already decided \u2014 no arity guessing)" + ], + "novel_claim": false, + "trivial": false + }, + { + "fn": "func_8017E014", + "arm": "opus", + "title": "sh then sll16/sra16 before jal is register reuse, not a reload", + "mechanism": "When a locally-computed HImode (short) value has just been truncated and stored to memory (sh) but the same value is still live in its register, and that value is used again immediately afterward with SImode signedness (e.g. as a call argument), gcc-2.7.2 re-derives the sign-extended 32-bit value in place via sll $r,16 ; sra $r,16 instead of reloading it from the address it just wrote \u2014 the register copy already exists, so cc1 never re-issues a load (lh/lhu) for a value it can re-widen for one register-arithmetic op cheaper. Writing the C so the callee reads back through the pointer instead of the local removes that live register copy and forces a genuine reload.", + "asm_tell": "`sh $vN, OFF($aX)` immediately followed by `sll $vN,$vN,16` then (often split across the following `jal`'s delay slot) `sra $aM,$vN,16` feeding the argument register of that `jal` \u2014 as opposed to a fresh `lhu`/`lh` of the same `OFF($aX)` address, which is what you'd see if the callee re-read the field instead of the just-computed local.", + "c_shape": "s16 ang = *(u16 *)(a0 + 0xFC) + 0x2D;\n*(s16 *)(a0 + 0xFC) = ang;\nfunc_8004787C(ang); /* pass the LOCAL again, not *(s16 *)(a0 + 0xFC) */", + "evidence": "Target asm (asm/ov_SC03_114/nonmatchings/ov_SC03_114_jr_8017BEBC/func_8017E014.s):\n lhu $v0,0xFC($a0); addiu $v0,$v0,0x2D; sh $v0,0xFC($a0); sll $v0,$v0,16; jal func_8004787C ; sra $a0,$v0,16\nDraft (s16 ang local, passed by value to the call) hit `{\"status\":\"match\",\"closeness\":0,\"nins\":23,\"residual\":[],...}` on the FIRST and only match_one invocation \u2014 there was no drop from a nonzero value in this transcript; the agent's earlier win was discarding the pack's warm-start body (an unrelated ~80-instruction +0x34/+0x20 state machine left by a prior failed attempt) before it ever reached match_one, then drafting straight from the 23-instruction .s and getting closeness 0 immediately. Confirmed final: `MATCH (23 ins) func_8017E014`.", + "cookbook_refs_used": [], + "novel_claim": false, + "trivial": false + }, + { + "fn": "func_8017FF74", + "arm": "sonnet", + "title": "Warm-start body was a different overlay's same-named function", + "mechanism": "Unknown/tooling artifact, not gcc codegen: overlay binaries are separately linked and reuse overlapping virtual-address ranges, so a bare label like func_8017FF74 recurs as entirely unrelated code in many overlay TUs (this run alone turned up distinct func_8017FF74 bodies in ov_SC06_008, ov_SC07_007, and ov_SC01_005). A pack/wave-harvest pipeline that caches a \"previous attempt\" or \"neighbour\" by bare name, without pinning it to the target's own overlay, can hand you code for the wrong function's overlay twin.", + "asm_tell": "The candidate C body's callee/global symbol set has ZERO overlap with the target .s's own relocation list. The pack's \"PREVIOUS ATTEMPT\" body referenced func_801319E0, func_8012BEE8, func_8012CBF4, func_80131C78, func_8017ED80 (an if/else field-0xA guard chain) \u2014 none of which appear anywhere in asm/ov_SC01_006/.../func_8017FF74.s, whose only data relocations are D_801BBCAC/D_801BBCC0/D_801BBCD4 (three 5-entry fn-ptr tables) selected by a field-0x70 switch. Zero relocation overlap is a compile-free tell to discard the body outright rather than spend an iteration patching it.", + "c_shape": "switch (v1) {\ncase 0:\ndefault:\n fn = D_801BBCAC[*(u16 *)(a0 + 2)]; break;\ncase 1:\n fn = D_801BBCC0[*(u16 *)(a0 + 2)]; break;\ncase 2:\n fn = D_801BBCD4[*(u16 *)(a0 + 2)]; break;\n}\nfn();", + "evidence": "match_one was invoked exactly ONCE in the entire run \u2014 on the from-scratch rebuild built directly from the .s and tail.data.s, never on the pack's decoy body (which was never compiled at all). Result: {\"status\": \"match\", \"closeness\": 0, \"nins\": 39, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", ..., \"closeness\": 0, \"nins_mine\": 39, \"nins_tgt\": 39, \"sig\": \"MATCH\"}}. Closeness went straight from \"never measured\" to 0 on the first and only try, precisely because the agent recognized and discarded the wrong-overlay decoy before burning a compile on it. Separately, the pack's \u00a7194-E \"TU neighbour\" pointer (func_801821F8, claimed to be in this same TU) was ALSO stale: `grep -n \"func_801821F8\" src/ov_SC01_006/ov_SC01_006_jr_8017ED5C.c` returned nothing \u2014 that symbol actually lives in six unrelated ov_SC06_* TUs, a second instance of the same cross-overlay name-collision trap in one pack.", + "cookbook_refs_used": [ + "\u00a7193-A", + "\u00a7194-E", + "\u00a7195-A" + ], + "novel_claim": true, + "trivial": true + }, + { + "fn": "func_8017E014", + "arm": "sonnet", + "title": "bgez+addiu(2^k\u22121)+sra k = signed div-by-2^k rounding", + "mechanism": "gcc-2.7.2 lowers a SIGNED integer division by a power-of-two constant (2^k) as an arithmetic right-shift with a round-toward-zero correction: a bare `sra` rounds toward -infinity for negative dividends, so the compiler tests the sign (`bgez`) and, only on the negative path, adds (2^k \u2212 1) before the `sra k`. This is a known, already-documented compiler law (\u00a7164-04/\u00a71-I5/\u00a7167-40/\u00a7172b-4) \u2014 this transcript did not discover it fresh, it just applied it correctly by writing the division literally.", + "asm_tell": "`bgez $vN,.L; nop; addiu $vN,$vN,(2^k\u22121); .L: sra $vN,$vN,k` with no explicit mask beforehand. Here: `bgez $v0,.L8017E048 / nop / addiu $v0,$v0,0x3F / .L8017E048: sra $v0,$v0,6` for a `/ 0x40` (k=6). Adjacent tell in the same block: `sll $v0,$v0,16 / sra $a0,$v0,16` in a `jal`'s delay slot \u2014 a truncate-then-widen sign-extend pair, not a fresh load.", + "c_shape": "Write the division literally on a signed (s32) intermediate \u2014 never hand-roll the bias:\n```c\ns16 temp;\ns32 q;\ntemp = *(u16 *)(a0 + 0xFC) + 0x2D; /* store truncates to s16 */\n*(s16 *)(a0 + 0xFC) = temp;\nq = func_8004787C(temp) / 0x40 + 0x80; /* reuse of `temp` (not a reload) re-widens via sll16/sra16; the /0x40 auto-produces the bgez/addiu(0x3F)/sra6 bias */\n```", + "evidence": "Only one match_one call exists in the whole transcript \u2014 the FIRST and only compile of draft1.c returned closeness 0 immediately: `{\"status\": \"match\", \"closeness\": 0, \"nins\": 23, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", \"profile\": null, \"bucket\": \"integration\", \"closeness\": 0, \"nins_mine\": 23, \"nins_tgt\": 23, \"sig\": \"MATCH\", \"detail\": {}}}`. There is no prior nonzero attempt to compare against; the agent derived the C directly from the raw `.s` (`asm/ov_SC03_114/nonmatchings/ov_SC03_114_jr_8017BEBC/func_8017E014.s`) plus a same-TU sibling template (`func_8017DE30`, same file, lines ~413-427) and it matched on the first try.", + "cookbook_refs_used": [], + "novel_claim": false, + "trivial": false + }, + { + "fn": "func_801810A4", + "arm": "sonnet", + "title": "Extra sll/sra (or andi) after jal to narrow-return callee", + "mechanism": "gcc-2.7.2 trusts the LOCAL call-site's own declared return type for a callee, not its true definition. If that call site's extern declares the callee narrower (s16/u16/s8/u8) than the value's actual use requires, gcc inserts a truncating/extending instruction sequence right after the jal, before the value is consumed. Casting the call through a function-pointer of the wider return type at just that one call site (leaving the callee's other, narrower TU-canonical decl untouched elsewhere) removes the insertion.", + "asm_tell": "`jal func_X` / delay-slot immediately followed by a compare/branch on `$v0` with NOTHING in between (target here: `jal func_80184B44` / `sw $v1,0xB0($v0)` / `addiu $v1,$zero,0x1` / `beq $v0,$v1,...`) \u2014 vs. the `sll $v0,$v0,0x10; sra $v0,$v0,0x10` (or `andi $v0,$v0,0xffff`) pair gcc-2.7.2 would emit there if the call site's own extern declared the callee's return narrower than the comparison needs.", + "c_shape": "extern s16 func_80184B44(void); /* unchanged TU-canonical decl, used elsewhere as s16 */\nif (((s32 (*)(void))func_80184B44)() == 1) { ... } /* call-site-only cast widens the return to s32, no extra truncation instructions emitted */", + "evidence": "Only ONE match_one invocation exists in the whole transcript, and it was already the final one: `.venv/bin/python tools/match_one.py func_801810A4 --c .run/t4/sonnet/scratch_func_801810A4/draft.c --asm-subdir asm/ov_SC05_017/nonmatchings/ov_SC05_017_jr_801808D4 --json` returned `{\"status\": \"match\", \"closeness\": 0, \"nins\": 40, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", \"profile\": null, \"bucket\": \"integration\", \"closeness\": 0, \"nins_mine\": 40, \"nins_tgt\": 40, \"sig\": \"MATCH\", \"detail\": {}}}`. There is no closeness-drop moment to report \u2014 the agent's FIRST draft was already byte-identical. It got there by discarding the stale `.run/backlog_drafts/func_801810A4.c` seed (whose symbols func_8012A828/func_80182B60/func_801292C8/etc. never appear in this function's own .s relocations, per Law 2) and instead copying, verbatim, the proven prologue (`*(s32*)(*(s32*)((s32)a0+0xCC)+0xB0)=2;`) and the `((s32 (*)(void))func_80184B44)() == 1` cast idiom from the immediately-adjacent, already-matched sibling function `func_801811D8` in the same destination TU (src/ov_SC05_017/ov_SC05_017_jr_801808D4.c), which shares the identical probe literals (10, 0x10, 0x15) against func_80184B44/func_801848AC.", + "cookbook_refs_used": [], + "novel_claim": false, + "trivial": false + }, + { + "fn": "func_8017EF90", + "arm": "opus", + "title": "Aliasing a pointer parameter into a local costs a saved register", + "mechanism": "gcc-2.7.2's global register allocator does not coalesce two distinct C-level names for the same runtime value at -O2, even when their live ranges never overlap. Introducing `s32 s0 = a0;` and then referencing `s0` (instead of `a0`) at sites after a `jal` gives the allocator a second pseudo that needs its own callee-saved hard register, so it grants a fresh one ($s1) and inserts a real `move` to get the value there, rather than recognizing it as the same value already parked in $s0 for the call-crossing lifetime.", + "asm_tell": "Frame is 8 bytes bigger than the target's (`addiu $sp,$sp,-0x20` vs target's `-0x18`) with one extra `sw $s1,off($sp)` beyond the target's single saved register, and a `move $s1,$s0`-shaped instruction sitting between the two independent use-groups where the target has none \u2014 the classic LENGTH-DRIFT symptom (mine=52 ins vs target=49, closeness 44 before removal, 0 after).", + "c_shape": "// BAD \u2014 costs a second callee-saved register + spurious move:\nvoid func_8017EF90(s32 a0) {\n s32 s0;\n s0 = a0;\n ... use s0 in group A ...\n ... jal something(...) ...\n ... use s0 in group B ...\n}\n\n// GOOD \u2014 collapses back to the single $s0 the target uses:\nvoid func_8017EF90(s32 a0) {\n ... use a0 directly everywhere, both before and after the jal ...\n}", + "evidence": "Draft 1 (`s32 s0; s0 = a0;`, used `s0` throughout): `.venv/bin/python tools/match_one.py func_8017EF90 --c d1.c ...` \u2192 \"DIFF func_8017EF90 mine=52 ins, target=49 ins, 44 mismatched / class: LENGTH-DRIFT\" (diff shows extra `sw s1,20(sp)` in the prologue and `move s1,s0` before the second use-group, vs target's single `addu $s0,$a0,$zero`). Draft 2 (identical body but every `s0` replaced by the raw parameter `a0`): same match_one call \u2192 \"MATCH (49 ins)\"; final whole-binary-equivalent check \u2192 `{\"status\": \"match\", \"closeness\": 0, \"nins\": 49, ...}`. Closeness 44 \u2192 0 solely from deleting the local alias.", + "cookbook_refs_used": [], + "novel_claim": false, + "trivial": false + }, + { + "fn": "func_800CAF9C", + "arm": "sonnet", + "title": "Switch case body layout follows source text order, not value", + "mechanism": "gcc-2.7.2 builds a switch's compare/branch chain from the case values themselves (equality/slti tests against 0x53, then 0x54-range, then 0x41 \u2014 independent of how you typed them), but it lays out each case's body basic-block in the TEXTUAL order the case clauses appear in the switch statement. The decision tree is value-driven; the physical block placement in .text is source-order-driven. Writing the clauses in a different order than the original left the correct branch logic but put the wrong body first, producing a near-total post-dispatch mismatch even though every earlier instruction (the compare chain itself) was already byte-identical.", + "asm_tell": "The compare/branch chain (lbu/beq v1,0x53/slti v1,0x54/beq v1,0x41/j) matches the target byte-for-byte for the first ~9 instructions, then every remaining instruction from the first case body onward is off: your case-A body's instructions (move/lhu/andi/jal sequence) sit where the target has case-B's body, and the mismatch cascades through the rest of the function tail (here: 47 of 104 instructions residual) even though both bodies are individually correct C.", + "c_shape": "switch (v1) {\ncase 0x41: /* body placed at the LOWER address in target .text \u2014 write it first */\n value = func_80148E54((void *)a0);\n if (value > 0) {\n *(s16 *)(p + 0x12) = func_80012ABC(*(s16 *)(p + 0x12), value, 4);\n }\n break;\ncase 0x53:\ncase 0x73: /* shared body at the HIGHER address \u2014 write it second */\n func_80148AFC((void *)a0);\n break;\n}", + "evidence": "draft1 (case 0x53/0x73 written before case 0x41 in source): match_one -> {\"status\": \"near\", \"closeness\": 71, \"nins\": 104, \"residual\": [[10,...],[14,...],...]} \u2014 diff shows the compare chain identical through index ~9, then a full cascade (func_80148AFC's \"move a0,s1\" landing where target has the func_80148E54/func_80012ABC body's nop, and every subsequent instruction shifted). Only change made: reordered the two case blocks (0x41 first, then 0x53/0x73) via Edit, no logic change. draft2: match_one -> {\"status\": \"match\", \"closeness\": 0, \"nins\": 105, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", ...}}. Whole-binary byte-gate accepted the final draft.", + "cookbook_refs_used": [], + "novel_claim": true, + "trivial": false + }, + { + "fn": "func_8017FF74", + "arm": "opus", + "title": "2-live-arm switch misses slti/bnez ordering test (LENGTH-DRIFT -2)", + "mechanism": "gcc-2.7.2's balance_case_nodes only builds the median-split ordering test when there are more than 2 case NODES (stmt.c's `if (i > 2)`, per cookbook \u00a7193-G correcting \u00a7164-54's stated \u22654 bound); with only two live values (1 and 2) plus a bare default, the switch is seen as 2 nodes and emit_case_nodes degenerates to a flat beq/beq equality staircase, dropping the slti-based range check entirely (-2 instructions). Writing an empty `case 0:` that shares the default arm's body raises the node count to 3, so the median split fires and the ordering test reappears. The transcript also surfaces an unstated wrinkle: because the added case shares default's own label (rather than being a separate empty fall-through arm), jump.c inverts the test's polarity \u2014 target emits `slti $v0,$v1,0x2` / `bnez -> default` rather than the canonical `slt` / `beqz -> right-subtree` that \u00a7193-G's byte evidence shows for an ordinary 3-node split.", + "asm_tell": "A back-to-back `beq`/`beq` staircase testing two case constants with NO leading `slti`/`sltiu` range check before the first branch, where your draft's switch has only two non-default arms \u2014 i.e. a 2-instruction LENGTH-DRIFT deficit relative to the target, which does carry `slti $vX, N` immediately before/interleaved with the first `beq`.", + "c_shape": "switch (*(s16 *)(a0 + 0x70)) {\ncase 0:\ndefault:\n f = D_801BBCAC[*(u16 *)(a0 + 2)];\n break;\ncase 1:\n f = D_801BBCC0[*(u16 *)(a0 + 2)];\n break;\ncase 2:\n f = D_801BBCD4[*(u16 *)(a0 + 2)];\n break;\n}", + "evidence": "v1 (plain default/case1/case2, no case 0): match_one JSON = {\"status\": \"near\", \"closeness\": 29, \"nins\": 37, ... \"verdict\": {\"klass\": \"LENGTH-DRIFT\", \"closeness\": 29, \"nins_mine\": 37, \"nins_tgt\": 39, \"sig\": \"LENGTH-DRIFT/-2?\", ...}} \u2014 residual showed the target's `slti $v0,$v1,0x2` / `bnez $v0,...` pair simply absent from the draft's beq/beq staircase. v2a (added `case 0: default:` sharing one body, raising node count to 3): match_one JSON = {\"status\": \"match\", \"closeness\": 0, \"nins\": 39, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", \"closeness\": 0, \"nins_mine\": 39, \"nins_tgt\": 39, \"sig\": \"MATCH\"}} \u2014 closeness 29 -> 0 on the very next iteration, no other C change.", + "cookbook_refs_used": [ + "\u00a7222 (pt. 2 \u2014 \"an empty leading case 0: is what puts the median split in\")", + "\u00a7193-G (\"the dispatch-topology oracle goes live at THREE case nodes, not four\" \u2014 corrects \u00a7164-54)" + ], + "novel_claim": false, + "trivial": false + }, + { + "fn": "func_80181894", + "arm": "opus", + "title": "Uniform sw/lw offset shift across whole diff = missing frame pad", + "mechanism": "gcc-2.7.2's frame size is callee-saved-register slots plus local variable space (rounded to alignment). When the true source has a local that reserves stack space but is never read (no address taken, no code effect), the C reconstruction must declare a same-size dead local or the frame is undersized \u2014 which does not just misplace one instruction but shifts every register-save/restore offset and the sp adjustment by the same constant delta, because they are all computed from the one frame-size constant.", + "asm_tell": "Every prologue/epilogue instruction (the `addiu $sp,$sp,-N` frame alloc/dealloc and every `sw`/`lw` of `$ra`/`$sN` to a stack offset) differs from the target by the exact same fixed delta, while every instruction in the function body proper is byte-identical \u2014 i.e. a match_one residual whose entries are ALL in the save/restore + sp-adjust set, never in the body.", + "c_shape": "void func_80181894(s32 a0) {\n s32 pad[2]; /* dead 8-byte local \u2014 frame padding (cookbook \u00a7226) */\n s32 obj;\n ...\n}\nAn unused, unread, address-untaken local array sized to exactly the missing byte count, declared among the other locals; it contributes to var_size but emits no instructions of its own.", + "evidence": "closeness 10 -> 0 after inserting `s32 pad[2];` as the first local. v1 match_one: {\"status\":\"near\",\"closeness\":10,\"residual\":[[0,\"27bdffe0 addiu sp,sp,-32\",\"27bdffd8 addiu $sp, $sp, -0x28\"],[1,\"afb10014 sw s1,20(sp)\",\"afb1001c sw $s1, 0x1C($sp)\"],[3,\"afbf001c sw ra,28(sp)\",\"afbf0024 sw $ra, 0x24($sp)\"],[4,\"afb20018 sw s2,24(sp)\",\"afb20020 sw $s2, 0x20($sp)\"],[6,\"afb00010 sw s0,16(sp)\",\"afb00018 sw $s0, 0x18($sp)\"], ... (mirrored in epilogue)]} \u2014 all 10 residual entries are the 4 saved-reg stores/loads + sp adjust, each off by exactly 8. v2 match_one after the pad: {\"status\":\"match\",\"closeness\":0,\"nins\":112,\"residual\":[]}.", + "cookbook_refs_used": [ + "\u00a7226", + "\u00a7227" + ], + "novel_claim": false, + "trivial": false + }, + { + "fn": "func_8017E54C", + "arm": "sonnet", + "title": "8-byte struct copy uses lwl/lwr+swl/swr; clamps have no else", + "mechanism": "gcc-2.7.2 cannot assume an extern struct is word-aligned at the call site, so a small (here 8-byte, two-halfword-pair) struct assignment lowers to the generic unaligned block-copy macro (lwl/lwr load pair, swl/swr store pair) instead of straight lw/sw, even though the source is really just an s16[4] with no actual misalignment. Separately, a single-bound clamp (`if (out of range) field = LITERAL;` with no else) only ever needs to touch memory in the taken branch, so gcc emits one conditional store with literally no instructions in the fall-through arm \u2014 it does not synthesize a redundant \"else: unchanged\" store.", + "asm_tell": "Back-to-back `lwl $r,N($base)` / `lwr $r,M($base)` immediately paired with `swl`/`swr` into the stack frame (not `lh`/`sh` field-by-field) copying a small struct-sized block; followed by `slti`+branch sequences where only ONE arm contains an `addiu`+`sh` (or plain `sh`) write and the other arm has no code at all \u2014 a one-sided clamp, not a ternary/ MIN-MAX pattern.", + "c_shape": "typedef struct { s16 v[4]; } Blk8_ADDR;\nextern Blk8_ADDR D_GLOBAL;\nvoid func(s32 a0) {\n Blk8_ADDR sp10 = D_GLOBAL; /* struct assign -> lwl/lwr+swl/swr */\n sp10.v[0] = 0; /* unconditional field reset */\n if (sp10.v[1] > -0x2D0) { sp10.v[1] = -0x2D0; } /* one-sided clamp, no else */\n if (sp10.v[2] < 0x340) { sp10.v[2] = 0x340; } /* one-sided clamp, no else */\n callee(a0, sp10.v);\n}", + "evidence": "Only one match_one call exists in the whole transcript, and it returned MATCH/closeness 0 immediately \u2014 there is no prior nonzero-closeness attempt to diff against: `{\"status\": \"match\", \"closeness\": 0, \"nins\": 32, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", \"profile\": null, \"bucket\": \"integration\", \"closeness\": 0, \"nins_mine\": 32, \"nins_tgt\": 32, \"sig\": \"MATCH\", \"detail\": {}}}`. The draft that produced it was built directly by copying the struct-copy+one-sided-clamp+forward shape from the near-identical in-tree sibling `func_8017DB94` (src/ov_SC06_030/ov_SC06_030_jr_8017C8D0.c), itself already annotated `/* 8 bytes, align 2 -> lwl/lwr/swl/swr copy (cookbook \u00a748-C2) */`. The pack's warm-start Ghidra body was discarded uncompiled (wrong, unrelated function).", + "cookbook_refs_used": [ + "\u00a748-C2", + "\u00a7179-E", + "\u00a7193-A", + "\u00a7194-E", + "\u00a7195-A" + ], + "novel_claim": false, + "trivial": false + }, + { + "fn": "func_8017F3F8", + "arm": "opus", + "title": "jalr delay slot has addu $a0,$s0 -> fn-ptr takes an arg", + "mechanism": "gcc-2.7.2 fills a call's branch-delay slot with whatever instruction is next in program order; when the callee (direct or through a function pointer) takes a parameter, that next instruction is the argument-register setup (`move`/`addu $aN,$sN`), so the slot is never idle. If the C instead declares the pointed-to function as taking no arguments, there is nothing to schedule into the slot and gcc emits `nop` there instead \u2014 so a real arg-register copy sitting in a jalr/jal delay slot is direct evidence of the callee's arity, not incidental scheduling.", + "asm_tell": "`jalr $v0` (indirect call through a loaded function-pointer-table entry) with its delay slot holding `addu $aN, $sN, $zero` (a live arg-register copy) instead of `nop`.", + "c_shape": "Type the dispatch-table element with the parameter the delay slot proves exists, and call it with that argument: `extern void (*D_80187988[])(s32); D_80187988[*(u16*)(a0+2)](a0);` \u2014 not `void (*D_80187988[])(void)` called with no argument, which would leave the slot empty.", + "evidence": "No nonzero-to-zero drop occurs in this transcript \u2014 the sole match_one call is also the final one, and it reports closeness 0 immediately: `{\"status\": \"match\", \"closeness\": 0, \"nins\": 38, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", ...}}` (both the scratch draft call and the resubmitted final-path call return this identical line). The reason there is no drop to trace: the agent applied the arity-from-delay-slot law (stated verbatim in `.run/t4/SYS.md` law 4, matching cookbook \u00a7263) *before* writing the first draft \u2014 reading the target `.s`'s `jalr $v0` / `addu $a0,$s0,$zero` pair and typing `D_80187988` as `(*)(s32)` called with `(a0)` from the outset, rather than copying the neighbouring `D_801878DC[...]()`/`D_8018793C[...]()` no-arg dispatch idiom verbatim from the destination TU (src/ov_SC02_037/ov_SC02_037_jr_8017AE2C.c lines ~6078-6094), which would have produced a `nop` in that slot and a nonzero residual. So the \"drop\" is compressed into a single successful compile rather than observed as a correction.", + "cookbook_refs_used": [ + "\u00a7263" + ], + "novel_claim": false, + "trivial": false + }, + { + "fn": "func_80181894", + "arm": "sonnet", + "title": "Uniform +8 stack-offset diff means a missing dead-local pad", + "mechanism": "gcc-2.7.2 sizes a function's frame from its locals' aggregate storage, not from what is actually read/written. A declared-but-never-referenced array local (size >= 2 elements, e.g. s32 pad[2]) still gets counted into var_size and pushes every other stack slot's address up by its size, whereas a single dead scalar local of non-BLKmode is optimized away and reserves nothing (per cookbook \u00a7162i1's \"8-byte floor\" rule). The source apparently had one more locally-scoped array/struct than the draft did, so the draft's frame was 8 bytes short.", + "asm_tell": "Every $sp-relative instruction in the prologue and epilogue (addiu $sp,$sp,-N / sw $s0../ra,OFF($sp) / matching lw's / addiu $sp,$sp,N at the end) differs from the target by one constant delta (here 0x20 vs 0x28, offsets 0x18/0x1C/0x20/0x24 vs 0x10/0x14/0x18/0x1C) while every non-frame instruction in the function body is already byte-identical \u2014 a pure frame-size residual, not a codegen residual.", + "c_shape": "Add a dead 2+-element array local (never read or written, no address taken) alongside the real locals to soak up the missing var_size bytes:\nvoid func_80181894(s32 a0)\n{\n s32 s2;\n s32 pad[2]; // dead \u2014 reserves the missing 8 bytes of frame\n ...\n}", + "evidence": "match_one closeness 10 -> 0 after adding `s32 pad[2];`. Before: {\"status\": \"near\", \"closeness\": 10, \"nins\": 112, \"residual\": [[0, \"27bdffe0 addiu sp,sp,-32\", \"27bdffd8 addiu $sp, $sp, -0x28\"], [1, \"afb10014 sw s1,20(sp)\", \"afb1001c sw $s1, 0x1C($sp)\"], ... [109, \"27bd0020 addiu sp,sp,32\", \"27bd0028 addiu $sp, $sp, 0x28\"]], \"verdict\": {\"klass\": \"IMM-VALUE\", \"profile\": \"cse\", \"bucket\": \"permuter\", \"detail\": {\"sample\": [[0, 65504, 65496], [1, 20, 28], [3, 28, 36], [4, 24, 32], [6, 16, 24], [105, 28, 36]], \"kinds\": {\"imm\": 10}}}}. After: {\"status\": \"match\", \"closeness\": 0, \"nins\": 112, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", ...}}. (A separate, larger drop from closeness 65 -> 10 earlier in the same run came from retyping a local `s16 v` to `s32 v` used in three magic-multiply modulo expressions \u2014 the residual before that fix was arithmetic/register-sequence divergence, not a uniform offset, so it is a different lesson.)", + "cookbook_refs_used": [ + "\u00a7162i1", + "\u00a7226", + "\u00a7226 addendum (P31 S58b)" + ], + "novel_claim": false, + "trivial": false + }, + { + "fn": "func_8017E7D0", + "arm": "opus", + "title": "Spurious nop before div-result store: reorder trailing statements", + "mechanism": "gcc-2.7.2's MIPS scheduler must bridge the fixed-latency hazard after the div/mflo sequence that produces the division result before that result register can be consumed by the store that uses it. It fills that hazard slot with an independent instruction only if one is already available earlier in the statement/RTL order; an unrelated global store placed AFTER the division-consuming statement in the C source is not visible to fill the gap, so the scheduler emits an explicit nop instead. Placing the independent store BEFORE the division-consuming statement gives the scheduler material to sink into the slot, eliminating the nop \u2014 pure C statement order (with no data dependency change) altered code generation.", + "asm_tell": "A masked diff of class LENGTH-DRIFT where the extra instruction is an assembler nop sitting immediately before the `sw` that stores a division result (the value just produced by `div`+`mflo`), while an adjacent, data-independent global store (`lui %hi(G)` / `sh $zero, %lo(G)($at)`) appears scheduled AFTER that sw in your draft but BEFORE it in the target \u2014 i.e. mine: `nop / sw ,N($s0) / lui at / sh zero,0(at)`; target: `lui $at / sh $zero / sw $a0,N($s0)` with no nop.", + "c_shape": "Swap the order of two independent trailing statements so the one with NO dependency on the division precedes the one that consumes it:\n```c\n/* target order \u2014 no nop emitted */\nD_8019F70C = 0;\n*(s32 *)(a0 + 0x48) = -D_80188A34 / ((s16)D_80188A2A[0] / 2);\n\n/* wrong order \u2014 draft (v1): forces an extra nop, mine=55 vs target=54 ins */\n*(s32 *)(a0 + 0x48) = -D_80188A34 / ((s16)D_80188A2A[0] / 2);\nD_8019F70C = 0;\n```", + "evidence": "v1 (division-store-then-D_8019F70C order): `DIFF func_8017E7D0 mine=55 ins, target=54 ins, 10 mismatched / class: LENGTH-DRIFT [structural] sig=LENGTH-DRIFT/1?` with mismatch starting `idx45: 00000000 nop | 3c01801a lui $at,%hi(D_8019F70C)`. v2 (D_8019F70C-then-division-store order, the only change): `MATCH (54 ins) func_8017E7D0`; final --json verify: `{\"status\": \"match\", \"closeness\": 0, \"nins\": 54, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", ... \"closeness\": 0, \"nins_mine\": 54, \"nins_tgt\": 54, \"sig\": \"MATCH\", ...}}`.", + "cookbook_refs_used": [], + "novel_claim": true, + "trivial": false + }, + { + "fn": "func_8017FB38", + "arm": "sonnet", + "title": "Single beqz IMM-OFFSET mismatch signals a missing early return", + "mechanism": "gcc-2.7.2 never merges/elides a later guard just because an earlier guard's failing path would also fail it. An explicit `return;` inside the earlier guard compiles to a direct branch to the function's one epilogue label; omitting it and letting control fall through to a later, logically-redundant guard (`if (field != 0) { call(...); }`) makes that later guard's own branch instruction the one that finally reaches the epilogue instead. Both C shapes produce identical instruction COUNT and identical runtime behavior \u2014 only the branch-target immediate of the guard's `beqz` differs, by exactly the distance between the \"direct\" epilogue jump and the \"via-shared-guard\" one.", + "asm_tell": "Exactly one mismatched instruction, same opcode/register/condition on both sides (`beqz $v0, ...`), only the branch's local-label immediate differs; match_one classifies it `IMM-OFFSET` with a `delta` equal to the instruction-count of the skipped/absorbed guard block \u2014 everything else in the function, before and after that single branch, is byte-identical.", + "c_shape": "/* WRONG (closeness=1): relies on the later guard being redundantly false */\n} else {\n t = field2C;\n if (t != 0) {\n t -= 0x10;\n field2C = t;\n if (t < 0) field2C = 0;\n }\n}\nif (field2C != 0) { func_80016450(field2C & 0xFF, 1); } /* falls through into THIS check when t==0 */\n\n/* RIGHT (closeness=0): explicit early return branches straight to the epilogue */\n} else {\n t = field2C;\n if (t == 0) {\n return;\n }\n t -= 0x10;\n field2C = t;\n if (t < 0) field2C = 0;\n}\nif (field2C != 0) { func_80016450(field2C & 0xFF, 1); }", + "evidence": "draft2 (before fix): `{\"status\": \"near\", \"closeness\": 1, \"nins\": 44, \"residual\": [[25, \"10400008 beqz\\tv0,88 \", \"1040000e beqz $v0, .L8017FBD8\"]], \"verdict\": {\"klass\": \"IMM-OFFSET\", \"bucket\": \"structural\", \"closeness\": 1, \"sig\": \"IMM-OFFSET/6\", \"detail\": {\"delta\": 6, \"n\": 1, \"kinds\": {\"imm\": 1}}}}`. draft3 (added `if (t == 0) { return; }` in place of the implicit fallthrough): `{\"status\": \"match\", \"closeness\": 0, \"nins\": 44, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", \"bucket\": \"integration\", \"closeness\": 0, \"sig\": \"MATCH\", \"detail\": {}}}`. (A prior, unrelated drop from closeness=25\u21921 between draft1\u2192draft2 was just fixing reversed if/else branch polarity \u2014 a plain misreading of the asm, not a compiler law.)", + "cookbook_refs_used": [], + "novel_claim": true, + "trivial": false + }, + { + "fn": "func_8017E54C", + "arm": "opus", + "title": "CC1 \"type undeclared\" error, not a closeness diff, blocked MATCH", + "mechanism": "match_one compiles the draft standalone with only `#include \"common.h\"` prepended; common.h does NOT pull in src/shared/engine_types.h (verified: only the real destination TU, ov_SC06_010_jr_8017A4AC.c, separately `#include`s engine_types.h \u2014 common.h alone never does, across the repo). So `extern Blk8_80126940 D_80126940;` referencing a type that genuinely exists in engine_types.h (and would resolve fine in the real TU) throws `'Blk8_80126940' undeclared (first use this function)` inside match_one's isolated harness. This is a build-harness scoping fact, not a gcc-2.7.2 codegen rule \u2014 the instruction SHAPE (lwl/lwr+swl/swr block copy, \u00a748-C2) was already correct on the very first draft; only the type's visibility was wrong.", + "asm_tell": "Not an asm-diff tell at all \u2014 the tell is on the compile side: `CC1 FAIL ... t.c:8: syntax error before 'D_80126940'` immediately followed by `'' undeclared (first use this function)` for a global/type that DOES exist elsewhere in the repo (grep confirms it in src/shared/engine_types.h) \u2014 it's simply out of scope for match_one's common.h-only harness.", + "c_shape": "Define the struct locally in the draft (same shape, per-function-suffixed name) instead of trusting an ambient shared-header type to be visible to the isolated harness:\n```c\ntypedef struct { s16 v[4]; } Blk8_80126940_8017E54C; /* same shape as engine_types.h's Blk8_80126940, but self-contained */\nextern Blk8_80126940_8017E54C D_80126940;\nBlk8_80126940_8017E54C sp10;\nsp10 = D_80126940; /* lwl/lwr + swl/swr block copy -- cookbook \u00a748-C2 */\nsp10.v[0] = 0;\nif (sp10.v[1] > -0x2D0) sp10.v[1] = -0x2D0;\nif (sp10.v[2] < 0x340) sp10.v[2] = 0x340;\nfunc_8017E5CC(a0, sp10.v);\n```", + "evidence": "d1.c used `extern Blk8_80126940 D_80126940;` -> match_one: \"Exit code 1 / CC1 FAIL ... t.c:9: `Blk8_80126940' undeclared (first use this function)\" (no closeness value at all -- the draft never compiled). d2.c, identical logic but the type swapped for a locally-defined `Blk8_80126940_8017E54C` -> match_one: `{\"status\": \"match\", \"closeness\": 0, \"nins\": 32, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", ...}}`. There is no intermediate nonzero-closeness compile anywhere in this transcript -- the only transition is compile-FAIL straight to closeness 0, and the fix was purely type-visibility, not an instruction-shape change (the shape was already right, per \u00a748-C2, in d1).", + "cookbook_refs_used": [ + "\u00a748-C2" + ], + "novel_claim": false, + "trivial": true + }, + { + "fn": "func_801804C0", + "arm": "opus", + "title": "Goto-joined guard tail steals the next call's arg-copy slot", + "mechanism": "Partially unknown: the transcript shows the effect predates delay-slot reorg (it survives -fno-delayed-branch), so it's a CFG/scheduling artifact of where the merge point physically lives, not a reorg.c filler rule. When three failure guards are hand-joined into one `goto fail;`/`fail:` block, gcc's scheduler apparently treats the single merge block as the placement site for the argument-copy of the call that FOLLOWS it in source order, hoisting that copy up into the guard block and leaving a `nop` in the real call's own delay slot (+1 instruction, LENGTH-DRIFT). Writing the same tail three times as separate early returns lets each guard's local block schedule its own successor call normally; gcc's cross-jump pass then merges the three identical tails back into one physical block AFTER that per-block scheduling has already happened, so the arg copy stays in the call's delay slot. Cookbook \u00a7224 names this law but does not derive the exact RTL rule either.", + "asm_tell": "Draft has one extra instruction (nins 55 vs target 54): the `jal ` that should carry the argument-copy (`move $a0,$v0` / `addu $a0,$s0,$zero`) in its own delay slot instead has a `nop` there, while that same move appears one basic block EARLIER, right after the previous guard's call, inside what should be a `bnez`-guarded fail path. match_one reports this as closeness 32 with sig \"LENGTH-DRIFT/1?\" and the residual diff shows the move/store pair shifted one slot early relative to the branch that should gate it.", + "c_shape": "Do NOT hand-join repeated failure tails with a goto:\n if (cond1) goto fail;\n ...\n if (cond2) goto fail;\n ...\nfail:\n func_8012CAE4(a0);\n return;\n\nInstead duplicate the tail inline at every guard and let gcc's cross-jump pass re-merge them:\n if (cond1) { func_8012CAE4(a0); return; }\n ...\n if (cond2) { func_8012CAE4(a0); return; }\n ...\n if (cond3) { func_8012CAE4(a0); return; }\n /* success path */", + "evidence": "d1 (goto-join): {\"status\": \"near\", \"closeness\": 32, \"nins\": 55, \"residual\": [[9,\"1440000b bnez v0,54\",\"1440000a bnez $v0, .L80180510\"], ...]} \u2014 d2 (split result var \"stage\") and d3 (register __asm__(\"$2\") pin on v0), both inert: \"near closeness= 32 nins= 55 LENGTH-DRIFT/1?\" \u2014 e1 (three duplicated `func_8012CAE4(a0); return;` early returns, no goto): \"match closeness= 0 nins= 54 MATCH\". Drop: closeness 32 -> 0 caused solely by replacing the goto/label join with duplicated inline tails.", + "cookbook_refs_used": [ + "\u00a7194-E", + "\u00a7193-A", + "\u00a7201-C", + "\u00a7193-D", + "\u00a7224" + ], + "novel_claim": false, + "trivial": false + }, + { + "fn": "func_801804C0", + "arm": "sonnet", + "title": "Extra hoisted arg-move when goto shares one fail label", + "mechanism": "gcc-2.7.2's optimize_reg_copy_1/local-alloc hoists a call's implicit argument-register move (e.g. move $a0,$s0) to the top of a basic block when a source-level goto creates a real CODE_LABEL with multiple incoming edges feeding that call (\u00a7193-D/\u00a7194-N's trigger). Writing the identical cleanup+return statement independently inline at each failure site, instead of goto-ing to one shared label, removes that CFG join point before reload, so no hoist happens \u2014 and gcc's own post-regalloc cross-jump pass then re-merges the byte-identical duplicated tails back into a single physical call site anyway, reproducing the target exactly.", + "asm_tell": "An extra `move $aN,$sN` (or `addu $aN,$sN,$zero`) sitting immediately before a conditional branch/its delay slot, rather than living in the branch's own delay slot right next to the shared call \u2014 paired with the masked instruction count running exactly one over the target's.", + "c_shape": "Bad (triggers the hoist, 55/54 ins):\nif (a) goto fail;\nif (b) goto fail;\nif (c == 0) goto success;\nfail: cleanup(p); return;\nsuccess: ...\n\nGood (MATCH, 54/54 ins) \u2014 duplicate the identical tail inline at every site, no goto/label:\nif (a) { cleanup(p); return; }\nif (b) { cleanup(p); return; }\nif (c == 0) { cleanup(p); return; }\n...success code...", + "evidence": "draft1 (goto fail -> label at end of fn): {\"status\":\"near\",\"closeness\":29,\"nins\":53,...} LENGTH-DRIFT; residual[22] mine=\"00402021 move a0,v0\" vs target=\"08060160 j .L80180580\" (missing the skip-over-success jump, everything after shifted by one). draft2 (goto success/fail:/success: repositioned mid-flow to match target shape): {\"status\":\"near\",\"closeness\":32,...} nins=55; residual[18] mine=\"00402021 move a0,v0\" vs target=\"14400005 bnez $v0, .L80180520\" (extra hoisted arg-move, one instruction too many). draft3 (three literal inline \"func_8012CAE4(a0); return;\" duplicates, no goto/label): {\"status\":\"match\",\"closeness\":0,\"nins\":54,\"residual\":[]} \u2014 MATCH.", + "cookbook_refs_used": [ + "\u00a7193-D", + "\u00a7194-N" + ], + "novel_claim": false, + "trivial": false + }, + { + "fn": "func_8017F608", + "arm": "sonnet", + "title": "Clamp store lands in $v0; target uses $a0 (REGALLOC-PERM)", + "mechanism": "Transcript shows only the empirical fix, not a compiler-source-verified cause, so treat this as unknown-in-detail. Best-supported hypothesis from the observed diffs: when a constant is tested as a raw literal in the guard and only bound to a named local inside the taken arm, gcc-2.7.2 rematerializes it as a fresh pseudo for the store, which local-alloc colors to a generic temp ($v0). Hoisting the SAME named local above the guard and testing it (not the literal) makes one pseudo span both the compare and the store; local-alloc then colors that single live range to whatever hard register the surrounding context already prefers (here $a0), matching the target instead of falling back to $v0. This same hoist-before-guard shape also flipped several BRANCH-POLARITY residuals earlier in the same draft, so it looks like a general effect of pseudo lifetime/scope on gcc-2.7.2's register choice, not a one-off.", + "asm_tell": "match_one reports class REGALLOC-PERM (shape-identical instruction stream, same instruction count, a pure register substitution) localized to a load-immediate + store pair immediately following a compare against that identical constant: mine `li $v0,N` / `sh $v0,off($sp)` vs target `addiu $aX,$zero,N` / `sh $aX,off($sp)`. verdict.sig literally reads `REGALLOC-PERM/$v0>$a0`.", + "c_shape": "/* closeness=2, stuck: literal tested in the guard, local named only inside the arm */\nif (sp10.v[0] < 0x600) {\n s16 clampval = 0x600;\n sp10.v[0] = clampval;\n}\n\n/* closeness=0, MATCH: hoist the SAME named local above the guard and test IT */\ns16 clampval = 0x600;\nif (sp10.v[0] < clampval) {\n sp10.v[0] = clampval;\n}", + "evidence": "Progression across drafts (all --asm-subdir asm/ov_SC02_028/nonmatchings/ov_SC02_028_jr_8017D898): draft10\u2192draft11 (hoisting the constant into a named local used by both the tail-clamp guards and their stores) took closeness 21\u21922, isolating the residual to exactly one pair: {\"status\":\"near\",\"closeness\":2,\"nins\":99,\"residual\":[[33,\"24020600 li v0,1536\",\"24040600 addiu $a0, $zero, 0x600\"],[34,\"a7a20010 sh v0,16(sp)\",\"a7a40010 sh $a0, 0x10($sp)\"]],\"verdict\":{\"klass\":\"REGALLOC-PERM\",\"sig\":\"REGALLOC-PERM/$v0>$a0\",...}}. A tried-and-failed lead in between: pinning the constant with `register s32 clampval __asm__(\"$4\")` on draft14 REGRESSED hard, closeness 2\u219262 with an extra instruction: {\"status\":\"near\",\"closeness\":62,\"nins\":100,...}. The actual fix \u2014 draft15, hoisting `s16 clampval = 0x600;` above the `if` and comparing `sp10.v[0] < clampval` instead of the literal \u2014 closed it: {\"status\":\"match\",\"closeness\":0,\"nins\":99,\"residual\":[],\"verdict\":{\"klass\":\"MATCH\",\"sig\":\"MATCH\",\"detail\":{}}}.", + "cookbook_refs_used": [ + "\u00a73-T4", + "\u00a732.2", + "\u00a7193-A", + "\u00a7194-E", + "\u00a717 (register __asm__ pins \u2014 grepped and tried, made things worse, abandoned)" + ], + "novel_claim": true, + "trivial": false + }, + { + "fn": "func_801808E8", + "arm": "opus", + "title": "Return copy vanishes: OR folds straight into $v0, one insn short", + "mechanism": "gcc-2.7.2 combine/cse can fold the final producing instruction (here the flags `or`) directly into the return register $v0 when a `return expr;` is the single successor of that computation in its own block. Forcing several distinct arms to converge on ONE shared return point (`goto ret;` from each arm to a trailing `ret: return v0;` label) removes that one-to-one relationship \u2014 the value must now be kept in a plain accumulator register ($a0) and copied to $v0 with an explicit `addu $v0,$a0,$zero` at the shared join, which is exactly the target's extra instruction.", + "asm_tell": "Target keeps the flags accumulator in a non-return register (`or $a0, $a0, $vX`) and, right before the epilogue's `lw $ra,...` reload chain, emits a lone `addu $v0, $a0, $zero` copy into the return register. A draft that computes the OR directly into `$v0` (one `return v0;` per arm, no shared join) is missing that copy and comes out exactly one instruction short \u2014 match_one reports it as LENGTH-DRIFT/-1 with a $v0/$v1 register swap through the preceding block.", + "c_shape": "/* each arm: */\nif (cond1) { ...; v0 |= x; goto ret; }\nif (cond2) { ...; v0 |= y; goto ret; }\n...; v0 |= z;\nret:\n return v0;\n/* not: three independent `return v0;` statements */", + "evidence": "Before the fix (d5): \"DIFF func_801808E8 mine=134 ins, target=135 ins, 24 mismatched ... class: LENGTH-DRIFT ... 116 | 00821025 or v0,a0,v0 | 00832025 or $a0, $a0, $v1 ... 126 | 8fbf0030 lw ra,48(sp) | 00801021 addu $v0, $a0, $zero\". After replacing each of the three `return v0;` sites with `goto ret;` to a shared trailing `ret: return v0;` label (d6): \"MATCH (135 ins) func_801808E8\" \u2014 closeness 24 -> 0. Final submit JSON confirmed: {\"status\": \"match\", \"closeness\": 0, \"nins\": 135, \"residual\": []}.", + "cookbook_refs_used": [ + "docs/cookbook-index.md (grepped for branch/cross-jump/layout terms)", + "\u00a73-T4", + "\u00a75a", + "\u00a73-D.", + "\u00a7162", + "\u00a7186", + "\u00a7195-F", + "\u00a7195-G", + "\u00a7224", + "\u00a7256 (read in full)", + "\u00a7136f/\u00a7136g (read, not the applicable fix)", + "tools/reference/gcc-2.7.2/jump.c (read directly, lines ~1690-1900, for the earlier 64->24 tail-duplication/cross-jump step)", + "\u00a745 Lever D / cookbook line 3309 'goto-shared-return isolates the exit li' (the fix that actually produced the 24->0 MATCH, cited by line number in the agent's own closing note)" + ], + "novel_claim": false, + "trivial": false + }, + { + "fn": "func_800CAF9C", + "arm": "opus", + "title": "Honest note: no closeness-drop moment \u2014 one-shot MATCH; same-address twin was a decoy", + "mechanism": "This transcript contains exactly ONE match_one call and it returned closeness 0 immediately \u2014 there is no N-to-0 transition to mine a C-fix from. The actual \"moment\" worth banking happened BEFORE any compile: the pack's two highest-value leads (a \"warm-start body\" and a 0.9062-similarity \"banked twin\" func_800CB028) were both discarded because none of their symbols (D_80078EC1, D_800CB7EC[], func_80162D28, func_800CB338) appear in this target's own relocations. Both decoys turned out to be func_800CAF9C-the-name in DIFFERENT overlay binaries (md_MAIN_041, md_MAIN_037) sitting at the same relative address/layout slot but compiled from unrelated source \u2014 i.e. same address + same name \u2260 same body. That is not a gcc code-generation idiom (mechanism for why gcc emits anything is inapplicable here); it is a decomp-tooling/overlay-banking artifact, and it is already codified in the cookbook as \u00a7150-B (\"same address + same name \u2260 same body,\" extended to DATA at the L13523 entry) \u2014 the agent never cited that \u00a7-number, but independently re-derived and correctly applied the identical rule via SYS.md's own Law 1 (spell every symbol from the target; a seed's per-location symbols are never portable). The rest of the draft (switch-vs-if reconstruction, zero-arity call, statement order) was a correct but unremarkable application of already-cited sections, not a new discovery.", + "asm_tell": "The tell is a symbol-set mismatch, not an instruction pattern: grep the TARGET .s for every `jal SYM` and `%hi(SYM)`/`%lo(SYM)` relocation and compare that set against the seed/twin's symbols. Target's real set was {func_80161208, func_80148E54, func_80012ABC, func_80148AFC, func_801466F0, func_80146A6C, func_80147324, func_801655E4, func_80019064, func_80165624, func_801487F4, func_80154274, D_80062BD0, D_800CBB00, D_800CBAE0} \u2014 completely disjoint from the seed/twin's {D_80078EC1, D_800CB7EC, func_80162D28, func_800CB338}.", + "c_shape": "No C construct reproduces this \u2014 the lever is procedural, run before drafting:\n`grep -oE '(jal +[A-Za-z_][A-Za-z0-9_]*)|%(hi|lo)\\([A-Za-z0-9_]*\\)' target.s | sed -E 's/jal +//; s/%(hi|lo)\\((.*)\\)/\\2/' | sort -u`\nthen diff that list against every symbol the candidate seed/twin uses; a non-empty diff means discard the seed entirely, even at 0.9+ stated similarity.", + "evidence": "No closeness>0 ever appeared. The only two match_one calls in the transcript both returned, verbatim: `{\"status\": \"match\", \"closeness\": 0, \"nins\": 105, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", \"profile\": null, \"bucket\": \"integration\", \"closeness\": 0, \"nins_mine\": 105, \"nins_tgt\": 105, \"sig\": \"MATCH\", \"detail\": {}}}` \u2014 first from the scratch draft, then re-confirmed byte-identically from the submit path. The diff that actually mattered was the pre-compile symbol-set check: `diff /tmp/x_tgt.txt /tmp/x_mine.txt && echo \"OK: symbol sets identical\"` \u2192 clean, only after the two decoy leads (warm-start body + the \"twin\" func_800CB028) had already been thrown out on inspection (\"none of its symbols ... appear in my target's relocations. Discarding it per law 1\").", + "cookbook_refs_used": [ + "\u00a7193-A", + "\u00a7194-E", + "\u00a7195-A", + "\u00a7199-G", + "\u00a7193-G", + "\u00a7222" + ], + "novel_claim": false, + "trivial": true + }, + { + "fn": "func_800CB578", + "arm": "opus", + "title": "REGALLOC-LOCAL s0/s1 swap: unify same-value pointer variables", + "mechanism": "gcc-2.7.2's local register allocator assigns callee-saved $sN slots to pseudo-registers by their live range; when one runtime pointer value is spread across several distinctly-named C locals (each a shorter, disjoint live range covering only one \"role\"), the allocator has no reason to pick the same $sN ordering it would give a single pseudo that lives across the whole tail. Splitting a value into multiple C variables can therefore silently permute which value lands in $s0 vs $s1 (and correspondingly which value ends up in $v0 vs $v1 at each use site). The transcript only demonstrates this empirically \u2014 a clean, whole-block s0<->s1 / v0<->v1 swap that vanishes once the three roles are folded into one variable \u2014 it does not derive gcc's exact slot-ordering rule, so the precise allocator mechanism stays unconfirmed beyond \"one variable, one long-lived pseudo, right slot.\"", + "asm_tell": "A whole contiguous block matches opcode-for-opcode and immediate-for-immediate, but two specific registers are swapped throughout the block (e.g. target `addu $s1,$v0,$s0` vs draft's `addu $s0,$v0,$s0`; target `lwl $v0,...` vs draft's `lwl $v1,...` on the matching lwl/lwr/swl/swr quad). match_one's own classifier buckets this as class REGALLOC-LOCAL, sig=REGALLOC-LOCAL, profile=regalloc, tagged [permuter].", + "c_shape": "/* WRONG: 3 C variables for one runtime pointer -> $s0/$s1 swapped, 30 ins off */\nu16 *q; u16 *r; u8 *u;\nq = D_800CC5B0 + off; /* loop-1 cursor */\n... func_800CB9F8(pos, q, ...); q += 0x10;\nr = D_800CC638 + off; /* call base */\n... func_800CB9F8(r, r+8, ...);\nu = &D_800CC638[0x10] + off; /* backward-shift cursor */\ndo { *(Blk8*)u = *(Blk8*)(u-8); u -= 8; } while (...);\n\n/* RIGHT: one long-lived pointer plays all 3 roles -> matches target's $s0/$s1 */\nu8 *p;\np = D_800CC5B0 + off;\n... func_800CB9F8(pos, (u16*)p, ...); p += 0x10;\np = D_800CC638 + off;\n... func_800CB9F8((u16*)p, (u16*)(p+8), ...);\np = &D_800CC638[0x10] + off;\ndo { *(Blk8*)p = *(Blk8*)(p-8); p -= 8; } while (...);", + "evidence": "match_one on the 3-variable draft (d2.c, after the destructive-division fix already applied): \"DIFF func_800CB578 mine=288 ins, target=288 ins, 30 mismatched / class: REGALLOC-LOCAL [permuter] sig=REGALLOC-LOCAL profile=regalloc\" \u2014 30 diffs, all register-only swaps (idx 175,196,200,213-265 etc., s0<->s1 and v0<->v1). After collapsing q/r/u into a single `u8 *p` (d3.c): \"MATCH (288 ins) func_800CB578\", confirmed via --json: {\"status\": \"match\", \"closeness\": 0, \"nins\": 288, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", ...}}. closeness 30 -> 0 in that one compile \u2014 no other change was made between d2 and d3 besides the pointer-variable merge.", + "cookbook_refs_used": [ + "\u00a7172b-1", + "\u00a7264", + "\u00a7195-A", + "\u00a717" + ], + "novel_claim": true, + "trivial": false + }, + { + "fn": "func_8017F608", + "arm": "opus", + "title": "One reused clamp-limit local across arms swaps $a0/$a1", + "mechanism": "gcc-2.7.2's local-alloc.c (local-alloc.c:472) refuses a cheap LOCAL-class allocno to any pseudo whose REG_N_DEATHS > 1; a single local materialized with three different constants across three separate if/else-if dispatch arms dies three times and gets promoted to a GLOBAL allocno, ranked by usage density over the whole function instead of its own tight scope, and loses the low register to a competing pseudo -- reading as a register swap against the target. Splitting it into one local per arm gives each pseudo exactly one death, so each stays LOCAL and lands in the register the target actually used. (Already documented, more precisely, as cookbook \u00a7136 rule 1.)", + "asm_tell": "match_one verdict klass REGALLOC-PERM, sig \"REGALLOC-PERM/$a0>$a1>$a0\" -- a clean two-way $a0<->$a1 register-map cycle at every site of an li/slti/subu-with-immediate triplet (e.g. mine `li $a0,0x800` / `subu $v0,$a0,$a1` vs target `addiu $a1,$zero,0x800` / `subu $v0,$a1,$a0`), with every opcode and immediate otherwise already byte-correct.", + "c_shape": "/* v9 (closeness 4): ONE local reused across 3 dispatch arms -> promoted to a global allocno, wins the wrong register */\ns16 k;\nif (sp10.v[0] < 0x700) {\n k = 0x600;\n if (sp10.v[0] < k) sp10.v[0] = k;\n} else if (sp10.v[2] < 0x1200) {\n k = 0x800;\n if (sp10.v[0] < k) *(s16*)(a0+0x2E) = k - sp10.v[0];\n} else {\n k = 0x7C0;\n if (sp10.v[0] < k) *(s16*)(a0+0x2E) = k - sp10.v[0];\n}\n\n/* v11 (closeness 0, MATCH): a SEPARATE local per arm -> each is a tight, single-death local allocno */\ns16 k2, k3, k4;\nif (sp10.v[0] < 0x700) {\n k2 = 0x600;\n if (sp10.v[0] < k2) sp10.v[0] = k2;\n} else if (sp10.v[2] < 0x1200) {\n k3 = 0x800;\n if (sp10.v[0] < k3) *(s16*)(a0+0x2E) = k3 - sp10.v[0];\n} else {\n k4 = 0x7C0;\n if (sp10.v[0] < k4) *(s16*)(a0+0x2E) = k4 - sp10.v[0];\n}", + "evidence": "match_one JSON across the draft ladder: v1 closeness 21 (direct literal compares) -> v9 closeness 4 after materializing a single reused `s16 k` per dispatch arm (mirrors sibling func_8017F420's idiom), residual verdict `{\"klass\":\"REGALLOC-PERM\",\"sig\":\"REGALLOC-PERM/$a0>$a1>$a0\",\"detail\":{\"map\":{\"$a0\":\"$a1\",\"$a1\":\"$a0\"}}}` at 4 sites -> v10 (renamed only the first arm's `k` to a second shared `lim`, sites 2/3 left on the old `k`) regressed to closeness 11 (\"== v10\\nnear 11\\n [26, '87a40010 lh a0,16(sp)', '87a30010 lh $v1, 0x10($sp)'] ... [55, '00651023 subu v0,v1,a1', '00a41023 subu $v0, $a1, $a0']\") -> v11 (gave each arm its own local: `s16 k, k2, k3, k4;`) hit closeness 0: \"== v11\\nmatch 0\". Final whole-file verify: `{\"status\": \"match\", \"closeness\": 0, \"nins\": 99, \"residual\": [], \"verdict\": {\"klass\": \"MATCH\", ...}}`.", + "cookbook_refs_used": [ + "\u00a7194-E (in-TU neighbour is the highest-yield source -- supplied by the target pack, not independently grepped)", + "\u00a7193-A (banked cross-overlay twin -- supplied by the pack; ultimately a dead end, discarded)", + "\u00a748-C2 (8-byte struct -> lwl/lwr/swl/swr copy -- cited only in the final draft's type comment)" + ], + "novel_claim": false, + "trivial": false + } + ], + "verify": [ + { + "fn": "func_800CAF9C", + "title": "Switch case body layout follows source text order, not value", + "verdict": "ADDENDUM", + "covered_by": "\u00a7255 \"AND CASE-BODY PLACEMENT\" (L24021) and its existing bound ADD-9 \u2192 \u00a7255 (L25383); \u00a7222 item 1 \"ARM ORDER IN THE SOURCE IS ARM ORDER IN THE ASM\" (L22755); mechanism already in the book at \u00a7199-G (L21011, `stmt.c:4749` + `stmt.c:5054-5056`)", + "entry_markdown": "**Addendum (P31 S62 T4, func_800CAF9C):** third measured exemplar, and it lands at the *smallest tree that has a root* \u2014 the regime this paragraph's DFS sentence was written from. `func_800CAF9C` (md_MAIN_015, 105 ins, byte-gate green) dispatches `{0x41, 0x53, 0x73}`; `balance_case_nodes`' `i == 3` arm roots on the median, so `beq $v1,0x53` is the FIRST test (`asm/md_MAIN_015/nonmatchings/md_MAIN_015/func_800CAF9C.s` @800CAFD4) \u2014 yet **the root's body sits SECOND**: case 0x41's body is `.L800CB008`, the shared `case 0x53: case 0x73:` body is `.L800CB034`. DFS-root-first is false here, and \u00a7199-G (L21011) says why it must be: `expand_end_case` takes `before_case = get_last_insn()` *after* every body is already in the stream (`stmt.c:4749`) and `reorder_insns` (`stmt.c:5054-5056`) hoists only the *header* in front of them \u2014 no pass ever permutes bodies. Read ADD-9's \"two regimes\" as one law plus misread ascending-order exemplars: **body order is source-clause order, always**; what survives of \u00a7255's sentence is its other half, that physical arm order does not name case VALUES.\n**Byte evidence.** Draft 1 spelled `case 0x53: case 0x73:` before `case 0x41:` \u2014 the compare chain (`lbu`/`beq 0x53`/`slti 0x54`/`beq 0x41`/`j`) was byte-identical through index ~9, then a total cascade from the first body onward: `near`, closeness 71, **47 of 104** residual. The ONLY edit was moving the two clauses (0x41 first); draft 2 \u2192 MATCH, 105/105, `residual: []`. **Diagnostic tell:** a residual that begins *exactly* at the first case body while the dispatch chain is already byte-clean \u21d2 reorder the case CLAUSES in source; do not touch polarity, empty cases, or the tree. **\u26a0 Bound:** this instance alone cannot separate source order from *ascending* order (0x41 < 0x53) \u2014 ADD-9's `func_8017F328` swap probe is what discriminates; this card's contribution is killing root-first at 3 nodes.", + "why": "Checked the whole switch cluster: \u00a755a's bullet (L4110-4115) already says \"case bodies emit in SOURCE order\" (func_801387B8, banked), \u00a7222 item 1 (L22755) states \"ARM ORDER IN THE SOURCE IS ARM ORDER IN THE ASM\" for dense switches (func_80183A1C: case 1 must precede case 0), and ADD-9 (L25383) measured it on a 29-case sparse tree with a one-word swap probe. \u00a7199-G (L21011) already carries the exact mechanism the distiller describes as new \u2014 bodies are in the insn stream in source order before `expand_end_case` runs, and `reorder_insns` hoists only the value-driven dispatch header in front of them, which IS \"decision tree value-driven, placement source-driven\". So the headline law is NOT new (also checked \u00a7135-1, \u00a7162a3, \u00a7163b, \u00a7163c, \u00a7164-11, \u00a7164-31, \u00a7164-37, \u00a7164-54, \u00a7164-55, \u00a7165-12, \u00a7165-28, \u00a7165-29, \u00a7193-G, \u00a7206.2, \u00a7256 \u2014 none states it better, none contradicts it). What IS new and worth an addendum: \u00a7255's \"AND CASE-BODY PLACEMENT\" (L24021) still asserts DFS-root-first, and ADD-9 only bounded it to \"two regimes with measured exemplars\" without telling a reader which one they are in. This function is a byte-proven counter-instance at 3 case nodes \u2014 the exact tree size \u00a7255's claim came from \u2014 with the median root's body verified second in .text (root `beq $v1,0x53` @800CAFD4 targets .L800CB034; case 0x41 body at .L800CB008, confirmed by reading the target .s). That collapses the two-regime picture and, with \u00a7199-G's cite, explains why no DFS regime can exist. Flagged the honest bound: 0x41 < 0x53, so this instance does not by itself separate source order from ascending order." + }, + { + "fn": "func_8017FB38", + "title": "Move+andi pair collapses to one andi unless arg pinned", + "verdict": "ADDENDUM", + "covered_by": "\u00a716Xy (L12925) \u2014 with \u00a7165-02/\u00a7165-09 (L13917), \u00a7165-03 (L13719), \u00a7165-04/\u00a7165-35 (L14619), \u00a7167-10 (L15245)", + "entry_markdown": "**Addendum (P31 S62 T4, func_8017FB38):** Fourth byte-instance, and a new cell \u2014 the cast is **narrower than the load**: `lh $v0,0x2C($a0) ; beqz ; addu $a0,$v0,$zero ; andi $a0,$a0,0xFF ; jal func_80016450` (0x8017FBC8, ov_SC07_000). \u00a716Xy's table carries `(u16)`-on-`s16` and `(u8)`-on-`s8`; the mixed `(u8)`-on-an-`lh` cell behaves identically, so read the law as *narrow memory load + narrower-or-opposite-signed cast at an SImode use*, not as a width match.\n**The crack re-derived \u00a716Xy's ablation row for row without finding it** \u2014 `v0 & 0xFF` \u2192 `andi $a0,$v0,0xff`, no copy (closeness 10, residual [36]); `u8 t = v0;` \u2192 neither instruction, residual `nop`; only the two-instruction form matches. It then reached MATCH by the expensive road: `register s32 a0r __asm__(\"$4\")` plus `__asm__(\"\" : \"=r\"(a0r) : \"0\"(a0r))` between the copy and the mask. **That launder is \u00a7165-04/\u00a7165-35's instrument** (the `\"=r\"`/`\"0\"` pair forces the value through an SImode register operand, blocking combine's fold into the `lh`) \u2014 whose \"honest scope: ONE A/B pair, body later abandoned\" note now has its second byte-proven instance. So the transcript's \"mechanism unknown at the RTL-pass level\" is answered by \u00a7165-03: the stranded SImode extension is a conflict-free orphan pseudo whose preferred class converges to `ST_REGS`, and `alter_reg` slots it.\n**Route to the declaration first, not the pin.** `s16 s = *(s16*)(a0+0x2C); if (s != 0) func_80016450((u8)s, 1);` buys the same pair with no `register __asm__` \u2014 which forfeits the family (\u00a737/\u00a7162p3) and, per \u00a7164-49, can sell you a schedule. The pin here was **never solo-ablated** against the barrier (\u00a7266), so cite the barrier and treat `$4` as an unproven rider.\n**Frame reconciliation \u2014 first function needing \u00a716Xy's and \u00a7167-10's counters SUMMED.** Target `.frame` is 0x30 with `sw $ra,0x28` and args=16 \u21d2 `vars = 24`, with zero `$sp` references anywhere in the body: three narrow-copy sites \u00d7 8 \u2014 two \u00a7167-10 compare-then-re-read-and-store-back copies (`addu $v1,$v0,$zero` at 0x8017FB58 and 0x8017FBA0) plus this \u00a716Xy cast-to-call copy. The draft hand-shipped `frame_pad[6]` for exactly those 24 bytes. **Falsifiable prediction:** spell all three sites as `s16` locals and the 24 bytes mint themselves \u2014 the pad then over-shoots to 0x48 and FAILs, the \u00a7162i1 footgun \u00a7167-10 already names.\n*Index gap:* `cookbook-index.md` L19 routes \"`andi` folded away in your output but present in the target\" to \u00a71/I2 + \u00a712 only. When the missing instruction is the **copy** and not the mask, the route is \u00a716Xy \u2192 \u00a7165-02 \u2192 \u00a7167-10.", + "why": "Not new: \u00a716Xy (L12925, \"THE `(u16)`-ON-A-HImode-VALUE PAIR COSTS 8 BYTES OF INVISIBLE FRAME PER SITE\") already states this law with the identical target shape (`addu $aN,$vN,$zero` + `andi` after a branch on a narrow memory load, feeding a call) and an isolated 4-line A/B whose three rows match the transcript's three measurements exactly \u2014 `(u16)s` on `s16 s` \u2192 copy+`andi`; `s & 0xFFFF` \u2192 `andi` with **no copy** (= the d2 residual `304400ff andi a0,v0,0xff`); `u16 s` \u2192 **neither** (= variant A's `nop`). \u00a7165-02/\u00a7165-09 (L13917) supplies the memory precondition and the construct table; \u00a7165-03 (L13719) supplies the RTL mechanism the transcript calls unknown (combine strands the SImode extension \u2192 `ST_REGS or none` orphan \u2192 `alter_reg` 8 bytes); \u00a7165-04/\u00a7165-35 (L14619) is precisely the `__asm__(\"\" : \"=r\"(t) : \"0\"(t))`-on-a-memory-loaded-narrow-value instrument the fix used. I confirmed against the target bytes (`asm/ov_SC07_000/nonmatchings/ov_SC07_000_jr_8017BEBC/func_8017FB38.s`): the value is `lh $v0,0x2C($a0)`, a narrow **memory** load \u2014 \u00a716Xy's precondition, and its explicit negative (\"value from a CALL RETURN \u2192 0\") is the only thing that could have excused the pin. Also checked and rejected as the covering \u00a7: \u00a7284 (L28986 \u2014 same asm-fence remedy but the *mask+mask* reassociation axis, and it is a header-only stub with body `#`), \u00a7249-3 (L23846, the `+ zr`/launder copy-resurrection family \u2014 general, not this construct), \u00a7162k1 (L11424) and \u00a7165-41 (L14726) (andi-counting, not the copy), \u00a7179-G (L17542, a pin *deleting* an `andi` \u2014 opposite polarity), \u00a73-I2/\u00a712 (the index's current route for this symptom), \u00a7167-10 (L15245), \u00a7266, \u00a737/\u00a7162p3/\u00a7164-49 (the pin's cost). ADDENDUM rather than COVERED because three things are genuinely new and byte-backed: the mixed-width cell (`(u8)` cast on an `lh`), a second instance for \u00a7165-04's single-A/B honest-scope caveat, and the first function where \u00a716Xy's and \u00a7167-10's orphan counters must be summed (3 copy sites \u00d7 8 = the target's 24 bytes of phantom `vars`, reconciled off the `.frame`/`sw $ra,0x28`/args=16 arithmetic). The submitted title's \"unless arg pinned\" is the half that does not survive: the pin was never ablated on its own (\u00a7266), the barrier is the load-bearing lever, and the declaration lever is cheaper than both." + }, + { + "fn": "func_8017FB38", + "title": "Single beqz IMM-OFFSET mismatch signals a missing early return", + "verdict": "ADDENDUM", + "covered_by": "\u00a7176-F row 3 (L17772, \"Misdiagnosis triage\" \u2014 `IMM-OFFSET` closeness 1, 44 ins, one `beqz` displacement off = a dropped conditional edge, `func_801878B8`)", + "entry_markdown": "**Addendum (P31 S62 T4, func_8017FB38):** row 3's tell reproduced verbatim on a second, independent function \u2014 `ov_SC07_000:func_8017FB38`, `IMM-OFFSET/6`, closeness 1, again **44 instructions**, again a lone `beqz $v0` whose *only* difference is the local-label immediate (mine `0x10400008` vs target `0x1040000E`; target asm `asm/ov_SC07_000/nonmatchings/ov_SC07_000_jr_8017BEBC/func_8017FB38.s:30`, the `beqz $v0,.L8017FBD8` at `8017FB9C`).\n\n**The dropped conditional edge has a SECOND C-level cause, and its fix is the mirror of row 3's.** Row 3's cause was a trailing statement written *after* a guard's closing brace (fix: move it in). This one is a **missing early `return;`**: the `t == 0` path was left to fall through into a later, logically-redundant `if (field2C != 0) { func_80016450(field2C & 0xFF, 1); }` instead of terminating, so that shared guard \u2014 not the epilogue \u2014 became what the `beqz` reaches. Spelling the bail-out explicitly, `} else { t = field2C; if (t == 0) { return; } t -= 0x10; \u2026 }`, gave **MATCH 44/44** with every other byte unchanged.\n\n**Read the SIGN of the immediate; it names the defect.** `match_one` emits residuals as `[i, mine, target]` (`tools/match_one.py:222`), so the two displacements are directly comparable. **Mine NEARER than the target's \u21d2 my C falls into a downstream block the target's C skips \u2014 add the `return;`.** **Mine FARTHER \u21d2 I terminate a path the target lets fall through \u2014 drop the terminator, or move the trailing statement inside the guard (row 3).** And the `delta` is a ruler, not noise: it is the instruction distance between the two candidate landing points (here 6, `+0x88` *inside* the shared guard \u2192 `+0xA0` the epilogue), so you can count straight to the block in question instead of searching the `.s`.\n\n**\u26a0 Do NOT bank the strong form \"gcc-2.7.2 never merges or elides a later guard whose earlier predecessor would also fail it.\"** The bytes refute it here: the fall-through draft did not land on the shared guard's *label*, it landed one instruction **inside** it (`+0x88`, past the `lh $v0,0x2C($a0)` reload jump1 knew was redundant given `$v0 == 0`) \u2014 which is exactly why the delta is 6 and not the guard block's full 7. The law is about **which block the edge enters**, not about guard elision. (Complements \u00a7225-2 / \u00a7225-8, which put early-return-vs-`if`-block on the prologue/epilogue and `j`-vs-branch axes with *large* residuals; this axis is the count-neutral one. \u00a7176-G's bound still applies: the same edge error is silent when the arm exit is a `j`.)", + "why": "Checked \u00a7176-F (row 3, L17772) \u2014 the tell is already banked there almost word for word: \"`IMM-OFFSET`, closeness **1** \u2014 all 44 instructions identical, one `beqz` displacement off by a small constant \u2192 **A dropped conditional edge** \u2026 trace which basic block each branch actually targets in the raw `.s` before assuming a scheduling nuance\" (`func_801878B8`, MATCH 44 ins). Same class, same closeness, same opcode, coincidentally the same instruction count. So this is not a NEW law. Also checked: \u00a7176-G (L19885/L19898, which explicitly BOUNDS \u00a7176-F row 3 to conditional branches and covers the `j`-blind sibling class); \u00a7164-55 (L13038, arm ORDER vs guard-clause spelling \u2014 costs a `j`, LENGTH-DRIFT +1, not count-neutral); \u00a7167-27 (L15655, both-arms-return swap, SHIFT-DRIFT/BRANCH-POLARITY); \u00a73-T4 and \u00a732.2 (branch polarity, indexed at cookbook-index L14); \u00a7225-2/\u00a7225-3/\u00a7225-8 (L22899/L24486ff, early-return vs `if`-block as a prologue/epilogue and `j`-fingerprint dial \u2014 15-ins and near-120 residuals, a different tell); \u00a7162f1 (L11211, non-void return type in delay slots); \u00a7136g-1 and \u00a7179-C/\u00a7182 (epilogue entries in cookbook-index L45/L1037/L1246). Grepped the S60/S61 addendum bundles (\u00a7274, \u00a7283, \u00a7294, \u00a7300) and the whole book for \"176-F\": the only existing addendum is to \u00a7176-F5 (L25842), nothing on row 3.\n\nWhat is genuinely new and byte-proven, hence ADDENDUM rather than COVERED: (1) a second, distinct C-level cause under row 3's single tell \u2014 a missing early `return;` rather than a statement placed outside a guard, with the opposite-direction fix; (2) the sign convention that discriminates the two, which is safe to state only because `tools/match_one.py:222` emits `[i, mine, target]` (verified in source \u2014 a distiller reading it the other way inverts the prescription); (3) the reading of `delta` as the instruction distance between the two candidate landing points.\n\nI trimmed one claim the evidence does not support. I read the target `.s` directly: target index 25 encodes `1040000E` \u2192 `8017FB9C + 4 + 56 = 8017FBD8` (the epilogue), and the draft's `10400008` \u2192 `8017FBC0` = `+0x88`, which is **not** the shared guard's label `.L8017FBBC` (`+0x84`) but one instruction inside it, past the `lh $v0,0x2C($a0)` reload. So the fall-through draft was jump-threaded past a reload gcc proved redundant \u2014 which directly contradicts the submitted mechanism sentence \"gcc-2.7.2 never merges/elides a later guard\", and also explains why delta is 6 while the shared guard block is 7 instructions. The addendum states the observable (which block the edge enters) and flags the over-strong generalization as not-to-be-banked. Everything else in the submission checks out against the bytes." + }, + { + "fn": "func_800CB578", + "title": "REGALLOC-LOCAL s0/s1 swap: unify same-value pointer variables", + "verdict": "COVERED", + "covered_by": "\u00a745 Lever A \u2014 MERGED ACCUMULATOR VARIABLES break a \"whole-function permutation\" (docs/matching-cookbook.md L3302); restated for POINTERS with its own diagnostic tells as \u00a7156 pole 1 / RC-14 MERGE (L10782, route at L10822); ref-count mechanism at \u00a776 (L6051)", + "entry_markdown": "", + "why": "Checked \u00a717 (L1376/L1384, the call-crossing $s0/$s1 swap class and its pin lever), \u00a7164-48 (L12864, the pin-free declared-width order lever for that same swap), \u00a7164-49 (L12890, which enumerates the pin-free allocation levers: \"declared width \u00a7163f, live-length slider \u00a747, ref-boost \u00a737, scope/reuse \u00a776/\u00a7162b1, RC-12 opaque copy \u00a7136d-1\"), \u00a745 Lever A (L3302), \u00a7156 (L10782-L10822), \u00a776 (L6028, L6051, L6303), \u00a7162b1's prior-art roster (L11076), \u00a744 Lever 3 (L3263), \u00a7136-1, \u00a7150, \u00a7167-24 (L15569), \u00a7176-B (L17650), \u00a7137 (L9344), and the \u00a7208 addendum's \"The inverse \u2014 naming can cost you\" (L24193-L24200). Index (docs/cookbook-index.md L156/L187/L648/L834/L1476/L1816) keys \u00a745 and \u00a7156 under exactly this symptom.\n\nThe claimed law IS \u00a745 Lever A, verbatim: \"When the target holds ONE $sN across disjoint value-regions ... gcc-2.7.2 global-alloc has no coalescing (K8), so one hard reg spanning disjoint regions can only come from one reused source variable. Merge the disjoint C variables into one \u2192 the allocno becomes call-crossing (K4, global.c:917) with a high merged ref-count \u2192 top density (K2, global.c:594 allocno_compare) \u2192 it allocates FIRST \u2192 plain regno first-fit (K3) reproduces the ENTIRE callee-saved permutation. AUDIT for reused-variable chains BEFORE calling a whole-function permutation unsteerable.\" \u00a7156 pole 1 already states the POINTER form (one `char *d; u8 *s;` reused as every phase's dst/src, 113\u219226 mismatches) plus the two diagnostic tells \u2014 \"(a) the same caller-saved reg hosting the same ROLE in every loop; (b) a callee-saved reg hosting values that individually never cross a call \u2014 both mean ONE reused source variable, THE 90s-dev frugality signature\" \u2014 and \u00a7156's Route line is the submission's prescription word for word: \"REUSE one variable before touching pins or densities \u2014 the \u00a717 pin is the fallback, not the opener.\" \u00a7162b1 (L11076) already indexes it as \"\u00a7156-1 (a merged scratch whose UNION range crosses a call goes $s0 everywhere)\". The submission adds no mechanism the book lacks \u2014 it explicitly declines to derive the allocator rule, whereas \u00a745-A/\u00a776 derive it from global.c:594 with citations and a reproducible priority computation.\n\nTwo corrections worth recording against the submission's framing rather than as new law. (1) The \"one runtime pointer VALUE spread across several distinctly-named C locals\" premise is factually wrong: I read `.run/t4/opus/scratch_func_800CB578/d2.c` vs `d3.c` and the three locals hold three DIFFERENT addresses \u2014 `D_800CC5B0 + off`, `D_800CC638 + off`, `&D_800CC638[0x10] + off`. That is precisely \u00a745-A's \"disjoint value-regions\", not a same-value split, so it is the covered case and not a new phenomenon. (2) \"no other change was made between d2 and d3 besides the pointer-variable merge\" is not literally true \u2014 the declared type also changed `u16 *`\u2192`u8 *`, with the arithmetic rescaled (`q + 8`/`q += 8` \u2192 `p + 0x10`/`p += 0x10`) to stay byte-equivalent. Both are 32-bit pointers so the mode is unchanged and the edit is byte-inert here, but per \u00a7150 the ablation was not single-variable, and \u00a7164-48/\u00a7162k1 make declared width a live confound for exactly this $s0/$s1 class \u2014 so this instance does not license any width claim.\n\nNo refutation: the outcome (closeness 30 \u2192 0 on the merge) is consistent with \u00a745-A. Note \u00a776 (L6303) already bounds the lever honestly \u2014 \"every merge lost, 43-3294 across 8 merges ... Diagnose whether you have a ranking problem or an identity problem before reaching for a merge\" \u2014 so a fresh n=1 win adds no new scope either." + }, + { + "fn": "func_8017E7D0", + "title": "Spurious nop before div-result store: reorder trailing statements", + "verdict": "NEW", + "covered_by": "\u2014", + "entry_markdown": "## \u00a7NNN \u2014 A HAZARD `nop` IN FRONT OF A DIV-RESULT STORE IS A STATEMENT-ORDER DEFECT: THE INDEPENDENT TRAILING STATEMENT MUST BE WRITTEN *BEFORE* THE DIVISION-CONSUMING ONE (P31 S62 T4; byte-proven func_8017E7D0)\n\n**THE TELL.** A `LENGTH-DRIFT +1` whose extra instruction is a bare `nop` sitting immediately before the `sw` that stores a division result, while an adjacent, data-independent global store (`lui $at,%hi(G)` / `sh $zero,%lo(G)($at)`) appears AFTER that `sw` in your draft and BEFORE it in the target:\n\n mine: \u2026 mflo $a0 ; nop ; sw $a0,0x48($s0) ; lui $at ; sh $zero,%lo(G)($at)\n target: \u2026 mflo $a0 ; lui $at ; sh $zero,%lo(G)($at) ; sw $a0,0x48($s0)\n\nDo not touch registers, pins, barriers or the permuter \u2014 the whole residual is one statement in the wrong place.\n\n**THE MECHANISM (two halves; the second is source-verified).** (1) cc1 sees `div` as ONE insn carrying the long `imuldiv` latency, so its consumer store is not ready for many cycles and the scheduler fills the shadow \u2014 but only from insns already available in LUID (= source) order. An independent statement written BEFORE the division statement sinks into that shadow; one written AFTER it is not pulled back into it. (2) The visible `nop` is **not gcc's** \u2014 maspsx splices it: after the `--expand-div` expansion, `_handle_nop_before_next_instruction` (`tools/maspsx/maspsx/__init__.py:642-675`, called at `:1137`) emits `nop # DEBUG: Reuse of ''` whenever the instruction following the expanded `mflo` reads the quotient register. So an empty shadow costs exactly one instruction, and *any* insn that does not read the quotient kills it. **Corollary (source-read, not A/B'd):** that same rule exempts a next instruction which uses `$at` while `nop_at_expansion` is false \u2014 which our pinned `--aspsx-version=2.56` gives (`maspsx.py:99-104`) \u2014 so a division result stored *straight to a global* through `lui $at` / `%lo(SYM)($at)` never pays this nop at all.\n\n**THE C SHAPE.** Swap the two independent trailing statements so the one with NO dependence on the division comes first:\n\n```c\n/* target order \u2014 shadow filled, no nop (54 ins) */\nD_8019F70C = 0;\n*(s32 *)(a0 + 0x48) = -D_80188A34 / ((s16)D_80188A2A[0] / 2);\n\n/* wrong order \u2014 empty shadow, maspsx nop (55 ins) */\n*(s32 *)(a0 + 0x48) = -D_80188A34 / ((s16)D_80188A2A[0] / 2);\nD_8019F70C = 0;\n```\n\n**BYTE EVIDENCE.** `func_8017E7D0` (ov_SC06_016, `_jr_8017C8D0`, 54 ins). Target tail `asm/ov_SC06_016/nonmatchings/ov_SC06_016_jr_8017C8D0/func_8017E7D0.s:50-53` = `mflo $a0 / lui $at,%hi(D_8019F70C) / sh $zero,%lo(D_8019F70C)($at) / sw $a0,0x48($s0)`, under the signed `--expand-div` form (`break 7` / `break 6`, \u00a7228-3). v1 (division store first): `mine=55, target=54, 10 mismatched, class LENGTH-DRIFT [structural]`, first mismatch `idx45: 00000000 nop | 3c01801a lui $at,%hi(D_8019F70C)`. v2 = the same file with ONLY those two statements swapped: `MATCH (54 ins)`; `--json` verify `{\"status\":\"match\",\"closeness\":0,\"nins\":54,\"residual\":[]}`. One function, both directions measured.\n\n*(Extends **\u00a716Xb** (L13142) \u2014 the `mult`\u2192`mflo` window read at the div's shadow: \u00a716Xb's tell is a ZERO-drift reorder and its lever is \"move the statement that FOLLOWS the multiplying statement\"; here the drift is +1 and the filler comes from the statement BEFORE, so \u00a716Xb's prescription points the wrong way. Instance of **\u00a72-T2** (L78). Kin of the **\u00a7253/\u00a7165-06** note (L25420: postfix `(*p)++` keeps the store after the `mfhi`/`bnez` pair, bare `++` sinks it before the div) and of **\u00a750-E** (L3616), which prices global-store REORDER the other way (an assembler-merged `lui $at`). **\u00a7179-B rule 4** (L17393) owns this same maspsx nop splicer for hand-written asm; this entry is its C-side face.)*", + "why": "Checked \u00a71-I4 (L60-65: the runtime-div `divu`/`bnez`/`break`/`mflo` shape \u2014 gives the expansion, says nothing about the trailing nop or statement order); \u00a72-T2 (L78-81: \"source statement order drives instruction scheduling\" \u2014 the generic parent, no div/nop case, no tell); \u00a716Xb / \u00a7164-59 (L13140-13155: the `mult`\u2192`mflo` WINDOW as a source-position oracle \u2014 closest prior art, same LUID mechanism, but a DIFFERENT gap (between mult and mflo, not after the mflo), a different symptom (its stated tell is \"LENGTH-DRIFT 0 \u2026 same instructions in a different order\"), and a lever that points the opposite way (\"move the statement that FOLLOWS the multiplying statement\") \u2014 following it here would not find this fix); \u00a7228-1..6 (L22995-23030: reading the divide \u2014 off-by-one compare, `%30` vs `%15`, `break 7`/`break 6` = signed var divide, `>>2` \u2260 `/4`; all about spelling the arithmetic, none about the trailing nop); \u00a7253/\u00a7165-06 note (L25420: postfix vs pre-increment decides store placement relative to the `mfhi`/`bnez` \u2014 an adjacent axis, different dial, no nop/length claim); \u00a7167-03, \u00a7167-39, \u00a7194-I/\u00a716N+3, \u00a7201-D (div magic/width/signedness \u2014 unrelated); \u00a767 (L5332/L5401: \"+1 ins with an unfilled LOAD-delay nop\" \u2014 the parameter-copy launder, different slot and different cure); the \u00a7135-13..15 scheduling bullets (L8917-8926: load-delay nop fills, hoist a load / split an RMW \u2014 load slots, not the div shadow); \u00a7179-B rule 4 (L17393-17400: maspsx's nop splicer `_handle_nop_before_next_instruction` is UNCONDITIONAL \u2014 states the exact mechanism but only as a hand-written-asm transcription rule, with no C-side statement-order face); \u00a7188 (L18157: assembler-artifact tails), \u00a750-E (L3616: reordering same-page global stores merges `lui $at` \u2014 store-order law, opposite direction/mechanism); \u00a7299 (L29479: statement boundary vs within-expression chain order). Grep of docs/cookbook-index.md for nop symptoms surfaces only \u00a75a/\u00a734 (branch-delay steal), \u00a7194-H, \u00a7243, \u00a7187/\u00a7195-J (GTE hazard nops) \u2014 nothing symptom-keyed to \"extra nop before a div-result store\". No \u00a7 states this law, so NEW. Two corrections folded into the entry rather than refuting the claim: the nop is spliced by maspsx post-cc1 (verified in tools/maspsx/maspsx/__init__.py:642-675 and :1129-1139), not emitted by gcc's scheduler as the distiller's mechanism says; and that splicer exempts a `$at`-using consumer under our pinned `--aspsx-version=2.56` (maspsx.py:91-104), which bounds when the tell can appear at all. Target asm re-read directly (func_8017E7D0.s:38-53) and it matches the claimed target order exactly." + }, + { + "fn": "func_8017F608", + "title": "Clamp store lands in $v0; target uses $a0 (REGALLOC-PERM)", + "verdict": "ADDENDUM", + "covered_by": "\u00a7211 \u2014 HOIST THE LOOP INIT ABOVE THE DOMINATING GUARD (L22347), together with its P31 S58b addendum item 3 (L24247, the \u00a747 live-length slider face)", + "entry_markdown": "**Addendum (P31 S62 T4, func_8017F608):** \u00a7211's half **(b)** \u2014 *\"hoisting it above the guard lengthens the pseudo's live range across the guard block, flipping the local-alloc contest\"* \u2014 fires with **no loop, no induction pseudo, and no instruction-count or delay-slot change at all**, so \u00a7211's own BOUNDARY (\"it only bites when the guard/branch actually has a fillable slot and the two induction pseudos actually contend\") is too narrow: the contest can be a single **constant** against local-alloc's generic-temp default. `func_8017F608` (ov_SC02_028, `jr_8017D898`, 99 ins) sat at closeness 2, `REGALLOC-PERM/$v0>$a0`, on exactly one pair \u2014 mine `li $v0,0x600` / `sh $v0,0x10($sp)` vs target `addiu $a0,$zero,0x600` / `sh $a0,0x10($sp)` \u2014 with \u00a7211's half (a) **already satisfied** (both builds put the `addiu` in the guard's own delay slot; only the colour differed). The fix: hoist the clamp constant into a named local **above** the guard and make the guard test the local, not the literal \u2014 `s16 clampval = 0x600; if (sp10.v[0] < clampval) { sp10.v[0] = clampval; }` \u2192 `match`, 99/99, residual `[]`; the stuck form declared the local *inside* the taken arm and tested the raw literal (closeness 2).\n**THE TELL IS IN THE TARGET, NOT IN YOUR DIFF.** Read the *neighbouring* guard's delay slot: `asm/ov_SC02_028/nonmatchings/ov_SC02_028_jr_8017D898/func_8017F608.s` holds `8017F680 addu $a0,$v1,$zero` (outer `slti \u2026,0x700` slot) and `8017F68C addiu $a0,$zero,0x600` (inner `slti \u2026,0x600` slot) \u2014 the constant **re-defines the same register an earlier guard's slot already loaded**, which is one named local with two defs spanning both guards, never a store-time temp. A fresh temp takes `$v0` by local-alloc's copy-suggestion default (\u00a7186c, as corrected by \u00a7194-F). This is \u00a7208's pseudo-set principle run in the **merge** direction (\u00a7208 splits one expression into two locals to buy two registers; this merges a compare operand and a store source into one local to buy one register), and it is a second counter-example to \u00a7137's \"source-level levers are a dead end for REGALLOC-PERM\": the edit changes the pseudo SET, which \u00a7137's R/L arithmetic cannot see.\n**BOUNDS (read these before quoting the recipe).** (1) The A/B moved **two things at once** \u2014 declaration position *and* the compare's operand \u2014 so per \u00a7266 neither half is independently citable; probe decl-above-with-literal-test before treating \"test the local, not the literal\" as the load-bearing clause. (2) The rematerialisation story is the drafter's hypothesis, not read out of a `-dl`/`-dg` dump; what is byte-proven is the source edit \u2192 MATCH. (3) The drafter's side claim that the same hoist \"also flipped several BRANCH-POLARITY residuals earlier in the same draft\" is unquantified and unverified \u2014 treat as a lead, not a law. (4) **Tried and failed, worth as much:** `register s32 clampval __asm__(\"$4\")` on the same body REGRESSED 2 \u2192 62 with **+1 ins** (99 \u2192 100) \u2014 another row for \u00a7257/\u00a7268's pin ledger, and a straight confirmation of \u00a7176-B: on a 2-instruction REGALLOC-PERM, reach for the naming/scope lever first and the pin never.", + "why": "Checked \u00a7211 + its P31 S58b addendum (L22347/L24231, items 1-5), \u00a747 (live-length slider), \u00a748-A1/-A2/-A3 (allocno-pricing dials: \"sink the init to raise, hoist to the join to lower\"), \u00a776 (scope/reuse is C's only route to the allocno class), \u00a7137 (REGALLOC-PERM as two-compile arithmetic; its \"source levers are a dead end\" clause), \u00a7176-B (a 1-4 ins REGALLOC-PERM is usually not regalloc), \u00a7186c + \u00a7194-F (which allocator owns the value; $v0-by-copy-suggestion vs $aN-by-set_preference \u2014 and \u00a7194-F carries the literally identical `REGALLOC-PERM/$v0>$a0`, 99 ins, 2-mismatched signature on func_80186A58, but for a call-result store, a different construct), \u00a7208/\u00a7209 (naming granularity owns the register split; the \"opposite direction\" note sinks a LOAD into an if-condition, not a constant), \u00a7194-B and its S60/dj addendum (the unconditional pre-hoisted copy before a guard \u2014 same lever family, but it buys an instruction's EXISTENCE, not a colour), \u00a7249-2 (a single named local assigned twice lengthens a range to coalesce two loads), \u00a7257 items 1-4 and \u00a7268 (pin ledger), \u00a7285 (pre-initialising with a shared constant before a branch), plus a keyword sweep of the S58b/S60/S61 addenda mega-sections \u00a7258/\u00a7267/\u00a7269/\u00a7274/\u00a7278/\u00a7283/\u00a7294/\u00a7300 and the symptom-keyed docs/cookbook-index.md (REGALLOC-PERM, hoist, clamp, live range, block-scope rows). No \u00a7 states this law: none covers a CONSTANT hoisted into a named local above a plain `if` guard with the guard respelled to test the local, and \u00a7211 \u2014 the closest \u2014 explicitly bounds itself to loop inits with contending induction pseudos and a fillable slot. So it is not COVERED and not NEW: \u00a7211's law-(b) already states the exact mechanism, and this card extends it beyond loops while bounding \u00a7211's BOUNDARY sentence \u2014 an addendum. Not REFUTED: I read the target myself and it corroborates the claim beyond the transcript \u2014 `8017F680 addu $a0,$v1,$zero` and `8017F68C addiu $a0,$zero,0x600` show one register serving two guards' delay slots, i.e. one local with two defs, which is stronger evidence for the merged-pseudo reading than the drafter's rematerialisation hypothesis. The confounded A/B (two edits at once) and the unverified branch-polarity side claim are recorded as bounds rather than grounds for refusal." + } + ] +} \ No newline at end of file diff --git a/.run/t4/judge.json b/.run/t4/judge.json new file mode 100644 index 000000000..6254c36c6 --- /dev/null +++ b/.run/t4/judge.json @@ -0,0 +1,216 @@ +{ + "arms": { + "haiku": { + "banked": [ + "func_8017E014", + "func_8017EF90", + "func_8017F590", + "func_8017FF74", + "func_80180F5C", + "func_80184FB4" + ], + "n_banked": 6, + "drafts": 20, + "note": "rc=0 46s 20 staged | banked set derived from the gate log (judge detection bug fixed after)" + }, + "sonnet": { + "banked": [ + "func_800CAF9C", + "func_8017E014", + "func_8017E54C", + "func_8017E7D0", + "func_8017EF90", + "func_8017F3F8", + "func_8017F590", + "func_8017F608", + "func_8017FB38", + "func_8017FF74", + "func_801804C0", + "func_80180F5C", + "func_801810A4", + "func_80181894" + ], + "n_banked": 14, + "drafts": 20, + "note": "rc=0 37s 20 staged | banked set derived from the gate log (judge detection bug fixed after)" + }, + "opus": { + "banked": [ + "func_800CAF9C", + "func_800CB578", + "func_8017E014", + "func_8017E54C", + "func_8017E7D0", + "func_8017EF90", + "func_8017F3F8", + "func_8017F590", + "func_8017F608", + "func_8017FB38", + "func_8017FF74", + "func_801804C0", + "func_801808E8", + "func_80180F5C", + "func_801810A4", + "func_80181894", + "func_80184FB4" + ], + "n_banked": 17, + "drafts": 20, + "note": "rc=0 16s 20 staged | banked set derived from the gate log (judge detection bug fixed after)" + }, + "haiku2": { + "banked": [ + "func_8017F3F8", + "func_801804C0" + ], + "n_banked": 2, + "drafts": 14, + "note": "rc=0 23s 14 staged" + }, + "ds": { + "banked": [ + "func_800CAF9C", + "func_8017E014", + "func_8017E7D0", + "func_8017EF90", + "func_8017F3F8", + "func_8017F590", + "func_8017FB38", + "func_8017FF74", + "func_80180F5C", + "func_801810A4", + "func_80184FB4" + ], + "n_banked": 11, + "drafts": 20, + "note": "rc=0 40s 20 staged" + } + }, + "fns": { + "func_8017E014": [ + "haiku", + "sonnet", + "opus", + "ds" + ], + "func_8017EF90": [ + "haiku", + "sonnet", + "opus", + "ds" + ], + "func_8017F590": [ + "haiku", + "sonnet", + "opus", + "ds" + ], + "func_8017FF74": [ + "haiku", + "sonnet", + "opus", + "ds" + ], + "func_80180F5C": [ + "haiku", + "sonnet", + "opus", + "ds" + ], + "func_80184FB4": [ + "haiku", + "opus", + "ds" + ], + "func_800CAF9C": [ + "sonnet", + "opus", + "ds" + ], + "func_8017E54C": [ + "sonnet", + "opus" + ], + "func_8017E7D0": [ + "sonnet", + "opus", + "ds" + ], + "func_8017F3F8": [ + "sonnet", + "opus", + "haiku2", + "ds" + ], + "func_8017F608": [ + "sonnet", + "opus" + ], + "func_8017FB38": [ + "sonnet", + "opus", + "ds" + ], + "func_801804C0": [ + "sonnet", + "opus", + "haiku2" + ], + "func_801810A4": [ + "sonnet", + "opus", + "ds" + ], + "func_80181894": [ + "sonnet", + "opus" + ], + "func_800CB578": [ + "opus" + ], + "func_801808E8": [ + "opus" + ] + }, + "union": { + "picked": { + "func_8017E014": "haiku", + "func_8017EF90": "haiku", + "func_8017F590": "haiku", + "func_8017FF74": "haiku", + "func_80180F5C": "haiku", + "func_80184FB4": "haiku", + "func_800CAF9C": "sonnet", + "func_8017E54C": "sonnet", + "func_8017E7D0": "sonnet", + "func_8017F3F8": "sonnet", + "func_8017F608": "sonnet", + "func_8017FB38": "sonnet", + "func_801804C0": "sonnet", + "func_801810A4": "sonnet", + "func_80181894": "sonnet", + "func_800CB578": "opus", + "func_801808E8": "opus" + }, + "banked": [ + "func_800CAF9C", + "func_800CB578", + "func_8017E014", + "func_8017E54C", + "func_8017E7D0", + "func_8017EF90", + "func_8017F3F8", + "func_8017F590", + "func_8017F608", + "func_8017FB38", + "func_8017FF74", + "func_801804C0", + "func_801808E8", + "func_80180F5C", + "func_801810A4", + "func_80181894", + "func_80184FB4" + ], + "rc": 0 + } +} \ No newline at end of file diff --git a/.run/t4/judge.py b/.run/t4/judge.py new file mode 100644 index 000000000..3f1dcac05 --- /dev/null +++ b/.run/t4/judge.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +"""T4 judge — per-arm whole-binary gate (G3/P9, exit codes checked per R53), tree reset between arms, +then the union (best arm per fn) gated once and left in the tree for the commit. Reads targets from +.run/t4/targets.json and drafts from .run/t4//.c. Writes .run/t4/judge.json. +Usage: judge.py [arms...] (default haiku sonnet opus ds)""" +import sys, os, json, glob, subprocess, shutil, time +REPO = '/home/musashi/bfm-decomp'; os.chdir(REPO) +sys.path.insert(0, 'tools'); import corpus +UNION = '--union' in sys.argv +ARMS = [a for a in sys.argv[1:] if not a.startswith('--')] or ['haiku', 'sonnet', 'opus', 'ds'] +targets = json.load(open('.run/t4/targets.json')) +T = {t['name']: t for t in targets} +def sh(cmd, timeout=3600): + return subprocess.run(cmd, capture_output=True, text=True, timeout=timeout) +def open_fns(): + out = {} + for b in {t['binary'] for t in targets}: + s = corpus.stubs(b); out[b] = {x.symbol for x in (s.values() if isinstance(s, dict) else s)} + return out +def reset_tree(): + sh(['git', 'checkout', '-q', '--', 'src/', 'config/']); sh(['git', 'clean', '-fq', 'src/']) + r = sh(['git', 'status', '--porcelain', '--', 'src/', 'config/']) + assert not r.stdout.strip(), 'tree not clean after reset:\n' + r.stdout +def stage(arm, fns): + d = '.run/t4/stage_%s' % arm; shutil.rmtree(d, ignore_errors=True) + n = 0 + for fn in fns: + src = '.run/t4/%s/%s.c' % (arm, fn) + if not os.path.exists(src) or os.path.getsize(src) == 0: continue + b = T[fn]['binary']; os.makedirs('%s/%s' % (d, b), exist_ok=True) + shutil.copy(src, '%s/%s/%s.c' % (d, b, fn)); n += 1 + return d, n +def gate(arm, fns): + before = open_fns() + d, n = stage(arm, fns) + if n == 0: return {}, 'no drafts' + bins = ','.join(sorted({T[f]['binary'] for f in fns if os.path.exists('.run/t4/%s/%s.c' % (arm, f))})) + t0 = time.time() + r = sh(['flock', '.run/auto/draw.lock', '.venv/bin/python', 'tools/sweep_parallel.py', '--drafts', d, '--only', bins, '-j', '4'], 7200) + open('.run/t4/gate_%s.log' % arm, 'w').write(r.stdout + r.stderr) + # banked = INCLUDE_ASM lines the gate removed (git diff; immune to corpus's per-process cache) + d = sh(['git', 'diff', '-U0', '--', 'src/']).stdout + import re as _re + removed = set(_re.findall(r'^-INCLUDE_ASM\("[^"]+",\s*(func_\w+)\);', d, _re.M)) + banked = {fn for fn in fns if fn in removed} + return banked, 'rc=%d %ds %d staged' % (r.returncode, time.time() - t0, n) +def main(): + reset_tree() + res = {'arms': {}, 'fns': {}} + if os.path.exists('.run/t4/judge.json'): + old = json.load(open('.run/t4/judge.json')); res['arms'] = old.get('arms', {}) + for a, v in res['arms'].items(): + for f in v.get('banked', []): res['fns'].setdefault(f, []).append(a) + for arm in ARMS: + fns = [t['name'] for t in targets] + banked, note = gate(arm, fns) + res['arms'][arm] = {'banked': sorted(banked), 'n_banked': len(banked), 'drafts': sum(1 for f in fns if os.path.exists('.run/t4/%s/%s.c' % (arm, f))), 'note': note} + print('%-7s banked %2d / %2d drafts (%s)' % (arm, len(banked), res['arms'][arm]['drafts'], note), flush=True) + for f in banked: res['fns'].setdefault(f, []).append(arm) + reset_tree() + # union: cheapest winning arm per fn (ladder order), gated once and LEFT IN THE TREE for the commit + order = ['haiku', 'sonnet', 'ds', 'opus'] + pick = {} + for f, arms in res['fns'].items(): + pick[f] = sorted(arms, key=lambda a: order.index(a) if a in order else 99)[0] + ud = '.run/t4/stage_union'; shutil.rmtree(ud, ignore_errors=True) + for f, a in pick.items(): + b = T[f]['binary']; os.makedirs('%s/%s' % (ud, b), exist_ok=True) + shutil.copy('.run/t4/%s/%s.c' % (a, f), '%s/%s/%s.c' % (ud, b, f)) + if pick and UNION: + bins = ','.join(sorted({T[f]['binary'] for f in pick})) + r = sh(['flock', '.run/auto/draw.lock', '.venv/bin/python', 'tools/sweep_parallel.py', '--drafts', ud, '--only', bins, '-j', '4'], 7200) + open('.run/t4/gate_union.log', 'w').write(r.stdout + r.stderr) + d = sh(['git', 'diff', '-U0', '--', 'src/']).stdout + import re as _re + removed = set(_re.findall(r'^-INCLUDE_ASM\("[^"]+",\s*(func_\w+)\);', d, _re.M)) + res['union'] = {'picked': pick, 'banked': sorted(f for f in pick if f in removed), 'rc': r.returncode} + print('union: %d picked, %d banked (rc=%d) — LEFT IN TREE for the commit' % (len(pick), len(res['union']['banked']), r.returncode)) + json.dump(res, open('.run/t4/judge.json', 'w'), indent=1) + # per-band table (R41: denominators) + band = lambda n: '<=50' if n <= 50 else ('51-120' if n <= 120 else '>120') + for arm in res['arms']: + row = {} + for t in targets: + k = band(t['nins']); row.setdefault(k, [0, 0]); row[k][1] += 1 + if t['name'] in res['arms'][arm]['banked']: row[k][0] += 1 + print('%-7s ' % arm + ' '.join('%s: %d/%d' % (k, v[0], v[1]) for k, v in sorted(row.items()))) +if __name__ == '__main__': + main() diff --git a/.run/t4/sample.json b/.run/t4/sample.json new file mode 100644 index 000000000..6162c76e5 --- /dev/null +++ b/.run/t4/sample.json @@ -0,0 +1,122 @@ +[ + { + "binary": "ov_SC07_000", + "fn": "func_8017FB38", + "nins": 44, + "cls": "K-NEVER-TOUCHED" + }, + { + "binary": "ov_SC02_031", + "fn": "func_80180F5C", + "nins": 37, + "cls": "K-NEVER-TOUCHED" + }, + { + "binary": "ov_SC01_006", + "fn": "func_8017FF74", + "nins": 39, + "cls": "K-NEVER-TOUCHED" + }, + { + "binary": "ov_SC03_028", + "fn": "func_80184FB4", + "nins": 29, + "cls": "K-NEVER-TOUCHED" + }, + { + "binary": "ov_SC05_017", + "fn": "func_801810A4", + "nins": 40, + "cls": "K-NEVER-TOUCHED" + }, + { + "binary": "ov_SC03_114", + "fn": "func_8017E014", + "nins": 23, + "cls": "K-NEVER-TOUCHED" + }, + { + "binary": "ov_SC03_124", + "fn": "func_8017EF90", + "nins": 49, + "cls": "K-NEVER-TOUCHED" + }, + { + "binary": "ov_SC02_031", + "fn": "func_8017F590", + "nins": 9, + "cls": "K-NEVER-TOUCHED" + }, + { + "binary": "ov_SC06_010", + "fn": "func_8017E54C", + "nins": 32, + "cls": "K-NEVER-TOUCHED" + }, + { + "binary": "ov_SC02_037", + "fn": "func_8017F3F8", + "nins": 38, + "cls": "K-NEVER-TOUCHED" + }, + { + "binary": "ov_SC02_027", + "fn": "func_80181894", + "nins": 112, + "cls": "K-NEVER-TOUCHED" + }, + { + "binary": "md_MAIN_015", + "fn": "func_800CAF9C", + "nins": 105, + "cls": "K-NEVER-TOUCHED" + }, + { + "binary": "ov_SC03_011", + "fn": "func_801804C0", + "nins": 54, + "cls": "K-NEVER-TOUCHED" + }, + { + "binary": "ov_SC06_016", + "fn": "func_8017E7D0", + "nins": 54, + "cls": "K-NEVER-TOUCHED" + }, + { + "binary": "ov_SC02_028", + "fn": "func_8017F608", + "nins": 99, + "cls": "K-NEVER-TOUCHED" + }, + { + "binary": "md_SC07_004", + "fn": "func_801A8E34", + "nins": 118, + "cls": "M-DRAFT-extend-tell" + }, + { + "binary": "md_MAIN_019", + "fn": "func_800CB578", + "nins": 288, + "cls": "M-DRAFT-extend-tell" + }, + { + "binary": "ov_SC04_004", + "fn": "func_80181D1C", + "nins": 36, + "cls": "M-DRAFT-extend-tell" + }, + { + "binary": "ov_SC05_003", + "fn": "func_801808E8", + "nins": 135, + "cls": "M-DRAFT-extend-tell" + }, + { + "binary": "md_MAIN_016", + "fn": "func_800CAF84", + "nins": 117, + "cls": "M-DRAFT-extend-tell" + } +] \ No newline at end of file diff --git a/.run/t4/targets.json b/.run/t4/targets.json new file mode 100644 index 000000000..63b57c524 --- /dev/null +++ b/.run/t4/targets.json @@ -0,0 +1,202 @@ +[ + { + "name": "func_8017FB38", + "addr": "0x8017fb38", + "nins": 44, + "binary": "ov_SC07_000", + "sub": "asm/ov_SC07_000/nonmatchings/ov_SC07_000_jr_8017BEBC", + "asm": "asm/ov_SC07_000/nonmatchings/ov_SC07_000_jr_8017BEBC/func_8017FB38.s", + "tu": "src/ov_SC07_000/ov_SC07_000_jr_8017BEBC.c", + "cls": "K-NEVER-TOUCHED" + }, + { + "name": "func_80180F5C", + "addr": "0x80180f5c", + "nins": 37, + "binary": "ov_SC02_031", + "sub": "asm/ov_SC02_031/nonmatchings/ov_SC02_031_jr_8017AE2C", + "asm": "asm/ov_SC02_031/nonmatchings/ov_SC02_031_jr_8017AE2C/func_80180F5C.s", + "tu": "src/ov_SC02_031/ov_SC02_031_jr_8017AE2C.c", + "cls": "K-NEVER-TOUCHED" + }, + { + "name": "func_8017FF74", + "addr": "0x8017ff74", + "nins": 39, + "binary": "ov_SC01_006", + "sub": "asm/ov_SC01_006/nonmatchings/ov_SC01_006_jr_8017ED5C", + "asm": "asm/ov_SC01_006/nonmatchings/ov_SC01_006_jr_8017ED5C/func_8017FF74.s", + "tu": "src/ov_SC01_006/ov_SC01_006_jr_8017ED5C.c", + "cls": "K-NEVER-TOUCHED" + }, + { + "name": "func_80184FB4", + "addr": "0x80184fb4", + "nins": 29, + "binary": "ov_SC03_028", + "sub": "asm/ov_SC03_028/nonmatchings/ov_SC03_028_jr_80184914", + "asm": "asm/ov_SC03_028/nonmatchings/ov_SC03_028_jr_80184914/func_80184FB4.s", + "tu": "src/ov_SC03_028/ov_SC03_028_jr_80184914.c", + "cls": "K-NEVER-TOUCHED" + }, + { + "name": "func_801810A4", + "addr": "0x801810a4", + "nins": 40, + "binary": "ov_SC05_017", + "sub": "asm/ov_SC05_017/nonmatchings/ov_SC05_017_jr_801808D4", + "asm": "asm/ov_SC05_017/nonmatchings/ov_SC05_017_jr_801808D4/func_801810A4.s", + "tu": "src/ov_SC05_017/ov_SC05_017_jr_801808D4.c", + "cls": "K-NEVER-TOUCHED" + }, + { + "name": "func_8017E014", + "addr": "0x8017e014", + "nins": 23, + "binary": "ov_SC03_114", + "sub": "asm/ov_SC03_114/nonmatchings/ov_SC03_114_jr_8017BEBC", + "asm": "asm/ov_SC03_114/nonmatchings/ov_SC03_114_jr_8017BEBC/func_8017E014.s", + "tu": "src/ov_SC03_114/ov_SC03_114_jr_8017BEBC.c", + "cls": "K-NEVER-TOUCHED" + }, + { + "name": "func_8017EF90", + "addr": "0x8017ef90", + "nins": 49, + "binary": "ov_SC03_124", + "sub": "asm/ov_SC03_124/nonmatchings/ov_SC03_124_jr_8017AE2C", + "asm": "asm/ov_SC03_124/nonmatchings/ov_SC03_124_jr_8017AE2C/func_8017EF90.s", + "tu": "src/ov_SC03_124/ov_SC03_124_jr_8017AE2C.c", + "cls": "K-NEVER-TOUCHED" + }, + { + "name": "func_8017F590", + "addr": "0x8017f590", + "nins": 9, + "binary": "ov_SC02_031", + "sub": "asm/ov_SC02_031/nonmatchings/ov_SC02_031_jr_8017AE2C", + "asm": "asm/ov_SC02_031/nonmatchings/ov_SC02_031_jr_8017AE2C/func_8017F590.s", + "tu": "src/ov_SC02_031/ov_SC02_031_jr_8017AE2C.c", + "cls": "K-NEVER-TOUCHED" + }, + { + "name": "func_8017E54C", + "addr": "0x8017e54c", + "nins": 32, + "binary": "ov_SC06_010", + "sub": "asm/ov_SC06_010/nonmatchings/ov_SC06_010_jr_8017A4AC", + "asm": "asm/ov_SC06_010/nonmatchings/ov_SC06_010_jr_8017A4AC/func_8017E54C.s", + "tu": "src/ov_SC06_010/ov_SC06_010_jr_8017A4AC.c", + "cls": "K-NEVER-TOUCHED" + }, + { + "name": "func_8017F3F8", + "addr": "0x8017f3f8", + "nins": 38, + "binary": "ov_SC02_037", + "sub": "asm/ov_SC02_037/nonmatchings/ov_SC02_037_jr_8017AE2C", + "asm": "asm/ov_SC02_037/nonmatchings/ov_SC02_037_jr_8017AE2C/func_8017F3F8.s", + "tu": "src/ov_SC02_037/ov_SC02_037_jr_8017AE2C.c", + "cls": "K-NEVER-TOUCHED" + }, + { + "name": "func_80181894", + "addr": "0x80181894", + "nins": 112, + "binary": "ov_SC02_027", + "sub": "asm/ov_SC02_027/nonmatchings/ov_SC02_027_jr_8017D898", + "asm": "asm/ov_SC02_027/nonmatchings/ov_SC02_027_jr_8017D898/func_80181894.s", + "tu": "src/ov_SC02_027/ov_SC02_027_jr_8017D898.c", + "cls": "K-NEVER-TOUCHED" + }, + { + "name": "func_800CAF9C", + "addr": "0x800caf9c", + "nins": 105, + "binary": "md_MAIN_015", + "sub": "asm/md_MAIN_015/nonmatchings/md_MAIN_015", + "asm": "asm/md_MAIN_015/nonmatchings/md_MAIN_015/func_800CAF9C.s", + "tu": "src/md_MAIN_015/md_MAIN_015.c", + "cls": "K-NEVER-TOUCHED" + }, + { + "name": "func_801804C0", + "addr": "0x801804c0", + "nins": 54, + "binary": "ov_SC03_011", + "sub": "asm/ov_SC03_011/nonmatchings/ov_SC03_011_jr_8017C730", + "asm": "asm/ov_SC03_011/nonmatchings/ov_SC03_011_jr_8017C730/func_801804C0.s", + "tu": "src/ov_SC03_011/ov_SC03_011_jr_8017C730.c", + "cls": "K-NEVER-TOUCHED" + }, + { + "name": "func_8017E7D0", + "addr": "0x8017e7d0", + "nins": 54, + "binary": "ov_SC06_016", + "sub": "asm/ov_SC06_016/nonmatchings/ov_SC06_016_jr_8017C8D0", + "asm": "asm/ov_SC06_016/nonmatchings/ov_SC06_016_jr_8017C8D0/func_8017E7D0.s", + "tu": "src/ov_SC06_016/ov_SC06_016_jr_8017C8D0.c", + "cls": "K-NEVER-TOUCHED" + }, + { + "name": "func_8017F608", + "addr": "0x8017f608", + "nins": 99, + "binary": "ov_SC02_028", + "sub": "asm/ov_SC02_028/nonmatchings/ov_SC02_028_jr_8017D898", + "asm": "asm/ov_SC02_028/nonmatchings/ov_SC02_028_jr_8017D898/func_8017F608.s", + "tu": "src/ov_SC02_028/ov_SC02_028_jr_8017D898.c", + "cls": "K-NEVER-TOUCHED" + }, + { + "name": "func_801A8E34", + "addr": "0x801a8e34", + "nins": 118, + "binary": "md_SC07_004", + "sub": "asm/md_SC07_004/nonmatchings/md_SC07_004", + "asm": "asm/md_SC07_004/nonmatchings/md_SC07_004/func_801A8E34.s", + "tu": "src/md_SC07_004/md_SC07_004.c", + "cls": "M-DRAFT-extend-tell" + }, + { + "name": "func_800CB578", + "addr": "0x800cb578", + "nins": 288, + "binary": "md_MAIN_019", + "sub": "asm/md_MAIN_019/nonmatchings/md_MAIN_019", + "asm": "asm/md_MAIN_019/nonmatchings/md_MAIN_019/func_800CB578.s", + "tu": "src/md_MAIN_019/md_MAIN_019.c", + "cls": "M-DRAFT-extend-tell" + }, + { + "name": "func_80181D1C", + "addr": "0x80181d1c", + "nins": 36, + "binary": "ov_SC04_004", + "sub": "asm/ov_SC04_004/nonmatchings/ov_SC04_004_jr_8017AE2C", + "asm": "asm/ov_SC04_004/nonmatchings/ov_SC04_004_jr_8017AE2C/func_80181D1C.s", + "tu": "src/ov_SC04_004/ov_SC04_004_jr_8017AE2C.c", + "cls": "M-DRAFT-extend-tell" + }, + { + "name": "func_801808E8", + "addr": "0x801808e8", + "nins": 135, + "binary": "ov_SC05_003", + "sub": "asm/ov_SC05_003/nonmatchings/ov_SC05_003_jr_8017BEBC", + "asm": "asm/ov_SC05_003/nonmatchings/ov_SC05_003_jr_8017BEBC/func_801808E8.s", + "tu": "src/ov_SC05_003/ov_SC05_003_jr_8017BEBC.c", + "cls": "M-DRAFT-extend-tell" + }, + { + "name": "func_800CAF84", + "addr": "0x800caf84", + "nins": 117, + "binary": "md_MAIN_016", + "sub": "asm/md_MAIN_016/nonmatchings/md_MAIN_016", + "asm": "asm/md_MAIN_016/nonmatchings/md_MAIN_016/func_800CAF84.s", + "tu": "src/md_MAIN_016/md_MAIN_016.c", + "cls": "M-DRAFT-extend-tell" + } +] \ No newline at end of file diff --git a/docs/tool-designs/frontier-analysis-s61.md b/docs/tool-designs/frontier-analysis-s61.md index 695236c34..d745a625d 100644 --- a/docs/tool-designs/frontier-analysis-s61.md +++ b/docs/tool-designs/frontier-analysis-s61.md @@ -534,3 +534,12 @@ resolver stock drained: 54 + 20 further banks from the same stage dirs, 7 left a diagnoses. Fleet: 2,397 stubs, 98.5% instr, 96.8% distinct at T3 close (sweep #6 count in the phase log). What this changes in §4: step 5's waves can now target md_ switch functions and table-bearing overlay drafts without a "carve wall" bucket; step 6's H/I/M walls are unchanged. + +## Addendum 5 — 2026-08-26 (S62), plan step 4 executed: the model ladder is measured + +Whole-binary gate, 20 stratified fns, identical packs: haiku 8 · sonnet 14 · deepseek 11 · opus 17; +sonnet ∪ deepseek = 15 = opus on every band except >120 (opus 2/2, all others 0). Step 5's waves +therefore run ≤50 on Sonnet + DeepSeek in parallel with Opus on the residue, 51–120 on Sonnet with +Opus escalation, >120 on Opus, and route M-extend-tell straight to step 6. The pre-ox "Haiku for +≤50" doctrine is retired. The probe banked its union (17 fns) and distilled §306/§306a. Fleet at +T4 close: 2,380 stubs, 98.5% instr, 96.8% distinct. diff --git a/phase-ends/CURRENT_PHASE.md b/phase-ends/CURRENT_PHASE.md index b1e619693..29d308c4e 100644 --- a/phase-ends/CURRENT_PHASE.md +++ b/phase-ends/CURRENT_PHASE.md @@ -2470,3 +2470,26 @@ was a different function (drafts are stored by NAME; same name ≠ same code acr byte-proven transcripts distilled (37 agents) → 6 novel claims → §306 + four §306a addenda banked. DeepSeek arm: serial launch was the holdup (one process, ~7 min/fn); 15/20 drafted, the last 5 relaunched as parallel shards — its row + the union commit + the routing rule follow. + +**T4 DONE — the routing rule, measured at the whole-binary gate (20 fns, identical packs):** + +| arm | ≤50 (11) | 51–120 (7) | >120 (2) | total | cost | +|---|---|---|---|---|---| +| haiku (incl. the un-confounded re-run) | 7 | 1 | 0 | 8 | subscription | +| sonnet | 9 | 5 | 0 | 14 | subscription | +| deepseek-v4-flash (MAXTOK 8k, 24 turns) | 9 | 2 | 0 | 11 | **$0.56 total** ($63.03→$63.59 on the key) | +| opus | 10 | 5 | 2 | 17 | subscription | +| **sonnet ∪ deepseek** | **10** | **5** | 0 | 15 | — | +| all four | 10 | 5 | 2 | 17 | — | + +**Rule (cheapest arm within ~5pp of the best, per band):** ≤50 → Sonnet AND DeepSeek in parallel +(their union equals opus's 10/11), Opus only on their residue · 51–120 → Sonnet (= opus 5/7), +Opus escalation · >120 → Opus · haiku dropped from the ladder (adds 0 on top of sonnet∪deepseek; +its value was only ever cost, and the cheap slot is DeepSeek's at ~$0.03/fn) · the three fns no +arm banked are all M-extend-tell → T6 wall track, never waves. §3.5 falsifiers: "Haiku ≥ Sonnet on +≤50" refuted (7 vs 9); arms disagree by band → split routing stands. Caveat (R41): n=20, one fn +per band is 9–14pp; the union numbers are the robust ones. Union commit `commit:3128` (+17 fns; +fleet 2,380 stubs, 98.5% / 96.8%). Artifacts: `.run/t4/judge.json`, `claude_banked.json`, +`distill_out.json`, `sample.json`, `targets.json`. Harness fixes shipped en route: `prior_draft` +law-1c filter; the judge's corpus-cache blind spot; the serial-vs-sharded api_agent launch +(shard by target; one process serialises at ~7 min/fn). DeepSeek key: $63.59/$70, account ≈$5.