mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-27 22:45:39 -04:00
5f937bdda5
- 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
86 lines
12 KiB
Markdown
86 lines
12 KiB
Markdown
# 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 emits `src/800.c` (file-scope `INCLUDE_ASM` stubs) + per-function `asm/nonmatchings/800/*.s`. Consumed by `make extract`.
|
||
- `include/common.h` — **new, committed** prelude (`#include "include_asm.h"` + `u8..f32` typedefs). Every `src/*.c` includes it; splat references but does NOT generate it.
|
||
- `Makefile` — added: `MAPFILE` + `-Map` on the `ld` line (asm-differ symbol lookup); `C_SRCS`; `OBJS` excludes `asm/nonmatchings/*` (those are textually `.include`d by the .c); the **`build/src/%.o: src/%.c` c-rule** (cpp→cc1→maspsx→as); `CPPFLAGS`/`CC1FLAGS` (pinned §5.4 flags); **`MASPSX_FLAGS := --expand-div`** (pinned). `check-env` untouched.
|
||
- `diff_settings.py` — **new**, repo root. asm-differ config: arch `mipsel` (R3000), object mode vs `expected/`, gnu map, `mipsel-linux-gnu-objdump`.
|
||
- `tools/decompile.py` — **new**, m2c wrapper (fn → `asm/nonmatchings/**/<fn>.s` → m2c `mipsel-gcc-c` scaffold).
|
||
- `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 hardcoded `mips-` 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 + `-j` sweet-spot, §4 flag gotchas, §5 hard-residual classes). The compounding knowledge base.
|
||
- `requirements-python.txt` — **new, committed** venv freeze (the Phase-4-deferred freeze). Notably **`pycparser` held <3.0** (3.0 removed `plyparser`, breaking the pinned permuter — hard-won). `pip install -r` reproduces 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_80018F20` byte-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 expected` regenerate them.
|
||
- `.run/permuter/func_80015A74/` — permuter scratch (recipe in cookbook §3; the C also lives guarded in `src/800.c`).
|
||
- `.venv/` — now reproducible from `requirements-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`, SHA1 `143dbb89f34491258bbc27810d0a12ec8b43a8dd` == 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_80018F20` byte-exact (asm-differ **score 0** — the `sltiu` range-check probe); `func_80015A74` division idioms exact (the `--expand-div` find — `divu`+`break` + `multu 0xCCCCCCCD`); `func_80016714` every 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 = 60` on `func_80015A74` (== asm-differ); `-j 8` ≈ 6600 candidates/30 s (parallel works); `-j 30` oversubscribes → exit 144 (sweet spot ~8–16).
|
||
- **2 instruction-identical near-misses** guarded NON_MATCHING (`func_80015A74` hoisted-invariant scheduling; `func_80016714` phantom empty stack frame) — decomp-permuter candidates (cookbook §5).
|
||
- **LZSS spike (read-only recon):** `LzssDecodeSector` needs 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`: only `config/`, `Makefile`, `src/`, `include/common.h`, `diff_settings.py`, `tools/decompile.py`, `tools/permuter/`, `docs/`, `requirements-python.txt`, `CLAUDE.md` tracked; **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.
|