Commit Graph

124 Commits

Author SHA1 Message Date
Christopher Williams 6da840f3ab phase12: recovery merge 9 — 635 bodies / 644 regions (+33, past the cycle-1 checkpoint)
All four workers stopped when the account ran out of credit. This commit recovers every row they
had produced and verified but not yet reported, and it is the first thing done on resumption.

RECOVERY AUDIT, because work that was produced but never claimed is INVISIBLE to the merge flow
(charter rule 13):
  untracked src/ files produced this phase          11
  distinct addresses claimed in ANY staging file    29
  src/ files claimed by nothing (orphans)            0
  claims whose source file is missing                0

One false alarm worth recording: my first pass globbed `.run/p12/w-*/claims.tsv` and reported
`src/func_801008DC.c` as an orphan. It is claimed -- in `claims-altcc1.tsv`, which is a DELIBERATE
separate pool because those rows need the `cc1bin` override. The audit tool was wrong, not the
worker. A `src/`-vs-claims audit must glob EVERY claims file a charter defines, not just the default
one, or it manufactures orphans.

I re-verified ALL 29 claimed rows from fresh --work directories with `--symbols` (and
`--cc1 gcc-2.8.1-psx/cc1` for the four `cc1bin` rows) BEFORE merging: **29 of 29
`differing_bytes=0 result=MATCH`, exit 0.** Then one `apply --skip-registered` over the combined
input: 19 skipped as already merged, 10 added, 0 rejected.

  sf3_match gate   c_regions=644  differing_bytes=0  result=MATCH
                   sha1 e173426c157384ebf1b6caf8c6fea18a85a14af9  (unchanged)
  make check       exit 0        extents-verify  regions=644 disagreements=0 AGREE
  make worklist    listed=984, excluded_already_registered=642
  registry audit   644 rows, 635 distinct bodies, 0 missing, 4 carry cc1bin

The 10 recovered rows, and they are the phase's best evidence that the dispatch re-cut worked:

  0x8006D3B0 (192 B) and 0x8006AA88 (244 B)  -- worker A, adjacency
  0x8006D250 (172 B) and 0x8006D2FC (180 B)  -- worker B, family/adjacency, sibling with a 5th arg
  0x8002515C (60 B) and 0x800254B0 (60 B)    -- worker D: BOTH twins of the registered 0x80025070,
                                                a THREE-member family, and twins of each other
  0x80101C5C (32 B) and 0x80102FE4 (48 B)    -- worker D, GTE class
  0x80091490 (84 B)                          -- worker C, DEPENDENT, merged on my ruling (below)
  0x801008DC (88 B)                          -- worker C, `cc1bin=gcc-2.8.1-psx`, 3 `jr $31`

**RULING ON THE DEPENDENT CLAIM `0x80091490`.** Worker C flagged it rather than quietly merging it,
correctly applying charter rule 2: its callee `0x80018284` is NOT reconstructed, and the 3-argument
prototype is INFERRED from the call site (the tell is `move a1,zero` -- an integer 0, not a null
pointer in v0). Ruling: MERGE, with the dependency recorded here and in the ledger. Reasoning:
(i) rule 2 forbids verifying against a SUPERSET, and no superset was used; (ii) the whole-binary
gate proves the bytes exact, so the call site's register setup is right; (iii) the residual risk is
that a future `0x80018284` reconstruction disagrees about arity, and **that risk is caught by the
gate the moment it happens** -- the row would stop matching and the cause would be visible. The
alternative, holding a verified body, costs a body for a risk the gate already covers.
**The worker who takes `0x80018284` must treat `0x80091490`'s prototype as a hypothesis to check.**

Also recorded: `excluded_already_registered` still lags the registry by exactly 2 (642 vs 644). The
lag did NOT grow when this merge added four negatives-derived rows, so it is not "negatives rows are
counted elsewhere" -- it is a fixed pair of registry rows the counter never counts. Still open,
still a diagnostic rather than a gate, still to be reconciled at the close with the full census.

Phase position: 602 -> 635 bodies, +33. The cycle-1 checkpoint was +30.
2026-09-24 18:15:57 -04:00
Christopher Williams b8502c5693 phase12: merges 7-8 — 625 bodies / 634 regions (from 621 / 630)
Merge 7: worker D's 0x8006AD5C (84 B, a GOAL B row, first spelling) and 0x8006D1C4 (140 B).
Merge 8: worker C's GTE pair 0x80010810 / 0x8009C69C (60 B each, on the DEFAULT toolchain, via
the inline-asm hatch). Every row verified from a fresh --work dir against the exact md5, merged to
a candidate, gated whole-binary, promoted only on result=MATCH.

  sf3_match gate     c_regions=634  differing_bytes=0  result=MATCH
                     sha1 e173426c157384ebf1b6caf8c6fea18a85a14af9  (unchanged)
  make check         exit 0     extents-verify  regions=634 disagreements=0 AGREE
  registry audit     634 rows ordered, non-overlapping, 625 distinct sources, 0 missing

INTEGRITY CHECK THAT PASSED, ON A REAL HAZARD. Worker C edited the headers of the three
ALREADY-MERGED cc1bin sources after I merged them, changing their md5s (6afb89a7 / 51ab9083 /
c30c5bfa). The registry points at those paths, so a content change would have made it stale. I
re-ran the full gate on the current registry: MATCH. Comments only. This is exactly what the
md5-in-claim-row guard exists for, and here the whole-binary gate is what settled it.

C'S INLINE-ASM HATCH WAS JUSTIFIED TWICE, AND THE SECOND REASON IS A NEW FINDING.
The GTE pair is the d=0/15 identical pair, so one solve meant two bodies. The hatch was needed
because (1) ~20 spellings and ALL TEN vendored cc1 builds fold the dead `move t0,a1` into the
negation, and `((y ^ -1) + 1)` is the only spelling reaching the right LENGTH while lowering to
nor+addiu -- so length alone was never evidence; and (2) THE ORIGINAL'S NEGATION IS THE TRAPPING
`sub` (funct 0x22), not `subu` (0x23). objdump prints `neg` for the original and `negu` for the
candidate, so a mnemonic comparison cannot see it: a one-byte funct-field difference, cookbook 6's
class. With the copy fixed but the C negation kept, the row sits at differing_bytes=1.

AND C CLOSED THE ALT-CC1 QUESTION AGAINST ITS OWN INTEREST. On the 0x80050674 pair the residual is
differing_bytes=1 on the default cc1 and differing_bytes=22 on BOTH 2.8.1 and 2.91.66-psx, so it is
not a compiler-revision artifact. C then declined to spend the inline-asm hatch on it, on the
grounds that the residual is a mundane operand order inside an otherwise all-C body and the hatch
would be doing COSMETIC work. Ruling: endorsed. The hatch is for shapes plain C provably cannot
express; pinning three registers to win one operand order is not that. The row is classified with
its mechanism and an exhausted dimension instead.

