- splat.us.exe.yaml: text subseg asm->c (INCLUDE_ASM scaffold); include/common.h (committed prelude); Makefile c-rule cpp->cc1->maspsx->as + -Map - PIN (G8): gcc-2.7.2-psx -O2 -G0 -mips1 -mcpu=3000 -mgas -msoft-float -fgnu-linker + maspsx --aspsx-version=2.56 --expand-div; byte-exact on func_80018F20 + instruction-identical across 3 idiom classes; --expand-div REQUIRED for div/rem (SETUP.md §5.4 PINNED, Makefile MASPSX_FLAGS) - harness: diff_settings.py (asm-differ mipsel object mode), tools/decompile.py (m2c wrapper), tools/permuter/ (decomp-permuter harness + objdump shim), expected/ - 14 real functions matched (asm-differ score 0): func_80018F20 + 13 accessors; +14 splat-auto empty no-ops; make check BYTE-IDENTICAL throughout - 2 instruction-identical near-misses guarded NON_MATCHING (func_80015A74 scheduling, func_80016714 phantom frame) -> decomp-permuter candidates - flywheel: docs/matching-cookbook.md, memory + CLAUDE.md step 5 (consult+evolve), R16 - requirements-python.txt: venv freeze (pycparser<3.0 pinned — permuter needs plyparser) - LZSS spike: needs jump-table-in-rodata workflow (102 tables, mixed blob) -> Phase 7 - venv: asm-differ + permuter deps installed - bumps project version 1.5.0 -> 1.6.0
12 KiB
PhaseEnd — Phase 6: Compiler fingerprint + first matched functions
Date: 2026-06-14 · Project Version: 1.6.0 · Phase Status: Complete (substantively; the LZSS milestone gate is carried to Phase 7 per Drew — it uniquely needs the jump-table-in-rodata workflow)
Build Log
Files created/changed and complete — do not recreate:
config/splat.us.exe.yaml— text subsegment flipped[0x800, asm, 800]→[0x800, c, 800]. splat now emitssrc/800.c(file-scopeINCLUDE_ASMstubs) + per-functionasm/nonmatchings/800/*.s. Consumed bymake extract.include/common.h— new, committed prelude (#include "include_asm.h"+u8..f32typedefs). Everysrc/*.cincludes it; splat references but does NOT generate it.Makefile— added:MAPFILE+-Mapon theldline (asm-differ symbol lookup);C_SRCS;OBJSexcludesasm/nonmatchings/*(those are textually.included by the .c); thebuild/src/%.o: src/%.cc-rule (cpp→cc1→maspsx→as);CPPFLAGS/CC1FLAGS(pinned §5.4 flags);MASPSX_FLAGS := --expand-div(pinned).check-envuntouched.diff_settings.py— new, repo root. asm-differ config: archmipsel(R3000), object mode vsexpected/, gnu map,mipsel-linux-gnu-objdump.tools/decompile.py— new, m2c wrapper (fn →asm/nonmatchings/**/<fn>.s→ m2cmipsel-gcc-cscaffold).tools/permuter/compile.sh+tools/permuter/bin/mips-linux-gnu-objdump— new, committed decomp-permuter harness (build-faithful compile command + the objdump shim the permuter's hardcodedmips-name needs). Per-function setup recipe in cookbook §3.docs/matching-cookbook.md— new, the evolvable idiom catalog (I1–I4 idioms, T1–T3 techniques, §3 permuter recipe +-jsweet-spot, §4 flag gotchas, §5 hard-residual classes). The compounding knowledge base.requirements-python.txt— new, committed venv freeze (the Phase-4-deferred freeze). Notablypycparserheld <3.0 (3.0 removedplyparser, breaking the pinned permuter — hard-won).pip install -rreproduces the venv.docs/SETUP.md— §6.6 (the matching loop + NON_MATCHING guard convention) and §5.4 PINNED block (the compiler triple, G8).CLAUDE.md— step 5 in the Session Start Protocol: matching phases read the cookbook + §5.4, and evolve the cookbook + tooling (the flywheel).src/800.c— the matched-C record (committed): 14 real functions in C (func_80018F20byte-clamp + 13 accessors), 2#ifdef NON_MATCHING-guarded near-misses (func_80015A74,func_80016714), and 14 splat-auto empty no-ops (void f(void){}).phase-ends/PhaseEnd_Phase6.md— this file.phase-ends/CURRENT_PHASE.md— absorbed into this file and deleted (P8).
Local artifacts (gitignored / regenerable — do NOT recreate):
asm/nonmatchings/800/*.s(2054 per-function disasm) +include/*.inc+undefined_*_auto.txt+build/+expected/—make extract/make build/make expectedregenerate them..run/permuter/func_80015A74/— permuter scratch (recipe in cookbook §3; the C also lives guarded insrc/800.c)..venv/— now reproducible fromrequirements-python.txt.- Memory (outside repo):
matching-cookbook(the flywheel pointer) + MEMORY.md index line.
Tools/packages installed (this phase): venv pip — asm-differ runtime (watchdog 6.0.0, Levenshtein 0.27.3+RapidFuzz, cxxfilt 0.3.0; colorama already present) and decomp-permuter (pycparser 2.23 (<3.0 pin), toml 0.10.2, PyNaCl 1.6.2+cffi). All pinned in requirements-python.txt.
Verification results (literal):
make clean && make extract && make build && make check→build/us/SLUS_007.26, SHA1143dbb89f34491258bbc27810d0a12ec8b43a8dd== original (BYTE-IDENTICAL) — through the cpp→cc1→maspsx→as c-path, both at 100% INCLUDE_ASM (the flip) and with 14 real C functions linked.- Compiler triple PINNED + confirmed (G8):
func_80018F20byte-exact (asm-differ score 0 — thesltiurange-check probe);func_80015A74division idioms exact (the--expand-divfind —divu+break+multu 0xCCCCCCCD);func_80016714every instruction identical (phantom-frame residual). psx≈cdk and 2.56≈2.67 are byte-equivalent on non-discriminating functions. - 14 real functions matched (asm-differ score 0):
func_80018F20+ 13 accessors (func_80019198/378/388/398/3A8,func_8001AA78/AA88,func_800168B4,func_80019018,func_8001B22C/B374/B384/B85C). +14 splat-auto empty no-ops = 28 byte-matched total. - decomp-permuter harness validated:
base score = 60onfunc_80015A74(== asm-differ);-j 8≈ 6600 candidates/30 s (parallel works);-j 30oversubscribes → exit 144 (sweet spot ~8–16). - 2 instruction-identical near-misses guarded NON_MATCHING (
func_80015A74hoisted-invariant scheduling;func_80016714phantom empty stack frame) — decomp-permuter candidates (cookbook §5). - LZSS spike (read-only recon):
LzssDecodeSectorneeds gcc's switch jump table placed byte-exact at 0x80072A38, inside a mixed blob of 102 jump tables + 161 other data labels → the jump-table-in-rodata workflow → Phase 7 opener (unlocks ~102 switch-functions). git status: onlyconfig/,Makefile,src/,include/common.h,diff_settings.py,tools/decompile.py,tools/permuter/,docs/,requirements-python.txt,CLAUDE.mdtracked; zero ROM-derived/generated bulk staged; all tooling committable; deps frozen.
Milestone achieved: The matching loop is proven and industrialized — the asm→c flip rebuilds byte-identically, the compiler triple is pinned by evidence, and 14 real functions (+14 splat-auto empties) compile to byte-identical machine code (asm-differ score 0; make check BYTE-IDENTICAL). The roadmap's LZSS gate is deferred to Phase 7 (Drew's framing — LZSS uniquely needs the jump-table-in-rodata workflow).
Next: Phase 7 — open in plan mode with a fresh context window. Start with the jump-table-in-rodata workflow → LZSS (first table) → the other ~101 switch-functions; then matching-loop-at-scale (difficulty/duplicate/progress reports, the auto-match harvester) + the Gen1 close-out (≥25 matched, overlay map emulator-proven).
Deviations
| Item | Plan | Actual | Reason |
|---|---|---|---|
| LZSS milestone gate | match in Phase 6 | deferred to Phase 7 | needs the jump-table-in-rodata workflow (roadmap-scoped Phase 7); Drew's milestone-framing decision |
| Task D "≥3 score-0 probes" | 3 score-0 probes | 1 score-0 probe + 2 instruction-identical near-misses + 13 tiny-leaf score-0 matches | the medium idiom-rich probes hit gcc byte-exactness quirks (scheduling, phantom frame); the pin is established by instruction-level identity across 3 idiom classes |
| Task F approach | match probes + LZSS | pivot to tiny trivial leaves (185 available) | medium probes quirk-prone; tiny leaves one-shot from a pattern (the harvester triage, live) |
| Empty functions | (n/a) | splat auto-emitted empty C for 14 no-ops → free byte-matches | splat c-mode behavior (discovered) |
| Execution order | A→B→C→D… (checklist) | harness (C) before the ladder (D) | the ladder is scored by asm-differ — the harness is a prerequisite (per the approved plan) |
--expand-div |
(not anticipated) | pinned globally | the division idiom requires it (bare divu never matches); key fingerprint find |
| Tooling (Task T / #9) | not in the original plan | added mid-phase (Drew-directed): permuter harness built+validated; m2c --context seed deferred to Phase 7 |
Drew directive; the flywheel's tier-2 |
| venv requirements freeze | Phase-4-deferred | done at Phase-6 close (requirements-python.txt) |
Drew flagged the dep-loss risk; the planned time |
Commit Message
feat(phase-6): compiler pinned + matching loop proven (14 matches) — LZSS to Phase 7
- splat.us.exe.yaml: text subseg asm->c (INCLUDE_ASM scaffold); include/common.h
(committed prelude); Makefile c-rule cpp->cc1->maspsx->as + -Map
- PIN (G8): gcc-2.7.2-psx -O2 -G0 -mips1 -mcpu=3000 -mgas -msoft-float -fgnu-linker
+ maspsx --aspsx-version=2.56 --expand-div; byte-exact on func_80018F20 +
instruction-identical across 3 idiom classes; --expand-div REQUIRED for div/rem
(SETUP.md §5.4 PINNED, Makefile MASPSX_FLAGS)
- harness: diff_settings.py (asm-differ mipsel object mode), tools/decompile.py
(m2c wrapper), tools/permuter/ (decomp-permuter harness + objdump shim), expected/
- 14 real functions matched (asm-differ score 0): func_80018F20 + 13 accessors;
+14 splat-auto empty no-ops; make check BYTE-IDENTICAL throughout
- 2 instruction-identical near-misses guarded NON_MATCHING (func_80015A74 scheduling,
func_80016714 phantom frame) -> decomp-permuter candidates
- flywheel: docs/matching-cookbook.md, memory + CLAUDE.md step 5 (consult+evolve), R16
- requirements-python.txt: venv freeze (pycparser<3.0 pinned — permuter needs plyparser)
- LZSS spike: needs jump-table-in-rodata workflow (102 tables, mixed blob) -> Phase 7
- venv: asm-differ + permuter deps installed
- bumps project version 1.5.0 -> 1.6.0
Rules Added This Phase
| Rule | Reason |
|---|---|
R16 — The matching automation flywheel. Consult the knowledge base (docs/matching-cookbook.md + the pinned triple §5.4 + the decomp-permuter harness + the m2c --context) before each match, and after each match — especially a hard-won near-miss ground to byte-perfect — feed the GENERALIZABLE lesson back into both the cookbook (the idiom/technique) and the tooling (a permuter PERM_* recipe / weight, an m2c context entry). Don't over-encode one-offs; the target is the average function trending to one-shot + a shrinking hard tail, not literal zero-intervention. Wired into the matching-cookbook memory + CLAUDE.md step 5. |
Drew-directed (2026-06-14). The ~99.9%-autonomous matching model only scales if each match makes the next one cheaper; this makes "consult + evolve" a standing obligation, not a one-off. |
PhaseEnd Changelog
v1.5.0 → v1.6.0 — Phase 6 complete (substantively; the LZSS gate is carried to Phase 7, which it fits better — it uniquely needs the jump-table-in-rodata workflow). The matching loop is proven and industrialized: the asm→c flip rebuilds byte-identically; the compiler triple is PINNED by evidence (gcc-2.7.2-psx -O2 -G0 … + maspsx --aspsx-version=2.56 --expand-div), with --expand-div the key discovery; the full harness is wired (asm-differ object mode + tools/decompile.py + the decomp-permuter harness + the expected/ baseline); 14 real functions match at asm-differ score 0 (a byte-clamp + 13 accessors) plus 14 splat-auto empties, make check BYTE-IDENTICAL throughout. The tiny-leaf harvest validated the harvester triage (easy=auto, medium=permuter, hard=defer) — there are 185 tiny leaves and ~102 switch-functions waiting. A knowledge flywheel is now standing (cookbook + memory + CLAUDE.md step 5 + R16), and the venv is reproducible (requirements-python.txt, with the hard-won pycparser<3.0 pin). Two instruction-identical near-misses are guarded as permuter candidates. The LZSS recon scoped Phase 7's opener.
🛑 Stop Here
PhaseEnd written; CURRENT_PHASE.md absorbed and removed. Drew commits AND pushes this PhaseEnd plus the whole Phase-6 change set (R6/R8 — one commit lands: config/, Makefile, src/800.c, include/common.h, diff_settings.py, tools/decompile.py, tools/permuter/, docs/ (SETUP §5.4/§6.6 + matching-cookbook.md), requirements-python.txt, CLAUDE.md, PhaseEnd_Phase6.md). Start a fresh session, in plan mode, for Phase 7 — opening with the jump-table-in-rodata workflow → LZSS. Keep this file forever.