diff --git a/src/ov_SC01_077/ov_SC01_077_after.c b/src/ov_SC01_077/ov_SC01_077_after.c index 34eca21315..94a4a1c7f9 100644 --- a/src/ov_SC01_077/ov_SC01_077_after.c +++ b/src/ov_SC01_077/ov_SC01_077_after.c @@ -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) */ diff --git a/src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c b/src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c index 1d87ef5871..ac84bb7ef6 100644 --- a/src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c +++ b/src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c @@ -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) */ diff --git a/src/ov_SC01_077/ov_SC01_077_jr_8015444C.c b/src/ov_SC01_077/ov_SC01_077_jr_8015444C.c index 49b0105acc..e760ba9307 100644 --- a/src/ov_SC01_077/ov_SC01_077_jr_8015444C.c +++ b/src/ov_SC01_077/ov_SC01_077_jr_8015444C.c @@ -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) */ diff --git a/src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c b/src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c index e51eab062d..b1d3822da5 100644 --- a/src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c +++ b/src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c @@ -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.