OPEN, RECORDED WITH THE EXPERIMENTS RATHER THAN A GUESS: the workflow's checklist item
`excluded_already_registered == registry size` held exactly through merges 5, 6 and 7 and diverges
at merge 8 (634 registry rows, counter 632). Two hypotheses were tested and BOTH REFUTED: the two
new rows are not extent-graded non-exact (both are `exact`/`term=jr_ra`), and it is not "negatives
rows are counted under the negatives filter instead" (11 registry rows have negatives addresses,
but removing only the 2 newest from the registry reproduces the count exactly). Two failed
root-cause attempts, so per AGENTS.md rule 10 it is recorded with the evidence instead of a third
guess. No correctness risk, and that is measured: the whole-binary gate proves all 634 rows
byte-exact simultaneously, the registry audit is clean, and extents-verify agrees. It is a
diagnostic, not a gate. To be reconciled at the close, where the negatives-index reconciliation
happens anyway and is probably the same question.
2026-09-24 17:30:17 -04:00
Christopher Williams 677d982398 phase12: merge 6 — worker A's 572 B body -> 621 bodies / 630 regions; exclude the 0x800C3490 fragment
Worker A's third claim, verified from a fresh --work dir against the exact md5 in the claim,
merged to a candidate, gated whole-binary, promoted only on result=MATCH. It is the largest body
any worker has taken this phase (572 B).

  sf3_match gate    c_regions=630 differing_bytes=0 result=MATCH
                    sha1 e173426c157384ebf1b6caf8c6fea18a85a14af9  (unchanged)
  make check        exit 0        make extents-verify   regions=630 disagreements=0 AGREE
  make worklist     listed=989, excluded_already_registered=630
  registry audit    630 rows, 621 distinct sources, 0 missing, 3 carry cc1bin

**ADJACENCY IS NOW 3-FOR-3 FOR WORKER A, ACROSS THREE BANDS.** 0x800297F4 (204 B, band 0) ->
0x800298C0 (392 B, band 1) -> 0x80029A48 (572 B, band 2): three CONSECUTIVE bodies, 1168 bytes,
four spellings total, with the density ranker not involved in any of the three picks. The chain
ends where no row starts at the next boundary. That is the strongest single piece of dispatch
evidence in the phase, and it is why the fresh-band files were re-cut to put adjacency above
density inside a band.

Worker A's lever, recorded because it is an operator RECOVERY rule rather than a spelling tip: the
original is `if ((x1 < 0 && x2 > 0) || (x1 > 0 && x2 < 0))`, and the truth-table-equivalent
if/else ladder is EXACTLY ONE INSTRUCTION SHORT. The emitted stream is `bgez x1` / `bgtz x2` /
`blez x1` / `bgez x2` -- each term's first test branches over its own second test -- and the ladder
has no `bgez x1` to emit. So the four BRANCH SENSES let you write the operator down without
guessing; cookbook 83's "branch direction distinguishes && from ||" turned into a recovery rule.
And `(x1 ^ x2) < 0` is the same predicate with the wrong codegen: the original compares.

**0x800C3490 IS EXCLUDED FROM THE WORKLIST.** Cookbook 114 (worker A, Phase 10) records it as a
FRAGMENT: it starts mid-expression, its body is a SHARED TAIL (`addiu sp,sp,48; jr ra`) that also
appears at 0x800C3470-0x800C348C, and it cannot be matched standalone. The extents table still
grades it `exact` with `term=jr_ra` because the boundary walk sees a well-formed terminal, so the
tool cannot catch it -- which is exactly why it needs a recorded exclusion rather than a tool rule.
Worker A recognised it for the SECOND time in Phase 12 and skipped it instead of spending reading
budget, which is the signal that the exclusion belongs in the Makefile: a row that has to be
re-identified by hand every phase is a row the dispatch should not be offering.

Added with its provenance in the Makefile comment, the same mechanism Phase 10 used for
0x8010080C's false extent start. excluded_named_exclusion 8 -> 9.

Ledger updated with merges 2-5, the cc1bin lever and its gate, the mechanism correction, the gp
pair rewrite and both of my failures in it, the dispatch measurement (369 of 993 rows abut SOME
region = noise; only 5 abut a PHASE-12 match = the signal), and the partition-membership fix.
2026-09-24 17:25:51 -04:00
Christopher Williams 63008d5a49 phase12: merges 3-5 — 620 bodies / 629 regions (from 604 / 613), incl. the first cc1bin rows
FIVE claim batches, every row re-verified by the coordinator from a fresh `--work` directory
against the exact md5 in the claim, merged to a candidate, gated whole-binary, promoted only on
`result=MATCH`. Merges 3 and 4 are separated so the every-3rd-merge full audit falls on merge 3.

  sf3_match gate      c_regions=629  differing_bytes=0  result=MATCH
                      sha1 e173426c157384ebf1b6caf8c6fea18a85a14af9  (unchanged)
  make clean && make all   exit 0      (full audit at merge 3)
  cmp                      exit 0
  make check               exit 0
  make extents-verify      regions=629 disagreements=0 result=AGREE
  make worklist            listed=991, excluded_already_registered=629
  registry audit           629 rows, ordered, non-overlapping, 620 distinct sources, 0 missing
  make test                318 tests, OK

**THE FIRST THREE `cc1bin` REGIONS ARE IN THE REGISTRY**, and the gate is byte-exact for the
whole binary with them -- the alternative-cc1 lever works end-to-end, not just in a scratch
harness. All three came from worker C, all three match with gcc-2.8.1-psx/cc1 and NONE matches
with the default (56/60/52 B LENGTH-MISMATCH). They carry `cc1bin=gcc-2.8.1-psx` in the region
row and the gate re-checks the >=2-exit-jump restriction on every build.

  0x800FF43C (64 B) and 0x800FF47C (64 B) — a TWIN PAIR, raw words d=2/16. They are also two of
    the rows worker F left open at the Phase 11 close with a named mechanism and a named untried
    lever. C closed them with the cc1 lever, not a re-spelling.
  0x80108578 (56 B) — needed TWO levers: the cc1 AND a source shape that re-tests the guard each
    iteration (`for(;;){ if (a0==0) return 0; ... }`), because the original's loop back edge
    targets the function's FIRST instruction. A `while` spelling rotates the loop, skips the
    guard, and leaves the 1-byte residual an earlier attempt had recorded.

**AND `0x800298C0` IS AN OVERTURN — the recorded negative, merged as worker A's claim.** Worker A
took it on ADJACENCY (the row immediately after its own previous match) and matched it first try,
overturning cookbook 128's named mechanism. It is also the row Phase 11's close had to release as
STALE-TAKEN because both holders had gone out of context: the row the close flagged as invisible
became a body in the next phase. Cookbook 182 demonstrated end-to-end.

**Worker D's GOAL B produced bodies, not just a classification:** 92/92 of its unclassified rows
are now classified, and TWO of them were raw-word twins of registered bodies and MATCHED
(0x8006AE04 = 1/20 words from 0x8006B66C; 0x80017A38 = 1/18 from 0x80017B50). A Goal B result that
adds zero bodies would still have been met; this one added two.

Worker B's 0x8006B964 is the fourth match in ONE translation unit (0x8006B0A8/B328/B5F0/B964),
which is what prompted the dispatch re-rank below.

MEASURED, AND IT IS THE PHASE'S DISPATCH RESULT SO FAR: every body Phase 12 has produced came from
ADJACENCY, a family/twin finder, or a named structural class -- none from the top of a
density-ranked file, and B hit the leading indicator (four consecutive small-residual near-misses,
no new mechanism) doing exactly that. So the fresh-band files are now ordered band-first, then by
ADJACENCY TO A PHASE-12 MATCH, then density. Plain adjacency is NOT discriminating -- 369 of 993
rows (37%) abut SOME registered region, in every band -- but only FIVE rows abut a region matched
this phase, and the mechanical key independently flagged 0x8006B398, which worker D had already
chosen from B's handoff. The key agrees with the human pick.

