mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-27 22:45:39 -04:00
85fb289db582d842fc41dc059fa187bb992e76ea
6 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6e0b1605c6 |
fix(phase-30 S40): cast_call_sites read a RETURN as a prototype and deleted it — 0/39 sweep becomes 18/39
THE BUG. tools/cast_call_sites.py classifies a declaration line with
^([ \t]*)(extern\s+)?([A-Za-z_][\w \t\*]*?)\b([A-Za-z_]\w*)\s*\(([^;{]*)\)\s*;
Feed it a return statement and `return` is a perfectly good identifier where a type is expected:
return func_8012CB64((s32)out, -0xC0, 0x40, -0x60, 0);
^^^^^^ captured as the return TYPE, func_8012CB64 as the DECLARED NAME
so the "rewrite this decl to canonical" path REPLACED the statement with
`extern s32 func_8012CB64(s32,s32,s32,s32,s32);`, DELETING the return. In C89 a declaration after a
statement is a parse error, so the damage surfaced as a bare syntax error in the DRAFT -- reading as
the draft's fault, not the tool's. 9 of 9 staged members of family 0x801848dc lost their return.
fix: a keyword guard (a declaration's type-specifier can never begin with a statement keyword)
family_sweep --hseq --band all over 5 families: 0/39 -> 18/39 banked (only the guard changed)
⚠️ AND THE TRAP INSIDE THE FIX: the obvious R33 move is "route it through cdecl". CHECKED, and it is
WRONG -- cdecl.parse() is a DECLARATOR-GRAMMAR parser that assumes it was handed a declaration; it
reports `return func_X(...);` as declaring func_X and `if (f(a));` as declaring `if`.
Statement-vs-declaration is a question cdecl does not answer. Routing there would have been a silent
non-fix that looked principled. §134's law still holds for line-SHAPE masking; this is a different
question.
BLAST RADIUS (measured, not assumed -- R14): cast_call_sites is in gate_stage's DEFAULT pipeline
(canon_resident_calls -> cast_call_sites -> sig_unify -> harvest_verify) and has been since Phase 20.
Of 44,833 stored drafts, 318 (0.7%) carry a `return f(...);` line this mis-reads, across 67 callees
(func_8014F468 x41, func_8014F6F4 x37, func_8014F74C x32, ratan2 x25). Every one, every time it
passed the gate pipeline, lost its return and failed as PLUMBING. Part of the historical plumbing
tail is this bug.
ALSO IN THIS COMMIT
- S5 CALIBRATION WAVE (8 agents, ultracode, 1.31M tokens). Pool VERIFIED FIRST (R14 -- Fable's whale
claim was 3/4 wrong): measured 1,677 clusters / 5,795 fns / 319,755 ins at a 3.68x multiplier vs
its claimed 1,689 / 5,956 / 326,261 at 2.7x -- its numbers hold, and the multiplier is BETTER.
Result: 8/8 match_one MATCH (close=0), and 5/8 banked whole-binary -- the §52b/§61 gap is
integration, not codegen. Banked: func_801822E0 func_8017EC98 func_801851A8 func_80189A34
func_80188E10 (693 ins x1 before propagation). Not banked: func_8018B238 (FAILED),
func_8017EF54 + func_801802EC (NEAR) -- drafts kept in .run/wave-s40/ for recovery.
- 18 member-banks from the re-run sweep (the cross-address free-h_exact pool: h_exact-identical at
DIFFERENT addresses, which dedup_propagate correctly refuses since it assumes position-locking --
family_sweep is the right lane).
- cookbook §143 (this bug + the cdecl trap + the blast radius); index regenerated.
VERIFIED: make clean && make extract-all && make check-all -> 140 passed, 0 failed of 140.
Fleet 12419169 -> 12420375 instr; distinct +1,526 / +5 uniq; fn-count +23. audit-digest OK.
0 NON_MATCHING (G4).
NEW IDIOM FROM THE WAVE, not yet folded into §31 (agent was told to write only its draft): a byte
counter must be spelled `cnt + 0xff`, NOT `cnt - 1`. Both are mod-256 identical and both compile to
one addiu, but gcc-2.7.2 picks the immediate encoding from the SOURCE SPELLING (0xFFFF vs 0x00FF).
Also flagged: .run/ghidra_c/func_8017EF54.c is a stale decompile of the WRONG function.
|
||
|
|
1576570271 |
fix(phase-30 S1e): the distinct-code "regression" was a STALE DIGEST — alias lever ungated
The S38 checkpoint gated the phase's best lever ("do NOT scale the alias lever") on
distinct-code falling 89.3 -> 89.2. It never fell.
PROOF (each commit's metric recomputed from its OWN committed tree, 0 unresolved):
commit:1426 TRUE : instr 12394533 distinct 5022306 (77895 uniq)
commit:1426 COMMITTED: instr 12402412 distinct 5029324 (78025 uniq) <- stale
HEAD TRUE == COMMITTED: instr 12405402 distinct 5025082 (77952 uniq)
=> true delta 843->HEAD: instr +10869, distinct +2776 ins / +57 uniq. ALL ROSE.
The 843 digest was generated from a working tree still holding work REVERTED before the
commit landed (+7,879 ins / +130 uniq overstated) and never regenerated, so the next
HONEST digest read as a fall. => THE ALIAS LEVER IS UNGATED (scale it, §61 small batches).
Both recorded leads were wrong (R14): progress.py:423's SIG regex feeds fn-count ONLY
(neither weighted metric sees a C identifier — both derive matched = sig - corpus.stubs),
and "the harvest reverted functions to INCLUDE_ASM" died on one grep (483 removed, 0 added).
The 3-grep proof: identical sigs + unchanged tools/ + zero +INCLUDE_ASM => HEAD's stub set
is a strict subset => both numerators are FORBIDDEN to fall.
THREE INSTRUMENT DEFECTS, all one class (a bare except around a fail-CLOSED oracle):
- progress.py stub_addrs wrapped corpus.stubs in `except Exception: return set()`. An empty
stub set means "could not answer", not "no stubs", so matched = sig - stubs credited EVERY
function. Byte-witnessed: instr 100.00% / distinct 100.00% in a tree with no asm/. Now
propagates.
- cast_call_sites.tu_for + reconcile_tu.tu_for had the identical swallow, falling back to the
default <ov>.c instead of the jr/-O0 split TU — silently reinstating the exact bug
cast_call_sites' own docstring says it exists to fix. A wrong-TU reconcile fails the gate,
and this phase's base rate is ~24k PLUMBING vs 4,917 DIFF, so it presents as a codegen wall.
Now propagate CorpusError; ValueError fallback for curated names preserved; derived-TU path
re-verified (a _jr_ split stub resolves correctly, both tools agree).
NEW GATE (R34 — the byte-gate is a null oracle for DOCUMENTS; check-all stays 140/140 over a
stale digest forever): tools/audit_digest.py + `make audit-digest`, wired into tools-health
after report. Recomputes the three headline metrics from the current tree and fails if the
committed digest disagrees. Compares INTEGERS, not percentages — the +7,879-instruction
staleness printed as "94.4%" on both sides. Negative-control-proven against the stale 843
digest (fails, exit 1) and green on HEAD.
Verified: make report exit 0 (dedup-check 1910 validated / 0 failed, C1 coverage
241216/241216); audit-digest OK; cookbook-index OK (398 sections); metrics unchanged by the
fix (94.40% / 89.18%). No src/ or config/ edits — no bytes touched, nothing banked.
cookbook §140 · decision-log 2026-08-04 · SETUP.md inventory (R21) · R14/R32/R34/R35.
|
||
|
|
611622c9e7 |
feat(phase-29): T78 — PsyQ-symbol widening: func_8012F40C 0/137 -> 137/137 (three places, not one)
I called this "a one-line predicate widening". It was THREE, and fixing the first two changed nothing
— the sweep still reported 0/547 (cookbook §115):
1. canonical_map : re.fullmatch(r'func_[0-9A-Fa-f]{8}') + keyed by parsed ADDRESS
2. DECL_LINE_RE : (func_[0-9A-Fa-f]+) as the name group
3. split_sig_string : \bfunc_[0-9A-Fa-f]+\s*\(
Each is a SILENT SKIP indistinguishable from "no conflict found". With 1+2 done the symbol reached 3
and died there; only tracing transform's internals (`callees cast: 0` while the canonical map plainly
held `s32 RotTransPers(s32, s32, s32*, s32*)`) located it. THE TRAP WORTH REMEMBERING: a partial fix
to a name-form assumption produces the exact symptom of no fix at all, so a correct hypothesis looks
refuted. Curated naming increases as RE quality improves, so any func_-only predicate is
rot-by-design — the same shape as stub_map's (Phase 26-A).
RESULT: func_8012F40C 0/137 -> 137/137. The other three families (801759D8, 80146750, 80142B2C) still
fail on different causes.
GATES: R22 clean-fleet 140 passed, 0 failed of 140; tools-health OK; dedup 1886/0; 0 NON_MATCHING.
METRICS: instr 86.7% (+4,932 ins); fn-count 91.19% -> 91.23% (+137); distinct +0 (byte-identical).
NEXT: the byte-VARIANT tier is worth re-sweeping — T70 banked 1/10 BEFORE the callee axis existed, and
26 families remain unswept by the two levers added since.
|
||
|
|
f4502f11bb |
fix(phase-26a): A3c — the recovery passes were reconciling 95% of drafts against the WRONG TU
FIRST CONSUMER MIGRATION onto the cdecl oracle — and the compiler taught me two things I had
wrong, one of which reopens a wall that has been closed since Phase 15.
1. cdecl.compatible() — "will cc1 accept these two declarations of one name?"
The predicate four tools each half-implement and get wrong: norm_sig / _norm_type collapse the
int family to ONE token, so a SIGNEDNESS change reads as "already compatible" and gets no
rewrite -- while cc1 REJECTS that redeclaration. Right about codegen, wrong about the front end,
which never reaches codegen.
2. THE ADJUDICATOR MUST BE THE COMPILER THAT COMPILES YOUR CODE (cookbook §51g LAW 9).
I wrote the rules from the C standard, then let a compiler judge. It contradicted me -- and then
the RIGHT compiler contradicted the first one. Three different answers:
declarations in one TU | standard | modern gcc | gcc-2.7.2 cc1
typedef int X; twice | error | ACCEPTS | ERROR
extern u16 X; + volatile u16 X| error | error | ACCEPTS
void X(s16); then void X(); | error | error | ACCEPTS
void X(); then void X(s16)| error | error | ERROR
--compat now adjudicates with tools/bin/gcc-2.7.2-psx/cc1, the front end that actually
arbitrates the build: 1,485/1,485 live corpus pairs agree, 0 disagree, 0 skipped.
3. THE PRIZE: the Phase-15 narrow-param wall rests on a false premise.
The no-prototype rule is ORDER-DEPENDENT. `void X(s16); void X();` COMPILES; only the reverse
fails. Phase 15 closed "the 159 arity/narrow-param conflicts" as "no clean deterministic fix --
it is simply C's default-promotion rule". cc1 does not enforce that rule in the direction the
wall assumed. Four three-line probes, 90 seconds, zero tokens. -> A10 RE-TEST TARGET.
Probe the compiler for FACTS; read its source only for LEVERS; byte-validate both. (We read
gcc-papermario for five phases believing it was 2.7.2. It was 2.8.1.)
4. THE MIGRATION: cast_call_sites canonicalized 95.1% of drafts against a TU that would never
compile them. `--src-file` is an OPTIONAL HAND-PASSED flag defaulting to src/<ov>/<ov>.c, and no
caller knows about the Phase-26 _jr_<ADDR> carves: ov_SC01_077 has 263 open stubs across 12 TUs
and only 13 are in the main .c -- while harvest_verify (A3) correctly splices into the real one.
Now DERIVED from corpus.stubs() (the INCLUDE_ASM line is self-describing), with the canonical map
derived from cdecl.tu_scope() (cpp -- so macro-injected DEFINE_func_* decls are finally visible).
Callee-conflict repair reach: 8 -> 58 of 196 drafts (7x).
5. AND THE NULL RESULT, REPORTED AS SUCH (P9/R14). Those 58 banked ZERO functions. The historical
draft tail fails on CODEGEN, not plumbing -- func_801387B8, which the audit blames on a single
unparsed `[4]`, is really 67/100 instructions off with a $s0/$s1 swap (that claim does not
reproduce on today's tree). The real gain is narrower and still worth having: 52 drafts moved
from "won't compile" to "compiles, N instructions off" -- from an INVISIBLE failure that reads as
a compiler wall into a SCORED near-miss the permuter and the §47/§48 dials can act on. That is
the audit's thesis, not a bank. THREE times in one session a confirmed mechanism produced a null
consequence.
Also: my own new audit printed "ALL ORACLES GREEN" while silently skipping 100% of its corpus (a
missing -Isrc). The exact bug class, in the tool written to hunt it. An unadjudicable check is not
a passed check.
R22 clean-fleet: make clean + extract-all + check-all -> 136 passed, 0 failed of 136
src/ untouched (0 changes) make audit-cdecl: green --compat: 1485/1485
NEXT: sig_unify + reconcile_decls carry the SAME wrong-TU bug (same --src-file flag).
|
||
|
|
cbf7e774ab |
feat(phase-21): split-aware dedup_propagate + cast_call_sites --src-file (unlock the _a.c vein)
- dedup_propagate split-aware: overlay_files() handles ov_SC01_077's Phase-19 split (main + _a/_o0); source-find scans all, member-loop edits the right file, structural-check spans all. Single-file overlays unchanged (default path). Validated: source-find (--check-only 134 members) + fail-closed revert. - cast_call_sites --src-file: canonicalize callee decls against the file the draft lands in (cross-file loose typing — a callee declared differently in main vs _a.c). Default = main, unchanged. - FINDING (cookbook §24): the fresh reach-134 fuel (66+ fns) is in ov_SC01_077_a.c (a tooling gap, not difficulty); bodies are matchable (func_8012C098 cast-banked) BUT matching/propagation hits the same §16/§20 loose-typing wall (func_8012C098 cross-overlay def-conflict -> x1; func_8012F274 within-_a RotTransSV multi-sig). x134 yield uncertain -> a small measurement wave is the next probe. - no net banks this turn; fleet 62.31% unchanged; ov_SC01_077 verified d19c9580 (R22). sig_unify reverted. |
||
|
|
6c5bd1c4a9 |
feat(phase-20): cast_call_sites.py — the §17a-1 callee-cast cap tool; 6 recovered, fleet 58.63%->58.82%
- tools/cast_call_sites.py: per draft, callee whose canonical TU sig != draft's intended sig -> decl->canonical (kills in-TU conflicting types, keeps symbol in scope) + call-site cast ((ret(*)(args))func_X)(args) (codegen-neutral: gcc folds the cast of a known symbol -> direct jal). Pure --in/--out; whole-binary gate is the arbiter (G3/P9). Pipeline: canon_resident_calls -> cast_call_sites -> sig_unify -> harvest_verify --chunk 1 - recovered 6 of T6 batch-1's 33 gate-fails byte-identical (func_80153C44/8015CF58/ 801711FC/80161BE0/801683D8/8015F948); 5 propagated x134 + 1 local; check-all 136/136 - R14 CORRECTION of cookbook §20's '~33' projection (byte-proven): the batch is NOT mostly callee-conflicts — ~6 callee (cast-recovered), ~18 DEF-side loose-typing wall (def's matchable sig conflicts with the unchangeable caller-canonical; caller-side fix BLOCKED because INCLUDE_ASM declares no symbol -> a shared macro's internal extern is load-bearing in the 133 stub overlays), + a small DATA tail. The def-side needs re-drafting under the caller-canonical sig (future wave), not a text transform - cookbook §20 RESOLUTION + SETUP tool inventory (R16/R21/R30); CURRENT_PHASE log (R28) - fleet 58.63% -> 58.82%, dedup 1501 -> 1508, 0 NON_MATCHING (G4) |