mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-26 21:36:06 -04:00
docs(phase-20): T4 — distill the codegen map (cookbook §20) + R14 loop-guard correction
- cookbook §20: the wave-at-scale GATE CAP (match_one over-predicts; the in-TU loose-typing call-graph wall; all recovery levers fail; the only fix is automating §17a-1 call-site casts = Phase-21 cap lever) + the stale-.o diagnostic lesson - CONFIRMED-irreducible classes w/ byte-evidence: §10 store-vs-load (8014F2E0/80150528), §10 hoist-vs-remat (80149374/801493D0); NEW classes: IV-combine combine_givs (80177AD4), hoisted-invariant prologue order (80177F84), the -O1 build-infra class (80161A90) - operational gotchas: Workflow args-as-array, harvest_verify --chunk 1 - R14 correction to §17a: loop-guard (8012C2D0) is STRENGTH-REDUCTION/IV-final-value addressing, NOT operand-order - SETUP §tooling: exemplar_miner.py (T2) + build_engine_types.py typedef-lift (T1) (R21) - T3c cited (written up), T4 done; CURRENT_PHASE handoff refreshed
This commit is contained in:
+2
-1
@@ -668,9 +668,10 @@ Every script under `tools/` (plus the two report make-targets), grouped by purpo
|
||||
| | `tools/dedup_integrate.py` | Byte-honesty validator for `config/dedup.us.yaml` code-shares (`--check`; fail-closed on sig-hash drift). |
|
||||
| | `tools/dedup_propagate.py` | **(Phase 15, cookbook §14)** Match-once → propagate-many: lift a matched body, author a `DEFINE_func_<ADDR>()` macro in `src/shared/engine_core.h`, instantiate it at every onboarded overlay sharing that `h_exact`, byte-gate each (fail-closed), register in `dedup.us.yaml`. `--addr`/`--auto-from`/`--check-only`. `find_site` accepts brace on the same OR next line (Phase-15 fix — next-line-brace defs were silently un-propagated). |
|
||||
| | `tools/sig_unify.py` | **(Phase 15, cookbook §14d)** Deterministic hard-tail recovery: unify a gate-failing draft's FULL signature set — callee externs **and** the draft's OWN definition signature — to the banked-canonical decls (`--overlay`/`--in`/`--out`); the whole-binary byte-gate stays the arbiter. Recovered 32 of 191 standalone-MATCH conflict-blocked drafts, zero agent tokens. |
|
||||
| | `tools/gen_harvest_targets.py` / `tools/canon_draft_decls.py` / `tools/build_engine_types.py` | Callee-sig-aware target manifest (§14b/c) / callee-extern canonicalizer (§14c) / additive shared-types-header extractor (`src/shared/engine_types.h`, §14 struct). |
|
||||
| | `tools/gen_harvest_targets.py` / `tools/canon_draft_decls.py` / `tools/build_engine_types.py` | Callee-sig-aware target manifest (§14b/c) / callee-extern canonicalizer (§14c) / additive shared-types-header extractor (`src/shared/engine_types.h`, §14 struct; **Phase 20: also lifts typedefs — anon-struct / fn-ptr / alias — with collision + tagged-struct-typedef-overlap guards, closing the §19 type-blocked propagation cap**). |
|
||||
| | `tools/canon_resident_calls.py` | **(Phase 19 / T2, cookbook §17a-3a)** Link-miss recovery: rewrite each `func_<ADDR>` in a draft to the **curated** resident symbol name when that address has one in the stacked symbol files (`0x8004CFEC`→`ratan2`), so the linker resolves it. Pure draft-text (body bytes unchanged); **run FIRST** in the recovery pipeline `draft → canon_resident_calls → sig_unify → harvest_verify`. |
|
||||
| | `tools/fix_arity_callers.py` | **(Phase 19 / T3, cookbook §17a-3b/§19)** The no-prototype recovery for the dominant gate-failure class: a banked SHARED caller in `engine_core.h` declares the callee `extern <ret> func_X(void);`, conflicting with a real def that takes args. Rewrites that caller decl to `extern <ret> func_X();` (`--apply`, byte-neutral; skips narrow-param defs via `--drafts`; `--revert` undoes). Re-gate after. |
|
||||
| | `tools/exemplar_miner.py` | **(Phase 20 / T2, cookbook §20)** Residual router: consume `.run/wall_taxonomy.json` (bucket/nins) + per-overlay reach (dedup_propagate's computation) → route every residual stub to a lever (WAVE / STRUCT / PINS / STUB) ranked by reach×size → `docs/exemplar_curriculum.md` + `.run/exemplar_routing.json` (wave-target list). "Scan all residuals, size the pools, pick the teachers." Caveat: its `mismatch` is the M2C-draft mismatch, not the hand-match floor. |
|
||||
| | `config/dedup.us.yaml` / `src/shared/*.h` | The code-share registry + the shared bodies (one macro → N sites, byte-gated). |
|
||||
| | `make report` / `make sig-refresh` / `make sig-overlays` | Convenience targets: reports (+`--cross`) / Ghidra signature-dump / Ghidra-free sign all 134 overlays. |
|
||||
|
||||
|
||||
@@ -1180,10 +1180,13 @@ worked-example templates). Measured on ov_SC01_077 tractable reach-134 residuals
|
||||
per-file `CC1FLAGS := -O0`). HIGH ROI: ~18 fns × reach-134. Members incl. func_8013C360,
|
||||
func_8013B568/B598/B6A0/B7AC/B7F4/B83C/BC7C/BCDC/BD34/BD74, func_8013C08C/C0F8/C360/C414/C938/C964,
|
||||
func_80144B9C, func_801457A4. (→ Phase 19 build-infra task.)
|
||||
- **gcc-2.7.2 loop-guard operand-ORDER** (func_8012C2D0, 1-off). The duplicated loop entry-guard emits the
|
||||
loop-INVARIANT operand first (`beq end,p`) where the target has the biv first (`beq p,end`); `loop.c`
|
||||
`get_condition` canonicalization, NOT pin/polarity/barrier/source-steerable. Distinct from the register-ORDER
|
||||
class (which pins DO fix) and from narrow-param. A genuine residual — stub.
|
||||
- **gcc-2.7.2 loop-guard** (func_8012C2D0). **[Phase-20 R14 CORRECTION — see §20]** the real residual is gcc
|
||||
STRENGTH-REDUCTION / IV-final-value ADDRESSING, NOT operand-order: the loop end is formed as `&D_80120194`
|
||||
(the array base) `+ 0x658C` — the base materialized SEPARATELY then offset (an induction-variable final value),
|
||||
while the start is its own symbol `D_801202A0`. The right C is base-relative end + `lhu`/`u16` (gets
|
||||
structurally close), but gcc -O2 CONSTANT-FOLDS `base + N` into one address (`lui %hi; addiu %lo`), so no clean
|
||||
C form (pointer-var, struct-array index) reproduces the separate base materialization. Still a genuine residual
|
||||
— stub; the (uncracked) lever direction is forcing the unfolded IV-final-value, not operand order.
|
||||
- (also: func_8014F2E0 4-off = §10 store-vs-load schedule placement, base-preservation-vs-load-order mutually
|
||||
exclusive — a real §10 residual.)
|
||||
|
||||
@@ -1256,3 +1259,74 @@ same-name-different-layout collisions historically) but NOT typedefs → ~16 ban
|
||||
**Lever (Phase 20): extend the type-lift to typedefs/local types** → recovers those ×134 for ~0 agent tokens AND
|
||||
raises every future batch's realized yield. The harvest's bottleneck has moved from "can we match it" to "can we
|
||||
share it."
|
||||
|
||||
## §20 The wave-at-scale GATE CAP + residual-class verdicts (Phase 20)
|
||||
|
||||
Phase 20 closed the §19 propagation cap (T1 below) then ran the §17/§19 wave + recovery over the reach-134
|
||||
≤90-ins tail — and hit a HARDER gate cap than Phase 19's. All byte-verified.
|
||||
|
||||
### THE CAP: at this tail the match_one→gate gap is the LOOSE-TYPING CALL-GRAPH wall (not cheap plumbing)
|
||||
Batch numbers: 41/48 `match_one` MATCH (85%), but only **8/41 survived the whole-binary gate (~80% gap** — vs
|
||||
Phase 19's ~30%). The gap is NOT the §17a/§19 cheap declaration-plumbing (link-miss/arity recovered **~0** here).
|
||||
Byte-verified cause: a callee (e.g. `func_80153C74`) is declared with **CONFLICTING types at different overlay
|
||||
sites**, so the draft's extern hits in-TU `conflicting types` — a COMPILE error, not a byte miss. `match_one`
|
||||
OVER-PREDICTS because it compiles STANDALONE with the draft's own externs **and** masks jal/%hi/%lo — it never
|
||||
sees the overlay's conflicting decls. **Every recovery lever fails at this tail** (all 0): `sig_unify` /
|
||||
`canon_draft_decls` impose a single "canonical" that's wrong for some sites (loose-typing); `fix_arity_callers`
|
||||
(not arity); **no-proto externs** (incompatible with the overlay's NARROW existing decls — `void f()` can't
|
||||
co-exist with `void f(s16)`); strip-externs (implicit-int ≠ target). **The ONLY fix is the §17a-1 per-site
|
||||
function-pointer cast** `((ret(*)(args))func_X)(…)` — it takes the callee's ADDRESS and calls with the draft's
|
||||
intended sig, so there's no global decl and no conflict. Agents don't apply it reliably → **AUTOMATE it: a
|
||||
recovery pass that, per draft, DROPS the conflicting callee extern + CASTS the call to the draft's sig, then
|
||||
gates.** That is the Phase-21 cap lever (recovers a batch's lost ~33 reach-134 AND lifts every wave's gate-pass
|
||||
from ~20% toward ~80%+). The bottleneck has moved again: matching ✓ (85%) → sharing ✓ (§19 type-lift) → now
|
||||
**in-TU declaration reconciliation under loose typing.**
|
||||
|
||||
### Diagnostic lesson: a failed in-TU build leaves a STALE `.o`
|
||||
When you substitute a draft and the in-TU build FAILS to compile, `build/src/<…>.o` retains the PREVIOUS
|
||||
(target/stub) bytes → `objdump` of that `.o` shows a FALSE "byte-match." **Always trust the whole-binary SHA
|
||||
gate, not a per-function `objdump`** (cost a real detour: func_80153C44's stale `.o` looked identical while the
|
||||
true failure was a `conflicting types` compile error).
|
||||
|
||||
### NEW / CONFIRMED residual classes (this session)
|
||||
- **§10 store-vs-load scheduling (func_8014F2E0, func_80150528) — CONFIRMED unsteerable.** Wave agents tried
|
||||
for-init / barrier / precompute / volatile; `sched.c` tie-break: the IV-init lands before the loop guard not in
|
||||
the preheader, and the `D_x=0` store schedules BETWEEN two arg-loads instead of after both — mutually exclusive
|
||||
with base-preservation. Stub.
|
||||
- **§10 hoist-vs-remat regalloc tie-break (func_80149374, func_801493D0) — CONFIRMED.** gcc caches a `sp+off`
|
||||
buffer address in a freed callee-saved reg + moves (cheaper by its count) where the target REMATERIALIZES
|
||||
`addiu $a,$sp,off` per call. Array-decay / pins / barriers / permuter all fail. Stub.
|
||||
- **IV-combine divergence (func_80177AD4) — NEW.** Our cc1's `combine_givs` won't fold a halfword RMW
|
||||
(`lhu;sh -2(p)`) into the byte biv (`sb 0(p)`) the way the target does (one IV at `p+0x20`); it spawns a
|
||||
dedicated 2nd IV → wrong base constant (`p+0x1e`). Probe-confirmed (a non-RMW `*p`/`*(p-2)` pair combines fine;
|
||||
the RMW spawns the 2nd IV). Genuine codegen divergence, not source-typeable. Stub.
|
||||
- **Hoisted-invariant PROLOGUE ORDER (func_80177F84).** 3 prologue insns in the wrong order — gcc emits the
|
||||
pinned-`$a2` pointer init before the two hoisted loop-invariant constants; the target emits the constants
|
||||
first. No prologue permutation is < 3-off; the permuter can't run (the `register __asm__` pins are rejected by
|
||||
pycparser). §3/§5-class — stub.
|
||||
- **The -O1 class (func_80161A90) — NEW build-infra (extends §18).** A function built `-O1` (frame 0x18, `lhu`
|
||||
reload, unfolded base, load-delay nops) inside an otherwise-`-O2` overlay. `match_one` (hardcoded `-O2`) CANNOT
|
||||
match it. Like the §18 `-O0` class but `-O1` → needs its own `-O1` split file (target-specific
|
||||
`CC1FLAGS := -O1`). Detect: prologue/scheduling between `-O0` (`21F0A003`) and `-O2`.
|
||||
|
||||
### Operational gotchas (cost real time)
|
||||
- **Workflow `args`:** pass the target list as a JSON ARRAY, not a JSON string — a stringified array reaches the
|
||||
script as one string and `args.map` throws (`names.map is not a function`). Defensive:
|
||||
`const names = Array.isArray(args) ? args : JSON.parse(args)`.
|
||||
- **`harvest_verify --chunk 1` for wave batches.** With `--chunk >1`, ONE draft that fails to COMPILE (a
|
||||
loose-typing conflict) fails the whole chunk's build and the bisection mis-attributes the innocent neighbors as
|
||||
failures. Gate wave drafts one at a time when the failure mode is compile-conflicts.
|
||||
|
||||
### What WORKED — the Phase-20 reusable wins
|
||||
- **T1 — the typedef type-lift** (`tools/build_engine_types.py` extended). Added `find_typedefs()` (brace-aware:
|
||||
anon-struct `typedef struct{…}N;`, fn-ptr `typedef r(*N)();`, alias) + same-name-different-layout collision +
|
||||
tagged-struct-typedef overlap guards; emits typedefs in source order AFTER the named structs (deps like
|
||||
`A→S` preserved). **Closed the §19 type-blocked propagation cap** — 9 reach-134 fns ×134 for ~0 agent tokens,
|
||||
byte-neutral (`--strip` removes the defs; type decls emit no code).
|
||||
- **T2 — the residual router** (`tools/exemplar_miner.py`). Consumes `wall_taxonomy.json` + per-overlay reach
|
||||
(dedup_propagate's computation) → routes every residual to a lever (WAVE / STRUCT / PINS / STUB) →
|
||||
`docs/exemplar_curriculum.md` + the reach-134 wave-target list + `.run/exemplar_routing.json`. The "scan all
|
||||
residuals, pick the teachers / size the pools" router. Caveat: its `mismatch` is the M2C-DRAFT mismatch, NOT
|
||||
the hand-match floor (a loop-guard buckets STRUCTURAL_MISS at mismatch-16 yet hand-floors to 1).
|
||||
|
||||
*(T3a `%lo`-folding `-O0` (§18 open residual) is still open — to be added here when cracked.)*
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
- [~] T3b — loop-guard (`func_8012C2D0`, worked in ov_SC03_014). **R14: exemplar MISLABELED** — it's gcc **strength-reduction/addressing** (end = base `D_80120194` + 0x658C as an induction-var final-value), NOT operand-order. 2 drafts: base-relative-end + `lhu`/`u16` now correct; residual = gcc materializes the base separately before +0x658C (resists the constant-fold my C produces). Likely crackable via indexed-struct-array, but a deep per-fn induction-var grind. Prior "irreducible" was an incomplete framing. PAUSED for the ROI checkpoint below.
|
||||
- [ ] T3c — store-vs-load scheduling tie-break (`func_8014F2E0`; `sched.c`)
|
||||
Each: read the gcc-papermario pass → try the C lever → `match_one` → crack (byte-gate + propagate + cookbook idiom) OR cite-irreducible (cookbook verdict).
|
||||
- [ ] **T4 — Distill the gcc-2.7.2 codegen map** · **Max** (R16)
|
||||
Consolidate §17/§18 + T3 into a complete codegen reference (idiom OR cited-irreducible per class) in `docs/matching-cookbook.md` + `docs/hand-matching-process.md`.
|
||||
- [x] **T4 — Distill the gcc-2.7.2 codegen map** · DONE (Max, R16)
|
||||
Wrote **cookbook §20** (the wave-at-scale gate cap = loose-typing call-graph wall; store-vs-load + §10 hoist-vs-remat + IV-combine + hoisted-invariant-order CONFIRMED-irreducible with byte-evidence; the **-O1 class**; the stale-`.o` + args-as-array + `--chunk 1` gotchas; the T1/T2 wins) + an **R14 correction** to the §17a loop-guard entry (strength-reduction, not operand-order). `%lo` (T3a) left as a placeholder to append when cracked.
|
||||
- [ ] **T5 — Ghidra-C regen for fresh wave targets** · xHigh + **R23/R29**
|
||||
Stop MCP → `DecompileFunctions.java` over fresh tractable reach-134 stubs → restart → **pause + prompt `/mcp`** → `get_binary_info` (G2).
|
||||
- [~] **T6 — Enriched waves (batch 1 done)** · Ultracode
|
||||
@@ -27,14 +27,13 @@
|
||||
**Optional / conditional (decide live, else → Phase 21):** `-O0` ×134 rollout (only if T3a cracks `%lo`); the 28 giants (deferred to a focused Phase-21 deep session).
|
||||
|
||||
## Current task pointer — MULTI-SESSION CONTINUE (Drew, 2026-06-21: not closing; resume in a fresh session)
|
||||
**Done + committed:** T1 (cap, +0.35%), T2 (router), T3b (cited — R14 reframe), T6 batch-1 (8 banked, +0.28%). **T3c verdict IN** (cite-irreducible, byte-confirmed via the wave — `sched.c` tie-break; just needs the T4 write-up). Fleet **58.00%→58.63%**, 136/136 byte-identical, 0 NON_MATCHING. Tree clean (HEAD `commit:0169`).
|
||||
**Done + committed:** T1 (cap, +0.35%), T2 (router), T3b (cited — R14 reframe), **T3c (cited — written up in §20)**, **T4 (codegen map §20 + §17a correction)**, T6 batch-1 (8 banked, +0.28%). Fleet **58.00%→58.63%**, 136/136 byte-identical, 0 NON_MATCHING.
|
||||
|
||||
**Remaining, prioritized for the fresh session (the plan is approved — resume autonomously, P3/§Session-Start step 4):**
|
||||
1. **[THE CAP — highest ROI] Build the §17a auto-call-site-cast recovery tool.** The precisely-diagnosed gate cap (see T6 finding above): the wave's 33 match_one-MATCH drafts fail in-TU on loose-typed callee conflicts; the fix is per-site fn-ptr casts `((ret(*)(args))func_X)(…)` (drop the draft's conflicting extern, cast the call to the draft's intended sig, gate). Recovers T6 batch-1's 33 reach-134 (~+1%) AND lifts every future wave from ~20%→~80%+ gate-pass. Drafts preserved: `.run/drafts-t6-cn/` (agent originals, post canon_resident_calls); failures list `.run/t6_fails.txt`; gate good-sha `d19c9580…`.
|
||||
2. **T4 — distill the gcc codegen map** into `docs/matching-cookbook.md`: T3b (strength-reduction reframe), T3c + §10 hoist-vs-remat + IV-combine (`combine_givs`) + the loop-guard — all cite-irreducible with byte-evidence; the **-O1 class** (`func_80161A90`, build-infra); the **match_one→gate in-TU loose-typing cap** + the stale-`.o` diagnostic lesson.
|
||||
3. **T3a — %lo-folding -O0** (`func_8013C360` cluster; entangled with the -O0 split infra).
|
||||
4. **T5 — Ghidra-C regen** (R23/R29: stop MCP → DecompileFunctions.java → `/mcp`) for fresh reach-134 targets → more T6 wave batches (now with the cast-recovery → full yield). Cache is 300; ov_SC01_077 has 668 stubs.
|
||||
5. **[build-infra] -O1/-O0 split files** to bank the -O1 (`func_80161A90`) + the 6 matched -O0 fns ×134 (per-overlay -O0 splits, uniform offsets, scriptable).
|
||||
1. **[THE CAP — highest ROI] Build the §17a auto-call-site-cast recovery tool** (cookbook §20). The diagnosed gate cap: the wave's 33 match_one-MATCH drafts fail in-TU on loose-typed callee conflicts; the fix is per-site fn-ptr casts `((ret(*)(args))func_X)(…)` (drop the draft's conflicting extern, cast the call to the draft's intended sig, gate). Recovers T6 batch-1's 33 reach-134 (~+1%) AND lifts every future wave from ~20%→~80%+ gate-pass. Drafts preserved: `.run/drafts-t6-cn/` (agent originals, post canon_resident_calls); failures `.run/t6_fails.txt`; gate good-sha `d19c9580…`.
|
||||
2. **T3a — %lo-folding -O0** (`func_8013C360` cluster; cookbook §18 open residual; entangled with the -O0 split infra). Append the verdict/crack to cookbook §20's placeholder.
|
||||
3. **T5 — Ghidra-C regen** (R23/R29: stop MCP → DecompileFunctions.java → `/mcp`) for fresh reach-134 targets → more T6 wave batches (now with the cast-recovery → full yield). Cache is 300; ov_SC01_077 has 668 stubs.
|
||||
4. **[build-infra] -O1/-O0 split files** to bank the -O1 (`func_80161A90`) + the 6 matched -O0 fns ×134 (per-overlay -O0 splits, uniform offsets, scriptable).
|
||||
|
||||
**Reusable wave recipe (proven this session):** `gen_harvest_targets --min-reach 134 --max-nins 90` → filter to cached Ghidra-C → Workflow (one agent/target, §17 toolkit, **pass args as a JSON array NOT a string** — the gotcha that bit us) → `canon_resident_calls` → **the new cast-recovery** → `harvest_verify --chunk 1` (chunk>1 mis-attributes compile-erroring drafts) → `dedup_propagate --auto-from`.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user