mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-10-03 00:05:11 -04:00
fix(phase-26): permuter silently no-op'd on every GTE draft (+ --asm-subdir)
Second silent no-op of the §G class, found while permuting func_8017BEBC (close=2):
- hide_asm() is built for __asm__ STATEMENTS and `register __asm__("$sN")` pins inside a
function body (it scans back to the previous ;{} and forward to the next top-level ;).
A draft whose GTE ops are #defines CONTAINING __asm__ (the PsyQ inline_c.h convention,
i.e. most renderer code) therefore had its macro DEFINITIONS chewed up, swallowing the
function itself -> pycparser 'Function <fn> not found in base.c' -> decomp-permuter
no-op'd in 0s. base.c contained ZERO occurrences of the target function.
FIX: cpp_expand_macros() pre-expands with `cpp -P` so each GTE op becomes an inline
__asm__ statement hide_asm can carry via the b64 pragma. Applied ONLY when a
'#define ... __asm__' is present -> macro-free drafts byte-untouched.
- p16_permute was hardcoded to OV=ov_SC01_077's MAIN object, so no core in another overlay
or split object could be permuted at all. Added --asm-subdir (threaded explicitly: a
def-time default arg cannot see a mutated global).
LESSON (cookbook): permuter 'no match (0s)' is a TOOLING failure signature, never a real
search result. Verify workers actually ran.
Verified: base.c now holds the function; 16 workers searching on func_8017BEBC.
This commit is contained in:
@@ -2351,7 +2351,7 @@ Cracked **6 of 8** reach-134 §G giants — `func_801571C4` (permuter), `func_80
|
||||
|
||||
**BANKING PATTERNS (main + `_a` split):** caller-extern reconcile (`void`→canonical return, 571C4); **asm-label alias** for sibling-decl signedness/proto conflicts (`extern u16 X __asm__("D_x")`, 372B0) — beats reconcile's `*(u16*)&D_x` cast whose address-of PERTURBS regalloc; **canonical call-cast + anonymize the shared `/s` struct** for isomorphic-sibling ports (76D94). **×134 blockers (§A gaps → tool fixes):** register-asm pins + overlay-local *named* types fail `dedup_propagate.compiles_standalone` → bank ×1 (needs a pin/asm self-containment shim + a uniquely-renamed type-lift).
|
||||
|
||||
**TOOLING (flywheel):** fixed a silent permuter bug — `tools/p16_permute.py` header comments broke `cpp` → decomp-permuter no-op'd `(0s)` on EVERY commented draft (`strip_c_comments`); NEW `tools/permuter_ils.py` (warm-restart iterated-local-search — descends where a cold run plateaus: 48094 72→29). **Escalation: model to close=2 by hand, THEN directed-permuter the residual** (372B0's last lever fell at permuter iter 291; permuter is low-ROI at close=8).
|
||||
**TOOLING (flywheel):** fixed a silent permuter bug — `tools/p16_permute.py` header comments broke `cpp` → decomp-permuter no-op'd `(0s)` on EVERY commented draft (`strip_c_comments`); **a SECOND silent no-op of the same class (Phase 26 session 6): a draft whose GTE ops are `#define`s CONTAINING `__asm__` (the PsyQ `inline_c.h` convention — i.e. most renderer code) got its macro DEFINITIONS chewed up by `hide_asm` (which is built for `__asm__` statements / `register` pins and scans to the nearest `;{}`), swallowing the function itself → pycparser `Function <fn> not found in base.c` → permuter no-op `(0s)`. Fix: `cpp_expand_macros()` pre-expands with `cpp -P` so each GTE op becomes an inline `__asm__` statement hide_asm can carry — applied ONLY when a `#define ... __asm__` is present, so macro-free drafts are byte-untouched. LESSON: the permuter reporting `no match (0s)` is a TOOLING failure signature, never a real search result — always confirm workers actually ran.** `p16_permute` also gained `--asm-subdir` (it was hardcoded to ov_SC01_077's main object, so no core in another overlay/split object could be permuted at all); NEW `tools/permuter_ils.py` (warm-restart iterated-local-search — descends where a cold run plateaus: 48094 72→29). **Escalation: model to close=2 by hand, THEN directed-permuter the residual** (372B0's last lever fell at permuter iter 291; permuter is low-ROI at close=8).
|
||||
|
||||
## §38 — The WHALE `func_80144B9C` (770 ins): the -O0 struct-assign memcpy idiom + the -O0 reach-134 ×134 rollout (Phase 24 T7 §G, cheap Opus — no Fable5, no calls.c)
|
||||
|
||||
|
||||
+30
-2
@@ -139,11 +139,36 @@ def strip_c_comments(c):
|
||||
return COMMENT_RE.sub("", c)
|
||||
|
||||
|
||||
_DEFINE_ASM_RE = re.compile(r'^\s*#\s*define\b[^\n]*__asm__', re.M)
|
||||
|
||||
|
||||
def cpp_expand_macros(c):
|
||||
"""Macro-expand a draft whose GTE ops are `#define`s CONTAINING `__asm__` (the PsyQ `inline_c.h`
|
||||
convention this codebase uses for RTPT/RTPS/NCLIP/etc).
|
||||
|
||||
`hide_asm` is built for `__asm__` STATEMENTS and `register ... __asm__("$sN")` pins inside a function
|
||||
body: it scans back to the previous `;`/`{`/`}` and forward to the next top-level `;`. A multi-line
|
||||
`#define gte_ldv0(r0) __asm__ volatile (...)` has neither boundary where hide_asm expects, so it chews
|
||||
through the macro DEFINITIONS and swallows the function itself. pycparser then reports "Function <fn>
|
||||
not found in base.c" and decomp-permuter **silently no-ops in 0s** — the same false-negative signature
|
||||
as the §G comment bug, and it would disable the permuter on every GTE-using draft (i.e. most of the
|
||||
renderer code). Pre-expanding with cpp turns each GTE op into an inline `__asm__` statement, which
|
||||
hide_asm then hides correctly via the b64 pragma carrier.
|
||||
|
||||
Applied ONLY when the collision is actually present, so macro-free drafts are byte-untouched."""
|
||||
if not _DEFINE_ASM_RE.search(c):
|
||||
return c
|
||||
p = subprocess.run(["mipsel-linux-gnu-cpp", "-P", "-nostdinc", "-"],
|
||||
input=c, capture_output=True, text=True)
|
||||
return p.stdout if p.returncode == 0 and p.stdout.strip() else c
|
||||
|
||||
|
||||
def make_base_c(draft_c):
|
||||
"""permuter base.c = scalar typedefs + the draft's custom typedefs/#defines/externs + the
|
||||
M2C_FIELD-expanded, asm-hidden body. Keeping externs + custom types is essential: without them the
|
||||
permuter matches in a DIFFERENT context than the whole-binary build (-> winners don't byte-gate)."""
|
||||
body = strip_c_comments(draft_c)
|
||||
body = cpp_expand_macros(body) # GTE `#define ... __asm__` would otherwise be eaten by hide_asm
|
||||
body = expand_m2c_field(body)
|
||||
body = hide_asm(body)
|
||||
body = drop_preproc_and_scalar_typedefs(body)
|
||||
@@ -226,6 +251,9 @@ def main():
|
||||
ap.add_argument("--winners", default=".run/permuter-winners")
|
||||
ap.add_argument("--klass", default=None,
|
||||
help="§31 residual class to direct the mutation weights (default: auto-lookup from the backlog)")
|
||||
ap.add_argument("--asm-subdir", default=ASM,
|
||||
help="target asm dir (default: ov_SC01_077's main object). A core in another overlay "
|
||||
"or split object needs its own, e.g. asm/ov_SC01_000/nonmatchings/ov_SC01_000_after")
|
||||
a = ap.parse_args()
|
||||
os.chdir(REPO)
|
||||
os.makedirs(a.winners, exist_ok=True)
|
||||
@@ -237,7 +265,7 @@ def main():
|
||||
funcs = []
|
||||
for cf in sorted(glob.glob(a.from_drafts + "/*.c")):
|
||||
fn = os.path.basename(cf)[:-2]
|
||||
r = sh([PY, "tools/match_one.py", fn, "--c", cf, "--asm-subdir", ASM])
|
||||
r = sh([PY, "tools/match_one.py", fn, "--c", cf, "--asm-subdir", a.asm_subdir])
|
||||
first = (r.stdout.strip().splitlines() or ["?"])[0]
|
||||
m = re.search(r"(\d+) mismatched", first)
|
||||
if m and 1 <= int(m.group(1)) <= a.near_max:
|
||||
@@ -251,7 +279,7 @@ def main():
|
||||
if not os.path.exists(cf):
|
||||
print(f" [{k}/{len(funcs)}] {fn}: no draft"); continue
|
||||
klass, where = (a.klass, "") if a.klass else klass_for_fn(fn)
|
||||
pd = setup(fn, open(cf).read(), klass=klass, where=where)
|
||||
pd = setup(fn, open(cf).read(), asm_subdir=a.asm_subdir, klass=klass, where=where)
|
||||
if not pd:
|
||||
print(f" [{k}/{len(funcs)}] {fn}: setup failed (target.o)"); continue
|
||||
_prof = permuter_weights.classify(klass, where)
|
||||
|
||||
Reference in New Issue
Block a user