PARTITION MEMBERSHIP IS NOW A HASH OF (band, address), not a position in a list. I had this wrong
twice and told the workers membership was stable when it was not: first partitioned by
density-sorted position (correcting the payload bug moved rows; band-0 top-20 overlap 1/20), then
by (band, address) POSITION, which is stable only while the row set is unchanged -- and the set
shrinks by design as rows register (1001 -> 993). Verified: removing 10 rows moves 0 assignments.
2026-09-24 17:23:50 -04:00
Christopher Williams f5c3426e85 phase12: merge 2 — worker B's 3 rows -> 607 bodies / 616 regions
All three re-verified from fresh --work dirs against the EXACT md5 in the claim rows
(4ad25781c69f611450544ae097936d1a / 7188c87c37bfc87b8e0ebfddd47ded97 /
542de2b2242dd8e6149440e4bb034f77), merged to a candidate, gated whole-binary, promoted only
on result=MATCH.

  sf3_match range (x3, --symbols)    exit 0, differing_bytes=0, result=MATCH
  sf3_merge check-claims             exit 0, problems=0
  sf3_match gate --expect-sha1       exit 0, c_regions=616, differing_bytes=0, result=MATCH
                                     sha1 e173426c157384ebf1b6caf8c6fea18a85a14af9
  make check                         exit 0
  make extents-verify                regions=616 disagreements=0 result=AGREE
  make worklist                      listed=998, excluded_already_registered=616

WORKER B'S CHARTER CORRECTION, CONFIRMED INDEPENDENTLY. The charter's §3d verify command
omitted `--symbols config/symbols.tsv`. Without it this entire family is +4 LENGTH-MISMATCH,
because the gp-marked global D_80121E88 auto-resolves to an absolute address and becomes
lui+lw. Measured on 0x8006B5F0:

  without --symbols:  candidate_bytes=128  result=LENGTH-MISMATCH   exit 1
  with    --symbols:  differing_bytes=0    result=MATCH             exit 0

That is the THIRD defect in the charter I wrote for this phase (after the claims-file format
and the evidence-file name). A verify command that does not match the configuration the gate
uses is a command that reports a false negative, and it would have had workers discarding
correct spellings as LENGTH-MISMATCH.

TWO REUSABLE MECHANISMS FROM B, both named rather than ground out:

(a) A REGISTER RESIDUAL CAN BE ARGUMENT ARITY. 0x8006B328 came out at correct length with
    exactly 4 differing bytes, all the a1-vs-v1 register field of the record pointer. Cause:
    the callee was called with TWO arguments. With one argument cc1 allocates the record
    pointer to v1; with two, both values are already in a0/a1, the call emits NO setup at all,
    and the emitted code differs only in that field. Diagnostic: correct length + the only
    residual is one value in an argument register => try declaring an extra parameter.

(b) A LOOP-INVARIANT CONSTANT MUST BE A NAMED LOCAL DECLARED INSIDE THE GUARDED SCOPE, and
    the `s4` save is the tell. On 0x8006B0A8 (frame 40, saves ra+s4+s3+s2+s1+s0): a literal
    `&= -2049` rematerialises the `li` inside the loop and the whole s4 save/restore pair
    vanishes; a named local before the `while` hoists the `li` above the guard test and
    reschedules the entry block (20 differing bytes); declaring it inside
    `if (p != 0) { int hmask = ...; while (...) }` MATCHES. The guard wrapper is what matters,
    not the loop form (do/while is byte-identical to while). A SECOND mask in the same function
    must stay a literal -- the hoist is per-statement, not per-function.

  make test   306 tests, OK   (from 301)
2026-09-24 17:08:21 -04:00
Christopher Williams b3e3ec3ea7 phase12: merge 1 — worker C's two ADJACENT-TWIN negatives -> 604 bodies / 613 regions
The phase's first two bodies, and both are recorded negatives rather than worklist rows.
Worker C found them with the NAMED UNTRIED LEVER I handed it: tools/sf3_family driven over the
101 negative rows against the 611 registered regions. I measured candidates=0 for the fresh
band and told C that said nothing about its own pool. It didn't:

  score >= 1.000 :  3 rows   (NOT 0)
  score >= 0.99  :  4 rows
  score >= 0.95  : 15 rows

  -> 0x800518BC (88 B) -> 0x80051864   MATCHED (this merge)
  -> 0x800B34A4 (88 B) -> 0x800B34FC   MATCHED (this merge)
  -> 0x8010804C (16 B) -> 0x80085B80   blocked class (gp-thunk), correctly not attempted

Transfer rate 2/2 on the unblocked score-1.000 rows, against finding 166's 7/7.

I re-verified BOTH claims from fresh --work directories before merging (differing_bytes=0,
result=MATCH, exit 0 each), merged to a candidate, gated the whole binary, and promoted only on
result=MATCH.

  ./tools/sf3_merge apply ...                      exit 0, added_regions=2
  ./tools/sf3_match gate --expect-sha1 e173...     exit 0, c_regions=613
                                                   differing_bytes=0, result=MATCH
                                                   sha1 e173426c157384ebf1b6caf8c6fea18a85a14af9
  make check                                       exit 0
  make extents-verify                              regions=613 disagreements=0 result=AGREE
  registry audit                                  613 rows, ordered, non-overlapping,
                                                   604 distinct sources, 0 missing files

Worklist regenerated. It still lists 1001 rows, and the reason is worth recording because it
looks like nothing happened:

  excluded_already_registered   611 -> 613   (+2)
  excluded_recorded_negative    169 -> 167   (-2)

The two rows were NEVER in the worklist -- they were excluded as recorded negatives. Registering
them MOVES each row from the negatives-exclusion bucket to the registered bucket, so `listed`
is unchanged while both counts shift by exactly 2. excluded_already_registered == the registry
size, as required. No partition refilter is needed: both rows were in C's negatives partition,
not in any fresh partition.

WORKER C'S FINDING, which is worth more than the two bodies and is a Goal-B-grade result: BOTH
rows' recorded class strings were WRONG ABOUT THEIR OWN MECHANISM.

  0x800518BC was classed "return-merge/sltiu", with a note that three spellings all came out
    80 B and sltiu fixed the compare byte but not the 8-byte layout. RAW WORDS: 21 of 22 words
    are IDENTICAL to matched 0x80051864. The sole difference is the forward `j` to the shared
    return, and its target differs only because `j` encodes an ABSOLUTE address and the two
    bases are 0x58 apart. The body is a literal twin; no re-spelling was ever needed.

  0x800B34A4 was classed "alloc-tiebreak" (bit-index/base register pair a0/a1 vs cc1 a0/v1).
    RAW WORDS: 20 of 22 identical to matched 0x800B34FC; both differences are the OFFSET
    CONSTANT (`li a0,3` vs `li a0,20`). The register pair the note asked for was already there.
    The lever is the constant, not the allocator.

Both rows abut their matched twin, so the negatives index contains whole RUNS of repeated
bodies -- and C's operational conclusion is that the cheapest finder is a raw-word HAMMING scan
against the 611 registered regions, which the histogram tool only half-finds. C is running that
scan across all 101 rows now. That map, not the bodies, is likely the largest thing this phase
produces.

Note the discipline this vindicates: an unclassified negative is not overturnable (finding 182),
but these two WERE classified -- wrongly, in the same dimension, by earlier sessions that then
wrote off the row. Cookbook 182's rule needs its other half: a recorded mechanism is a HYPOTHESIS,
and a WRONG named mechanism is as unattemptable as no mechanism at all until someone reads the
raw words.
2026-09-24 16:58:39 -04:00
Christopher Williams 6141079f9b phase11: merge 61 — worker E's 0x8007F9B0 -> 602 bodies / 611 regions 2026-09-24 11:43:43 -04:00
Christopher Williams b0ed17d494 phase11: merge 60 + cookbook 181-182 — 601 bodies / 610 regions
Worker F's 0x8002622C (44 B) — A RECORDED NEGATIVE OVERTURNED, with a new class.

The old record said 'cc1 folds it, unreachable' and tried FOUR ALGEBRAIC re-spellings. All four
were doomed: the fold is at RTL combine, not in the front end, so no re-spelling can avoid it.
Only LIVENESS can. Same body with 'return 0' is 32 B LENGTH-MISMATCH; with 'return n' (the
difference live past the addition) it is 44/0/MATCH. Diagnostic that proves the pass: cc1 -da
shows the minus present in the .flow dump and gone in the .combine dump, while cse/cse2/jump/
loop/sched/sched2 all still contain it.

