feat(phase-30 S48-T6): wave 4 tail — 9 more banked once the tree was clean

The 17 drafts that read 0/17 against the poisoned tree were never 17 codegen
failures. Re-gated on a clean tree: 9 banked, then re-gated TOGETHER per binary
(a draft that passes alone can still collide with a sibling in the same TU —
which is exactly what `morph_lerp` and `struct V8` did) — 9 of 9 held.

  ov_SC03_118 func_80183D68 · ov_SC06_008 func_8017F8CC func_80183BF4
  ov_SC06_011 func_8017EDA4 · ov_SC07_006 func_8017FDF8
  ov_SC06_018 func_80184370 func_80184944 func_8018A974 func_8018EDB8

Wave 4 final: 40 targets -> 35 agent-MATCH -> 27 BANKED (68%).

One stray comment in one draft had masked eight real matches.
This commit is contained in:
Drew T
2026-08-12 12:49:39 -06:00
parent 043532b475
commit 63dd763b9e
6 changed files with 885 additions and 9 deletions
+152 -1
View File
@@ -3981,7 +3981,158 @@ void func_80183C58(void *a0) {
INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_jr_8017FB84", func_80183C94);
INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_jr_8017FB84", func_80183D68);
#include "common.h"
/* ---- integration surface: every decl below AGREES with the visible file-scope decl in
* src/ov_SC03_118/ov_SC03_118_jr_8017FB84.c (the destination TU), lines noted. ---- */
extern s32 func_8012BD14(void); /* not in TU */
extern s32 func_80029178(s32 arg); /* TU:65 */
extern void func_801746DC(void); /* TU:1253 */
extern s32 func_80178B18(s32 param_1, s32 param_2); /* TU:2538 */
extern s32 func_801789AC(s32 arg0); /* TU:2535 */
extern void func_80178D18(void); /* TU:2545 -- called w/ arg via cast */
extern s32 func_8014CB58(void); /* TU:561 */
extern void func_8014B598(s32 a0, s32 a1); /* TU:472 */
extern s32 func_8016F1AC(void); /* TU:351 */
extern void func_80175454(void); /* TU:1105 */
extern void func_800D0C48(s32 a0); /* TU:1469 */
extern s32 func_8014CB2C(void); /* TU:560 */
extern void func_8012A018(s32 a, s32 b); /* TU:2412 */
extern void func_801842DC(s32 a0); /* TU:4023 INCLUDE_ASM (no decl) */
extern s32 func_80183830(void); /* TU:3930 INCLUDE_ASM (no decl) */
extern void func_80029124(s32, s32); /* TU:50 */
extern s32 func_8016F0AC(); /* TU:1945 */
extern void func_8012C588(s32 a0, s32 a1); /* not in TU */
extern void func_8002959C(void); /* not in TU */
extern void func_8018416C(s32 param_1); /* TU:3992 def (later) */
extern void func_801841C0(s32 param_1); /* TU:4008 def (later) */
extern void func_80184430(void *a0); /* TU:4030 def (later) */
extern s32 D_801151D4; /* TU:323 */
extern M2C_UNK D_8018DA14; /* TU:2712 */
extern u8 D_8018EBE8[];
extern u8 D_8018EBF8[];
extern u8 D_8018EBD8[];
extern u8 D_8018EBC8[];
extern u8 D_80126948[]; /* TU:667 */
extern s16 D_801D4348;
extern s16 D_801D434A;
extern s16 D_801D434C;
extern s16 D_801D4350;
extern s16 D_801D4352;
extern s16 D_801D4354;
extern s32 D_801D4358;
extern s32 D_801D435C;
void func_80183D68(s32 a0) {
s32 p;
u16 t34;
if (func_8012BD14() < 0x401 || *(u16 *)(a0 + 0x34) == 1 || *(u16 *)(a0 + 0x34) == 2) {
switch (*(u16 *)(a0 + 0x34)) {
case 0:
if ((func_80029178(0x8B) & 0xFF) == 0) {
func_801746DC();
func_80178B18(a0, (s32)D_8018EBE8);
*(u16 *)(a0 + 0x34) = 2;
} else {
func_801746DC();
func_80178B18(a0, (s32)D_8018EBF8);
*(u16 *)(a0 + 0x34) = *(u16 *)(a0 + 0x34) + 1;
}
break;
case 1:
if (func_801789AC(a0) == 1) {
/* file-scope decl in the host TU is `void func_80178D18(void)` but the call
site passes a0 -- call through a cast so the decl stays byte-compatible. */
((void (*)(s32))func_80178D18)(a0);
*(u16 *)(a0 + 0x34) = 3;
}
break;
case 2:
if (func_801789AC(a0) == 1) {
((void (*)(s32))func_80178D18)(a0);
*(u16 *)(a0 + 0x34) = 4;
}
break;
case 3:
if ((func_80029178(0xB8) & 0xFF) == 0 && func_8014CB58() == 3) {
func_8014B598(3, (s32)&D_8018DA14);
if (func_8016F1AC() != 0 && (*(u16 *)(a0 + 0x100) & 4) == 0) {
*(u16 *)(a0 + 0x100) |= 4;
*(s32 *)(a0 + 0x1C) = 0;
func_80175454();
}
if (*(u16 *)(a0 + 0x100) & 4) {
*(s32 *)(a0 + 0x1C) = *(s32 *)(a0 + 0x1C) + 1;
}
if ((*(u16 *)(a0 + 0x100) & 2) == 0 && *(s32 *)(a0 + 0x1C) == 0x5A) {
*(u16 *)(a0 + 0x100) |= 2;
func_800D0C48(0);
}
if ((*(u16 *)(a0 + 0x100) & 1) == 0 &&
(func_8014CB2C() & 0x8000007F) == 0x80000003) {
p = D_801151D4;
t34 = *(u16 *)(a0 + 0x34) + 1;
*(u16 *)(a0 + 0x100) |= 1;
*(u16 *)(a0 + 0x34) = t34;
D_801D4348 = *(s32 *)(p + 0x48);
D_801D434A = *(s32 *)(p + 0x4C);
D_801D434C = *(s32 *)(p + 0x50);
D_801D4350 = *(s32 *)(p + 0x3C);
D_801D4352 = *(s32 *)(p + 0x40);
D_801D4358 = (s32)D_8018EBD8;
D_801D4354 = *(s32 *)(p + 0x44);
D_801D435C = (s32)&D_8018EBD8[8];
func_8012A018((s32)func_80184430, 0);
func_801842DC(p);
*(s32 *)(a0 + 0x1C) = 0;
}
}
break;
}
if ((func_80029178(0x8B) & 0xFF) == 0) {
return;
}
/* func_801841C0(a0) is written out on all three paths: gcc cross-jumps the common
tail but leaves the three $a0 arg-setups, which then fill the three branch delay
slots (a single shared call puts the move in the jal's own slot instead). */
if (func_8014CB58() == 3) {
func_8018416C(a0);
func_801841C0(a0);
} else {
*(u8 *)(*(s32 *)(a0 + 0xCC) + 0x27) = 0x88;
func_801841C0(a0);
}
} else {
if ((func_80029178(0x8B) & 0xFF) == 0) {
return;
}
*(u8 *)(*(s32 *)(a0 + 0xCC) + 0x27) = 0x88;
func_801841C0(a0);
}
if (*(u16 *)(a0 + 0x100) & 1) {
*(s32 *)(a0 + 0x1C) = *(s32 *)(a0 + 0x1C) + 1;
if (*(s32 *)(a0 + 0x1C) == 0x78) {
D_801D4358 = (s32)D_8018EBC8;
D_801D435C = (s32)&D_8018EBC8[8];
func_8012A018((s32)func_80184430, 0);
func_801842DC((s32)D_80126948);
}
if (func_80183830() != 0) {
func_80029124(0xB8, 1);
*(u16 *)(a0 + 0x100) &= ~1;
func_8016F0AC();
func_8012C588(0x249, a0);
*(u16 *)(a0 + 0x2) = 2;
*(u8 *)(*(s32 *)(a0 + 0xCC) + 0x27) = 0x88;
*(u8 *)(*(s32 *)(a0 + 0xD0) + 0x27) = 0x89;
func_8002959C();
}
}
}
void func_80184164(void) {
}
+135 -2
View File
@@ -4192,7 +4192,83 @@ void func_8017F624(s32 param_1)
}
INCLUDE_ASM("asm/ov_SC06_008/nonmatchings/ov_SC06_008_jr_8017C294", func_8017F8CC);
extern s16 D_8019C91C[];
extern u16 D_80126B5E;
extern u16 D_80126B62;
extern u16 D_80126B66;
void func_8017F8CC(s32 a0)
{
switch (*(u16 *)(a0 + 0x34)) {
case 0:
{
s16 idx = *(s16 *)(a0 + 0x70);
s16 *p = &D_8019C91C[(idx - 1) * 4];
s16 gx = *(s16 *)&D_80126B5E;
s16 gz;
s32 p20;
if (!(*p < gx)) {
break;
}
p++;
if (!(gx < *p)) {
break;
}
p++;
gz = *(s16 *)&D_80126B66;
if (!(*p < gz)) {
break;
}
if (!(gz < p[1])) {
break;
}
p20 = *(s32 *)(a0 + 0x20);
*(u32 *)(p20 + 4) = *(u32 *)(p20 + 4) & 0x7FFFFFFF;
{
s32 t;
*(s16 *)(a0 + 0x88) = D_80126B5E;
*(s16 *)(a0 + 0x6) = D_80126B5E;
t = D_80126B62 - 0x80;
*(s16 *)(a0 + 0x8A) = t;
t = *(u16 *)(a0 + 0x34);
{
register s16 z __asm__("$3");
t = t + 1;
z = D_80126B66;
*(u16 *)(a0 + 0x34) = t;
*(s16 *)(a0 + 0x8C) = z;
*(s16 *)(a0 + 0xE) = z;
}
}
break;
}
case 1:
{
s16 v;
v = *(u16 *)(a0 + 0xA) + 8;
*(u16 *)(a0 + 0xA) = v;
if (v < *(s16 *)(a0 + 0x8A)) {
break;
}
*(s16 *)(a0 + 0x2) = 1;
*(u16 *)(a0 + 0x34) = 0;
break;
}
}
}
INCLUDE_ASM("asm/ov_SC06_008/nonmatchings/ov_SC06_008_jr_8017C294", func_8017F9FC);
@@ -6444,7 +6520,64 @@ extern void func_8012B370(int a0);
}
INCLUDE_ASM("asm/ov_SC06_008/nonmatchings/ov_SC06_008_jr_8017C294", func_80183BF4);
#include "common.h"
extern void func_8012B23C(s32 a0);
extern void func_8012CBCC(s32 a0);
extern u8 *func_8012913C(s32 a0);
extern void func_8012C218(void *a0);
void func_80183BF4(s32 a0)
{
s32 s0 = a0;
s32 ret;
u16 val;
if (*(u16 *)(s0 + 2) == 0) {
*(u8 *)(s0 + 0xC0) = 1;
*(u16 *)(s0 + 2) = *(u16 *)(s0 + 2) + 1;
func_8012B23C(s0);
*(s32 *)(s0 + 0x1C) = 1;
return;
}
*(s32 *)(s0 + 0xDC) = *(s32 *)(s0 + 0x14);
ret = ((s32 (*)(s32))func_8012CBCC)(s0);
if (ret != 0 && (ret & 0x6000) != 0) {
s32 cnt = *(s32 *)(s0 + 0x1C) - 1;
*(s32 *)(s0 + 0x1C) = cnt;
if (cnt == 0) {
goto L80183CF4;
}
*(s32 *)(s0 + 0x14) = -*(s32 *)(s0 + 0xDC) >> 2;
}
val = *(u16 *)(s0 + 0xFC) + 1;
*(u16 *)(s0 + 0xFC) = val;
if ((val & 1) != 0) {
s32 np = (s32)func_8012913C(0x23);
if (np != 0) {
*(u16 *)(np + 6) = *(u16 *)(s0 + 6);
*(u16 *)(np + 0xA) = *(u16 *)(s0 + 0xA);
{
u16 t = *(u16 *)(s0 + 0xE);
*(u32 *)(np + 0x18) = 0;
*(u32 *)(np + 0x14) = 0;
*(u32 *)(np + 0x10) = 0;
*(u16 *)(np + 0x34) = 0x3000;
*(u16 *)(np + 0xE) = t;
}
}
}
if (*(s16 *)(s0 + 0xA) < 0x20) {
return;
}
L80183CF4:
func_8012C218((void *)s0);
}
extern void func_80019064(void *a0);
+45 -1
View File
@@ -3961,7 +3961,51 @@ void func_8017ED68(void *a0) {
}
INCLUDE_ASM("asm/ov_SC06_011/nonmatchings/ov_SC06_011_jr_8017BEBC", func_8017EDA4);
extern s32 func_8012C354(s32 a0, s32 a1);
extern void func_8001C214(s32 a0, s32 a1);
extern void func_8012A828(s32 *a0, s32 a1);
extern void func_8012B23C(void *a0);
extern u8 D_8019E204[];
extern s32 D_8019E238[];
extern u8 D_8018077C[];
void func_8017EDA4(void *a0) {
if (func_8012C354((s32)a0, (s32)D_8019E204) != 0) {
u16 v1 = *(u16 *)((s32)a0 + 0x70);
if (v1 & 0x10) {
u16 v0 = v1 & ~0x10;
*(u16 *)((s32)a0 + 0x70) = v0;
func_8001C214(*(s32 *)((s32)a0 + 0x20), D_8019E238[(s16)v0]);
*(s32 *)(*(s32 *)((s32)a0 + 0x20) + 0x4) |= 0x60000040;
} else {
func_8001C214(*(s32 *)((s32)a0 + 0x20), D_8019E238[(s16)v1]);
}
if (*(s16 *)((s32)a0 + 0x70) == 8 || *(s16 *)((s32)a0 + 0x70) == 0xA) {
*(s16 *)((s32)a0 + 0x34) = 3;
*(u16 *)((s32)a0 + 0x5C) = 0x8800;
}
if (*(s16 *)((s32)a0 + 0x70) == 9) {
*(s16 *)((s32)a0 + 0x34) = 2;
*(u16 *)((s32)a0 + 0x5C) = 0x8800;
}
if (*(s16 *)((s32)a0 + 0x70) == 0xB) {
*(s16 *)((s32)a0 + 0x34) = 4;
}
*(u8 *)((s32)a0 + 0xC0) = 1;
*(s32 *)((s32)a0 + 0xB4) = -1;
*(u8 *)((s32)a0 + 0x75) = 8;
func_8012A828((s32 *)a0, (s32)D_8018077C);
*(u16 *)((s32)a0 + 0x2) = *(u16 *)((s32)a0 + 0x2) + 1;
func_8012B23C(a0);
*(s16 *)((s32)a0 + 0xAE) = -1;
}
}
INCLUDE_ASM("asm/ov_SC06_011/nonmatchings/ov_SC06_011_jr_8017BEBC", func_8017EEEC);
+102 -2
View File
@@ -6800,7 +6800,53 @@ INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8017C24C", func_801841E
INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8017C24C", func_8018431C);
INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8017C24C", func_80184370);
#include "common.h"
extern s32 func_80187130(s32 a0, s32 a1);
extern u16 D_80126B96;
extern s32 func_8018765C(s32 a0);
extern u16 D_800B99DA;
extern s32 func_80143B6C(s32 a0, s32 a1);
extern void func_80189E60(s32 a0, s32 a1);
extern s32 func_8012BEE8();
void func_80184370(s32 p)
{
s32 e;
if (func_80187130(p, 0x64) == 1) {
D_80126B96 = 4;
}
*(s32 *)(p + 0x10) = *(s32 *)(p + 0x10) * 31 / 32;
*(s32 *)(p + 0x18) = *(s32 *)(p + 0x18) * 31 / 32;
func_8018765C(p);
if ((u32)D_800B99DA % 6 == 0) {
e = func_80143B6C(p, 0);
if (e != 0) {
*(s16 *)(*(s32 *)(e + 0xCC) + 0x18) = 0x5000;
*(s16 *)(*(s32 *)(e + 0xCC) + 0x1A) = 0x5000;
}
}
if (*(s32 *)(p + 0x94) == 8) {
func_80189E60(p, 0x8C0);
}
if (*(u16 *)(p + 0x34) == 0) {
if (*(s32 *)(p + 0x94) == 0xE) {
*(u16 *)(p + 0x98) = 0;
*(s32 *)(p + 0x1C) = 0x1E;
*(u16 *)(p + 0x34) += 1;
}
} else {
if (func_8012BEE8(p) != 0) {
*(s16 *)(p + 2) = 6;
}
}
}
INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8017C24C", func_801844C0);
@@ -6945,7 +6991,61 @@ void func_80184904(s32 param_1)
}
INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8017C24C", func_80184944);
#include "common.h"
extern s32 func_8012BCCC(s32 a0);
extern s32 func_8012B8E4(s32 a0, s32 a1);
extern s32 func_8012BEE8();
extern s32 func_8012B8A4(s16 *a0);
extern s32 func_8012B608(s32 a0, s32 a1, s32 a2);
extern void func_8012B178(s32 a0, s32 a1);
extern s32 func_8018765C(s32 a0);
extern void func_801877E4(s32 a0);
extern s32 rand(void);
void func_80184944(s32 p)
{
s32 v;
s32 r;
s32 t;
v = func_8012BCCC(p);
if (v > 0x144000) {
*(s16 *)(p + 2) = 0x13;
return;
}
if (v > 0xC4000) {
if ((*(u32 *)(p + 0xE0) & 0x40) == 0) {
r = func_8012B8E4(p, 8);
*(u16 *)(*(s32 *)(p + 0x20) + 0x12) += r;
}
} else {
if ((u32)(v - 0x24000) <= 0xA0000) {
*(s16 *)(p + 2) = 0x15;
return;
}
if ((*(u32 *)(p + 0xE0) & 0x40) == 0) {
if (func_8012BEE8(p) != 0) {
t = func_8012B8A4((s16 *)p);
*(u32 *)(p + 0xE8) = (t + 0x800) & 0xFFF;
r = rand();
*(s32 *)(p + 0x1C) = r % 0x40 + 0x40;
}
}
r = func_8012B608(*(s16 *)(*(s32 *)(p + 0x20) + 0x12), *(s32 *)(p + 0xE8), 8);
*(u16 *)(*(s32 *)(p + 0x20) + 0x12) += r;
}
func_8012B178(p, 0xFFF9C000);
*(u32 *)(p + 0xE0) &= 0xFFFFFFBF;
if (func_8018765C(p) == 0) {
func_801877E4(p);
}
}
INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8017C24C", func_80184AA8);
+292 -2
View File
@@ -4358,7 +4358,145 @@ INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_80187AEC", func_8018A7C
INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_80187AEC", func_8018A86C);
INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_80187AEC", func_8018A974);
/* func_8018A974 — allocates a semi-trans LineF2 GPU packet, projects two
* world-space points via RotTransPers, and (if both are on-screen with
* non-negative depth-clip flags) links the packet into the current
* double-buffer's OT at the first point's depth, then notifies via
* func_80016638.
*
* STEP 0 sibling search (§160g) — three already-MATCHED exemplars supplied
* the whole shape, none needed independent discovery:
*
* 1. src/shared/engine_core.h DEFINE_func_8012D3B4() — identical prologue
* (temp_v0=func_80010A08(0x10); word4=arg2; func_8004914C/func_800491AC
* (&D_800AF648); two RotTransPers calls with the exact
* `(d>0) && (flag>=0) && (RotTransPers(...)>0) && (flag>=0)` guard) but
* calls a real `SetLineF2()` (code 0x40, no semi-trans) and a real
* `AddPrim()` (single evaluation). Our target's asm has NEITHER a
* `jal SetLineF2` NOR a `jal AddPrim` — both are fully inlined, which
* is the key structural difference this draft encodes (code 0x42 =
* semi-trans LineF2 needs hand-set fields; AddPrim needs the classic
* macro body since a *variable* depth offset appears nowhere in the
* real AddPrim()-calling siblings).
*
* 2. src/ov_SC03_119/ov_SC03_119_jr_8017FB84.c func_80185944 — byte-MATCH
* (177/177 ins). Its header names this exact tail idiom: "link-ins
* the packet into the current double-buffer's OT (the PSY-Q `addPrim`
* macro pair) and calls func_80016638", with `func_80016638(&D_800A6518
* [*bidx*20], depth, 1)` as its literal call form (there depth=0x10
* constant, here depth=temp_v0_2). Confirms the "3 lhu D_800B9A02,
* address cached / value reloaded" shape and the general addPrim(ot,p)
* == setaddr(p,getaddr(ot)); setaddr(ot,p) reading.
*
* 3. src/ov_SC03_119/ov_SC03_119_jr_8017FB84.c func_801860E8 (same TU,
* just above func_80185944) — its own header documents **Lever B**,
* byte-measured: "THE OT TABLE MUST BE AN ARRAY_REF (`D_800A651C[i].a`),
* NOT `*(s32*)((u8*)&D_800A651C + i*20)`. With the pointer-arith
* spelling gcc allocates a phantom 8-byte stack temp that is never
* referenced... The temp only appears when the SAME lookup expression
* occurs in two statements; the ARRAY_REF form kills it." This was the
* load-bearing fix for this draft too — confirmed empirically here via
* the `cpp|cc1 … | grep '.frame'` pipeline (§162i's own diagnostic
* method): pointer-arith form gave `vars=16` (0x38 frame, +8 over
* target's 0x30); switching every `D_800A651C` reference to
* `D_800A651C[idx].a` ARRAY_REF form (using the `OtBlk` shape from
* engine_types.h:525, local-suffixed here since match_one compiles
* standalone) dropped it straight to `vars=8` (0x30, exact). This
* generalizes §162i1's "only a BLKmode local reserves frame space" law
* to a second, distinct anonymous-temp source (a raw-pointer-arith
* symbol expression repeated in 2 statements) — worth a cookbook
* addendum since §162i1 as written only covers dead-local pads.
*
* REGISTER-ALLOCATION LEVERS (found empirically, byte-verified against this
* function's own .frame/.s, not inherited from the exemplars above):
* - `bidx` pinned to $8 ($t0): natural (unpinned) allocation put the
* shared D_800B9A02-address pointer in $a3 instead, cascading a
* one-register shift through the whole mask/index register set.
* - `mask1` (0xFFFFFF) pinned to $7 ($a3); `tag0` (the packet's old tag
* word, read once before the first addPrim half) pinned to $4 ($a0) —
* both needed to reproduce the target's exact a2/a3/t0/t1 register
* picks for the addPrim RMW pair. `mask2` (0xFF000000) and `depth4`
* (temp_v0_2*4) are explicitly NOT pinned — pinning them re-introduced a
* 2-instruction schedule swap between the depth<<2 and the 0xFF000000
* `lui`; left as plain (named, for depth4) / literal (for mask2)
* locals, natural allocation lands them correctly.
* - `rgb` (arg2, the color word) pinned to $16 ($s0): unpinned, arg2 and
* the &D_800AF648 matrix address land in $s1/$s0 (swapped from target).
* `rgb`'s single SET made it a sched1 "birthing insn"
* (`birthing_insn_p`: `reg_n_sets==1`) — boosted to max priority in
* sched1's BACKWARD scan, which schedules a boosted insn late (cookbook
* "birthing-boost prologue-order" lever). A zero-byte non-volatile
* re-tie `__asm__("" : "=r"(rgb) : "0"(rgb));` placed immediately after
* `rgb = arg2;` gives it a 2nd SET (boost dead) with zero emitted code,
* and the 3-instruction prologue cluster (save $s0 / set $s0=arg2 / set
* $a0=0x10 for the alloc call) reorders to the target's exact sequence.
*
* INTEGRATION SURFACE (checked against destination TU
* src/ov_SC06_018/ov_SC06_018_jr_80187AEC.c):
* func_80010A08, func_8004914C, func_800491AC, RotTransPers, D_800AF648,
* D_800B9A02, D_800A6518 all match the TU's own existing extern spellings
* verbatim (grepped at TU lines 2448/2632-2633/4674 and the
* func_8018F694/func_8018F060 block). D_800A651C and func_80016638 have NO
* file-scope declaration anywhere in the TU (only ever appear inside other
* INCLUDE_ASM'd/unbanked functions) — declared here exactly as the
* func_801860E8/func_80185944 MATCHed precedent declares them: `OtBlk
* D_800A651C[]` (locally as `OtBlk_8018A974` — match_one compiles
* standalone without ../shared/engine_core.h; at bank time this collapses
* onto the TU's own already-visible `OtBlk` from engine_types.h:525,
* identical layout, a copy-edit not a fresh investigation) and
* `void func_80016638(void *a0, s32 a1, s32 a2)`.
*/
typedef struct { s32 a; s32 b[4]; } OtBlk_8018A974; /* == engine_types.h OtBlk (0x14) */
void func_8018A974(s32 arg0, s32 arg1, s32 arg2)
{
extern void *func_80010A08(s32);
extern void func_8004914C(void *);
extern void func_800491AC(void *);
extern s32 RotTransPers(s32, s32, s32 *, s32 *);
extern u8 D_800AF648;
extern OtBlk_8018A974 D_800A651C[];
extern u8 D_800A6518[];
extern short D_800B9A02;
extern void func_80016638(void *a0, s32 a1, s32 a2);
s32 sp10;
s32 sp14;
s32 temp_v0_2;
void *temp_v0;
s32 ot;
s32 depth4;
register u16 *bidx __asm__("$8");
register u32 mask1 __asm__("$7");
register s32 rgb __asm__("$16");
register u32 tag0 __asm__("$4");
rgb = arg2;
__asm__("" : "=r"(rgb) : "0"(rgb)); /* zero-byte 2nd SET: kills the sched1 birthing boost */
temp_v0 = func_80010A08(0x10);
*(u8 *)((u8 *)temp_v0 + 3) = 3;
*(s32 *)((u8 *)temp_v0 + 4) = rgb;
*(u8 *)((u8 *)temp_v0 + 7) = 0x42;
func_8004914C(&D_800AF648);
func_800491AC(&D_800AF648);
temp_v0_2 = RotTransPers(arg0, temp_v0 + 8, &sp10, &sp14);
if ((temp_v0_2 > 0) && (sp14 >= 0) &&
(RotTransPers(arg1, temp_v0 + 0xC, &sp10, &sp14) > 0) && (sp14 >= 0)) {
/* addPrim(otp, p) == setaddr(p, getaddr(otp)), setaddr(otp, p) */
mask1 = 0xFFFFFF;
bidx = (u16 *)&D_800B9A02;
depth4 = temp_v0_2 * 4;
tag0 = *(u32 *)temp_v0;
*(u32 *)temp_v0 = (tag0 & 0xFF000000) |
(*(u32 *)(depth4 + D_800A651C[*bidx].a) & mask1);
ot = D_800A651C[*bidx].a;
*(u32 *)(depth4 + ot) =
(*(u32 *)(depth4 + ot) & 0xFF000000) | ((u32)temp_v0 & mask1);
func_80016638(&D_800A6518[*bidx * 20], temp_v0_2, 1);
}
}
INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_80187AEC", func_8018AB00);
@@ -5757,7 +5895,159 @@ void func_8018E9BC(void *arg) {
}
INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_80187AEC", func_8018EDB8);
#include "common.h"
/* func_8018EDB8 — ov_SC06_018 / ov_SC06_018_jr_80187AEC
*
* Direct structural sibling of func_8018E188 (SAME TU, banked MATCH,
* src/ov_SC06_018/ov_SC06_018_jr_80187AEC.c:5255). Shares VERBATIM:
* - the 0x60 gate + 0x1D snapshot block,
* - the 0x78/0x60 decrement block (AND form — see E188's @stuck note),
* - the 0x82&1 finisher, the C8/C9 pokes,
* - the two 8-iteration spawn loops (0x281 / 0x23).
* Diverges: no leading func_8002D4C8 in the 0x76<0 arm, E188's CC/D0/D4
* particle reseed is replaced by a "hand off to the 0x64 owner" block
* (5C/60/5E/D8/76/62 pokes + func_8018D870), and the else arm gains a
* leading func_8002D4C8(0x9B7, 0).
*
* Regalloc target (identical to E188): p pinned $s1, e/spawn-ptr coalesce
* on $s0, loop counter $s2, the hoisted constant 2 on $s3.
*
* @class: regalloc-order
* @stuck: none — MATCH (170 ins), iteration 3. Body was E188 verbatim on
* iteration 1; both residuals were in the 12-insn 0x64 hand-off tail:
* (1) `-0xA` stored through a `u16 *` folds to the unsigned 0xFFF6 and emits
* `ori $v0,$zero,0xfff6`; the target's `addiu $v0,$zero,-0xA` needs the
* store spelled `*(s16 *)(q + 0x76)` (§162k-adjacent width law, applied
* to a CONSTANT rather than a load).
* (2) the tail pointer lives in TWO registers — $s0 (the `lw 0x20` base) and
* $a0 (every store base + the func_8018D870 arg). Spelling that as
* `register s32 q __asm__("$4"); q = iv;` alone is NOT enough: with the
* 0x20 load left BELOW the copy, local-alloc's `optimize_reg_copy_1`
* (§162j1) rewrites that surviving use $s0 -> $a0, and the now-$a0-based
* load can no longer be scheduled above the $a0-based stores (they may
* alias), costing two load-delay nops = the +2 LENGTH-DRIFT.
* LEVER: `optimize_reg_copy_1`'s substitution scan runs FORWARD from the copy
* only, so hoisting the surviving use ABOVE `q = iv` in SOURCE order puts it
* out of reach — `sv = *(s32 *)(iv + 0x20);` before the copy. It keeps $s0,
* and being source-first it also legally precedes the store block, which is
* what recovers the target's schedule (lw / sh / sh / lhu interleave). This
* is a second, zero-side-effect defeat for §162j1 that works where its
* in-place-SET lever cannot: the surviving use here is a LOAD, which has no
* way to also SET its own base register.
*/
extern s32 rand(void);
extern void func_8016AA50(s32, s32);
extern s32 func_8016B428(s32);
extern void func_80019064(void *);
extern void func_8002A520(int);
extern void func_8002A790(int);
extern void func_8002D4C8(s32, s32);
extern s32 func_8012C588(s32, s32);
extern u8 *func_8012913C(s32);
extern void func_8012C218(void *);
extern void func_8018D870(void *);
extern u8 D_801D1210;
void func_8018EDB8(void *arg) {
register u8 *p __asm__("$17"); /* $s1 */
s32 e;
s32 i;
s32 iv;
p = (u8 *)arg;
e = *(u8 *)(p + 0x5E);
if (*(s16 *)(p + 0x60) != 0) {
if (e == 0x1D) {
*(u16 *)(p + 0x82) = 0;
*(u16 *)(p + 0x7C) = *(u16 *)(p + 0x06);
*(u16 *)(p + 0x7E) = *(u16 *)(p + 0x0A);
*(u16 *)(p + 0x80) = *(u16 *)(p + 0x0E);
}
{
s32 dec;
s32 q = *(s32 *)(p + 0x78);
if (q != 0 && *(s16 *)(p + 0x60) != 0) {
dec = ((s32)*(s16 *)(p + 0x60) * (s32)*(s16 *)(q + 0x30)) >> 12;
if (dec < 1) dec = 1;
} else {
dec = *(s16 *)(p + 0x60);
}
*(u16 *)(p + 0x76) = *(u16 *)(p + 0x76) - dec;
((void (*)(void *, s32))func_8016AA50)(p, dec);
}
if (*(u16 *)(p + 0x82) & 1) {
((void (*)(void *))func_8016B428)(p);
func_80019064(&D_801D1210);
}
}
if (e != 0x1D) {
if (*(u8 *)(p + 0xC8)) func_8002A520(p);
if (*(u8 *)(p + 0xC9)) func_8002A790(p);
}
if (*(s16 *)(p + 0x76) < 0) {
i = 0;
do {
iv = ((s32 (*)(s32, void *))func_8012C588)(0x281, p);
if (iv != 0) {
*(s32 *)(iv + 0x1C) = 2;
*(u16 *)(iv + 0x12) = (rand() & 0x1F) - 0x10;
*(u16 *)(iv + 0x16) = -((rand() & 0x0F) + 0x10);
*(u16 *)(iv + 0x1A) = (rand() & 0x1F) - 0x10;
}
i++;
} while (i < 8);
i = 0;
do {
iv = (s32)func_8012913C(0x23);
if (iv != 0) {
s32 r;
s32 sv;
r = rand();
*(u16 *)(iv + 0x06) = *(u16 *)(p + 0x06) + (r & 0x3F) - 0x20;
r = rand();
*(u16 *)(iv + 0x0A) = *(u16 *)(p + 0x0A) - (r & 0x3F) - 0x20;
r = rand();
sv = *(u16 *)(p + 0x0E);
*(s32 *)(iv + 0x18) = 0;
*(s32 *)(iv + 0x14) = 0;
*(s32 *)(iv + 0x10) = 0;
*(u16 *)(iv + 0x0E) = sv + (r & 0x3F) - 0x20;
r = rand();
*(u16 *)(iv + 0x34) = (r & 0x17FF) + 0x1800;
}
i++;
} while (i < 8);
{
register s32 q __asm__("$4");
s32 sv;
iv = *(s32 *)(p + 0x64);
sv = *(s32 *)(iv + 0x20);
q = iv;
*(u16 *)(q + 0x5C) = 1;
*(u16 *)(q + 0x60) = 0;
sv = *(u16 *)(sv + 0x12);
*(u16 *)(q + 0x5E) = 0x1D;
*(s32 *)(q + 0xD8) = 0;
*(s16 *)(q + 0x76) = -0xA;
*(u16 *)(q + 0x62) = sv - 0x800;
func_8018D870((void *)q);
}
func_8012C218(p);
} else {
func_8002D4C8(0x9B7, 0);
*(u16 *)(p + 0x5C) = 0x8800;
*(u16 *)(p + 0x60) = 0;
*(u8 *)(p + 0xC1) = 0;
*(u8 *)(p + 0xC2) = 0x10;
}
}
// @class: none
// @stuck: none — MATCH (397/397 ins, match_one + rtu_match).
+159 -1
View File
@@ -4085,7 +4085,165 @@ INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006_jr_8017BEBC", func_8017F5D
INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006_jr_8017BEBC", func_8017F9AC);
INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006_jr_8017BEBC", func_8017FDF8);
#include "common.h"
/* ---------------------------------------------------------------------------
* func_8017FDF8 (ov_SC07_006 / jr_8017BEBC) — 317 ins, MATCH.
*
* A 5x-expanded 16-colour CLUT cross-fade: for each of five 16-entry 5:5:5
* palettes, lerp channel-wise between a "from" table and a "to" table by
* t = (x - 0x800) * 2 (clamped at 0), then hand the built CLUT to
* func_800599B8(tbl, clut). The alpha/STP bit is COPIED (not lerped) from
* whichever endpoint is dominant (t < 0x800 -> the "from" table).
*
* ===========================================================================
* INTEGRATION SURFACE (§161c) — this draft needs NO edit to the host TU.
* ===========================================================================
* Host TU: src/ov_SC07_006/ov_SC07_006_jr_8017BEBC.c, INCLUDE_ASM at line 4088.
*
* - func_800599B8 takes TWO args here, but the host carries a ONE-arg
* prototype `extern void func_800599B8(u16 *);` at FILE SCOPE, line 609,
* visible at the INCLUDE_ASM slot. This draft keeps that exact prototype
* (byte-identical text) so the decl AGREES with the host and is free, and
* defuses the arity clash with a call-site cast — the same idiom already
* used by matched code at src/ov_SC03_099/ov_SC03_099_jr_8013C98C.c:1439
* and src/ov_SC06_008/ov_SC06_008_jr_8013C98C.c:1439.
*
* BYTE-PROVEN, this wave, on the two alternatives that avoid the cast:
* (A) add `extern void func_800599B8();` beside host line 609
* -> CC1 FAIL, "too many arguments to function" x5. A non-prototype
* decl does NOT override an already-visible prototype; gcc-2.7.2
* forms the composite type and the prototype wins.
* (B) put `extern void func_800599B8();` at BLOCK scope inside
* func_8017FDF8, host line 609 untouched
* -> CC1 FAIL, identical errors. Block scope does not shadow a
* visible file-scope prototype for arity checking here.
* So the empty-paren form only works if host line 609 is REPLACED (not
* added beside). That variant is also byte-verified MATCH and is kept at
* probes/E_widen_decl.c if the cleaner source shape is preferred; it costs
* a one-line decl edit to the host (byte-neutral — the host has no other
* reference to func_800599B8, grep: line 609 is decl-only).
*
* - All fifteen data symbols declared below have ZERO occurrences in the host
* TU (verified by grep), so no type conflicts. `pal_lerp` likewise has 0
* occurrences (no clash with the host's existing `morph_lerp` inline).
* Names that DO appear elsewhere in src/ (D_801F60C0, D_8018DF60..80) are
* all in other overlays' TUs, never in this host.
*
* - src/shared/engine_core.h:27426 also declares `extern void
* func_800599B8(u16 *);` but inside a macro body, so it only reaches block
* scope at an expansion site — it does not add a file-scope decl here.
*
* ===========================================================================
* LOAD-BEARING SOURCE SHAPES (all independently re-verified this wave)
* ===========================================================================
* 1. D_801F60A0 MUST stay ONE array symbol indexed [0]/[16]/[32]/[48]/[64].
* cse's use_related_value is what emits the `addiu $s0, $s0, 0x20` that
* seeds each following block's loop pointer from the pointer just passed
* to func_800599B8. REFUTED this wave: five separate externs
* D_801F60A0/C0/E0/6100/6120 give 319 ins vs 317, 258 mismatched (the two
* extra instructions are the lost related-value adds).
*
* 2. The nr/ng/nb named temps are load-bearing — do NOT inline them back.
* All three `mult` result pseudos carry `pref LO_REG` and tie in
* allocno_compare, so the tie breaks on allocno number: the lowest-numbered
* product takes $lo, reload spills $lo, and retry_global_alloc re-places it
* on the LAST free register, shifting every loop-carried pseudo one register
* down. Writing the three channels into named temps re-orders sched1's
* output (not the final sched2 order), which re-orders reg_live_length and
* hence the allocno priorities, so the blue product keeps $lo and lands on
* $v1 while red/green take $t1/$t0. Alternatives, same 317 ins, all
* NON-matching: fully inlined 104 diffs; hoist dr,dg 35; hoist raw products
* pr,pg,pb 104; hoist unmasked sums sr,sg 114; hoist nr / nr,ng 104 / 114.
* Register pins are NOT the lever here (cf. §162j1) — this is a global.c
* priority/tie problem; statement granularity is the lever.
*
* 3. Branch polarity: the loop-invariant `slti $t7, $a3, 0x800` is LICM-hoisted
* and the target branches with `bnez` (on the TRUE arm). That requires the
* source to spell the condition INVERTED with swapped arms — `if (t >=
* 0x800) hi = *a...; else hi = *b...;`. The intuitive `if (t < 0x800)`
* with unswapped arms emits `beqz` and is a 5-instruction structural miss.
*
* FAMILY REACH x2: the second binary templates mechanically — identical shape,
* only the ten source-table symbols and the five func_800599B8 arg tables
* change. Note for the remap: constraint (1) means the sibling's five
* destination CLUTs must likewise be one base symbol at 0x20 stride.
* ------------------------------------------------------------------------- */
extern void func_800599B8(u16 *);
extern u16 D_801F60A0[];
extern u16 D_801CACA8[];
extern u16 D_801CAD48[];
extern u16 D_801CAD28[];
extern u16 D_801CADA8[];
extern u16 D_801CAD08[];
extern u16 D_801CAD88[];
extern u16 D_801CAC68[];
extern u16 D_801CACE8[];
extern u16 D_801CAC88[];
extern u16 D_801CACC8[];
extern u8 D_8018DF60[];
extern u8 D_8018DF68[];
extern u8 D_8018DF70[];
extern u8 D_8018DF78[];
extern u8 D_8018DF80[];
/* MUST be `inline` — 5 expansions, 0 jal, and the caller's frame is 0x20. */
static inline void pal_lerp(u16 *dst, u16 *b, u16 *a, s32 x)
{
s32 t;
s32 i;
s32 hi;
s32 r;
s32 g;
s32 bl;
s32 nr;
s32 ng;
s32 nb;
t = (x - 0x800) * 2;
if (t < 0) {
t = 0;
}
for (i = 0; i < 16; i++) {
/* NOTE the polarity: `>= 0x800` (not `< 0x800` with swapped arms) —
LICM hoists the slti and gcc branches on the TRUE arm, giving the
target's `bnez` rather than a `beqz`. */
if (t >= 0x800) {
hi = *a & 0x8000;
} else {
hi = *b & 0x8000;
}
r = *b & 0x1F;
g = *b & 0x3E0;
bl = *b & 0x7C00;
nr = (r + ((((*a & 0x1F) - r) * t) >> 12)) & 0x1F;
ng = (g + ((((*a & 0x3E0) - g) * t) >> 12)) & 0x3E0;
nb = (bl + ((((*a & 0x7C00) - bl) * t) >> 12)) & 0x7C00;
*dst = hi | nr | ng | nb;
b++;
a++;
dst++;
}
}
void func_8017FDF8(s32 x)
{
pal_lerp(&D_801F60A0[0], D_801CACA8, D_801CAD48, x);
((void (*)(u8 *, u16 *))func_800599B8)(D_8018DF60, &D_801F60A0[0]);
pal_lerp(&D_801F60A0[16], D_801CAD28, D_801CADA8, x);
((void (*)(u8 *, u16 *))func_800599B8)(D_8018DF68, &D_801F60A0[16]);
pal_lerp(&D_801F60A0[32], D_801CAD08, D_801CAD88, x);
((void (*)(u8 *, u16 *))func_800599B8)(D_8018DF70, &D_801F60A0[32]);
pal_lerp(&D_801F60A0[48], D_801CAC68, D_801CACE8, x);
((void (*)(u8 *, u16 *))func_800599B8)(D_8018DF78, &D_801F60A0[48]);
pal_lerp(&D_801F60A0[64], D_801CAC88, D_801CACC8, x);
((void (*)(u8 *, u16 *))func_800599B8)(D_8018DF80, &D_801F60A0[64]);
}
/* func_801802EC — banked from the S40 wave-1 draft.
* The draft carried the TYPES, the six SVECTOR2 externs and `static inline morph_lerp` so that