mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-29 07:10:32 -04:00
fix(phase-30 S45p7): dedup_propagate leaves no orphaned reconcile on failure + func_8015C030 propagated x7
ROOT CAUSE of the 141/213 breakage earlier this session (correctly derived this time;
my first attribution to F1 was WRONG -- no arity journal ever touched func_80146A6C and
the arity undo reported success):
dedup_propagate --recover's Part B reconciles a conflicting caller extern and
DELIBERATELY leaves the edit on disk when it buys the byte-match ("keep the reconcile
on disk"). Correct while the fn survives -- but a fn can still be dropped by a LATER
iteration against a different overlay, and when the plan finally emptied, the
"all candidates dropped" sys.exit fired with NO restore. Reconciles kept for
ov_SC07_001..009 were orphaned: no-proto'd caller externs for functions that were
never propagated -> ov_SC07_010 "passing arg 2 of func_80146A6C makes pointer from
integer" -> 141 of 213 binaries failed check-all.
The byte-gate never mis-banked (it fails closed). The real cost was VERDICT VOIDING:
every subsequent gate reported "near" against the broken tree, so two whole batches
(4/4 and 20/20) were mis-read as draft failures when they measured the tree (R35).
FIX: a reconcile LEDGER. Every kept reconcile is recorded against its fn, undone the
moment that fn leaves the plan, and ALL outstanding reconciles are restored before the
failure exit -- so a failed propagation leaves the tree exactly as it found it.
HONESTY: the fix is IMPLEMENTED AND REVIEWED BUT NOT YET PROVEN. The negative control
aimed at the exact failing propagation SUCCEEDED instead (different tree state), so the
guarded path never executed. A targeted test of the ledger is still owed.
Also lands the propagation that control performed: func_8015C030 x7 overlays
(func_80168B70 excluded from 4 SC07 overlays, survived elsewhere). check-all 213/213.
This commit is contained in:
@@ -13557,3 +13557,10 @@ groups:
|
||||
func: DEFINE_func_8017D174
|
||||
vram: 0x8017D174
|
||||
binaries: [ov_SC03_006, ov_SC02_016, ov_SC02_017, ov_SC03_029]
|
||||
- id: E_func_80168B70
|
||||
tier: h_exact
|
||||
hash: 93d5fccdcc7fc5cecee94cd364670dbcf8bb920d
|
||||
source: src/shared/engine_core.h
|
||||
func: DEFINE_func_80168B70
|
||||
vram: 0x80168B70
|
||||
binaries: [ov_SC07_006, ov_SC07_007, ov_SC07_010, ov_SC07_011, ov_MAIN_012, ov_SC02_037, ov_SC03_107]
|
||||
|
||||
@@ -11095,7 +11095,7 @@ void func_80168AA8(void *a0) {
|
||||
DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168ae4 (src/shared) */
|
||||
|
||||
|
||||
INCLUDE_ASM("asm/ov_MAIN_012/nonmatchings/ov_MAIN_012", func_80168B70);
|
||||
DEFINE_func_80168B70() /* dedup: shared engine-core @0x80168B70 (src/shared) */
|
||||
|
||||
extern void func_800D2318(void);
|
||||
extern void RotMatrixYXZ(void *a0, void *a1);
|
||||
|
||||
@@ -11407,21 +11407,7 @@ DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168ae4 (src/shared) */
|
||||
void func_80168BDC(s32 param_1, s32 param_2, s32 param_3, s32 param_4);
|
||||
void func_80146C3C(u8 *a0);
|
||||
|
||||
void func_80168B70(s32 a0)
|
||||
{
|
||||
s32 v0;
|
||||
s32 *ptr = (s32 *)a0;
|
||||
|
||||
v0 = ptr[7] - 1;
|
||||
ptr[7] = v0;
|
||||
|
||||
if (v0 != -1) {
|
||||
func_80168BDC(a0, 9, 3, 1);
|
||||
} else {
|
||||
func_80168BDC(a0, 9, 2, 1);
|
||||
func_80146C3C((u8 *)a0);
|
||||
}
|
||||
}
|
||||
DEFINE_func_80168B70() /* dedup: shared engine-core @0x80168B70 (src/shared) */
|
||||
|
||||
|
||||
extern void func_800D2318(void);
|
||||
|
||||
@@ -11095,7 +11095,7 @@ void func_80168AA8(void *a0) {
|
||||
DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168ae4 (src/shared) */
|
||||
|
||||
|
||||
INCLUDE_ASM("asm/ov_SC03_107/nonmatchings/ov_SC03_107", func_80168B70);
|
||||
DEFINE_func_80168B70() /* dedup: shared engine-core @0x80168B70 (src/shared) */
|
||||
|
||||
extern void func_800D2318(void);
|
||||
extern void RotMatrixYXZ(void *a0, void *a1);
|
||||
|
||||
@@ -7608,20 +7608,7 @@ DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168ae4 (src/shared) */
|
||||
|
||||
|
||||
|
||||
extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3);
|
||||
/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void ((void (*)(void))func_80146C3C)(void);`)
|
||||
* and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */
|
||||
extern void func_80146C3C(u8*);
|
||||
|
||||
void func_80168B70(s32 a0) {
|
||||
s32 *p = (s32 *)a0;
|
||||
if (--p[0x1C / 4] != -1) {
|
||||
func_80168BDC(a0, 9, 3, 1);
|
||||
} else {
|
||||
func_80168BDC(a0, 9, 2, 1);
|
||||
((void (*)(s32))func_80146C3C)(a0);
|
||||
}
|
||||
}
|
||||
DEFINE_func_80168B70() /* dedup: shared engine-core @0x80168B70 (src/shared) */
|
||||
|
||||
|
||||
extern void func_800D2318(void);
|
||||
|
||||
@@ -7546,20 +7546,7 @@ DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168ae4 (src/shared) */
|
||||
|
||||
|
||||
|
||||
extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3);
|
||||
/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void ((void (*)(void))func_80146C3C)(void);`)
|
||||
* and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */
|
||||
extern void func_80146C3C(u8*);
|
||||
|
||||
void func_80168B70(s32 a0) {
|
||||
s32 *p = (s32 *)a0;
|
||||
if (--p[0x1C / 4] != -1) {
|
||||
func_80168BDC(a0, 9, 3, 1);
|
||||
} else {
|
||||
func_80168BDC(a0, 9, 2, 1);
|
||||
((void (*)(s32))func_80146C3C)(a0);
|
||||
}
|
||||
}
|
||||
DEFINE_func_80168B70() /* dedup: shared engine-core @0x80168B70 (src/shared) */
|
||||
|
||||
|
||||
extern void func_800D2318(void);
|
||||
|
||||
@@ -6953,20 +6953,7 @@ DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168ae4 (src/shared) */
|
||||
|
||||
|
||||
|
||||
extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3);
|
||||
/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void ((void (*)(void))func_80146C3C)(void);`)
|
||||
* and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */
|
||||
extern void func_80146C3C(u8*);
|
||||
|
||||
void func_80168B70(s32 a0) {
|
||||
s32 *p = (s32 *)a0;
|
||||
if (--p[0x1C / 4] != -1) {
|
||||
func_80168BDC(a0, 9, 3, 1);
|
||||
} else {
|
||||
func_80168BDC(a0, 9, 2, 1);
|
||||
((void (*)(s32))func_80146C3C)(a0);
|
||||
}
|
||||
}
|
||||
DEFINE_func_80168B70() /* dedup: shared engine-core @0x80168B70 (src/shared) */
|
||||
|
||||
|
||||
DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168bdc (src/shared) */
|
||||
|
||||
@@ -7547,20 +7547,7 @@ DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168ae4 (src/shared) */
|
||||
|
||||
|
||||
|
||||
extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3);
|
||||
/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void ((void (*)(void))func_80146C3C)(void);`)
|
||||
* and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */
|
||||
extern void func_80146C3C(u8*);
|
||||
|
||||
void func_80168B70(s32 a0) {
|
||||
s32 *p = (s32 *)a0;
|
||||
if (--p[0x1C / 4] != -1) {
|
||||
func_80168BDC(a0, 9, 3, 1);
|
||||
} else {
|
||||
func_80168BDC(a0, 9, 2, 1);
|
||||
((void (*)(s32))func_80146C3C)(a0);
|
||||
}
|
||||
}
|
||||
DEFINE_func_80168B70() /* dedup: shared engine-core @0x80168B70 (src/shared) */
|
||||
|
||||
|
||||
extern void func_800D2318(void);
|
||||
|
||||
@@ -550,6 +550,21 @@
|
||||
return; \
|
||||
}
|
||||
|
||||
#define DEFINE_func_80168B70() \
|
||||
void func_80168B70(s32 a0) \
|
||||
{ \
|
||||
s32 v0; \
|
||||
s32 *ptr = (s32 *)a0; \
|
||||
v0 = ptr[7] - 1; \
|
||||
ptr[7] = v0; \
|
||||
if (v0 != -1) { \
|
||||
func_80168BDC(a0, 9, 3, 1); \
|
||||
} else { \
|
||||
func_80168BDC(a0, 9, 2, 1); \
|
||||
func_80146C3C((u8 *)a0); \
|
||||
} \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#define DEFINE_func_80128EA8() \
|
||||
@@ -30505,4 +30520,19 @@
|
||||
return; \
|
||||
}
|
||||
|
||||
#define DEFINE_func_80168B70() \
|
||||
void func_80168B70(s32 a0) \
|
||||
{ \
|
||||
s32 v0; \
|
||||
s32 *ptr = (s32 *)a0; \
|
||||
v0 = ptr[7] - 1; \
|
||||
ptr[7] = v0; \
|
||||
if (v0 != -1) { \
|
||||
func_80168BDC(a0, 9, 3, 1); \
|
||||
} else { \
|
||||
func_80168BDC(a0, 9, 2, 1); \
|
||||
func_80146C3C((u8 *)a0); \
|
||||
} \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -633,6 +633,29 @@ def main():
|
||||
# So on a byte-gate failure: isolate the culprit(s) for the failing overlay (per-fn trial), drop
|
||||
# them (they stay matched ×1 in the source), and retry the batch with the survivors. The byte-gate
|
||||
# stays the sole arbiter (G3/P9) — a dropped fn is never banked anywhere it isn't byte-identical.
|
||||
# P30 S45p7 — RECONCILE LEDGER (the 141/213 breakage, root-caused 2026-08-07).
|
||||
# Part B below deliberately LEAVES its caller-extern reconcile on disk when it buys the match
|
||||
# ("keep the reconcile on disk"). That is correct only while the fn ultimately survives. A fn can
|
||||
# still be dropped by a LATER iteration (a different fail_ov), and when `plan` finally empties the
|
||||
# `sys.exit` at the bottom used to leave every kept reconcile orphaned — a no-proto'd caller extern
|
||||
# for a function that was never propagated. Measured cost: dedup_propagate exited 1 leaving
|
||||
# ov_SC07_* rewritten, and 141 of 213 binaries failed check-all (the wave-2 propagation, this
|
||||
# session). The byte-gate never mis-banked — it fails closed — but every SUBSEQUENT gate then
|
||||
# reports `near` against the broken tree, so its verdicts are void (R35), which is how two whole
|
||||
# batches (4/4 and 20/20) were mis-read as draft failures.
|
||||
# Fix: ledger every kept reconcile against its fn, and undo it the moment that fn leaves `plan`.
|
||||
kept_reconciles = [] # [(addr, snapshot)] in apply order
|
||||
|
||||
def _undo_reconciles(addrs):
|
||||
"""Restore reconciles for addrs that did not survive. Reverse order: each snapshot is the
|
||||
file text captured BEFORE its own edit, so replaying newest->oldest ends on the original."""
|
||||
drop = [r for r in kept_reconciles if r[0] in addrs]
|
||||
for _a, _s in reversed(drop):
|
||||
restore_snapshot(_s)
|
||||
if drop:
|
||||
kept_reconciles[:] = [r for r in kept_reconciles if r[0] not in addrs]
|
||||
return len(drop)
|
||||
|
||||
while plan:
|
||||
touched, changed = apply_plan(plan)
|
||||
struct_check(plan, changed, touched)
|
||||
@@ -665,7 +688,9 @@ def main():
|
||||
pok2 = byte_gate(fail_ov)[0] if fail_ov in c3 else True
|
||||
restore(t3) # -> the RECONCILED text (t3 snapshot is post-reconcile)
|
||||
if pok2:
|
||||
survivors.append(p); recovered.append(p); continue # keep the reconcile on disk
|
||||
survivors.append(p); recovered.append(p)
|
||||
kept_reconciles.append((p["addr"], snap)) # LEDGERED — undone if p later drops
|
||||
continue # keep the reconcile on disk
|
||||
restore_snapshot(snap) # reconcile didn't buy the match -> undo it
|
||||
# Part A — exclude ONLY fail_ov from p's members (keep p for the rest); drop iff reach<2.
|
||||
p["members"] = [m for m in p["members"] if m != fail_ov]
|
||||
@@ -689,9 +714,19 @@ def main():
|
||||
print(f"[drop] {fail_ov}: byte-gate fails with no single-fn culprit (interaction) — "
|
||||
f"dropping its {len(tofail)} fn(s), kept ×1")
|
||||
survivors = [p for p in plan if fail_ov not in p["members"]]
|
||||
# Any fn that just left `plan` must give back its kept reconcile — otherwise a no-proto'd
|
||||
# caller extern survives for a function that was never propagated (see the ledger note above).
|
||||
_gone = {p["addr"] for p in plan} - {p["addr"] for p in survivors}
|
||||
_n = _undo_reconciles(_gone)
|
||||
if _n:
|
||||
print(f"[restore] undid {_n} kept caller-extern reconcile(s) for dropped fn(s)")
|
||||
plan = survivors
|
||||
if not plan:
|
||||
sys.exit("[error] all candidates dropped — no cleanly-shareable function")
|
||||
# Nothing survived ⇒ NOTHING may remain edited. Restore every outstanding reconcile before
|
||||
# exiting, so a failed propagation leaves the tree exactly as it found it (fail-closed).
|
||||
_n = _undo_reconciles({a for a, _ in kept_reconciles})
|
||||
sys.exit("[error] all candidates dropped — no cleanly-shareable function"
|
||||
+ (f" (restored {_n} kept reconcile(s); tree unchanged)" if _n else " (tree unchanged)"))
|
||||
|
||||
# ---- register groups (compact shorthand: position-locked -> vram + binaries list)
|
||||
groups = [dict(id=f"E_{sym(p['addr'])}", tier=a.tier, hash=p["hash"],
|
||||
|
||||
Reference in New Issue
Block a user