Commit Graph

274 Commits

Author SHA1 Message Date
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 9ff344834c phase11: cookbook 130-131 + document the ranker's size bias
130: worker D found the ranker's top is SIZE-BIASED -- redundancy and size are correlated because
a bigger body has more chances to repeat a 3-gram. Its top was 3288 B at 0.91 while the rows it
was matching sat at 0.64-0.74 in the small tail. The metric is not wrong about rows of equal
size; the raw score just cannot be compared across bands. Since the milestone counts BODIES, a
3288 B row and a 248 B row are worth one body each and the large one costs many times the
context. Worker D's effective filter was redundancy PER UNIT SIZE, now approximated by
--max-size, which is documented in the tool.

131: an unreferenced ARRAY local homes but an unreferenced SCALAR does not -- second independent
instance, identical mechanism, so it is a rule: when the frame is a clean multiple of 8 bytes
short and everything else is identical, add an unreferenced array local of that size.
2026-09-24 10:31:23 -04:00
Christopher Williams 6589668e85 phase11: tools/sf3_family — search for a matched row's siblings (cookbook 125)
Worker D's finding 125 said three members of one family were found by three different means
and 'the finder varies, the price does not', concluding that families should be SEARCHED FOR
explicitly rather than waited for. This implements that: for every unmatched worklist row,
find the already-matched row with the highest similarity, where similarity is an
opcode-histogram cosine (registers erased, nops dropped, per finding 120) multiplied by the
size ratio so a shared multiset at a different scale does not count.

A BUG WORTH RECORDING: the first version read config/match_worklist.tsv with the regions
column layout, so column 0 (the RANK) was read as the address. It returned ZERO candidates at
every threshold, which is what exposed it -- a silently wrong address yields no matches rather
than an error. Both layouts are now parsed by named functions with the offset documented.
2026-09-24 10:30:58 -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 c12d206078 phase11: ledger RULE fix (worker A) + retire the roving list
Worker A found that last-row-wins makes an unconditional 'released' from a worker who never
held the row WRONGLY FREE IT from whoever does. A found two addresses held as wip by worker C
and correctly re-appended C's wip row rather than its own released -- had it not, C's rows
would have been silently released and both workers could have started them.

New rule: only append 'released' for an address you yourself appended 'wip' for. Order is
check (range-aware) -> if free append wip -> work -> append your own released.

Second finding from the same episode: the roving list I gave worker A deliberately overlapped
the other partitions and its first two picks were both already held. A shared queue is only
worth it when the work is NOT already partitioned. Once the partitions were re-ranked on
global redundancy, the roving list was strictly worse than a worker's own slice, so it is
retired. That is the fifth defect found in a coordinator-written rule this phase.
2026-09-24 10:29:04 -04:00
Christopher Williams 81d3ffb5b9 phase11: merge 33 + cookbook 125-126 — 554 bodies / 563 regions
Worker D's 0x8009F890 (248 B, first attempt) -- the THIRD member of a family it had already
matched twice, and the three were found by three different means: the size ranker, adjacency,
and the rebuilt global-redundancy rank. 'The finder varies, the price does not.' The
operational conclusion: families should be SEARCHED for explicitly rather than waited for.

126: worker D withdrew a named direction after checking its own notes and finding it had
already been tested and made the row worse. It declined a fifth spelling on a falsified lever
and corrected the row's record to the honest state. A direction that has been tested and
failed must be struck, or the next worker inherits a false lead.
2026-09-24 10:28:48 -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 34ffbad9ad phase11: cookbook 123-124 — a counter-intuitive clamp lever, and a confound in my own partition design
123: a 'clamp to zero' written as a BITWISE MASK (x & (x >> 31)) compiles branchlessly while
the same thing as a TERNARY branches -- the opposite of the intuition. Worker B's MIN half now
matches exactly with the bitwise form. The MAX half (branchless slt/negu/and, i.e. x & -(x>0))
remains UNREACHED by any ternary or bitwise spelling, with a plausible SDK min/max macro as the
source. Recorded as a named open question.