181: when an original keeps an arithmetically-cancelling pair (subu+addu, x-c+c), the intermediate
is LIVE PAST the second operation — find the later reader.

182: a negative with a NAMED mechanism is overturnable; one without is not. 'cc1 folds it' is not
a classification; 'RTL combine cancels it, and algebraic re-spelling cannot reach combine' is,
and it immediately implies the liveness lever.
2026-09-24 11:42:17 -04:00
Christopher Williams 5c3e5d0e07 phase11: *** 600 DISTINCT MATCHED BODIES — TARGET REACHED ***
609 regions / 600 distinct bodies, from the 484 / 493 baseline at phase start (+116 regions,
+116 bodies). Worker E's 0x80077D04 (120 B) is the 600th.

VERIFIED, not asserted:
  full-binary gate   : 609 regions, differing_bytes=0, result=MATCH, exit 0
  make check         : 253 tests, OK, exit 0
  every source file  : present on disk and tracked by git
  working tree       : clean

Corpus maximum 1232 B; 28 regions exceed the 244 B ceiling the phase was planned around.

Delivered by six workers over the phase: A 46, D 21, B 17, C 13, E 14, F 3 = 114 claims,
every one gated before merge.

The phase's premise was falsified early and replaced with working tooling:
 - the 244 B ceiling was a DISPATCH ARTEFACT (5 of 427 rows above it had ever been attempted);
 - ASPSX does not fill delay slots at all, so the authorised post-pass shrank from a modelling
   project to one mnemonic (maspsx=moves) plus an epilogue swap (maspsx=epilogue);
 - cost is TIE-BREAK DENSITY, not size, measured independently by two workers from opposite
   directions;
 - the GTE class moved from BLOCKED to OPEN (worker D's 0x800F3E18 is the first GTE row matched
   in this project).
2026-09-24 11:37:10 -04:00
Christopher Williams efbcc2bd01 phase11: merge 59 — worker F's 0x800261C0 -> 599 bodies / 608 regions, ONE to the milestone 2026-09-24 11:33:44 -04:00
Christopher Williams eeaaf5b059 phase11: merge 58 — worker E's 0x80027D88 -> 598 bodies / 607 regions, TWO to the milestone 2026-09-24 11:30:55 -04:00
Christopher Williams 7a93946795 phase11: merge 57 + cookbook 170-171 — 597 bodies / 606 regions, THREE from the milestone
Worker E's 0x8005E17C and 0x8002FAB8; worker F's first two claims 0x800FBE84 (216 B, FIRST
SPELLING with worker A's derivation) and 0x80026274 (108 B).

170 generalises the argument-evidence levers (157/164) into a mechanism: a redundant ENTRY-BLOCK
copy of an argument means that value is still live at a call whose argument setup CLOBBERS that
same register. The copy is materialised in the entry block because the tie to a0's home is
illegal. The test that nailed it: the same body with a 2-arg call is 104 B LENGTH-MISMATCH; with
the 3-arg call it is 108/0. Two prior corpus instances had the copy AT the call; this is the
hoisted-to-entry variant.

171: worker F confirmed EXHAUSTIVELY that the constant-division divisor is unique per magic --
(n*M)>>(32+s) == n/D has exactly one D. So finding 67's identity is not an approximation.
2026-09-24 11:27:41 -04:00
Christopher Williams c091483083 phase11: merge 55 + cookbook 123 SOLVED + 167-168 — 592 bodies / 601 regions
Worker E's 0x8002311C (160 B) CLOSES COOKBOOK 123'S OPEN QUESTION. Finding 123 recorded the
branchless MAX0 (x & -(x > 0)) as unreached -- 'no ternary and no bitwise spelling reached it'.
Worker E solved it: the lever is NAMING THE BOOLEAN.

  return s & -(s > 0);        -> BRANCHES
  return s > 0 ? s : 0;       -> branches
  flag = s > 0; return s & -flag;  -> EXACT (slt / negu / and)

Mechanism: naming the comparison forces cc1 to materialise it as a VALUE (slt) rather than a
test feeding a branch. That is finding 44's 'name the boolean' lever applied to the MAX half --
finding 44 previously had only the cond-into-&& direction for this family.

167: a 4-byte store cc1 DELETES means the object's address is never taken -- fold the word into
the array whose address IS taken by a call.

168: s = f(); s += f(); s += f(); loses one instruction vs three named results summed.
2026-09-24 11:22:01 -04:00
Christopher Williams db6022c9f7 phase11: merge 54 + cookbook 166 — 590 bodies / 599 regions
0x800F3DC0 (88 B) — a ONE-WORD sibling of the matched 0x800F3E18, found by worker E via
sf3_family at ratio 1.000 and confirmed by raw-word diff: identical in all 22 words except the
COP2 command field (0x4B70000C vs 0x4B78000C). The route was one copy, two renames and one field
change; every __asm__ and register binding carried over untouched.

166 records it, and notes it is the MIRROR of finding 161: on 0x800F3E18 the field 0x178000c was
the WRONG answer (one byte off, 0x170000c correct); on 0x800F3DC0 0x178000c IS correct. A count
tells you a field is COMMON, not that it is right -- and a ratio-1.000 sibling is the cheapest
place to learn which one a row wants. When the family tool reports one, diff the raw words FIRST.
2026-09-24 11:18:58 -04:00
Christopher Williams f4e14569bd phase11: merge 53 — worker E's 0x800B0E64 -> 589 bodies / 598 regions 2026-09-24 11:13:40 -04:00
Christopher Williams 22cfdc874d phase11: merge 50 — worker E's 0x80107DE8 -> 587 bodies / 596 regions 2026-09-24 11:06:56 -04:00
Christopher Williams a74bc32306 phase11: merge 49 + cookbook 162 — 586 bodies / 595 regions
Worker A's final row 0x8010A6C4 (132 B, first attempt, maspsx=epilogue) -- its ninth epilogue
row and its 46th claim.

162: a callee called with DIFFERENT argument counts needs a NON-PROTOTYPE declaration --
func_8010A444(1) / (2, x) / (3, s1, s0) is only expressible as 'void func_8010A444();', the C89
empty-parameter form, not '(void)'. Same constraint that cost worker A a compile on 0x8002DD14.

Worker A's final totals: 46 claims (33 first-attempt), 95 evidence rows, 39 levers, 3 deferred
rows with derivations, 1 blocked row, 9 rows carrying maspsx=epilogue.
2026-09-24 11:05:36 -04:00
Christopher Williams f27691f6c3 phase11: merge 47 + cookbook 158 — 584 bodies / 593 regions
Worker A's 0x800F4B88 (128 B, first attempt) -- its eighth epilogue-class row and its last.

158: two type views over the same halfwords are DELIBERATE. The first helper call loads with lh
(signed) and the second with lhu (unsigned) over the SAME pointer, so the source declared a
short* view for one expression and an unsigned short* view for the other. Writing the whole row
as short* gives lh for the second call too and changes the bytes. When one function reads the
same field both ways, the mixed lh/lhu pair over one pointer is the evidence.
2026-09-24 11:01:49 -04:00
Christopher Williams eb4b23b219 phase11: merge 46 + cookbook 155-157 — 584 bodies / 593 regions
Worker A's three epilogue rows (0x800F452C, 0x800F6DD0, 0x800F6E50).

155 is a DISPATCH finding: the epilogue list is ALSO a family list. 0x800F6DD0 and 0x800F6E50 are
siblings differing in exactly two ways, and worker A read one and got the second for free, both
first try. Adjacent pairs already identified: 0x800F6DD0/0x800F6E50, 0x800F42AC/0x800F452C,
0x800FFFEC/0x80100038. A worker taking an epilogue row should read its NEIGHBOURS first -- the
class was selected on a TAIL SHAPE, and tail shape correlates with the translation-unit layout
that makes neighbours siblings. Generalised: any class selected by a structural feature clusters
its results by address.

156: the three writes are ASSIGNMENTS not accumulations -- the original never loads the old
destination value, so writing += adds three loads.

157: fewer argument registers set than parameters means the source passes its OWN LIVE parameters
directly. Now confirmed on three rows.
2026-09-24 11:00:24 -04:00
Christopher Williams 57c1cd22f2 phase11: merge 44 + cookbook 151-153 — 580 bodies / 589 regions
Worker A's 0x800F4098 and worker D's 0x800FB54C (104 B, first attempt, maspsx=epilogue).

151: the 2^k-1 add-back rule is CONFIRMED on two independent divisors -- worker C derived it
from 63 (0x800FEE3C) and worker D found it again on 127 (0x800FB54C, magic 0x81024409). Same
structure, two divisors, so finding 67's decision table is complete and not hypothesised.

152: FIVE finders each produced bodies over worker D's 20 -- redundancy rank 6, size rank 5,
adjacency 4, epilogue class 2, constant-division census 1, family 1. No single finder dominates.
This broadens finding 109: 'five different finders each produced bodies, and the price was set
by the LEVER, not the finder.' The tools cover different parts of the population, so keep every
finder running rather than consolidating onto the current best.

153: a saved register can force a local to be SMALLER than the data written through it, and
enlarging it to fix that breaks the frame.
2026-09-24 10:55:29 -04:00
Christopher Williams 150e672b5a phase11: merge 42 + cookbook 144-146 — 574 bodies / 583 regions
Worker D's 0x800FAF84 (104 B), its first maspsx=epilogue match.

144: THE EPILOGUE CLASS NEEDS ONLY ONE TOKEN. Worker A asked for a second one; it does not need
it. The 120 rows split into two shapes -- A) lw $31 immediately before the release, which needs
the release moved AND a nop inserted after lw $31; B) other loads in between, where the release
moves and the trailing nop is DROPPED. My first implementation did A only and left every B row
4 bytes long. Verified on both: 0x800FFBEC (80/0/MATCH) and 0x800F44D0 (92/0/MATCH, a row worker
A had released as unfixable).

