mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-27 22:45:39 -04:00
a5e97739fb
Family 0x80146ab4 (18 ins, x138, PURE) had been failing 0/138 with `conflicting types for
func_80146A6C` — 208 of the ~398 conflicts in the sweep residue, its single dominant blocker.
DIAGNOSED BY READING THE DRAFT, after three levers were eliminated by measurement:
draft def : void func_80146A6C(s16 a0, s32 a1, s16 a2, s16 a3, u16 a4, s32 a5, s32 a6)
TU decl : extern s32 func_80146A6C(s32 a0, void *a1, s32 a2, s32 a3, s32 a4, s32 a5, s32 a6);
The NARROW PARAMS are the wall: C's default argument promotion means s16/u16 cannot agree with an
s32 prototype, and the `()` no-prototype escape is ILLEGAL precisely when a param promotes. Neither
declaration side can move.
- cast_call_sites: already on by default; wrong axis (fixes CALLEE decls, not the def's own).
- --normalize-self-decls: 0 banks + non-neutral reverts; wrong axis (the target's decl in callers).
- --fix-def-sig: measured 0/138, error UNCHANGED — it cannot reconcile a promoting param at all.
THE ESCAPE (§37/§124, S33-proven on func_80147364 — definition (u16,u16) vs 4,046 fleet decls,
banked x137 first try; 1,725 in-tree precedents): give the DEFINITION a private C identifier and
bind the emitted symbol with a GNU asm label, so the TU's declaration never meets the definition and
its type becomes irrelevant. Zero blast radius on every caller; byte-neutral by construction.
void aF80146A6C(<byte-true params>) __asm__("func_80146A6C");
void aF80146A6C(<byte-true params>) { ... }
RESULT: 138/138 banked, ~2,484 instructions, ZERO agent tokens. R22 clean-fleet 140/140.
.run/alias_defs.py applies the transform to a staged draft set.
NEXT: this is a CLASS lever, not a one-family fix — generalise it across the remaining sweep residue.
58 lines
2.7 KiB
Python
58 lines
2.7 KiB
Python
#!/usr/bin/env python3
|
||
"""Rewrite staged member drafts to the §37/§124 DEFINITION-SIDE ASM-LABEL ALIAS form.
|
||
|
||
WHY (byte-diagnosed, P30 S38 — the dominant sweep residue, 208 of ~398 conflicts):
|
||
A remapped member's definition can carry a signature that NO C declaration can reconcile with the
|
||
fleet's. Measured on family 0x80146ab4 (18 ins, ×138):
|
||
|
||
draft def : void func_80146A6C(s16 a0, s32 a1, s16 a2, s16 a3, u16 a4, s32 a5, s32 a6)
|
||
TU decl : extern s32 func_80146A6C(s32 a0, void *a1, s32 a2, s32 a3, s32 a4, s32 a5, s32 a6);
|
||
|
||
The narrow params are the wall: C's DEFAULT ARGUMENT PROMOTION means `s16`/`u16` cannot agree with
|
||
an `s32` prototype, and the `()` no-prototype escape is ILLEGAL precisely when a param promotes. So
|
||
neither side can move — `cast_call_sites` is the wrong axis (it fixes CALLEE decls, not the
|
||
definition's own signature) and `--fix-def-sig` cannot help either (measured: 0/138, error
|
||
unchanged).
|
||
|
||
THE ESCAPE (S33-proven on func_80147364: definition `(u16,u16)` vs 4,046 fleet decls, banked ×137
|
||
first try; 1,725 in-tree precedents): give the DEFINITION a private C identifier and bind the
|
||
emitted SYMBOL with a GNU asm label. The TU's declaration then simply never meets the definition,
|
||
so its type is irrelevant — ZERO blast radius on every caller.
|
||
|
||
void aF80146A6C(<the byte-true params>) __asm__("func_80146A6C");
|
||
void aF80146A6C(<the byte-true params>) { ... }
|
||
|
||
Byte-neutral by construction: the emitted symbol and the body are unchanged; only the C spelling of
|
||
the definition differs. The whole-binary byte-gate remains the sole arbiter (G3/P9).
|
||
|
||
Usage: alias_defs.py <staged_dir> <out_dir>
|
||
"""
|
||
import sys, os, re, glob, shutil
|
||
|
||
SRC, OUT = sys.argv[1], sys.argv[2]
|
||
shutil.rmtree(OUT, ignore_errors=True)
|
||
|
||
# `<type> func_<ADDR>(<params>)` at column 0, followed by the body brace — the definition head.
|
||
DEF = re.compile(r'^([A-Za-z_][\w \*]*?)\b(func_([0-9A-Fa-f]{8}))\s*\(([^;{]*)\)\s*\{', re.M)
|
||
|
||
n_in = n_out = 0
|
||
for p in sorted(glob.glob(os.path.join(SRC, '*', 'func_*.c'))):
|
||
n_in += 1
|
||
ov = os.path.basename(os.path.dirname(p))
|
||
txt = open(p).read()
|
||
m = DEF.search(txt)
|
||
if not m:
|
||
continue
|
||
ret, sym, addr, params = m.group(1).strip(), m.group(2), m.group(3), m.group(4)
|
||
alias = 'aF' + addr.upper()
|
||
if alias in txt: # already aliased
|
||
continue
|
||
head = f'{ret} {alias}({params})'
|
||
new = txt[:m.start()] + f'{head} __asm__("{sym}");\n{head}\n{{' + txt[m.end():]
|
||
d = os.path.join(OUT, ov)
|
||
os.makedirs(d, exist_ok=True)
|
||
open(os.path.join(d, os.path.basename(p)), 'w').write(new)
|
||
n_out += 1
|
||
|
||
print(f"alias_defs: {n_out} of {n_in} draft(s) rewritten to the def-side alias -> {OUT}")
|