124: worker B found that the partition design CONFOUNDED the size/redundancy measurement. The
partitions were rank-interleaved by the original worklist order (tier, size, address), not by
redundancy, so 'my <=200 B hit rate was high because my slice happened to be redundancy-rich,
not because small rows are inherently easy'. The within-worker evidence for 'cost is redundancy'
stands (A: 548 B first-attempt vs 176 B nine failures), but any CROSS-worker band comparison was
measuring the partition, not the rows. Fixed by re-partitioning on global redundancy. General
lesson: when work is divided among workers, any per-worker statistic is contaminated by the
division.
2026-09-24 10:25:21 -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 71aa4a2bc2 phase11: cookbook 116-118 — the ranker's blind spot, a new failure signature, and two negatives
116: worker A's hypothesis that the ranker cannot see LIVE RANGES (its conversion rate went to
zero over three consecutive rows, all failing on register allocation with the structure fully
confirmed). I tried to make it computable -- callee-saved registers saved and s-registers per
call -- and the proxy FAILED: matches span 0.00-2.00 s/call and failures span 0.17-1.33, with
a matched row having the HIGHEST s-register count and another matched row having 16 calls and
0 saved registers. The hypothesis is not refuted; the proxy is just not a good
operationalisation of 'live range'. Recorded as an open axis.

117: worker C's new signature -- correct control flow, +3 instructions, all three being j/jr
delay slots. DISTINCT from a nesting error (both give a small residual, but nesting moves
branch displacements while this moves instructions ACROSS a jump). Belongs with the post-pass
family.

118: call COUNT does not separate a real block from a call chain -- worker C's matched
0x80058CE8 is four loops that each call, and its false positive has zero calls. Fails in both
directions; recorded so nobody re-derives it.
2026-09-24 10:20:47 -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 46c260c252 phase11: worker B's adjudication makes the fragment check DISJOINT — 0 of 555
The first version of the fragment check fired on any nonzero sp offset and flagged 2 of the
555 registered regions. Worker B read its own hit (0x800B704C) before committing and showed
it is a LEGAL FRAMELESS LEAF WITH EIGHT ARGUMENTS: in o32 the callee's sp is unchanged at
entry, so sp+16..sp+28 IS the caller's outgoing area -- arguments 4-7 -- and reading it
before any addiu sp,sp,-N is exactly what a frameless >4-argument leaf looks like. Its
evidence: exactly one jr ra, zero jal, zero addiu sp,sp,-N, zero sw ra/lw ra across all 324
bytes, and no callee-saved register touched.

Excluding the incoming argument area (sp+0..sp+31) and flagging only a negative offset or an
offset beyond the 8-argument area makes the check DISJOINT:
  registered regions flagged:  2 of 555  ->  0 of 555
  suspects across 4 partitions:  5  ->  1
The one remaining suspect is worker A's 0x800C3490.

This is the cleanest example in the phase of a worker ADJUDICATING a tool's output rather
than obeying it -- B was explicitly told 'advisory, do not skip', read the row anyway, and
its adjudication turned a noisy heuristic into a precise one.
2026-09-24 10:18:50 -04:00
Christopher Williams 74199d1ab3 phase11: tools/sf3_rank --fragments + cookbook 114 — worker A's fragment anomaly
Worker A found 0x800C3490 is not a matchable body: it starts mid-expression with sw v0,32(sp)
before any frame setup, and its identical tail also appears at 0x800C3470, so it is a
shared/jump-target block Ghidra promoted to a function -- inside no region, with the code
before it in no worklist, so nobody can match it standalone.

Its generalised rule is narrower than 'first instruction is not prologue-like', because a
function may legally start with beq/sh/move: a row is a FRAGMENT if its first instruction
touches the stack before any addiu sp,sp,-N, reads a stack slot, or uses a callee-saved
register that is never saved.

Implemented as a --fragments scan. Measured: 5 suspects across all four partitions, but 2
false positives across the 555 REGISTERED regions, so it is ADVISORY not an exclusion --
sufficient-but-not-complete like the trapping check. A hit means read before spending a
spelling, never skip.
2026-09-24 10:17:38 -04:00
Christopher Williams e4b6423774 phase11: cookbook 111-113 + range-aware registry check — the third defect in a coordinator rule
111: $sp cannot be moved from C, so a stack switch is INLINE ASM and the statement SPLIT is
byte-load-bearing -- seven separate __asm__ volatile statements with the call between them,
and the nop in each jal's delay slot falls out of the following statement being a compiler
barrier. One asm block, or a register int sp, does not reproduce it.

112: an intermediate that must live in MEMORY (the original reloads it before each of three
calls; a scalar local gets register-allocated and loses 8 bytes of frame); the chained
assignment's store order; and finding 100's then/else diagnostic confirmed on a new row.