145: read the frame arithmetic and the saved-register offsets TOGETHER -- worker D's local had to
be 8 bytes not 12 because the saved s0 sits at sp+24 and the callee writes through sp+16. Third
instance of the size family, first where the constraint came from a saved register.

146: the SAME expression at two divisors produces two unrelated code shapes (/64 branchy bias vs
/63 add-back magic), which is why worker D's divisor sweep missed it.
2026-09-24 10:52:37 -04:00
Christopher Williams dce9c40896 phase11: merge 41 — 0x800F44D0 closes on the CORRECTED transform (shape B) — 573 bodies / 582 regions
Worker A released this row as 'the three-load-with-nop shape that the swap cannot fix' and
requested a --no-load-delay-nop token. It does not need one: the corrected transform already
handles it, by DROPPING the trailing nop for shape B rather than moving it. Verified: 92 bytes,
differing_bytes=0, MATCH, with maspsx=epilogue. It was 96 bytes before the fix.

So the epilogue class does NOT need a second token -- it needed the transform to distinguish the
two shapes, which worker A's report 24 is what revealed. The 120 rows should now be attemptable
with maspsx=epilogue alone.
2026-09-24 10:51:28 -04:00
Christopher Williams ea51ac9629 phase11: merge 40 + the epilogue transform now handles BOTH shapes — 571 bodies / 580 regions
Worker A's two epilogue-class rows (0x800F42AC 96 B, 0x80100038 104 B), both carrying the
maspsx=epilogue token -- the first rows closed through the new mode.

AND THE TRANSFORM IS NOW CORRECT FOR BOTH SHAPES, which worker A's report 24 showed was
necessary. The 120 rows split:
  A) lw $31 IMMEDIATELY before the release -> the release moves into the slot AND a nop must be
     inserted after lw $31, or j $31 lands in its load-delay slot.  0x800FFBEC.
  B) other loads between lw $31 and the release -> the release moves into the slot and the
     trailing nop is DROPPED; no load-delay nop is needed.  Worker A's 0x800F44D0.
My first implementation did A only and left every B row 4 bytes long. Both are handled now, and
the discriminator is whether the jump's own register was loaded immediately before the release.

A BUG WORTH RECORDING: reading out[-1] to find that preceding instruction saw maspsx's own
'#nop # DEBUG: ...' comment instead of the lw, silently producing the shape-B answer for a
shape-A row and turning a MATCH back into a LENGTH-MISMATCH. The scan now skips comments.
2026-09-24 10:50:09 -04:00
Christopher Williams a72a8d4127 phase11: merge 39 — 4 rows (A's 0x80038D48, 0x800F6D60; D's 0x8009B56C, 0x80018458)
Worker A's two: the addition operand-order row (a1[i]+a0[i] vs a0[i]+a1[i] -- same length,
16 bytes apart, because cc1 evaluates the right-hand operand first) and the unconditional
p[0]=0 that lands in a branch delay slot.

Worker D's two: 0x8009B56C closed on cookbook 43 trigger 1 after D had nearly written the row
off, and 0x80018458.
2026-09-24 10:44:06 -04:00
Christopher Williams 05be974ce2 phase11: THE EPILOGUE POST-PASS SHIPS (maspsx=epilogue) — 565 bodies / 574 regions
Finding 84 named the transform; it is now implemented and 0x800FFBEC matches (80 B, 0 differing)
where it was 6 differing bytes without it.

IT IS A SWAP, NOT A MOVE, and getting that wrong cost one implementation: the candidate is
lw $31,16(sp) / addiu sp,sp,24 / jr $31 / nop and the original is lw $31 / nop / jr $31 /
addiu sp,sp,24 -- SAME instruction count, two words swapped. My first version moved the release
after the jump and dropped the nop, producing 3 instructions instead of 4 and turning an 80-byte
row into a 76-byte LENGTH-MISMATCH. A 'small mechanical transform' still has to be checked
against the bytes.

SCALE: 120 unclaimed rows have the filled epilogue in the ORIGINAL (scanned every worklist row's
tail for jr $31 followed by a positive addiu sp,sp,N). They are mostly SMALL -- 76, 76, 80, 92,
92, 96, 104 B -- so this is a large class of cheap rows that were blocked on a HARNESS GAP rather
than on source shape. 770 other rows have the unfilled shape and need nothing.

The tracked patch is regenerated and verified to reproduce both modified maspsx files from the
pristine checkout.
2026-09-24 10:42:29 -04:00
Christopher Williams 686e906b97 phase11: merge 37 + calibrate sf3_family + cookbook 136-137 — 563 bodies / 572 regions
Worker A's 0x80036F70 (460 B, first attempt, family score 1.000 AND adjacent to its own
0x80036DA4). Its family run finished 7 for 7 with five first-spelling matches.

136: worker A CALIBRATED the family tool. It checked the two 0.97-scoring entries and NEITHER
shares its sibling's body at all -- one is a table-allocation routine, the other a summing
loop. '1.000 is the useful band; below ~0.99 the histogram is matching common idioms, not
bodies.' That is the same false-positive mode as the redundancy ranker (finding 110). The
default threshold is now 0.99.

