diff --git a/phase-ends/logs/Phase12.md b/phase-ends/logs/Phase12.md index 1de74f6..247e718 100644 --- a/phase-ends/logs/Phase12.md +++ b/phase-ends/logs/Phase12.md @@ -711,3 +711,140 @@ Authorised, and the macros go into `gtemac.h` the way the existing ones were add **Worth naming:** the GTE inline-asm hatch authorised earlier for `0x80010810`/`0x8009C69C` **was never used** — worker C matched both in plain C. So this would be the phase's first real asm row, which is a milestone and not a shortcut. + +--- + +## THE NEGATIVES RE-DISPATCH, AND THE FIVE DEFECTS IN THE FILE I DISPATCHED + +### Position after merge 11: **646 bodies / 655 regions. Phase: 602 → 646 = +44.** + +| worker | bodies | what produced them | +|---|---|---| +| D | 14 | fresh band + Goal B + twins + GTE + `0x800A6B38` | +| C | **12** | pool prio-1, 3 of them from rows whose recorded mechanism was half wrong | +| B | **11** | **pool prio-1, all three of the last batch at 1-4 spellings** | +| A | 7 | adjacency 3-for-3, plus `goto ret0` and the `maspsx=epilogue` token | + +### The five defects in my pool generator, and every one was found by a worker + +I generated the dispatch pools with a one-off script. The tracked index documents its own schema on +line 2 — `# Columns: addresssizestatusclass` — and **my script never read `status`**, +and knew nothing about two collaborator lists. Five defects, all shipped, **all found by the workers**: + +| # | defect | rows | who found it | +|---|---|---|---| +| 1 | `status=blocked` (charter §7: forbidden to attempt) dispatched **at prio 1** | 6 | **C**, refused three in its own pool | +| 2 | blocked FAMILY with `status=near-match` (`primitive-init`) also dispatched | 1 | **C** (listed `0x80012A48` as its next row) | +| 3 | Makefile `NAMED_EXCLUSIONS` invisible → leaked back as fresh work | 8 | **A** (found two, asked if they should be filtered) | +| 4 | workers' own `negatives.tsv` invisible → **94 of 115 classified rows re-served**, at the top of prio 1 | 94 | **C** ("treat them as done, not as fresh rows") | +| 5 | `named = class not in ('-','')` treats **`no-mechanism-yet`** as a named mechanism → **62 rows ordered backwards** | 62 | **A** in prose ("your pool led with rows I could not close") | + +Plus a seventh rule added after defect 4 was understood: **a worker's OWN classification is dropped +from that worker's own pool**, while still being dispatched to the others with a `classified-by:XY` +note — because the classification is the *lead* for a different worker and a *floor* for its author. +Worker C's words: *"they are the first four rows in the file's own prio-1 order, so a worker starting +at the top spends its first hours re-deriving my floor."* + +**This is a SCHEMA error, not a logic error**, and that is the transferable lesson: five separate bugs +all trace to generating a dispatch file from a table whose header I had not read. + +The fix is `tools/sf3_negpool` (17 tests) plus a `NAMED_EXCLUSIONS` variable in the Makefile so the +list has **one source of truth** — given to both `worklist` and `negpool`, because the whole defect +was two lists that must agree living in different files. Pools: 176 → **93** (a 24 / b 23 / c 28 / d 18). +Fail-first is **demonstrated rather than asserted**: the two integration tests were run against the +pools the workers were holding at that moment and **failed, naming all 8 leaked rows.** + +Two bugs in my own fix, found while making it: `names_a_mechanism` accepted the numeric dashboard +columns (`'1'` is not a mechanism), which made the rule vacuous and dropped 0 rows instead of 58; and +one of my tests asserted output that its own `--quiet` flag suppressed. + +**A counter caught a redundant fix of mine, which belongs in the record.** I had already "fixed" +defect 3 by hand, adding `--exclude 0x8001DC20` to `worklist` — and `excluded_named_exclusion` did not +move, because the row was already excluded under another rule and had reached the worker via the +*negatives pool*. Fixing one row by hand while the generator leaked was treating a symptom, and the +tool said so. + +**Process defect, reported by worker D and my error:** I re-cut the pools **twice mid-flight without +telling the workers**, the second time after saying the split was stable. D measured it (18 rows, two +of its worked rows gone) and asked that a re-cut say so explicitly. It now does, and the re-cut is +tool-driven rather than hand-done. + +### Two workers independently converged on what these negatives are worth + +* **B:** *"the recorded class named the SYMPTOM (a merged store, a folded shift, a strength-reduce) + while the fix was an ORDER or a claim-row option."* +* **C:** *"in every one the recorded class NAME was right and the recorded MECHANISM was half wrong."* + +Two partitions, same conclusion, and it cuts both ways — **the strongest argument for dispatching +onto named negatives, AND a warning that the class is a signpost, not an instruction.** This is +cookbook 182's "a negative with a named mechanism is overturnable" given a measured boundary. + +### The counter-evidence, recorded because it challenges my own decision + +**Worker D is 1-for-4 on prio-1 rows, having been 4-for-4 on Goal B**, and argues: *"what matters is +whether the row has been attempted with the current lever set, not which pool it is in."* D's read is +that the prio-1 rows are negatives *with recorded residuals* — rows previous attempts already pushed +to 1-6 bytes — so the surviving cost is the allocator/rematerialisation wall, while the unclassified +rows were productive for the opposite reason: nobody had ever asked them a structural question. +**A (3 for 5) and B (5 overturns) contradict it in their partitions.** Not re-deciding on nine rows; +watching the aggregate, and D's own conclusion is the re-rank criterion: not "which pool" but +"attempted with the current lever set". + +### Convergent findings, each reached independently by two workers + +1. **`constant-base-in-register` is TOOLCHAIN BEHAVIOUR, not a source puzzle.** A (`0x8010400C`): *no + source shape can both keep the address in a register and keep the 11-instruction count, because + keeping the address a value requires a use of the value and the original has none.* C + (`0x80107874`): *cc1 materialises a symbol address into a GPR only when the address ESCAPES + (passed, stored, incremented, or indexed by a variable); a plain constant-base store never does.* + **Same rule, found without knowledge of each other.** A's row has **1** `jr $31`, so the gate + correctly refuses `cc1bin` — the restriction doing exactly its job. +2. **The registry's gp marker is per-SYMBOL; the access form is per-SITE.** B needed + `gp=-D_80121BFC`, C needed `gp=-D_801226E0`, in the same hour, on rows where the index marks the + symbol `gp` and the original reads it absolutely. **Cookbook 46's per-site override is not an + exotic fallback** — it is what two workers needed on one afternoon. Both rows came out one + instruction SHORT without it. +3. **cookbook 42's coupling is refuted on BOTH of its own rows**, and the named-binding lever that + cracked `0x801008DC` does **not** transfer to `0x80050674`. D corroborated C's floor from a second + direction (2 spellings) and added a detail: the loop counter's `move t0,zero` must come first. + +### New mechanism-level results + +* **C pins finding 8's missing trigger.** `0x800AC9D8` and `0x800A6658` are the **same source shape** + (two symbols, variable byte offset, fresh per-store base): in the first cc1 **re-materialised the + base per store and matched**; in the second it **hoisted** and the row was lost. The trigger is + **which form cc1 picks for the address** — *through `$at`, a scratch register that cannot stay live + across a loop, no hoist is possible and the original's shape comes out; through a free GPR, cc1 + hoists and the row is lost.* On `0x800A6658` the four setup stores occupy `v0/a0/a1`, so cc1 reached + for GPRs. The row's untried lever is a spelling that raises register pressure at the loop head. +* **`maspsx=epilogue` decided from the CANDIDATE's tail, three independent confirmations** (A's + `0x8010036C`, C's `0x800FE970`). Shape A inserts a load-delay nop: candidate `lw ra,24(sp) / addiu + sp,sp,32 / jr ra / nop` vs original `lw ra,24(sp) / nop / jr ra / addiu sp,sp,32`. +* **A's `goto ret0` lever fixes two failures at once** (`0x800690E4`): CSE of the head test into the + back edge (→64 B), and a duplicated node test top-and-back-edge (→76 B). Both null exits sharing one + `ret0:` label makes the head test survive as a forward branch. +* **B's strength-reduction rule:** *"let the compiler do the strength reduction" is itself a + byte-required spelling* — the indexed `do/while` on `i != -1` gives the pointer walk with no counter + and the original's INDEX-FIRST `addu`; a hand-written pointer walk gives base-first and costs 3 + bytes; `for (i = len; i >= 0; i--)` keeps a counter and adds a redundant `bltz`. **A pointer-order + residual can be a hint to write the INDEX loop instead.** +* **Source-side controls accumulated:** `volatile` on a store target suppresses the jump-delay-slot + fill · `volatile` on a local reproduces a stack-routed temp · a loop-invariant constant must be a + NAMED local assigned before the loop for the prologue order to come out right · a loop-invariant + base must go through `$at` (or be forced to) or cc1 hoists it. + +### Two cross-reconstruction defects, both named rather than buried + +* **`func_800F8F3C` arity disagreement** (C). The registered `src/func_800F8F3C.c` declares + `int func_800F8F3C(void)` while C's call site in `0x800FE970` materialises a buffer argument + (`addiu a0,sp,16`) and discards the result. Per finding 162 C used the non-prototype declaration and + the call-site bytes are identical either way, so the match does not depend on it — **but whoever + revisits `0x800F8F3C` should know at least one caller passes it a buffer it may not use.** +* **`0x80091490`'s inferred prototype** for the unreconstructed `0x80018284` (merged on my ruling). + +### Two unclosable rows turned into decision inputs rather than losses + +* **A's `0x8006FAA0`**: the C is SOLVED and verified — `-fno-schedule-insns` gives + `differing_bytes=0`. Per cookbook 50 the flag is diagnosis-only and never attaches to a claim row, so + it stays a negative, but the row is now the cleanest post-pass/scheduler candidate in A's pool. +* **A's `0x8010400C`** and **C's `0x80107874`/`0x800F6ED0`**: classified with mechanisms, no lever left.