- docs/commit-map.tsv: 4,032 rows (ordinal of the ORIGINAL main -> rewritten hash, author/committer dates, subject); 1 pruned row of zeros (ordinal 1712, "session archive update"); 0 old hashes asserted; ordinal 1 unchanged by the rewrite (byte-identical) - resolve_tokens: 1,238 commit:NNNN tokens -> shortest-unique new hashes in 98 files (docs, phase-ends, logs, tool docstrings, 2 C comments, the A5 evidence logs); residue left as tokens: commit:1712 x4 (the pruned commit), commit:orphan-24 x2, commit:orphan-26, commit:orphan-35 (cited commits that exist in no lineage) - the rewrite (C4): filter-repo 2.47.0 on a bare clone of the C2 tip, 311 s, exactly 1 pruned, main 4,032 -> 4,031; the pre-rewrite history is mirrored in the private archive repo and in the local bundle - the proof (C5): verify_rewrite 4,031 pairs / 0 failures; absent_scan 0 offenders; gate_scan 0 offenders on the clone - adoption (C6): 100 text files differ at the tip, 0 purge paths, 0 added/deleted; leftover refs dropped; no gc yet - resolver skips tools/public_rewrite/ (its self-test fixtures are the token grammar, not citations); repo-local identity is the GitHub noreply address from here on; CURRENT_PHASE: C4–C7 logged, checkpoint -> NEXT = C8
21 KiB
PhaseEnd — Phase 18: Compiler-quirk research → the matching TOOLKIT, taught to waves at scale
Date: 2026-06-20 · Project Version: 1.17.0 · Phase Status: Complete (milestone confirmed by Drew, gate 2) · Generation: Gen2 (10th phase)
Gen2 phase 10 of the arc (8→9→10→11→12→13→15→16→17→18; 14 deferred to Gen3+). Ran across multiple sessions; the granular per-task/per-session trail (T0–T7, the verdict reversal, the 5-avenue ceiling test, the Step-3b calibration, the Step-1 wave + recovery diagnostics) is preserved on-demand at
phase-ends/logs/Phase18.md(R19 — NOT auto-loaded; consult only when researching a mechanism). This file is the synthesis. Owner decisions (Drew): pursue the research → on the "unsteerable" finding, hand-match everything (verdict reversal) → teach the toolkit to a calibration wave → bounded ~40 Step-1 wave (full tractable-247 sweep deferred to Phase 19) → close with a full Phase-19 backlog.
Build Log
Files created/changed and complete — do not recreate:
The research + the matching TOOLKIT (T0–T7 + verdict reversal):
tools/reference/(gitignored, SETUP §5.6) — cloned gcc-papermario (a6afc2af, the PSX gcc-2.7.2 lineage source) + xenogears-decomp (f27c0768, Square Oct-1998, our EXACT compiler). The primary-source corpus.tools/wall_taxonomy.py+docs/wall-taxonomy.md— census/size every residual class (carried from P17 T1).tools/ram_probe.py+docs/actor-struct.md— the actor/arg0 struct, recovered + live-verified (R10). Re-confirmed byte-NEUTRAL for matching (matching reads access width off the asm opcode, not a struct def).tools/ghidra_scripts/DecompileFunctions.java— headless Ghidra-C batch pre-pass (no /mcp); feeds the.run/ghidra_c/cache (300 fns) the waves draft from.tools/census_conflict_callees.py+tools/derive_canonical_sigs.py— the canonical-sig layer builders (one byte-neutrals32 func_X(s32…)per conflict callee; arity from Ghidra-C + asm read-before-write $a0–$a3). The 20-extern block at the top ofsrc/ov_SC01_077/ov_SC01_077.c(LOCAL; reach-1 names differ per overlay).docs/hand-matching-process.md— §1 loop / §2 idioms / §3a 5-move signature playbook / §7 wave / §8 the canonical-sig-layer finding / §8e Phase-18 outcome (corrected to the register-pinning toolkit).docs/matching-cookbook.md§17 — the matching TOOLKIT (the phase's durable deliverable): the call-crossing register-ORDER class is STEERABLE withregister __asm__PINS + a scheduling barrier (corrected from a wrong mid-phase "unsteerable" verdict); array-decay forces rematerialization; for-loop / statement-order control scheduling; thesig_unify-MANDATORY gate gotcha; the narrow-param loose-typing wall (stub). §16 reconciled (§10-vs-§16 tension resolved).docs/matching-cookbook.md§17a (THIS SESSION) — the toolkit at wave scale (measured close-rates) + the pipeline-integration recovery techniques (call-site casts not redeclaration + re-validate; stale siblingM2C_UNKdecl reconcile; the implicit-int caller class) + two NEW residual classes (per-file-O0; gcc loop-guard operand-order).src/shared/engine_core.h— the worked-example macrosDEFINE_func_8012B8E4(pins + barrier) andDEFINE_func_801399A8(for-loop/structural) + ~33 new Phase-18 reach-134 macros (the wave harvest).config/dedup.us.yaml— 1450 validated cross-binary groups (was ~1424 at phase start; +the Phase-18 propagations). 0 failed.src/ov_SC01_077/ov_SC01_077.c+ the 134 overlay.cfiles — the harvest source + the ×134 propagations; 2 recovery reconciles (func_8012A418 stale-sibling-decl; func_8012F68C parse-fix).
The Step-3b/Step-1 wave harness (gitignored under .run/, regenerable; the recipe is cookbook §17/§17a):
.run/harvest_wave_3b.js(calibration, 16 targets) +.run/harvest_wave_p18s1.js(Step-1, 31 targets, +embedded canonical callee sigs +call-site-cast/re-validate).tools/gen_harvest_targets.pygenerates the per-target callee-sig/data-decl manifest the Step-1 prompt embeds.phase-ends/CURRENT_PHASE.md→phase-ends/logs/Phase18.md(R19 archive).phase-ends/PhaseEnd_Phase18.md— this file.
Local artifacts (gitignored / regenerable): .run/ghidra_c/* (300 cached Ghidra-C), .run/drafts-*
(wave drafts + gate-staging dirs), .run/p18/* (T1/T2-T3 analyses, exemplar scratch), .run/harvest_*.js,
.run/*_targets*.json, build/, expected/, asm/, assets/. tools/reference/ (the cloned gcc/Xenogears
source). The 134 overlay Ghidra programs are script-reproducible (DB-commit deferred as regenerable).
Tools/packages installed: None — the whole phase used the existing Phase-4/6/10/11/12/13 toolchain + venv. (Wine deliberately NOT installed — W1 ruled build-divergence out by proxy; SETUP §4.8 unchanged.)
Verification results (literal) — the milestone proof:
make check-all→ 136 passed, 0 failed of 136 (main143dbb89…, resident8e17e02f…, all 134 overlays byte-identical; ov_SC01_077d19c9580…), reproduced from clean after every step (R22).make report: FLEET byte-identical 194,839 / 344,010 = 56.64% (phase start 55.51%); dedup-check 1450 validated, 0 failed; 0 NON_MATCHING in any default build (G4).- The TOOLKIT, byte-gated per class: register-ORDER →
func_8012B8E4MATCH via pins+barrier (the flagship "unsteerable" fn); array-decay →func_8012B4B888→52,func_80149544/584MATCH; structural/for-loop →func_801399A8MATCH. Honest stubs: narrow-param (func_80146A6C), loop-guard order (func_8012C2D0), §10 sched (func_8014F2E0), per-file -O0 (func_8013C360+~17). - The toolkit at WAVE scale (the bounded demonstration): Step-3b calibration 9/16 = 56% whole-binary
(vs the Phase-17 prompt's 33%); Step-1 (prompt v2: +embedded sigs +call-site-cast) 28/31 = 90%
match_one, 22 verified. Session fleet arc 55.58% → 56.64% (+1.06%, ~31 reach-134 matches; phase total 55.51→56.64 = +1.13%). git status: onlyconfig//tools//src//docs//phase-ends/tracked; zero ROM-derived/generated bulk staged. No Ghidra DB change this phase (matching used cached Ghidra-C + asm; thedb.*.gbfchurn is SessionStart-restart noise — R23 no-op, do NOT stage).
Milestone achieved (confirmed by Drew, gate 2): the knowledge-gated milestone — a per-class byte-gated
verdict (validated C idiom OR honest "unsteerable" verdict naming the exact gcc pass), §10-vs-§16 reconciled,
≥1 quirk-tail function newly matched, 136 binaries byte-identical throughout — is met and exceeded. The
phase's headline reversal: the highest-reach call-crossing register-ORDER class is STEERABLE (register __asm__ pins + a scheduling barrier), not "unsteerable" as a mid-phase draft wrongly concluded. The toolkit
was then taught to a parallel Ultracode wave and demonstrated at scale (close-rate 33% → 56% → 90%
match_one), banking ~31 reach-134 matches (fleet +1.13%).
Next: Phase 19 — Scale the toolkit (the wave-harvest at scale) + the cheap front-loaded levers. A fresh session, plan mode (Tier-1 Phase-Start). See the Phase-19 Backlog below for the full prioritized brief (the -O0 split-file fix first, recovery-tooling, then 50-target waves scaling to 100+, with an optional bounded gcc-research spike on the last residual classes). The infra is BUILT and staged.
Deviations
| Item | Plan | Actual | Reason |
|---|---|---|---|
| Register-ORDER verdict | T1–T5 concluded "UNSTEERABLE from C" | REVERSED — STEERABLE via register __asm__ pins |
I'd skipped the most direct lever; Drew's "we hand-match everything" pushed it; func_8012B8E4 byte-gated (P9/R14 self-correction). The "brute waves won't help" point stood; the HAND pin lever does. |
| Phase scope | research + a bounded quirk-tail demo (gate-1) | research + a calibration wave + a bounded Step-1 wave | the reversal made the toolkit teachable to waves; demonstrating it at scale (33%→56%→90%) is the real proof it raises the ceiling |
| Step-1 size | "scale the wave on the tail" | bounded 31 targets (Drew chose ~40; full tractable-247 → Phase 19) | gate-1 deferred the full sweep to Phase 19; Step 1 = a bounded demonstration of the taught wave |
| Wave close-rate gap | (not anticipated) | match_one MATCH ≫ whole-binary verified (12→9, 28→22) |
the gap is declaration plumbing (call-site casts the agents skip + sig_unify narrowing the extern), NOT codegen — recovered deterministically (cookbook §17a) |
| W1 Wine native-compiler check | bounded Wine run | ruled out by proxy, Wine dropped | 700+ matches prove our cc1's regalloc is faithful; the real compiler emits the same bytes → confirms, doesn't crack; heavy install not worth it (plan escape clause) |
| Effort | Max planning, xHigh/Ultracode execution | Max/xHigh research + debugging; Ultracode for the waves (Drew toggled); xHigh for this close | breadth-shaped waves under Ultracode (R26/R27); deep synthesis at xHigh (Drew's saved default) |
| New residual classes | (not anticipated) | per-file -O0 + gcc loop-guard operand-order surfaced at scale | the waves exposed classes the single-function research hadn't; the -O0 one is build-infra (high ROI), not a wall |
Commit Message
(Phase 18 landed as 16 commits d20b20c64..151da542a + this PhaseEnd.)
feat(phase-18): compiler-quirk research -> the matching TOOLKIT, taught to waves (v1.17.0)
- RESEARCH (T0-T7): read gcc-2.7.2 source (gcc-papermario) + mined Xenogears (our exact
compiler) -> per-class byte-gated verdicts. VERDICT REVERSAL (Drew "hand-match everything"):
the call-crossing register-ORDER class is STEERABLE via `register __asm__` pins + a scheduling
barrier (func_8012B8E4, the flagship "unsteerable" fn, byte-gated) -- not unsteerable.
- TOOLKIT (cookbook §17): pins+barrier (register swap), array-decay (hoist-vs-remat),
for-loop/statement-order (structural), sig_unify-mandatory; narrow-param = honest stub.
§16 reconciled (§10-vs-§16).
- TAUGHT TO WAVES (§17a): calibration 9/16=56% whole-binary (vs 33%); Step-1 (embedded
canonical callee sigs + call-site-cast/re-validate) 28/31=90% match_one, 22 verified.
~31 reach-134 matches banked, each propagated x134. fleet 55.51 -> 56.64% (+1.13%).
- FINDING: the match_one->gate gap is declaration plumbing (call-site casts not redeclaration;
stale M2C_UNK sibling-decl reconcile; implicit-int caller class) -- recovered deterministically.
- NEW residual classes: per-file -O0 (~18 fns, needs an -O0 split file like src/boot.c -> high
ROI) + gcc loop-guard operand-order (irreducible). W1: build-divergence ruled out by proxy.
- make check-all 136/136 byte-identical (R22); dedup 1450 validated/0 failed; 0 NON_MATCHING (G4).
- worklog -> phase-ends/logs/Phase18.md (R19); bumps 1.16.0 -> 1.17.0
Rules Added This Phase
| Rule | Reason |
|---|---|
None (governance). Phase 18's lessons are findings + techniques, recorded where the Phase-8/11/13/15/16/17 precedent puts them (cookbook, not rules): the register-pinning toolkit + the call-site-cast/re-validate + the sibling-decl/implicit-int recovery + the per-file -O0 and loop-guard residual classes → cookbook §17/§17a + hand-matching §8e. The verdict-reversal lesson ("never conclude unsteerable before trying register __asm__ pins; an intermediate metric is not the gate") is already governed by G3/P9/R14 and the dont-conclude-unsteerable-try-register-pins memory. The probe-before-investing / bounded-calibration-before-scale discipline is already cookbook (Phase 15) + effort-map. No new norm of conduct emerged. |
A research→toolkit→demonstrate phase produces knowledge, not governance. Existing rules covered the ground and the phase reinforced them (esp. R14: the "unsteerable" over-claim was an R14 failure mode, self-corrected). |
PhaseEnd Changelog
v1.16.0 → v1.17.0 — Phase 18 complete (Gen2 phase 10). Set out to "understand gcc-2.7.2 to raise the match-% ceiling," and delivered a matching TOOLKIT that does exactly that. Reading the real gcc-2.7.2 source
- mining Xenogears (our exact compiler) produced per-class byte-gated verdicts — and a decisive reversal
(Drew's "hand-match everything"): the highest-reach call-crossing register-allocation-ORDER class is
STEERABLE with
register __asm__pins + a scheduling barrier (the flagshipfunc_8012B8E4, byte-gated), not "unsteerable" as a mid-phase draft wrongly concluded (a clean R14 self-correction). The toolkit (pins+barrier · array-decay · for-loop/statement-order · sig_unify-mandatory; narrow-param = honest stub) was then taught to a parallel Ultracode harvest wave and demonstrated at scale: whole-binary close-rate 33% → 56% (Step-3b calibration) → 90%match_one(Step-1, after embedding each callee's canonical signature and teaching call-site casts). ~31 reach-134 functions matched (each propagated ×134), driving the fleet 55.51% → 56.64%, all 136 binaries byte-identical, 0 NON_MATCHING. The phase's reusable yield: the toolkit itself (cookbook §17), and the pipeline-integration recovery insight (§17a) — thematch_one→gate gap is declaration plumbing (call-site casts, stale-sibling-decl reconcile, implicit-int callers), recoverable for ~0 agent tokens, not a codegen wall. Two new residual classes surfaced at scale: the per-file -O0 class (~18 byte-exact-at-O0 functions needing their own -O0 split file — a high-ROI build-infra fix, not a wall) and the gcc loop-guard operand-order residual (irreducible). W1 ruled build-divergence out by proxy (Wine dropped). No new governance rules (techniques → cookbook). The harvest infra is BUILT and staged for Phase 19's scale-up.
Phase-19 Backlog — "Scale the toolkit" (Drew's research + wave recommendations, full context)
Phase 19 = scale the proven toolkit over the tractable reach-134 tail, front-loaded with two cheaper levers, run as a flywheel. Detailed plan is a fresh-session Tier-1 Phase-Start task; this is the prioritized brief, by ROI. (Decided with Drew at the Phase-18 close.)
- [HIGHEST ROI — DO FIRST] The per-file
-O0split-file fix (build-infra, no research, no agents). ~18 functions in ov_SC01_077 were built -O0 (prologue sig21F0A003=addu $fp,$sp,$zero; args spilled to frame; load-delay nops; redundantaddu rd,rs,$zero). Their correct C is byte-exact at -O0 but the overlay TU compiles-O2, and gcc-2.7.2 has no per-function optimize pragma (opt is per-file, Makefile). Fix = give them their own-O0-compiled split file with a target-specificCC1FLAGS := -O0 …(thesrc/boot.cprecedent, Phase 7). ~18 fns × reach-134 for an afternoon of Makefile work. Members: func_8013C360 + func_8013B568/B598/B6A0/B7AC/B7F4/B83C/BC7C/BCDC/BD34/BD74, func_8013C08C/C0F8/C414/C938/C964, func_80144B9C, func_801457A4. (One agent already produced verified-correct -O0 C for func_8013C360 — see logs.) - [COMPOUNDING — DO SECOND] Recovery-tooling investment between batches. This session's deterministic
recoveries (call-site arity/void casts; stale
M2C_UNKsibling-decl reconcile) banked matches for ~0 agent tokens. Fold them into the gate so the close-rate rises mechanically: teachsig_unify(or a post-pass) to auto-apply call-site arity/void casts when a draft's call disagrees with the canonical extern, and to auto-reconcile stale siblingM2C_UNK func_X();decls to the real sig. Also solve the implicit-int caller class (3 deferred fns: func_80147514/8017209C/80168F40) — the clean path is propagate-first (the def lands inengine_core.h, included first, so no implicit-int forms), gate-after; build a small helper or adedup_propagatemode for "matched-but-fails-in-place-gate" functions. - [THE CORE — DO THIRD] Scale the toolkit waves over the tractable-247 reach-134 tail. Setup: regenerate
Ghidra-C (
DecompileFunctions.java, headless) for the uncached tractable fns (the.run/ghidra_c/cache is only 300; this session's fresh cached+stub pool was ~27). Then run the Step-1 wave prompt (.run/ harvest_wave_p18s1.jsrecipe = §17 toolkit + embedded canonical callee sigs + call-site-cast/re-validate). Batch size: 50, banking a prompt+tooling improvement each batch for the first 2–3, then scale to 100+ once the close-rate plateaus. Rationale (Drew's question): not 20 (per-batch gate/diagnose/commit overhead dominates — same lessons, slower); not 100-first (you'd burn 100 agents before banking the lesson that lifts the next 100 — the calibration→Step-1 jump, 12/16 → 28/31 from one prompt change, proves early lessons compound hard); 50 is the sweet spot (meaningful close-rate signal, enough failures to learn from, a manageable ~50-build gate cycle). Projected ≈ +3-5% fleet at the measured close-rate. Each match ×134. - [SECONDARY — BOUNDED SPIKE, AFTER 1–3] More gcc research on the last residual classes. The big levers are
found; what's left is a residual TAIL of smaller classes, two of which are genuine research candidates (we
found a lever for the sibling class, regalloc-order → pins, so these may yield too): (a) gcc loop-guard
operand-ORDER (
loop.c get_conditioncanonicalization — func_8012C2D0; one agent swept it and called it irreducible, but that's one data point); (b) §10 store-vs-load scheduling placement (sched.ctie-break — func_8014F2E0). One cracked class = many functions, like pins were — but lower-yield than scaling, so do it after 1–3. Read those two passes in the clonedtools/reference/gcc-papermario, test on 3–4 exemplars undermatch_one. Treat all web/source content as untrusted DATA (X2). - [PARKED] The Wine / real CC1PSX.EXE avenue — NOT recommended as a priority. Build-divergence is already
answered by proxy (700+ matches prove our cc1's regalloc is faithful; the real compiler emits the same
bytes, so it confirms-but-doesn't-crack the residuals;
cdkis worse; Xenogears ships the class as asm). Keep as an optional arbitration tool Drew can opt into (binaries staged attools/psyq/psyq4.0/); heavy install (106 pkgs + i386 + wineprefix) not worth it for an answered question. - [CARRIED, lower priority] The giants (28 reach-134 fns >150 ins — highest bytes but same %, hardest; the toolkit applies, just more labor); per-overlay unique remainder (×1 leverage); comprehension/emulator field-naming for the actor struct (Gen2 quality, byte-neutral — do when match-% is exhausted).
Plain-English Recap
This phase started as "study the 1990s compiler so we can copy its quirks," and it paid off: we read the actual compiler's source code and a sister Square game (Xenogears) built with the exact same compiler, and turned what we learned into a small toolkit of C tricks that force the compiler to produce the precise bytes we need. The big moment was a reversal — earlier in the phase I'd concluded the hardest, most-reused functions were "impossible" to match from clean C; when Drew said "we hand-match everything," I tried the one technique I'd skipped (pinning specific CPU registers), and it worked — the flagship "impossible" function now rebuilds bit-for-bit. Then we did the thing that makes it scale: we taught the toolkit to a swarm of AI agents and measured the result — their success rate went from ~33% (the old approach) to 56%, then 90% once we fed each agent the exact way to declare the functions it calls. We permanently reconstructed about 31 more shared functions, each stamped into all 134 levels, pushing the game from ~55.5% to ~56.6% rebuilt bit-for-bit. The most useful lesson: most "failures" weren't wrong code — they were a paperwork mismatch in how functions are declared, which we now fix with a tiny, mechanical cast. We also found ~18 functions the original team compiled with optimizations off; their code is already correct and just needs its own build setting — a quick job for next phase that unlocks all 18 at once. Everything still rebuilds perfectly; nothing broke. Next phase scales this up: do the quick -O0 fix, make the gate auto-fix the paperwork, then run the agent waves in batches of 50 (learning between each), with optional deeper compiler research only for the last stubborn cases. The Wine route stays parked — we already have the answer it would give.
🛑 Stop Here
PhaseEnd written; CURRENT_PHASE.md archived → phase-ends/logs/Phase18.md (R19). Drew commits AND pushes
this PhaseEnd + any uncommitted Phase-18 work (R6/R8 — the matching commits d593ee20f..151da542a are already
landed; this PhaseEnd + the archived log are the close). No Ghidra DB change this phase (R23 no-op — the
db.*.gbf churn is restart-noise, do NOT stage it). Gen2 continues — do NOT start Phase 19 here. Start a
fresh session (plan mode) for Phase 19 — Scale the toolkit (the backlog above; -O0 fix first, then
recovery-tooling, then the 50-target waves). Keep this file forever.