137: a family's signature can be a CONSTANT TRIPLE -- worker A's 0x80036F70 differs from its
sibling only in six constants, whose signature is (A, A+12, A-58). Searchable in a way no
similarity metric can be, because the shapes are identical and only the immediates differ.
2026-09-24 10:37:35 -04:00
Christopher Williams bd3619d41e phase11: merge 36 — FIVE family-list rows in one pass -> 561 bodies / 570 regions
Worker A closed 0x800259A0, 0x80012918, 0x8006B2D4, 0x8003022C and 0x800506E4 -- every one a
sibling found by tools/sf3_family, which was built an hour ago from worker D's insight that
'the finder varies, the price does not' and therefore families should be SEARCHED for rather
than waited for.

That is the tool's first harvest and it is 5 bodies from one list. The family scores were
1.000/1.000/1.000/1.000/0.998 -- exact opcode-histogram and size matches against rows worker A
had already matched, so the levers transferred unchanged.
2026-09-24 10:35:06 -04:00
Christopher Williams 887155a733 phase11: merge 35 + 5-way re-partition — 556 bodies / 565 regions
Worker D's 0x80025ADC (136 B). Partitions re-interleaved 5 ways because workers B and C
are both at ~94% context and effectively exhausted, leaving 2 active workers against 45
remaining bodies. A fifth worker restores capacity.
2026-09-24 10:32:46 -04:00
Christopher Williams bc4c046625 phase11: merge 34 + cookbook 127-129 — 556 bodies / 565 regions
Worker C's 0x8009F4B4 (248 B) and 0x80068874 (156 B), both first attempt.

127 CLOSES WORKER D'S OPEN QUESTION. D left 0x800FEE3C's magic 0x82082083 unexplained; worker C
solved it and the answer is a general rule: the divisor 63 is of the form 2^k-1, which is why
cc1 uses that magic with an ADD-BACK (mfhi; addu; sra 5) instead of a plain shift. An add-back
magic is the tell for a 2^k-1 divisor, NOT for a large one. Finding 67's decision procedure is
now complete: no mflo -> constant division D = 2^(32+s)/M; mfhi+addu+sra -> a 2^k-1 divisor;
mfhi AND mflo -> a genuine 64-bit multiply.

128: worker C classified a division-by-constant row on decode WITHOUT attempting it, because
'every division expression has several equally-plausible spellings, so it is idiom-redundant by
construction'. That characterises the ranker's false-positive class from the SOURCE side for the
first time -- exactly the class finding 110 showed cannot be separated by operand comparison.

129: adjacency is now 9-for-9 across three workers (A 3/3, C 5/5, D 1/1).
2026-09-24 10:30:10 -04:00
Christopher Williams a3b5db4f61 phase11: merge 32 — worker A's three roving-list rows -> 555 bodies / 564 regions 2026-09-24 10:27:40 -04:00
Christopher Williams bbe342d5dd phase11: merge 31 — worker D's 0x80018210 -> 552 bodies / 561 regions 2026-09-24 10:26:31 -04:00
Christopher Williams bac9ab0d89 phase11: merge 30 + cookbook 120-122 — 550 bodies / 559 regions
Worker A's 0x800689DC and worker C's 0x8009F4B4.

120: worker B's justification for why the ranker works -- 'the allocator makes copies
non-identical, so OPCODE repetition survives while WORD repetition does not'. That is exactly
why finding 110's full-word metric failed and why the opcode metric works. A repeated source
block produces the same opcodes with different registers; requiring operands to match destroys
the signal rather than sharpening it.

121: the filled-delay-slot class has TWO sub-cases with DIFFERENT fixes -- reorg fills the slot
(source-shape hunt) versus maspsx mode changing WHICH instruction lands in the slot (a harness
token choice). Same diagnostic, different remedy. Check whether toggling maspsx changes the
fill before hunting a source shape.

122: NEW BLOCKED CLASS -- a GTE coprocessor body needs a harness token, not more spellings.
Worker B's 0x8001FAFC reads mfc2 $12/$13/$14 and branches on t7/s6 which are NOT the o32
argument registers, so the inputs arrive through a non-standard convention. Team rule: if a
body contains mfc2/mtc2, do not spend spellings on it -- these are tooling-blocked rows to be
worked as a batch once a token exists.
2026-09-24 10:24:53 -04:00
Christopher Williams e2bdada67b phase11: merge 29 + cookbook 105/119 — 548 bodies / 557 regions
Worker D's 0x80106AA8 (136 B, first attempt) -- found by the REDUNDANCY filter, not
adjacency, which is the first row where the ranker did the finding alone. Eight stores
through four global pointers, each re-materialised per store.

Cookbook 105's dial now has THREE measured settings: per statement (0x8006BC74 46x and
0x80106AA8 8x, both matched), once per block (matched), once per function (does not match).
So per-statement re-reads are the NORMAL shape, not an extreme.

119: worker D ran the fragment check, called 0x80058BA0 a confirmed fragment, then
SELF-CORRECTED -- it is legal, because in o32 a frameless leaf may both read and write the
caller's outgoing argument area (sp+0..sp+31). All three of D's suspects are legal. Worker B
found the read side, worker D the write side, and both had to read the row to do it: a
heuristic keyed on shape must state its exclusions, and only the worker reading the row can
find them.
2026-09-24 10:23:08 -04:00
Christopher Williams fc7ebc4b2d phase11: merge 28 + cookbook 115 — 547 bodies / 556 regions
Worker B's 0x80050CA8 (120 B, first attempt).

Lever: the status word is masked by TWO separate statements (&= -3; &= -5;), and the original
emits one load, two ands against two different constants, one store. Combining the masks
folds to a single and and LOSES an instruction -- the same principle as finding 81 (a slot
stored twice is two statements) applied to read-modify-write. Companion: the status load is
hoisted above nine halfword clears, so the clears' source order is only observable through
the store order.
2026-09-24 10:20:18 -04:00
Christopher Williams b7ccad87a5 phase11: merge 26 — 545 bodies / 554 regions
Worker A's 0x8006B7C0 (420 B) and worker D's 0x800307FC (92 B).
2026-09-24 10:14:45 -04:00
Christopher Williams 14e927fca6 phase11: merge 25 — 543 bodies / 552 regions
Worker A's 0x800914E4 (400 B), closed on the row assigned under the revised picking order
(adjacency first, then redundancy, preferring the smaller of similar-scored rows).
2026-09-24 10:13:18 -04:00
Christopher Williams 6b7239d83d phase11: merge 24 + cookbook 104 + workflow protocol — 542 bodies / 551 regions
Worker A's 0x80033DC8 (360 B) and 0x8006A98C (132 B), plus two gp symbol rows
(D_80122724, D_80122728) that unblock 0x800A4CA8.

PROCESS DEFECT FOUND AND FIXED. Worker C and worker D both matched 0x800320D8
independently and D overwrote C's source file. Nothing corrupted -- both spellings match
and the region still reports 276/0/MATCH -- but one worker's effort was duplicated. The
partitions are genuinely disjoint (273/279/278/271, union 1101 = sum), so there was NO
assignment error: the gap was that no worker could know another had started a row, since
the registry only knows about MERGED claims and both started before either merged. The
root cause is the adjacency rule (cookbook 99, 4-for-4) crossing partition boundaries --
the best dispatch heuristic found so far invalidated the assumption the assignment rested on.

