mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-10-01 15:43:06 -04:00
feat(phase-30 UC): wave-1 first 5 banked x138 heads (24,702 templatable ins)
func_8016706C func_80131A34 func_80161A90 func_801549F8 func_8014C4AC — each match_one-verified
by me before gating (R14), then whole-binary byte-gated. func_8014C4AC is the standout: the agent
resolved its 'conflicting types' with the §37/§124 ASM-LABEL ALIAS (s32 aF8014C4AC(...) __asm__
("func_8014C4AC")) instead of a header edit — exactly the alternative to the fleet-sed that failed
earlier today. 9 remain blocked on declaration conflicts.
This commit is contained in:
@@ -1582,7 +1582,108 @@ DEFINE_func_8014C3D0() /* dedup: shared engine-core @0x8014C3D0 (src/shared) */
|
||||
|
||||
DEFINE_func_8014C43C() /* dedup: shared engine-core @0x8014C43C (src/shared) */
|
||||
|
||||
INCLUDE_ASM("asm/ov_SC01_077/nonmatchings/ov_SC01_077_after", func_8014C4AC);
|
||||
/* func_8014C4AC @0x8014C4AC — 47 ins, reach x138 (134 *_after.c + 4 *_jr_80140608.c)
|
||||
*
|
||||
* STATUS: match_one MATCH (47/47) — and MATCH *with the fleet-canonical declaration present in the
|
||||
* same TU*, which is what the previous draft could not do.
|
||||
*
|
||||
* ==== INTEGRATION — SOLVED T0, NO HEADER EDIT (cookbook §37/§73/§124 asm-label alias) ===========
|
||||
* The blocker was the §73 RETURN axis: the byte-true def MUST return a value ($v0 is written on every
|
||||
* path — `addu $v0,$zero,$zero` x3 and `addu $v0,$t0,$zero`, and the final `sb $v1,0xC8($v0)` is based
|
||||
* on the RETURN pseudo), but `src/shared/engine_core.h` declares
|
||||
* extern void func_8014C4AC(s32 a0, s32 a1, s32 a2, s16 *a3, s32 a4);
|
||||
* inside 8 DEFINE_func_* macros (8014A380 / 8015E714 / 8014A1B0 / 8014C43C / 8015E5B0 / 8015DD74 /
|
||||
* 8014A850 / 801571C4). FOUR of them are instantiated at FILE scope in ov_SC01_077_after.c itself
|
||||
* (lines 1254 / 1262 / 1368 / 1583) — all ABOVE the INCLUDE_ASM stub at 1585 — so the def collides:
|
||||
* `conflicting types for func_8014C4AC`.
|
||||
*
|
||||
* MEASURED, not assumed (probes kept in .run/uc/):
|
||||
* - p_void_naive.c : conform the def to `void` -> compiles CLEAN (so the PARAM axis is already
|
||||
* canonical — the conflict is purely the return type) but 45 ins vs 47, 13
|
||||
* mismatched, class LENGTH-DRIFT. The two `addu $v0,$zero,$zero` delay-slot
|
||||
* copies, the `addu $v0,$t0,$zero` and the `j .L8014C560` all vanish. A void
|
||||
* def CANNOT carry this body.
|
||||
* - p_v1.c : void + `register s32 rv __asm__("$2")` used as the base of the last store —
|
||||
* byte-identical to the naive void output; the pin is a preference and gcc
|
||||
* coalesced rv into $t0 (§72), and the dead `rv = 0` sets are DCE'd.
|
||||
* - p_v2.c : ...+ `volatile` on the pinned reg — forces a stack frame (`addiu $sp,-8`),
|
||||
* 47 ins / 45 mismatched. Worse.
|
||||
* => the RETURN axis here is NOT dissolvable from the def side by casts or by pins.
|
||||
*
|
||||
* THE FIX (zero blast radius, T0, travels to all 138 siblings): the §37/§73 ASM-LABEL ALIAS — the same
|
||||
* form already banked for func_8016191C x137 and explicitly supported by family_remap's
|
||||
* `_alias_decl_for` (§124). The definition takes a DIFFERENT C identifier — so it never binds against
|
||||
* the canonical `extern void func_8014C4AC` — and a GNU asm label binds the emitted SYMBOL:
|
||||
*
|
||||
* s32 aF8014C4AC(...) __asm__("func_8014C4AC");
|
||||
*
|
||||
* Verified: `nm t.o` => `00000000 T func_8014C4AC` (global, correct name; no `aF8014C4AC` symbol).
|
||||
* Verified compilable ALONGSIDE the conflict: .run/uc/p_alias4.c carries FOUR copies of the canonical
|
||||
* `extern void func_8014C4AC(...)` AND a caller that calls it through that prototype, above the def —
|
||||
* MATCH (47 ins). Verified sweep-readable: `family_remap._alias_decl_for` resolves this file to
|
||||
* ('aF8014C4AC', line 2) and `_def_head_at` accepts the K&R head, so the ×138 propagation path is the
|
||||
* standard one. §30 #2's fleet `void`->`s32` widen of engine_core.h is therefore NOT needed and is
|
||||
* NOT proposed (it would be a T2/R22 edit; this is T0). Per §124: "Fix the reader, not the source."
|
||||
* ================================================================================================
|
||||
*
|
||||
* Body levers (unchanged from the isolation-MATCH draft — all C-level, no pins, no permuter):
|
||||
* 1. §48-C2 block copy: the lwl/lwr+swl/swr 8-byte move == a plain struct assign of a 2-byte-aligned
|
||||
* 4x u16 struct. BLOCK scope so it cannot collide with engine_types.h (§100/§102, rule D).
|
||||
* 2. THE CRACK (2 -> 0): NO local pointer variable — use the s32 param with a cast at every use. A
|
||||
* local `u8 *p = (u8 *)a0;` makes gcc coalesce the a0 PARM copy away and emit the body insn AFTER
|
||||
* the a4 stack-parm CONVERSION insn that assign_parms appends via `conversion_insns` — giving
|
||||
* `lhu $v1,0x10($sp)` BEFORE `addu $t0,$a0,$zero`, i.e. the first two instructions swapped. Not a
|
||||
* scheduling residual — assign_parms emission order.
|
||||
* 3. `u16 a4` in a K&R parameter list (§43/§99): gives the narrow `lhu $v1,0x10($sp)` stack read while
|
||||
* default-promoting to int, so it stays compatible with the ambient `s32 a4` prototype.
|
||||
* 4. Body inside the positive `||` arm with the bare `return 0;` LAST: that is what puts the shared
|
||||
* zero-return block AFTER the body (.L8014C55C) so reorg can copy `addu $v0,$zero,$zero` into the
|
||||
* two branch delay slots and retarget them to .L8014C560. The inverted `&&` + early-`return 0`
|
||||
* form puts the zero block BEFORE the body -> closeness 2.
|
||||
* 5. Global types: `lw` D_80126CD0 => s32; `lhu`-then-`sb` D_8012693A / D_801152B8 => u16 (identical
|
||||
* to engine_core.h's own macro-local decls at 19666/19667, so no conflict either way). Declared
|
||||
* at BLOCK scope so the unit carries no file-scope decl into any of the 138 sibling TUs.
|
||||
*/
|
||||
#include "common.h"
|
||||
|
||||
/* §37/§73/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h, inside 8
|
||||
* DEFINE_func_* macros); the byte-true body must return s32. Aliasing the C name sidesteps the
|
||||
* RETURN-axis conflict with no header edit. This line MUST travel with the body. */
|
||||
s32 aF8014C4AC(s32 a0, s32 a1, s32 a2, s16 *a3, s32 a4) __asm__("func_8014C4AC");
|
||||
|
||||
s32 aF8014C4AC(a0, a1, a2, a3, a4)
|
||||
s32 a0;
|
||||
s32 a1;
|
||||
s32 a2;
|
||||
s16 *a3;
|
||||
u16 a4;
|
||||
{
|
||||
typedef struct { u16 a, b, c, d; } V4U; /* block-scope: no file-scope conflict (§100) */
|
||||
extern s32 D_80126CD0;
|
||||
extern u16 D_8012693A;
|
||||
extern u16 D_801152B8;
|
||||
u16 t;
|
||||
|
||||
if (a0 == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (*(u16 *)a0 == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (a1 == 9 || a1 == 0x11 || a1 == 0x29 || a1 == 0xA || D_80126CD0 != a0) {
|
||||
t = *(u16 *)(a0 + 0x5C);
|
||||
*(u16 *)(a0 + 0x5E) = a1;
|
||||
*(u16 *)(a0 + 0x62) = a4;
|
||||
*(u16 *)(a0 + 0x60) = a2;
|
||||
*(u16 *)(a0 + 0x5C) = t | 1;
|
||||
*(V4U *)(a0 + 0x7C) = *(V4U *)a3;
|
||||
*(u8 *)(a0 + 0xC9) = D_8012693A;
|
||||
*(u8 *)(a0 + 0xC8) = D_801152B8;
|
||||
return a0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
DEFINE_func_8014C568() /* dedup: shared engine-core @0x8014C568 (src/shared) */
|
||||
|
||||
|
||||
@@ -2044,7 +2044,41 @@ void func_80131340(s32 a0)
|
||||
DEFINE_func_801319E0() /* dedup: shared engine-core @0x801319E0 (src/shared) */
|
||||
|
||||
|
||||
INCLUDE_ASM("asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_8012ACE0", func_80131A34);
|
||||
/* func_80131A34 — MATCH (37/37).
|
||||
* Keys:
|
||||
* - func_80131CF4 is the TU's 2-arg canonical `(int,int)` and the SECOND arg is the
|
||||
* incoming `a1` (kind). $a1 already holds it, so the call costs no arg-setup insn --
|
||||
* but the extra arg ref is what breaks the sched2 prologue save-order tie so
|
||||
* `sw $s0` schedules before `sw $ra` (cookbook §52a RC-3 / sched.md S7). Without it
|
||||
* the pair comes out ra-then-s0 (2 mismatches); the `const`-load form of RC-3 fixes
|
||||
* the tie but OVER-frees (the `lw $a0` hoists above both saves, 3 mismatches).
|
||||
* - Declaration is BLOCK-SCOPE on purpose, mirroring func_801312D0 (TU L1619) and
|
||||
* func_80131340 (TU L1822): the later DEFINE_func_80131AC8() re-declares
|
||||
* func_80131CF4 with the stale 1-arg engine_core canonical, so a file-scope 2-arg
|
||||
* decl here is a hard `conflicting types` (cc1 rc=33).
|
||||
* - func_8012A828 is the TU's file-scope canonical (L280) `(s32, void *)`; `p` rides in
|
||||
* $a1 from the return of func_80131CF4, so no move is emitted.
|
||||
*/
|
||||
extern void func_8012A828(s32 a0, void *a1);
|
||||
|
||||
s32 func_80131A34(s32 a0, s32 a1)
|
||||
{
|
||||
extern int func_80131CF4(int, int);
|
||||
void *p;
|
||||
|
||||
p = (void *)func_80131CF4(*(s32 *)(a0 + 0xBC), a1);
|
||||
if (p != 0) {
|
||||
if (a1 == 0xB || a1 == 8 || a1 == 0x20) {
|
||||
*(s32 *)(a0 + 0xC4) |= 4;
|
||||
} else {
|
||||
*(s32 *)(a0 + 0xC4) &= -5;
|
||||
}
|
||||
func_8012A828(a0, p);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
DEFINE_func_80131AC8() /* dedup: shared engine-core @0x80131AC8 (src/shared) */
|
||||
|
||||
|
||||
@@ -1209,7 +1209,77 @@ void func_8015444C(void *a0v, s32 *arg1x, s32 *arg2x, s32 *arg3x) {
|
||||
}
|
||||
|
||||
|
||||
INCLUDE_ASM("asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_8015444C", func_801549F8);
|
||||
/*
|
||||
* func_801549F8 — scan param->b4[] (s32[], length = the u8 at param->bc),
|
||||
* stepping the index by 1 or 2 per entry via the 27-entry jump table
|
||||
* jtbl_801D89A4; count the default/out-of-range entries. Returns (u8) count.
|
||||
*
|
||||
* REDRAFT NOTE (the decay fix, 2 levers — both required, both source-level):
|
||||
*
|
||||
* 1. `u8 n` (NOT `s32 n`) — THE fix for the stored draft's 3-off residual.
|
||||
* The stored draft's whole residual was the position of the loop-bound
|
||||
* keep-alive copy `addu $a3,$v0,$zero` among the three preheader insns:
|
||||
* draft emitted [lw base][copy][lui/addiu jtbl], target wants
|
||||
* [lw base][lui/addiu jtbl][copy]. The loop-invariant jtbl address is
|
||||
* moved by loop.c and lands AFTER every preheader source statement, so as
|
||||
* long as the copy is an ordinary preheader statement it can never follow
|
||||
* the hoist. Making `n` a u8 changes the RTL for `n = *(u8*)(p+0xBC)`:
|
||||
* cse1 no longer converts it into a plain reg-reg copy in place, and the
|
||||
* copy that ultimately feeds the exit test is emitted at loop_start AFTER
|
||||
* move_movables (post-loop pass), i.e. after the hoisted lui/addiu — the
|
||||
* target order. Nothing else in the function changes.
|
||||
*
|
||||
* 2. The `j = i` / `i = j + k` index form BLOCKS strength reduction, which
|
||||
* is what the stored draft's `register s32 i __asm__("$3")` pin was for.
|
||||
* Unblocked, -O2 turns base[i] into a walking pointer (2nd biv, 33 ins);
|
||||
* the target keeps the INDEX form (sll $v0,$v1,2; addu $v0,$v0,$a0; lw).
|
||||
* `j` is a mult=1/add=0 giv of the biv `i`, so SR declines and i/j get
|
||||
* coalesced into $v1 — no extra insn. This replaces the hard-register
|
||||
* pin, so the match is pin-free and safe to travel to sibling overlays.
|
||||
* (The pinned form — keep `register s32 i __asm__("$3")` + `i += 1/2` and
|
||||
* just change `n` to u8 — also MATCHes, 31 ins, if a pin is ever wanted.)
|
||||
*
|
||||
* OPT LEVEL: plain -O2. The unfilled load-delay nops are this psx gcc's
|
||||
* default, not an -O1 tell.
|
||||
*
|
||||
* VERIFIED: python3 tools/match_one.py func_801549F8 --c .run/uc/func_801549F8.c
|
||||
* --asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_8015444C
|
||||
* -> MATCH (31 ins)
|
||||
*/
|
||||
s32 func_801549F8(s32 param_1, s32 _arg1, s32 _arg2)
|
||||
{
|
||||
s32 i, j;
|
||||
u8 n;
|
||||
u8 count;
|
||||
s32 *base;
|
||||
|
||||
count = 0;
|
||||
i = 0;
|
||||
if (*(u8 *)(param_1 + 0xBC) != 0) {
|
||||
base = *(s32 **)(param_1 + 0xB4);
|
||||
n = *(u8 *)(param_1 + 0xBC);
|
||||
do {
|
||||
j = i;
|
||||
switch (base[j]) {
|
||||
case 0: case 2: case 7: case 8: case 9: case 10:
|
||||
case 0xd: case 0x10: case 0x11: case 0x12: case 0x13:
|
||||
case 0x14: case 0x19: case 0x1a:
|
||||
i = j + 1;
|
||||
break;
|
||||
case 3: case 4: case 5: case 6: case 0xb: case 0xc:
|
||||
case 0xe: case 0xf: case 0x15: case 0x16:
|
||||
i = j + 2;
|
||||
break;
|
||||
default:
|
||||
i = j + 1;
|
||||
count += 1;
|
||||
break;
|
||||
}
|
||||
} while (i != n);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
DEFINE_func_80154A74() /* dedup: shared engine-core @0x80154A74 (src/shared) */
|
||||
|
||||
|
||||
@@ -3723,7 +3723,44 @@ DEFINE_func_80161A30() /* dedup: shared engine-core @0x80161A30 (src/shared) */
|
||||
|
||||
DEFINE_func_80161A60() /* dedup: shared engine-core @0x80161A60 (src/shared) */
|
||||
|
||||
INCLUDE_ASM("asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_8015C32C", func_80161A90);
|
||||
extern void func_8014AC10();
|
||||
extern u8 D_80078E78[];
|
||||
extern u8 D_80078EC0;
|
||||
|
||||
void func_80161A90(s32 a0)
|
||||
{
|
||||
u8 *p = D_80078E78;
|
||||
s32 t;
|
||||
|
||||
/* INVERTED diamond: the `t = 0` arm must be the THEN arm.
|
||||
* (a) the balanced if/else puts `t = 0` AFTER the branch at regalloc time, so t
|
||||
* does not conflict with the entry `lh` temp and both land in $v0 (an
|
||||
* unconditional `s32 t = 0;` before the if costs $v0 -> $a1, 3 mismatches);
|
||||
* (b) with the zero-arm as the THEN arm, reorg steals it into the beqz delay slot
|
||||
* and relax_delay_slots drops the `j` -> 34 ins. The other polarity
|
||||
* (`if (x != 0) t = cmp; else t = 0;`) leaves the `j` + an unfilled slot, +2. */
|
||||
if (*(s16 *)(a0 + 0x1C8) == 0) {
|
||||
t = 0;
|
||||
} else {
|
||||
t = ((D_80078EC0 & 0x7F) == 6);
|
||||
}
|
||||
if (t != 0) {
|
||||
/* forces the `lhu 0x1C8($a0)` reload: without it cse reuses the entry `lh`
|
||||
* value across the join and folds the reload away. */
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
*(u16 *)(a0 + 0x1C8) -= 1;
|
||||
/* zero-byte 2nd set of p: kills p's qty-const in cse's skipped-block walk, so
|
||||
* `p[0x48]` stays `lbu 0x48($v1)` off the hoisted lui/addiu instead of being
|
||||
* folded back into a fresh %hi/%lo pair (cse_expr §H find_best_addr). */
|
||||
__asm__("" : "=r"(p) : "0"(p));
|
||||
}
|
||||
if ((p[0x48] & 0x7F) == 6) {
|
||||
if (*(s16 *)(a0 + 0x1C8) == 0) {
|
||||
func_8014AC10(0x3B);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DEFINE_func_80161B18() /* dedup: shared engine-core @0x80161B18 (src/shared) */
|
||||
|
||||
@@ -5443,7 +5480,25 @@ void func_80166F58(s32 param_1, s32 param_2, s32 param_3, s32 param_4)
|
||||
}
|
||||
|
||||
|
||||
INCLUDE_ASM("asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_8015C32C", func_8016706C);
|
||||
extern s32 D_8011D030;
|
||||
extern void func_80146C3C(void);
|
||||
|
||||
void func_8016706C(s32 param_1)
|
||||
{
|
||||
unsigned short *puVar1;
|
||||
short iVar2;
|
||||
|
||||
iVar2 = 0;
|
||||
puVar1 = ((unsigned short *)&D_8011D030);
|
||||
do {
|
||||
if ((unsigned int)*puVar1 == ((short)param_1)) {
|
||||
((void(*)(unsigned short *))func_80146C3C)(puVar1);
|
||||
}
|
||||
iVar2 = iVar2 + 1;
|
||||
puVar1 = puVar1 + 0x2c;
|
||||
} while (iVar2 < 0x1e);
|
||||
}
|
||||
|
||||
|
||||
// @class: schedule
|
||||
// @stuck: 16/279 masked. 3 runs, ONE residual class: gcc's list-scheduler puts `la $s2` (p=DATA) + `addu $s4,$zero,$zero` (i=0) BEFORE the callee-arg address setup (addiu $a1,$sp,0x10 / addu $a2,$a1,$zero); the target emits them AFTER. Inert to ~40 statement-order permutations + pin/barrier combos (sched priority dominates the LUID tie-break, sched.c rank_for_schedule). Runs: 19-24 (blk1), 111-114 (region-B cx-load rotation), 177-182 (blk3). Permuter fuel.
|
||||
|
||||
Reference in New Issue
Block a user