113: a region's END address is EXCLUSIVE, so the registry free-check must be a containment
test, not a string match. Worker A was wrongly blocked on 0x8006B7C0 because it is the
exclusive end of 0x8006B778's range. This is the third defect found in a coordinator-written
rule this phase; the workflow doc now requires a range test.
2026-09-24 10:16:34 -04:00
Christopher Williams f14744e76c phase11: merge 27 + cookbook 109-110 — 547 bodies / 556 regions
Worker D's 0x800307FC (92 B) and 0x80031EBC (112 B), both first spelling by adjacency.

109 records worker D's calibration claim -- 'adjacency finds the ROW, redundancy predicts the
PRICE' -- and the harder discipline behind it: D read 0x8006C044, identified it as a
tie-break-dense 3D-maths routine, and RELEASED it in favour of two small adjacent rows that
together cost less context than a first draft and returned two bodies instead of zero-to-one.

110 records a NEGATIVE RESULT from the coordinator. Worker C found a real false positive (the
ranker's top row is tie-break-dense, its score inflated by a repeated multu/mflo/sra idiom)
and proposed comparing full instruction words instead of opcodes. I implemented that and
measured it: it scores two KNOWN matches at ZERO and the known false positive HIGHEST. The
reason is fatal -- a genuine repeated source block does not produce identical instruction
words across copies because the allocator assigns different registers, so 'same opcodes,
different operands' describes a repeated block and a repeated idiom equally well. They are
indistinguishable at the instruction level. The opcode metric stays.
2026-09-24 10:16:20 -04:00
Christopher Williams 17e3b4496f phase11: pin the ranker's statistic + cookbook 107-108
Worker B's comparability caution: the redundancy score is the MAXIMUM of the 2/3/4-gram repeat
ratios, so a high score means 'some length scale is very repetitive', not 'repetitive at every
scale'. Any implementation using a mean or a different normalisation gives non-comparable
numbers, which matters because workers were sharing rankings across partitions. Pinned in the
tool's docstring with this tool named as the reference implementation.

107: finding 43's goto lever is NECESSARY BUT NOT SUFFICIENT -- worker B found it overshoots
(152 vs 140) on 0x800FCA90, and this is the second independent instance after worker A's
0x800256F0. Treat it as one attempt, not as the fix.
2026-09-24 10:14:56 -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 6fd8caeb5a phase11: cycle-1 ledger — 542 bodies / 551 regions, and the phase premise falsified
Records the full cycle: the 244 B ceiling was a dispatch artefact, ASPSX does not fill delay
slots (so the post-pass shrank from a modelling project to five lines), and the central finding
that cost is tie-break density rather than size -- measured independently by two workers from
opposite directions and now shared tooling.

Also records the four defects in coordinator work that workers found, the coordination defect
the best heuristic created, the new classes and levers (58-106), and two orchestrator notes:
the milestone counts BODIES not bytes, and a diagnostic should be routed to the row shape it
matches rather than broadcast.
2026-09-24 10:12:15 -04:00
Christopher Williams 9adcdf5529 phase11: the inflight ledger needs a RELEASE state (worker A's finding)
Worker A appended three rows optimistically, classified them as near-matches, and then had
no way to clear them -- because the ledger was append-only with no terminal state. Every
other worker would have skipped three free rows.

Fixed: a status column (wip / released) where the effective state of an address is its LAST
row, and a rule that a worker must append 'released' when it stops. Worker A's three rows
are released.

The general lesson is recorded in the workflow doc: a write-ahead log needs a TERMINAL
state, not just an opening one, or the log itself becomes the stale data it was meant to
prevent. Any append-only coordination file needs a way to say done.
2026-09-24 10:11:31 -04:00
Christopher Williams f89e46ad88 phase11: cookbook 105-106 — re-read granularity as a tunable; filter determines failure class
105: the pointer re-read granularity is a DIAL, measured in both directions by worker D --
re-read per store (700 B row, 46 times, matched), once per block (276 B row, matched),
per statement (80 bytes too long), and never (does not match). Cause is aliasing. It is the
same property as finding 45's named-locals family but as a COUNT rather than a yes/no.
Plus: a genuinely uninitialised read in the original must be preserved, not corrected.

106: which failure class a row lands in depends on the FILTER, not the band. Worker D's
cheap rows fail on frame/combiner/allocation/batch-shape and never on the branch
diagnostics, so those belong on tie-break-dense rows. Route a diagnostic to the row shape
it matches rather than broadcasting it.
2026-09-24 10:09:16 -04:00
Christopher Williams 024f3c58dc phase11: regenerate the worklist and refilter all partitions — 1080 rows remaining
The partitions had drifted: they still listed rows that have since merged, because make
worklist had not been re-run. Refiltered; every partition is now disjoint from the registry.
2026-09-24 10:08:36 -04:00
Christopher Williams bb1e3e5685 phase11: tools/sf3_rank — the redundancy ranker as shared tooling
Promoted from worker B's staging implementation (which independently reproduced worker A's
metric). Ranks worklist rows by tie-break density: for n = 2/3/4, the fraction of
n-instruction opcode subsequences already seen earlier in the body, best of three, with
NOPS EXCLUDED (worker A's refinement -- lw/nop pairs otherwise inflate arithmetic rows).

This is the phase's central dispatch finding made reproducible. Evidence: worker A matched
548 B / 460 B / 356 B / 204 B all on the FIRST spelling while its one nine-attempt failure
was the SMALLEST row it attacked (176 B); worker D matched 1232 B on the 2nd spelling and
700 B on the 3rd while its 248 B row took 4. Cost is set by tie-break density, not size.

Companion rule (cookbook 99): once a row is matched, the row ADJACENT to it beats even the
top of this list -- the binary is laid out by translation unit, so neighbours share the
author's habits. 4-for-4 across two workers.
2026-09-24 10:08:26 -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 197a070f2f phase11: merge 23 — 540 bodies / 549 regions, maspsx=moves verified through the merge
Worker A's 0x80068910 and worker B's 0x800FA5D8 -- the latter carrying the region token
maspsx=moves in column 4, which is the first use of the new mode through the real merge
flow. The token survived sf3_merge intact and the full 549-region gate is GREEN with the
mode active, so worker B's oracle-driven mode is now load-bearing on a registered region.

That closes the loop on worker B's ASPSX result: it ran all five SDK assemblers as a
read-only oracle, found ASPSX does not fill delay slots, concluded maspsx is faithful and
that the fills come from GNU as in reorder mode, identified move->addu as the only real
gap, and the resulting mode is now matching a region in the tracked registry.
2026-09-24 09:53:03 -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 2c3f5a33a5 phase11: cookbook 89-91 + gp symbol row D_80122464 — 531 bodies / 540 regions
Worker D's most transferable finding yet: the ADDRESS SYMBOL is what selects the addiu form.
A literal (int)0x8013F9B8 gives lui+ori; &D_8013F9B8 gives lui+addiu. So 'ori where the
original has addiu' means the source used a literal where it should reference the address
symbol -- a 40-byte error that presents as a LENGTH mismatch, which is why it reads like a
codegen problem. Extends findings 4/46.

90: a (gp)-relative access to an unregistered address is a SYMBOL REQUEST, not a source
problem -- the harness derives gp-ness only from config/symbols.tsv and no CLI option adds
it. Worker D verified its row by pointing --symbols at a scratch copy of the registry.

91: a real source-order quirk (two successive call addresses swapped) is preserved in a
match and is not a transcription error.
2026-09-24 09:44:33 -04:00
Christopher Williams e7e1ce9d30 phase11: merge 18 + cookbook 87-88 — 530 bodies / 539 regions
Worker A's 0x8009C904: 548 bytes, matched on the FIRST spelling. Cumulative evidence for the
redundancy ranker is now 548B/1st, 248B/3 spellings, 1232B/2, 700B/3 -- against 176B/9
failures for a tie-break-dense row.

Cookbook 87 is the important negative: cc1 does NOT unroll a constant-trip-count loop (the
for-loop form is 212 B against the original's 780 B), so whenever an original is unrolled the
SOURCE is unrolled too. That explains why the >800 B band is full of cheap rows -- those
bodies are unrolled in the source, and an unrolled repetitive body is exactly what the
ranker scores highest.
2026-09-24 09:43:12 -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 6ecf31be95 phase11: merge 11 — worker C's 0x8009F3A8 (268 B) -> 512 bodies / 521 regions
Worker C's measured dispatch finding is the most useful strategic result of the phase:
across its 15 above-ceiling rows, 'does the body contain a repeated block?' has
out-performed 'how big is it?' SIX TO ZERO. Every one of its six matches is a
repetitive or mechanically-determined body (struct assignment, repeated identical
block, record initialiser, transposition, repeated bounds check); every one of its
nine near-misses is tie-break-dense (long straight-line call sequence, pointer-walk
loop, table walk). Worker D's rows say the same thing from the other side: its 700 B
and 1232 B bodies cost 3 and 2 spellings while its 248 B body cost 4.

Also confirms cookbook 44's stated limit directly: the && chain and eight separate
if(...) goto fail; statements compile IDENTICALLY here, so the distinguishing fact is
'one combined condition with one trailing assignment', not the operator.
2026-09-24 09:32:08 -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 959e9c7808 phase11: ledger + CURRENT_PHASE at the compaction point — 501 bodies / 510 regions
Records worker B's oracle result overturning the post-pass framing (ASPSX does not
fill delay slots; maspsx is faithful to it; the fills come from GNU as reorder mode and
the only gap is one mnemonic), the new `maspsx=moves` mode, worker C's correction of a
too-broad Phase 10 coordinator fix (now opt-in `maspsx=nopmarker`), the per-worker cost
tables that show SHAPE not band is the variable, and the new findings 63-66 plus the
named-locals family's fourth mechanism and the char[4] block-move recipe.

Committed at the orchestrator's 70% context cap; compaction follows, safe because the
ledger and CURRENT_PHASE are current.
2026-09-24 09:16:31 -04:00
Christopher Williams c4f4bc0cfa phase11: CORRECTION — the Phase 10 #nop-honouring fix was TOO BROAD; now opt-in (maspsx=nopmarker)
Worker C found the defect while characterising an above-ceiling row: the Phase 10 fix
that made maspsx honour cc1's explicit `#nop` marker unconditionally is wrong for a
bare-symbol store consumer, because the store's own `lui $at` expansion fills the
delay slot and the marker is SPURIOUS. On 0x800AFDBC the original is
`lhu` / `lui at` / `sh` with NO nop, and honouring the marker costs 2 instructions --
turning a row that was otherwise byte-identical into a LENGTH-MISMATCH.

But the fix is genuinely needed for 0x80107C5C (112 vs 108). So the two rows want
opposite behaviour from the same instruction shape, and the honest resolution is to
make it a per-region mode rather than a global default.

  `maspsx=nopmarker`  ->  --honour-nop-marker   (default OFF)

VERIFIED BOTH WAYS:
  make check with the mode OFF   -> regions=510 AGREE, differing_bytes=0 MATCH, 253 tests OK
  0x80107C5C WITH the mode       -> 112 B, differing_bytes=0 MATCH
  0x80107C5C WITHOUT it          -> 108 B LENGTH-MISMATCH
So the default is the long-standing behaviour, and rows that need the marker opt in.

The tracked patch is regenerated and verified to reproduce both modified files exactly
from the pristine pinned checkout (tools/maspsx is git-ignored, so the patch is the only
reproducible carrier). docs/SETUP.md records the correction.

This is the third time this phase a worker found a defect in work the coordinator had
already shipped as verified -- the pattern is worth noting: a fix proven regression-free
against the CORPUS can still be wrong for an UNMATCHED row, because the corpus only
exercises the paths that already work.
2026-09-24 09:15:47 -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 803175ffe8 phase11: merge 5 — 496 bodies / 505 regions, FOURTH body above the old ceiling (new max 312 B)
Worker D's 0x8005A33C (312 B) and 0x8009F5AC (244 B) MATCH, both verified independently
and gated on the whole binary before promotion. Corpus max is now 312 B (244 -> 248 ->
264 -> 312 across two workers).

COOKBOOK 63 — NEW LEVER, the combiner constant-fold class. A residual of exactly one
addiu plus a matching shift in every displacement of one base register means cc1's
combiner folded a constant offset the original kept in a register. Writing the base
inline triggers it (308 B, one instruction short, 6 differing words all the same
thing); materialising the offset as a NAMED LOCAL blocks the fold and matches. It is a
4-byte class that is neither source structure nor scheduling, and it is INVISIBLE to a
mnemonic-level diff because every mnemonic is right.

COOKBOOK 64 — finding 59's family now has three load-bearing properties: row stride
(0x8009F6A0), total size (0x800308C4), and MEMORY RESIDENCE (0x8005A33C, where a union
lets cc1 scalarise the packed scratch into and/sra/sll).

COOKBOOK 65 — THE AMENDMENT TO FINDING 41 IS NOW MEASURED. Worker D ran the paired
control deliberately: the body immediately PRECEDING its first ceiling match, the
inverse transform with an identical frame shape. <=244 B: 1 attempted, 1 matched (100%),
first spelling. >244 B: 4 attempted, 3 matched (75%), 2-7 spellings. THE BAND WAS NOT
THE VARIABLE -- THE LEVER SET WAS. And the two LARGEST bodies were the EASIEST (312 B
and 264 B at 7 and 2 spellings), because a larger body has more redundant structure to
key off.

COOKBOOK 66 — a bounded negative with a named direction beats a blocked class: the
allocation failure on 0x800FF5A8 is the named-locals family in the OPPOSITE direction
(the original keeps a0 in TWO callee-saved registers, so what is missing is a named
local the allocator cannot coalesce).
2026-09-24 09:08:15 -04:00
Christopher Williams f188708009 phase11: ledger — the ceiling breaks three times, the content-free frame lever, sf3_merge fail-fast fixes
494 bodies / 503 regions. Three bodies above the old 244 B ceiling from two workers
(248/248/264 B). Cookbook 59 extended with the unreferenced-array-local mechanism
(cc1 gives unreferenced scalars no home but does allocate for unreferenced arrays),
the amended three-direction rule, the per-band cost table, the sf3_merge fail-fast
fixes, and the rank-instability broadcast.
2026-09-24 09:01:06 -04:00
Christopher Williams e7caec4443 phase11: merge 4 — 494 bodies / 503 regions, THIRD body above the old ceiling (new max 264 B)
Worker D's 0x800308C4 (264 B) MATCHES, verified independently and gated on the whole
binary before promotion. Three bodies now sit above the old 244 B ceiling
(248/248/264 B) from two independent workers, so the dispatch-artefact verdict is
settled by result.

COOKBOOK 59 EXTENDED WITH A NEW MECHANISM. D's first attempt was 8 words from a match
and all eight were the FRAME; 56 of 64 words were already byte-identical including
every call site, delay slot and register. The missing 8 bytes are an UNREFERENCED
ARRAY LOCAL, and the mechanism is measured across five spellings: cc1 allocates stack
space for an unreferenced ARRAY local but NOT for an unreferenced SCALAR local
(`int pad0, pad1;` gives no home; `int pad[2]` and `short pad[4]` both match). The 8
bytes are size-load-bearing and content-free -- a limit of the reconstruction, not a
recovered fact, recorded as such in the file header.

AMENDED RULE: when the residual IS the frame, vary the local aggregate's declaration
in THREE directions -- element type, row stride, and total size. Instance 1
(0x8009F6A0) needed the row stride; instance 2 (0x800308C4) needed the total size.
2026-09-24 09:00:06 -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 7b501ef8b0 cookbook: findings 58-62 — the size-band amendment, the ceiling break, division_check, the ASPSX oracle rule
Finding 58 AMENDS finding 41, which was Phase 10's headline result and drove every
dispatch decision. The "1-in-12 for 200-800 B" comparison was taken on a queue that
had never been attempted (5 of 427 rows above 244 B ever tried, 1.2%; three in an
excluded class; both non-excluded attempts near-matched). The first row attempted
above the ceiling matched. The band's measured yield is a function of the lever set at
the time of measurement, and the lever set grows -- RE-MEASURE a band before
concluding it is exhausted, and never treat a band as closed at ~1% attempt coverage.

Findings 59-62: the local-aggregate row-stride/element-size lever that broke the
ceiling (int t[3][4] not int t[9]; frame 48 vs 40; residual concentrated on the frame
adjustment); the division_check trapped class; the localisation of the maspsx/GNU-as
mutual exclusion with the developer's ASPSX-as-oracle-only rule; and the fail-fast
validation of region override keys in sf3_merge.
2026-09-24 08:56:45 -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
Christopher Williams eea8b670d4 phase11: ledger — D's census falsifies the phase premise, division_check class, post-pass authorized
Records: the "244-byte ceiling" is a DISPATCH ARTEFACT not a measured wall (5 of 427
rows ever attempted, 1.2%; three in an excluded class; both non-excluded attempts
near-matched; 84% unclassified ordinary code), so worker C is re-assigned above the
ceiling onto a 619-row dispatch file with 258 known-callee rows.

The new division_check exclusion class (break and div always co-occur; 0 of 493
registered regions contains either; worklist 1193 -> 1118).

Worker B's localisation of the maspsx/GNU-as mutual exclusion (ASPSX does both the
move->addu conversion and the fill; maspsx the first only, as the second only, and they
cannot be combined) with a 42-row/14% census in its partition, and the developer's
authorization of a tracked post-pass modelled on ASPSX's behaviour via oracle
characterisation -- ASPSX itself is a diagnostic oracle only, never a build stage,
because it is proprietary and git-ignored and a build depending on it could not be
reproduced.
2026-09-24 08:53:48 -04:00