Fix: .run/p11/inflight.tsv (write-ahead log alongside the merge registry's commit log),
with the protocol written up in docs/ORCHESTRATOR_WORKFLOW.md so the next orchestrator
inherits it.
2026-09-24 10:07:56 -04:00
Christopher Williams 73b706e667 phase11: merge 22 + cookbook 99-102 — 538 bodies / 547 regions
Worker A's 0x800556E8 and 0x80055654 (both first/second attempt).

Cookbook 99 is a DISPATCH rule, not a codegen one: take the row ADJACENT to one you just
matched. The binary is laid out by translation unit, so neighbours share the author's habits.
3 for 3, all first or second attempt, and it beat both the size ranker and the LRS ranker.

Cookbook 100 puts the three branch-shaped diagnostics side by side -- each maps a residual
shape to exactly one cause and each is a glance rather than a spelling:
  branch displacement words only -> block NESTING (95)
  first few instructions, right length -> then/else ORDER of a single-statement arm
  whole prologue, same multiset -> declaration vs assignment order
Vector copies are now confirmed on FIVE independent rows.
2026-09-24 09:51:10 -04:00
Christopher Williams 2472e2e2c1 phase11: merge 21 + cookbook 95-98 — 537 bodies / 546 regions
Worker A's two adjacent claims (0x80036B14, 0x80036DA4).

Cookbook 95 is the cleanest diagnostic of the phase: a correct-length candidate whose residual
is a handful of BRANCH WORDS means the block NESTING is wrong, not the code inside the blocks.
Worker A got exactly 656 bytes (correct length) with exactly 2 differing bytes, both branch
displacements, by writing two guards as siblings instead of nested. Residual = 2 bytes at a
branch displacement => go look at your braces.

Also: the project's 4-int vector type is identifiable from the frame (multiple of 16 with
offsets stepping by 16); vector copies are struct assignments (third independent confirmation);
and an OPEN question is recorded -- 'the original spills everything, cc1 promotes' -- with a
request for a recipe from any worker who has solved it.
2026-09-24 09:49:26 -04:00
Christopher Williams a1c41771e7 phase11: merge 19 + cookbook 92-94 — 535 bodies / 544 regions
Worker B's four first-attempt claims (0x800B255C, 0x8004857C, 0x80030858, 0x800909D8).

Cookbook 94 is the strategic one: worker B's failures cluster into exactly TWO mechanical
classes -- reorg slot-fill choice and rare-epilogue fill -- and neither is a shape problem.
Both are the post-pass family, which two workers have now independently arrived at and
stopped on. That is the strongest argument yet for writing the post-pass rather than
grinding these rows with source spellings.
2026-09-24 09:46:48 -04:00
Christopher Williams 6862af1d0b phase11: merge 17 + cookbook 84-86 — 526 bodies / 535 regions
Worker B's 0x80069580 (88 B) and 0x8007E7FC (96 B), plus two gp symbol rows
(D_80122168, D_801221D0).

Cookbook 84 is the harness row for the post-pass: worker B isolated the rare-epilogue
transform exactly (move the frame release into the jump slot AND insert the load-delay nop
after lw ra), and established the load-bearing detail that as will NOT perform this fill
because doing so would put jr ra in the lw ra load-delay slot. So a post-pass that merely
moves the release into the slot produces wrong code. Also measured: maspsx=off is WORSE on
this row (72 bytes) because it strips nops from the beqz/jalr slots the original keeps, so
the two mechanisms are not substitutes.

85: cc1 folds SYM+N into a single la and SIX spellings do not defeat it.
86: cc1 cross-jumps identical guards; goto to a shared return label is the named lever.
2026-09-24 09:41:56 -04:00
Christopher Williams 6fdcaf3740 phase11: merge 16 + cookbook 83 — 525 bodies / 534 regions
Worker C's 0x800320D8 (276 B), matched on the FIRST spelling where its sibling 0x80031FC4
took 5 -- the family lever measured, on one family, both ways. Finding 55's limit confirmed
on the same family: a third row calling the same callee is NOT the same body and sits at
+16 instructions. The family transfers the derivation method and the stable positions,
never the body.

Also recorded: an OR nested inside an && chain is observable from the branch DIRECTIONS --
bne to the call block on one test and bnez to the manual-copy block on the other is
if (x == 0 && (a != 6 || b == 0)) call; else manual;
2026-09-24 09:40:40 -04:00
Christopher Williams c22ef879e8 phase11: merge 15 + amend cookbook 67, add 82 — 524 bodies / 533 regions
Worker D's 0x800910BC (280 B), its 8th match.

TWO CORRECTIONS TO THE COORDINATOR'S OWN COOKBOOK ENTRY, both from measurement:
 - 67 was INCOMPLETE and cost worker D a spelling. The magic alone is AMBIGUOUS: D = 2^(32+s)/M
   where s is the shift of the sra after the mfhi. 0x2AAAAAAB is /6 at s=0, /12 at s=1, /24 at
   s=2, and worker D read it as /6 when the shift was 1. The corollary is worth having too: the
   same magic twice in one function is not a contradiction (0x66666667 serves both /10 at s=2
   and /5 at s=1, materialised once into a callee-saved register).
 - The named-local rule is PER-SITE within one function. Naming a result the original consumes
   immediately costs 2 words; naming one the original reuses is free. Apply the decision once
   per VALUE, not once per function.

That is now the fourth correction to coordinator work this phase, and every one came from a
worker measuring something the coordinator had asserted.
2026-09-24 09:39:25 -04:00
Christopher Williams 1a6a00970f phase11: merge 14 + cookbook 79-81 — worker A's redundancy ranker
Worker A built a repetitiveness score (repeated 2/3/4-instruction opcode subsequences,
normalised by body length) and produced the cleanest controlled comparison in the phase:
3 spellings on a 248 B repetitive row vs 9 failures on a 176 B tie-break-dense one. That
converts 'prefer a repetitive body' from a hunch into a sortable number, so size is
deprioritised as the ranking signal.

Also recorded: a transposed temp array is byte-required (int m[3][4] used as m[c][r]) with an
exact diagnostic -- right length + right instruction multiset + residual only on sp-relative
offsets means the frame LAYOUT is wrong, not the code; and when the original stores the same
slot twice, suspect two source statements rather than a scheduler quirk (GCC 2.7.2 has no DSE).
2026-09-24 09:38:05 -04:00
Christopher Williams 61be53994b phase11: merge 13 + cookbook 73-78 — 522 bodies / 531 regions
Worker C's 0x80031FC4 (276 B). Cookbook gains six entries, the most important of which is
worker C's correction of the COORDINATOR: a DEPENDENT row is one you cannot VERIFY, not one
you have MATCHED. C's 0x800A613C and 0x800FD120 had symbol rows outstanding, but
re-verifying against the tracked registry gave byte-identical results to the overlay runs --
both are still near-matches blocked on an ALLOCATION lever. Adding a symbol row unblocks the
verification, not the match; conflating the two would have had a worker stop working a row it
had not solved.

Also recorded: the address-taken value may be a PARAMETER not a local (frame 8 too big with
all offsets shifted by 8 is the tell); address-taken form forces a register; the struct
assignment is what BATCHES the loads where element stores serialise behind maspsx nops; and
the cop2 operand is the 25-bit field (0x486012 -> 0x4A486012).
2026-09-24 09:36:47 -04:00
Christopher Williams 96f41be66a phase11: merge 12 — worker B's P1 band (8 claims) + worker A's 0x800319F0
+9 bodies: worker B's 0x8005E340, 0x8002C7EC, 0x800A8224, 0x8006B6BC, 0x80045F1C,
0x800F8A0C, 0x8002E9AC, 0x800196B4 and worker A's 0x800319F0 (whose dependent gp row
D_80122320 landed in the previous merge).

One new gp symbol row: D_801226E0 (append-only; the registry now has 395 rows).

Worker B's P1 band is finished: 10 rows, 5 matched, 4 near-matches with exact residuals,
1 blocked. THREE of the four near-misses failed on scheduling/allocation with the control
flow already EXACT, and one is a reorg slot-fill choice -- so that band's remaining yield
is in that class, not in shape work.

