mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-10-02 07:59:52 -04:00
feat(phase-30 S47-F2): auto-alias conflicting data externs (partial: 2/44); real cause recorded
The 44 data-symbol conflicting-types failures are NOT the cdFileLocTable duplicate-typedef class.
They are genuine per-view type differences: D_80078EB4 is s16 at 2,409 sites and u16 at 1,341;
D_800AE620 is Blk20/s32/Mat32. For data the declared type drives the load (lh vs lhu), so
canonicalising would rewrite thousands of already-banked sites' codegen. The answer is one type
PER VIEW — the §37 asm-label alias the fleet already hand-writes for D_800AE620 (9 sites).
Shipped: scope_data_externs now auto-aliases a conflicting extern —
extern s16 aD80078EB4 __asm__("D_80078EB4");
keeping the draft's own type (byte-truth for that body) while the private C name makes collision
impossible and the asm label pins the emitted symbol, so codegen is unchanged. Fires only where
the TU declares that symbol with a DIFFERENT type text; same-type and already-aliased drafts are
untouched (4 controls, 2 of them negative). Applied on both scope paths — a staged draft's externs
arrive indented, so a demote-path-only fix reached 1 of 44.
R22 clean-fleet: check-all 213 passed / 0 failed of 213.
WHY ONLY 2 BANKED — the collision is DRAFT-vs-DRAFT, not draft-vs-TU. The failing draft declares
`extern s32 D_80114F24;` and ov_MAIN_012.c declares that symbol nowhere; the error lands at the
splice point. family_sweep stages every member of an (overlay, split) group into one TU before
gating, so two templated bodies with different views of one symbol collide with each other.
scope_data_fix sees one draft plus the pre-splice TU and structurally cannot see the others.
The real fix belongs in the staging loop, which knows the whole group: alias any data symbol
declared with >=2 distinct types across the drafts staged together. Not attempted here.
Three wrong inferences on this one task before reading a failing draft: scoped as the typedef
class; aliased only the demote path against the file's own comment; targeted the wrong collision.
This commit is contained in:
@@ -426,6 +426,32 @@ sibling lacks. **Now the largest class, and a different mechanism from every dec
|
||||
(D_801202A0 11 · func_8001534C 4) · `14` arity (too many/too few args) · `6` func_80145CEC.
|
||||
**Every former unknown turned out cheap** — declarations, arity, redefinitions. Not one wall.
|
||||
|
||||
## ⚠ TASK F2 (data-symbol conflicts) — **PARTIAL: 2 of 44.** Mechanism correct, target wrong.
|
||||
**NOT the cdFileLocTable class.** These are genuine PER-VIEW type differences, not duplicate
|
||||
typedefs: `D_80078EB4` is `s16` at 2,409 sites and `u16` at 1,341; `D_800AE620` is
|
||||
`Blk20`/`s32`/`Mat32`. For DATA the declared type drives the load (`lh` vs `lhu`), so canonicalising
|
||||
would rewrite thousands of BANKED sites' codegen — the memcpy trap at 100× scale. The answer is one
|
||||
type PER VIEW: the §37 asm-label alias, which the fleet already hand-writes for `D_800AE620` (9×).
|
||||
**Shipped:** `scope_data_externs` auto-aliases a conflicting extern (`extern s16 aD80078EB4
|
||||
__asm__("D_80078EB4")`) — keeps the draft's own type (byte-truth for that body), private C name
|
||||
makes collision impossible, asm label pins the emitted symbol ⇒ codegen unchanged. Fires ONLY where
|
||||
the TU declares that symbol with a different type text. 4 controls incl. 2 negatives. R22 **213/213**.
|
||||
|
||||
**WHY IT ONLY BANKED 2 — the collision is DRAFT-vs-DRAFT, not draft-vs-TU.** Evidence (finally
|
||||
read, after two wrong inferences): the failing draft declares `extern s32 D_80114F24;` and
|
||||
`ov_MAIN_012.c` declares that symbol **nowhere**; the error lands at line 24538 = the splice point.
|
||||
The sweep stages EVERY member of an (overlay,split) group into one TU before gating, so two
|
||||
templated bodies with different views of one symbol collide with each other. `scope_data_fix` sees
|
||||
one draft + the PRE-SPLICE TU, so it structurally cannot see the others.
|
||||
**→ The real fix belongs in the sweep's STAGING LOOP** (it knows the whole group): alias any data
|
||||
symbol declared with ≥2 distinct types across the drafts being staged together. Not attempted.
|
||||
|
||||
**MY ERROR LEDGER ON THIS ONE TASK (3):** (1) scoped it as the typedef class — wrong; (2) put the
|
||||
alias only on the demote path when the file's OWN comment says staged drafts arrive INDENTED — 1/44;
|
||||
(3) the collision is not the one the mechanism targets — 2/44. Each caught by measurement, but I
|
||||
burned three attempts on a 44-member class by inferring the shape instead of reading ONE failing
|
||||
draft, which took 90 seconds when I finally did it.
|
||||
|
||||
## ▶ RESUME HERE — D (and the leftovers below)
|
||||
**C is DONE.** For reference, the command was:
|
||||
`.venv/bin/python tools/dedup_extend.py --binaries ov_SC03_107,ov_MAIN_012,ov_SC02_037`
|
||||
|
||||
@@ -16564,7 +16564,125 @@ void func_80166654(void *a0) {
|
||||
}
|
||||
|
||||
|
||||
INCLUDE_ASM("asm/ov_MAIN_012/nonmatchings/ov_MAIN_012", func_80166690);
|
||||
|
||||
/* 12-byte sub-table record (stride 0xC) */
|
||||
|
||||
|
||||
extern s32 func_80017758(void *a0, void *a1);
|
||||
|
||||
s32 func_80166690(s32 param_1, s32 param_2)
|
||||
{
|
||||
|
||||
extern Blk20 aD800AE620 __asm__("D_800AE620");
|
||||
extern u8 D_80184418[];
|
||||
Frame_80166690 c;
|
||||
Blk20 blk;
|
||||
Rec_80166690 *sub;
|
||||
Rec_80166690 *pv;
|
||||
u8 *rec;
|
||||
int i6;
|
||||
int lt2;
|
||||
int p2;
|
||||
unsigned int uVar7, uVar8, uVar9;
|
||||
int iVar3;
|
||||
unsigned int w9, w7, w, t, w3b;
|
||||
int w3;
|
||||
short sVar10;
|
||||
register unsigned int zr __asm__("$0");
|
||||
|
||||
blk = aD800AE620;
|
||||
i6 = (short)param_1;
|
||||
c.tag = 0x50000000;
|
||||
rec = &D_80184418[i6 * 0xC4];
|
||||
sVar10 = 0;
|
||||
if (rec[0] == 0) {
|
||||
return 0;
|
||||
}
|
||||
sub = (Rec_80166690 *)(rec + 4);
|
||||
uVar9 = rec[1];
|
||||
uVar8 = rec[3];
|
||||
uVar7 = uVar8 >> 1;
|
||||
iVar3 = rec[2];
|
||||
w3 = iVar3 - 1;
|
||||
iVar3 = w3 + zr;
|
||||
if (w3 != -1) {
|
||||
lt2 = i6 < 2;
|
||||
p2 = (short)param_2;
|
||||
do {
|
||||
pv = &sub[(short)uVar9];
|
||||
c.v0 = pv->h[0];
|
||||
c.v1 = pv->h[1];
|
||||
c.v2 = pv->h[2];
|
||||
c.w0 = pv->h[3];
|
||||
c.w1 = pv->h[4];
|
||||
c.w2 = pv->h[5];
|
||||
sVar10 = sVar10 + 1;
|
||||
|
||||
if (lt2) {
|
||||
if (p2 == 0) {
|
||||
t = uVar8 + zr;
|
||||
c.c12 = t; c.c02 = t;
|
||||
t = uVar7 + zr;
|
||||
} else {
|
||||
w = ((int)(uVar8 << 16) >> 17) + ((int)(uVar8 << 16) >> 19);
|
||||
uVar7 = w + zr;
|
||||
c.c12 = w; c.c02 = w;
|
||||
t = uVar8 + zr;
|
||||
}
|
||||
c.c11 = t; c.c01 = t; c.c10 = t; c.c00 = t;
|
||||
} else {
|
||||
t = uVar8 + zr;
|
||||
c.c10 = t; c.c00 = t;
|
||||
t = uVar7 + zr;
|
||||
c.c12 = t; c.c02 = t; c.c11 = t; c.c01 = t;
|
||||
}
|
||||
|
||||
w9 = uVar9 - 1;
|
||||
uVar9 = w9 + zr;
|
||||
if ((int)(w9 << 16) < 0) {
|
||||
uVar9 = 0xF;
|
||||
}
|
||||
pv = &sub[(short)uVar9];
|
||||
c.v3 = pv->h[0];
|
||||
c.v4 = pv->h[1];
|
||||
c.v5 = pv->h[2];
|
||||
c.w3 = pv->h[3];
|
||||
c.w4 = pv->h[4];
|
||||
uVar8 = uVar8 - 0x10;
|
||||
w7 = uVar7 - 0x10;
|
||||
uVar7 = w7 + zr;
|
||||
c.w5 = pv->h[5];
|
||||
if ((int)(w7 << 16) < 0) {
|
||||
uVar7 = 0;
|
||||
}
|
||||
|
||||
if (lt2) {
|
||||
if (p2 == 0) {
|
||||
t = uVar8 + zr;
|
||||
c.c32 = t; c.c22 = t;
|
||||
t = uVar7 + zr;
|
||||
} else {
|
||||
t = uVar7 + zr;
|
||||
c.c32 = t; c.c22 = t;
|
||||
t = uVar8 + zr;
|
||||
}
|
||||
c.c31 = t; c.c21 = t; c.c30 = t; c.c20 = t;
|
||||
} else {
|
||||
t = uVar8 + zr;
|
||||
c.c30 = t; c.c20 = t;
|
||||
t = uVar7 + zr;
|
||||
c.c32 = t; c.c22 = t; c.c31 = t; c.c21 = t;
|
||||
}
|
||||
|
||||
__asm__ __volatile__("" :: "r"(iVar3));
|
||||
func_80017758(&c, &blk);
|
||||
w3b = iVar3 - 1;
|
||||
iVar3 = w3b + zr;
|
||||
} while ((short)w3b != -1);
|
||||
}
|
||||
return (short)sVar10;
|
||||
}
|
||||
|
||||
|
||||
extern void func_801484B0(s32 a0, s32 a1);
|
||||
extern void func_80148534(s32 a0, s32 a1);
|
||||
|
||||
@@ -7078,7 +7078,16 @@ INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8017C24C", func_8018540
|
||||
|
||||
INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8017C24C", func_80185440);
|
||||
|
||||
INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8017C24C", func_801854C0);
|
||||
|
||||
extern void func_8012A828(s32, void*);
|
||||
extern short aD801CC358 __asm__("D_801CC358");
|
||||
void func_801854C0(short *a0) {
|
||||
*(short *)((char *)a0 + 0x2) = 0x1D;
|
||||
*(short *)((char *)a0 + 0x34) = 0;
|
||||
*(int *)((char *)a0 + 0x1c) = 0xf;
|
||||
((void (*)(s32 *, s32))func_8012A828)((s32 *)a0, (int)&aD801CC358);
|
||||
}
|
||||
|
||||
|
||||
// @class: schedule
|
||||
// @stuck: none — MATCH (181 ins, relocation-masked)
|
||||
|
||||
@@ -93,6 +93,31 @@ def _body_open_brace(body, func):
|
||||
_ASM_LABEL_RE = re.compile(r'__asm__\s*\(\s*"([^"]+)"\s*\)')
|
||||
|
||||
|
||||
def _norm_ws(s):
|
||||
return re.sub(r'\s+', ' ', s.strip())
|
||||
|
||||
|
||||
def _decl_type_text(line, sym):
|
||||
"""The declared TYPE of `sym` in one `extern` line — everything between `extern` and the
|
||||
declarator, plus any array suffix. None if the line does not declare it."""
|
||||
m = re.search(rf'^\s*extern\s+(.*?)\b{re.escape(sym)}\b\s*(\[[^\]]*\])?', line)
|
||||
return (m.group(1) + (m.group(2) or '')) if m else None
|
||||
|
||||
|
||||
def _file_scope_data_types(tu_text):
|
||||
"""{sym -> declared type text} for every file-scope data extern ANYWHERE in the TU.
|
||||
|
||||
`_file_scope_data_syms` answers "declared ABOVE the insertion point", which is the right
|
||||
question for the drop/demote decision and the WRONG one for conflict detection: a file-scope
|
||||
declaration BELOW the body must still agree with a block-scope extern inside it."""
|
||||
out = {}
|
||||
for ln in FILE_EXTERN_RE.findall(tu_text):
|
||||
d = DATA_SYM_RE.search(ln)
|
||||
if d:
|
||||
out.setdefault(d.group(0), _decl_type_text(ln, d.group(0)))
|
||||
return out
|
||||
|
||||
|
||||
def is_asm_alias(ln):
|
||||
"""True for a §37/§84 ASM-LABEL ALIAS: `extern T ident… __asm__("SYM");` with ident != SYM.
|
||||
|
||||
@@ -121,9 +146,11 @@ def fix(body, tu_text, insert_pos, func):
|
||||
qualifies, when the body declares no data externs, or when the opening brace can't be located."""
|
||||
above = _file_scope_data_syms(tu_text[:insert_pos])
|
||||
|
||||
tu_types = _file_scope_data_types(tu_text)
|
||||
demote = [] # (line_text, sym)
|
||||
dropped = [] # syms the TU ALREADY declares above us — redundant, see below
|
||||
keep_lines = []
|
||||
aliased = [] # (sym, alias) — renamed at the end, both scope paths
|
||||
for ln in body.split('\n'):
|
||||
# BLOCK-SCOPE TOO, not just col-0 (Phase 29 SESSION-22). §8d demotes these externs on the way
|
||||
# in, so by the time a sibling draft is STAGED they are already indented — and a col-0-only
|
||||
@@ -135,6 +162,16 @@ def fix(body, tu_text, insert_pos, func):
|
||||
if d and d.group(0) in above and not is_asm_alias(ln):
|
||||
dropped.append(d.group(0))
|
||||
continue
|
||||
# THE ALIAS MUST REACH THIS BRANCH TOO (P30 S47). A staged draft's externs are ALREADY
|
||||
# indented (see the note above), so they arrive here, not in the col-0 demote path —
|
||||
# and a first cut that aliased only demoted lines fired on 1 member out of 44.
|
||||
if d and not is_asm_alias(ln):
|
||||
_mine, _theirs = _decl_type_text(ln, d.group(0)), tu_types.get(d.group(0))
|
||||
if _mine and _theirs and _norm_ws(_mine) != _norm_ws(_theirs):
|
||||
_al = f'aD{d.group(0)[2:]}'
|
||||
ln = (re.sub(rf'\b{re.escape(d.group(0))}\b', _al, ln, count=1)
|
||||
.rstrip().rstrip(';') + f' __asm__("{d.group(0)}");')
|
||||
aliased.append((d.group(0), _al))
|
||||
keep_lines.append(ln)
|
||||
continue
|
||||
if FILE_EXTERN_RE.match(ln): # col-0 extern (match => anchored at col 0)
|
||||
@@ -161,17 +198,50 @@ def fix(body, tu_text, insert_pos, func):
|
||||
dropped.append(d.group(0))
|
||||
continue
|
||||
keep_lines.append(ln)
|
||||
if not demote and not dropped:
|
||||
if not demote and not dropped and not aliased:
|
||||
return body, []
|
||||
|
||||
def _rename(txt):
|
||||
"""Point every USE at the alias; never touch the asm label (it names the real symbol)."""
|
||||
for _s, _a in aliased:
|
||||
txt = re.sub(rf'\b{re.escape(_s)}\b', _a, txt)
|
||||
txt = txt.replace(f'__asm__("{_a}")', f'__asm__("{_s}")')
|
||||
return txt
|
||||
|
||||
stripped = '\n'.join(keep_lines)
|
||||
if not demote: # drops only — no block to place
|
||||
return stripped, dropped
|
||||
return _rename(stripped), dropped
|
||||
at = _body_open_brace(stripped, func)
|
||||
if at is None: # can't place them safely -> leave the body alone
|
||||
return body, []
|
||||
block = ''.join(f' {ln}\n' for ln, _ in demote)
|
||||
return stripped[:at] + '\n' + block + stripped[at:], [s for _, s in demote] + dropped
|
||||
|
||||
# AUTO-ALIAS A DEMOTED EXTERN THE TU DECLARES WITH A DIFFERENT TYPE (P30 S47).
|
||||
# Demoting to block scope does NOT avoid a conflict: C requires a block-scope `extern` to agree
|
||||
# with any file-scope declaration of the same object in the TU, wherever it sits. And for DATA
|
||||
# the type is not cosmetic — it drives the load: `s16` vs `u16` is `lh` vs `lhu`. The fleet
|
||||
# genuinely reads one address at several widths (§16 loose typing): D_80078EB4 is `s16` at 2,409
|
||||
# sites and `u16` at 1,341, D_800AE620 is `Blk20`/`s32`/`Mat32`. Canonicalising would rewrite
|
||||
# thousands of BANKED sites' codegen, so the answer is not one type — it is one type PER VIEW.
|
||||
# That is the §37 asm-label alias, and the fleet already hand-writes it for exactly this symbol:
|
||||
# extern Mtx8_8017C910_8017C910 aD800AE620 __asm__("D_800AE620"); (9 sites)
|
||||
# Emitting it automatically keeps the draft's own type — byte-truth for THIS body — while the
|
||||
# private C name makes a collision impossible. Codegen is unchanged: the asm label fixes the
|
||||
# emitted symbol, so the same load reaches the same address.
|
||||
# Scoped deliberately: only fires where the TU actually declares the symbol with a DIFFERENT
|
||||
# type text, so a member that never had a conflict is untouched.
|
||||
block_lines = []
|
||||
for ln, sym in demote:
|
||||
mine, theirs = _decl_type_text(ln, sym), tu_types.get(sym)
|
||||
if mine and theirs and _norm_ws(mine) != _norm_ws(theirs):
|
||||
alias = f'aD{sym[2:]}'
|
||||
block_lines.append(re.sub(rf'\b{re.escape(sym)}\b', alias, ln, count=1)
|
||||
.rstrip().rstrip(';') + f' __asm__("{sym}");')
|
||||
aliased.append((sym, alias))
|
||||
else:
|
||||
block_lines.append(ln)
|
||||
block = ''.join(f' {b}\n' for b in block_lines)
|
||||
return _rename(stripped[:at] + '\n' + block + stripped[at:]), \
|
||||
[s for _, s in demote] + dropped
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
Reference in New Issue
Block a user