Two levers recorded from it:
 - The address-taken value may be a PARAMETER, not a local. 0x80045F1C's frame is only 40
   bytes yet it touches sp+56 and passes &a4 -- the FIFTH parameter, whose home is the
   caller's outgoing-argument area at frame+16. Modelling it as a local reproduces the same
   instruction SHAPE with a 48-byte frame and every offset +8 (22 differing bytes). So
   'right shape, frame 8 too big, all offsets shifted by 8' => check for a parameter first.
 - Address-taken form forces a register: 'int *p = &SYM;' gives la into a saved register
   plus indirection, where reading the symbol directly gives the macro pair and no save.
2026-09-24 09:35:26 -04:00
Christopher Williams 79765257ad phase11: merges 9-10 — 511 bodies / 520 regions, MAX 1232 B (worker D)
+10 bodies in one gated pass from all three active workers: worker D's 0x8010AF50
(1232 B — the >800 B band broken on the FIRST attempt, in TWO spellings, 5.0x the old
244 B ceiling) and 0x80031BBC (260 B); worker C's 6 claims including the re-tested
0x800AFDBC which my nopmarker correction closed with NO source change; worker A's
0x8006B214 and 0x80027CA0.

Four gp symbol rows added (D_80122320, D_80121F2C, D_80122128, D_801226DC), APPEND-ONLY.

PROCESS BUG FOUND AND FIXED IN MY OWN FLOW: the merge chain piped the gate into grep and
chained with &&, which PROMOTED A DIFFED REGISTRY -- grep succeeds whenever it finds the
word 'result=' regardless of the verdict. Caught by the following make check (806734
differing bytes), reverted, and the registry restored from the last green commit. The
merge flow now lives in .run/p11/merge.sh, which gates on the gate's EXIT CODE and
refuses to promote on failure.

Worker D's recognition, which is worth more than the row: mult + mfhi + sra with NO mflo
is a CONSTANT DIVISION, not a 64-bit multiply. 0x4BDA12F7 is ceil(2^45/27648); a genuine
64-bit multiply emits mfhi AND mflo in every available cc1. Recover the divisor from the
magic as D = ceil(2^(32+s)/M), never from the constant's face value.
2026-09-24 09:31:08 -04:00
Christopher Williams 55e59b3a4a phase11: merges 6-7 + the maspsx=moves mode — 501 bodies / 510 regions, MAX 700 B
+5 bodies: worker A claims 5-8 (0x800507A0, 0x80017B50, 0x800BBAC8, 0x800AFACC) and
worker D's 0x8006BC74 (700 B). Every candidate gate MATCH before promotion; all md5s
verified on disk.

*** 700 B IS THE LARGEST BODY EVER MATCHED IN THIS PROJECT *** — 175 instructions,
2.9x the old 244 B ceiling, and the FIRST match in the 401-800 B band. It cost THREE
spellings, fewer than worker D's own 248 B row (four). Both residuals were mechanical:
a missing `li 4096 / sw` pair hidden inside a run of 46 zero stores ("a run of repeated
stores is not a run of identical stores -- read every immediate"), and four extra
pointer reloads fixed by naming the sub-object pointer ONCE for the three byte stores
of 255 while leaving the fourth store its own re-read (cookbook 45's named-locals
family at its cheapest). Nothing about 700 bytes was hard: the body is large but highly
REDUNDANT, and redundancy is what a matcher keys off.

NEW HARNESS MODE `maspsx=moves` (worker B's oracle result, developer-authorized).
Worker B ran all five SDK assemblers (ASPSX 2.56/2.67/2.79/2.81/2.86) and every
supported option as a read-only oracle and found that **ASPSX does NOT fill delay slots
at all** -- it produces maspsx's exact shape. So maspsx is FAITHFUL to ASPSX, and the
fills in the original did not come from ASPSX. That overturns the "model ASPSX's fill"
framing: what fills the slots is GNU `as` in REORDER mode, i.e. maspsx OFF, and the only
real gap is ONE MNEMONIC -- `as` expands cc1's `move` to `or` where ASPSX emits `addu`.

So the mode is `maspsx=off` plus a single `move`->`addu` rewrite, letting `as` fill
exactly the slots cc1 left empty while cc1's own `.set noreorder` windows are preserved.
DEMONSTRATED: 0x800FA5D8 now reports 132 bytes / differing_bytes=0 MATCH where default
maspsx gives 148 LENGTH-MISMATCH. 7 new tests; suite 246 -> 253.

REGRESSION-VERIFIED: make check green at 510 regions / 253 tests with the mode OFF, so
every one of the 510 regions is byte-identical. The mode stays opt-in per region --
worker B measured the counterexample 0x8002D2BC, which has the SAME cc1 shape but whose
original keeps the store before the jr with a nop, so the original's assembler behaves
differently in different files.
2026-09-24 09:13:19 -04:00
Christopher Williams 7d7f41fbaa phase11: merge 3 — 493 bodies / 502 regions, TWO bodies above the old 244 B ceiling
+8 bodies: worker A claims 1-4 (0x8005E820, 0x80012DE8, 0x8001644C, 0x800A6880)
and worker C claims 1-4 (0x80048180, 0x800B107C, 0x80082868, 0x80036134).
Candidate gate MATCH before promotion; all 8 md5s matched on disk.

FOUR gp symbol rows added for 0x80017C6C (D_80121A2C/34/3C/44), coordinator-verified
against the payload: the original materialises them with addiu $2,gp,244/252/260/268.

C's claim 4 (0x80036134, 248 B) is the SECOND body above the old ceiling, matched on
lever-c-large row 1 — so two independent workers have now matched above 244 B, and the
dispatch-artefact verdict is confirmed by result rather than by inference.

sf3_merge format fixes, both triggered by real worker files:
  - a bare header row is now rejected with "looks like a column HEADER" instead of a
    confusing "not a hex address: 'start'"
  - a lone `-` in the override column means "no overrides", matching the absent-value
    convention the other tracked tables use
Suite 246 tests OK; make check green: regions=502 AGREE, differing_bytes=0 MATCH.
2026-09-24 08:59:21 -04:00
Christopher Williams 5d41f97421 phase11: *** CEILING BROKEN *** — 485 bodies / 494 regions, new max 248 B
Worker D's claim 0x8009F6A0..0x8009F798 (248 B) MATCHES. Verified independently by the
coordinator on a fresh work dir (candidate_bytes=248 differing_bytes=0 MATCH) and
gated on the whole binary before promotion. This is the FIRST body above 244 B ever
matched, and it sets a new corpus maximum (previous max 244 B at 0x80099078).

THE LEVER (worker D, 4 spellings): the local working buffer must be a 3x4 word array
(`int t[3][4]`, only columns 0..2 used), NOT `int t[9]`. The 4-WORD ROW STRIDE IS
BYTE-LOAD-BEARING: it moves the 2nd and 3rd triples to 0x10 and 0x20, makes the frame
48 B instead of 40 B, and leaves the unused 0x0C/0x1C slots the original shows. New
instance of cookbook 54 (a 2-D array's row stride is byte-load-bearing). The element
type is the other half: `short` locals let cc1 drop the sign extension (lhu/subu, no
frame); `int` locals keep it (lh/negu).

Diagnostic broadcast: correct length + right instruction multiset and order + residual
concentrated on the FRAME ADJUSTMENT and every sp-relative offset => suspect a local
aggregate's row stride / element size, not the control flow. D's variant (c) was a
textbook case: 19 differing bytes, all of them the frame size and the address shift
that follows from it, closed by one array-shape change.

Also in this commit — a fail-fast fix to sf3_merge. Worker D placed the source md5 in
the claim row's 4th column, which sf3_merge passed through as a region override, so the
row MERGED and only `sf3_match gate` failed later with "unknown override key 'md5'".
sf3_merge now validates override keys at merge time and rejects the row with a message
naming the valid keys and pointing at report.tsv for per-claim metadata. 4 new tests,
suite 242 -> 246, OK. The candidate gate caught it; the tracked registry was untouched.
2026-09-24 08:56:26 -04:00