T5.1.c2b: pad fields to pad_<off> (28), prim_layout_class post-relocation invariant, struct-map regen

This commit is contained in:
Drew T
2026-09-30 11:07:50 -06:00
parent 0864b69b45
commit 516b2db94d
5 changed files with 94 additions and 44 deletions
+14 -14
View File
@@ -1,8 +1,8 @@
# The struct map — the types the fleet's cast sites need (Phase 37 T1)
> **Generated by `tools/type_census.py` at `6f01332bc` (2026-09-30T06:31:48Z); never typed (R75).** Every raw pointer-cast dereference in `src/` was assigned to a base (a parameter, a local, a global by value or by address, an absolute address) and the bases were clustered by EVIDENCE only — the same global symbol, an argument passed to a callee's parameter, an assignment or return, a pointer field read, a shared body's fleet-wide identity — never by a similar offset signature. Each cluster is one type the tree needs. Names are placeholders (`Unkstruct_<addr>`: the lowest global instance's address, else the lowest member function's); fields are placeholders (`unk<HEX>`) until Phase 38 cites evidence.
> **Generated by `tools/type_census.py` at `0864b69b4` (2026-09-30T17:06:52Z); never typed (R75).** Every raw pointer-cast dereference in `src/` was assigned to a base (a parameter, a local, a global by value or by address, an absolute address) and the bases were clustered by EVIDENCE only — the same global symbol, an argument passed to a callee's parameter, an assignment or return, a pointer field read, a shared body's fleet-wide identity — never by a similar offset signature. Each cluster is one type the tree needs. Names are placeholders (`Unkstruct_<addr>`: the lowest global instance's address, else the lowest member function's); fields are placeholders (`unk<HEX>`) until Phase 38 cites evidence.
**19130 types explain 498866 of 502985 dereference sites (99.2 %); 4119 sites have a base the census could not classify; 856 types carry a width conflict.** Merges by evidence: param 38884, assign 16776, arg 10821, field 9909, ret-of 4320, return 1524, assign& 1184, arg& 270.
**19124 types explain 498868 of 502986 dereference sites (99.2 %); 4118 sites have a base the census could not classify; 857 types carry a width conflict.** Merges by evidence: param 38876, assign 16795, arg 10825, field 9909, ret-of 4331, return 1524, assign& 1184, arg& 270.
| # | type | sites | bodies | fields | size ≥ | width conflicts (+sign mixes) | instance globals | pointer globals |
|---:|---|---:|---:|---:|---:|---:|---|---|
@@ -16,7 +16,7 @@
| 8 | `Unkstruct_8e3dbe51` | 3930 | 131 | 5 | 0x4c | 0 (+1 sign) | | |
| 9 | `Unkstruct_8014A1B0` | 3778 | 542 | 13 | 0xfe | 0 (+1 sign) | | |
| 10 | `Unkstruct_80128EA8` | 3311 | 632 | 18 | 0x38 | 0 (+13 sign) | | |
| 11 | `Unkstruct_ade2e8a0` | 3271 | 196 | 26 | 0xa4 | 3 (+9 sign) | | |
| 11 | `Unkstruct_57a28935` | 3271 | 196 | 26 | 0xa4 | 3 (+9 sign) | | |
| 12 | `Unkstruct_1fa9e810` | 3144 | 131 | 3 | 0x49 | 0 (+0 sign) | | |
| 13 | `Unkstruct_30b21035` | 3100 | 124 | 13 | 0x54 | 0 (+0 sign) | | |
| 14 | `Unkstruct_80126948` | 2986 | 832 | 34 | 0xa4 | 0 (+10 sign) | D_80126948, D_80126E60 | D_80114EE8, D_801151D4, D_80126948, D_80126E60… |
@@ -104,7 +104,7 @@
| 96 | `Unkstruct_80115128` | 667 | 534 | 1 | 0x2 | 0 (+0 sign) | D_80115128 | |
| 97 | `Unkstruct_896c95f7` | 665 | 133 | 3 | 0x5e | 0 (+0 sign) | | |
| 98 | `Unkstruct_61789347` | 665 | 133 | 0 | 0x0 | 0 (+0 sign) | | |
| 99 | `Unkstruct_44ce00ff` | 665 | 133 | 0 | 0x0 | 0 (+0 sign) | | |
| 99 | `Unkstruct_04834d6a` | 665 | 133 | 0 | 0x0 | 0 (+0 sign) | | |
| 100 | `Unkstruct_801345F8` | 665 | 133 | 0 | 0x0 | 0 (+0 sign) | | |
| 101 | `Unkstruct_801345F8` | 665 | 133 | 0 | 0x0 | 0 (+0 sign) | | |
| 102 | `Unkstruct_801152A0` | 665 | 399 | 1 | 0x2 | 0 (+0 sign) | D_801152A0 | |
@@ -130,10 +130,10 @@
| 122 | `Unkstruct_78eaa73c` | 553 | 7 | 15 | 0x10a | 0 (+7 sign) | | |
| 123 | `Unkstruct_8011512E` | 532 | 133 | 1 | 0x2 | 0 (+0 sign) | D_8011512E | |
| 124 | `Unkstruct_8016BFA8` | 532 | 133 | 4 | 0x24 | 0 (+0 sign) | | |
| 125 | `Unkstruct_30befa8c` | 520 | 130 | 3 | 0xfe | 0 (+0 sign) | | |
| 126 | `Unkstruct_01082f7a` | 520 | 520 | 1 | 0x4 | 0 (+0 sign) | | |
| 127 | `Unkstruct_db0ee441` | 516 | 129 | 4 | 0x38 | 0 (+0 sign) | | |
| 128 | `Unkstruct_7476116e` | 516 | 129 | 1 | 0x4 | 0 (+0 sign) | | |
| 125 | `Unkstruct_7476116e` | 532 | 133 | 1 | 0x4 | 0 (+0 sign) | | |
| 126 | `Unkstruct_30befa8c` | 520 | 130 | 3 | 0xfe | 0 (+0 sign) | | |
| 127 | `Unkstruct_01082f7a` | 520 | 520 | 1 | 0x4 | 0 (+0 sign) | | |
| 128 | `Unkstruct_db0ee441` | 516 | 129 | 4 | 0x38 | 0 (+0 sign) | | |
| 129 | `Unkstruct_290ce261` | 504 | 126 | 3 | 0xfe | 0 (+0 sign) | | |
| 130 | `Unkstruct_d6100faa` | 504 | 126 | 3 | 0x20 | 0 (+0 sign) | | |
| 131 | `Unkstruct_5ebcbddc` | 504 | 126 | 3 | 0xe | 0 (+0 sign) | | |
@@ -152,11 +152,11 @@
| 144 | `Unkstruct_8016B5C0` | 399 | 133 | 2 | 0x1c | 0 (+0 sign) | | |
| 145 | `Unkstruct_8016BFA8` | 399 | 133 | 3 | 0x6 | 0 (+0 sign) | | |
| 146 | `Unkstruct_8011F7F0` | 399 | 133 | 3 | 0x42 | 0 (+0 sign) | D_8011F7F0 | |
| 147 | `Unkstruct_1649b9a7` | 392 | 28 | 8 | 0x38 | 0 (+2 sign) | | |
| 148 | `Unkstruct_ea5ed765` | 390 | 130 | 1 | 0xdb | 0 (+0 sign) | | |
| 149 | `Unkstruct_80135260` | 390 | 130 | 3 | 0x6 | 0 (+0 sign) | | |
| 150 | `Unkstruct_a472a6ba` | 387 | 129 | 3 | 0x1c | 0 (+0 sign) | | |
| 151 | `Unkstruct_630777a3` | 387 | 258 | 2 | 0x5 | 0 (+0 sign) | | |
| 147 | `Unkstruct_630777a3` | 399 | 266 | 2 | 0x5 | 0 (+0 sign) | | |
| 148 | `Unkstruct_1649b9a7` | 392 | 28 | 8 | 0x38 | 0 (+2 sign) | | |
| 149 | `Unkstruct_ea5ed765` | 390 | 130 | 1 | 0xdb | 0 (+0 sign) | | |
| 150 | `Unkstruct_80135260` | 390 | 130 | 3 | 0x6 | 0 (+0 sign) | | |
| 151 | `Unkstruct_a472a6ba` | 387 | 129 | 3 | 0x1c | 0 (+0 sign) | | |
| 152 | `Unkstruct_f2440762` | 384 | 128 | 1 | 0x4 | 0 (+0 sign) | | |
| 153 | `Unkstruct_80164BDC` | 384 | 128 | 5 | 0x64 | 0 (+1 sign) | | |
| 154 | `Unkstruct_8c2ab96e` | 384 | 128 | 3 | 0x8 | 0 (+0 sign) | | |
@@ -207,4 +207,4 @@
| 199 | `Unkstruct_76f4299b` | 262 | 131 | 1 | 0x4a | 0 (+0 sign) | | |
| 200 | `Unkstruct_3816caab` | 260 | 130 | 2 | 0x1c | 0 (+0 sign) | | |
*19130 types in all; the table shows the 200 largest by site count. The full map with every type's layout (offset, width, sign, site count per field) and conflicts is `.run/P37/census/struct_map.json`.*
*19124 types in all; the table shows the 200 largest by site count. The full map with every type's layout (offset, width, sign, site count per field) and conflicts is `.run/P37/census/struct_map.json`.*
+16 -16
View File
@@ -874,7 +874,7 @@ typedef struct { s16 m[3][3]; s32 t[3]; } Mtx32_801A54F0;
typedef struct { s16 m[3][3]; s32 t[3]; } Mtx32_801A5698;
typedef struct { s32 w[8]; } Mtx32_801A90D8;
typedef struct { s16 m[3][3]; s32 t[3]; } Mtx32b;
typedef struct { Blk16X q0; Blk16X q1; s32 pad0, pad1; } Mtx40;
typedef struct { Blk16X q0; Blk16X q1; s32 pad_20, pad_24; } Mtx40;
typedef struct { s16 m[3][3]; s32 t[3]; } MtxX;
typedef struct {
s16 m[9];
@@ -1227,7 +1227,7 @@ typedef struct { s16 x; s16 y; s16 z; s16 pad; } SVec801A5674;
typedef struct { SVec801A5674 v[2]; s32 col[2]; s32 tag; s32 pad; } Prim801A5674;
typedef struct { s16 m[4]; } SVec801A8E34;
typedef struct { s16 vx, vy, vz, pad; } SVecX;
typedef struct { SVecX v[4]; CVecX c[4]; s32 code; s32 pad2; } PrimX;
typedef struct { SVecX v[4]; CVecX c[4]; s32 code; s32 pad_34; } PrimX;
typedef struct { s16 vx, vy, vz, pad; } SVec_801A2400;
typedef struct { s16 vx, vy, vz, pad; } SVec_801A3180;
typedef struct { s16 vx, vy, vz, pad; } SVec_801A39D0;
@@ -1661,7 +1661,7 @@ typedef struct { u32 addr : 24; u32 len : 8; u32 code; } DrTpage;
typedef struct {
u8 pad0[8];
s32 w8;
u8 pad12[10];
u8 pad_C[10];
u8 b16;
u8 pad17[2];
u8 b19;
@@ -1766,7 +1766,7 @@ typedef struct {
/* 0x18 */ u32 xy2;
/* 0x1C */ u8 u2;
/* 0x1D */ u8 v2;
/* 0x1E */ u16 pad0;
/* 0x1E */ u16 pad_1E;
} FT3_16D78;
typedef struct {
/* 0x00 */ u32 tag;
@@ -1782,11 +1782,11 @@ typedef struct {
/* 0x18 */ u32 xy2;
/* 0x1C */ u8 u2;
/* 0x1D */ u8 v2;
/* 0x1E */ u16 pad0;
/* 0x1E */ u16 pad_1E;
/* 0x20 */ u32 xy3;
/* 0x24 */ u8 u3;
/* 0x25 */ u8 v3;
/* 0x26 */ u16 pad1;
/* 0x26 */ u16 pad_26;
} FT4_16F18;
typedef struct {
s32 unk00; /* 0x00 */
@@ -1907,11 +1907,11 @@ typedef struct {
u32 tag;
u8 r0, g0, b0, code;
s16 x0, y0;
u8 r1, g1, b1, pad1;
u8 r1, g1, b1, pad_F;
s16 x1, y1;
u8 r2, g2, b2, pad2;
u8 r2, g2, b2, pad_17;
s16 x2, y2;
u8 r3, g3, b3, pad3;
u8 r3, g3, b3, pad_1F;
s16 x3, y3;
} G4_80017778;
typedef struct {
@@ -1950,11 +1950,11 @@ typedef struct {
u32 rgb2; /* 0x1C */
s16 x2, y2; /* 0x20 */
u8 u2, v2; /* 0x24 */
u16 pad2; /* 0x26 */
u16 pad_26; /* 0x26 */
u32 rgb3; /* 0x28 */
s16 x3, y3; /* 0x2C */
u8 u3, v3; /* 0x30 */
u16 pad3; /* 0x32 */
u16 pad_32; /* 0x32 */
} GT4_80017930;
typedef struct {
/* 0x0 */ u32 xy;
@@ -2167,9 +2167,9 @@ typedef struct {
u32 tag;
u8 r0, g0, b0, code;
s16 x0, y0;
u8 r1, g1, b1, pad1;
u8 r1, g1, b1, pad_F;
s16 x1, y1;
u8 r2, g2, b2, pad2;
u8 r2, g2, b2, pad_17;
s16 x2, y2;
} PG3;
typedef struct {
@@ -2257,7 +2257,7 @@ typedef struct {
u16 v1; /* 0x18 */
u16 v2; /* 0x1A */
u16 v3; /* 0x1C */
u16 pad1; /* 0x1E */
u16 pad_1E; /* 0x1E */
} Prim20;
typedef struct {
u32 unk0;
@@ -2671,7 +2671,7 @@ typedef struct {
s16 field_0x34; /* 0x34 */
} Struct80078E78;
typedef struct {
u8 pad15[0x15];
u8 pad_0[0x15];
u8 field_0x15; /* 0x15 */
u8 pad16[0x3];
u8 field_0x19; /* 0x19 */
@@ -2689,7 +2689,7 @@ typedef struct {
s32 field_0x28; /* 0x28 */
} Struct800D2FEC;
typedef struct {
u8 pad15[0x15];
u8 pad_0[0x15];
u8 field_0x15;
u8 pad16[0x3];
u8 field_0x19;
+27
View File
@@ -0,0 +1,27 @@
# T5.1.c2b — census relocation batches ov-block/md/main/headers, pad renames, prim_layout_class control
## Changed
- Batches via `tools/lift_types.py --census .run/P38/census/outside_canon.tsv --batch <b> [--apply] --excluded-out .run/P38/t51/c2_excluded.txt`, census re-run (`--no-cache`) before each dry-run; every dry-run `census rows with no movable def found: 0`, excluded 0.
- c36cb4980 ov-block: 694 groups / 1044 defs (646 -> engine_types.h, 5 -> struct_types.h), 566 files.
- b0b9c4c46 md: 297 groups / 359 defs (12 et, 285 st), 61 files.
- c3ae8d6da main: 223 groups / 236 defs (all st), 9 files.
- headers commit: 47 groups / 49 defs (21 et, 26 st), 21 files.
- Pad renames (T5.c2 precedent `pad_<HEXOFF>`), 28 fields / 15 canon types, def-only: no member accesses exist (`git grep '(\.|->)pad{0,1,2,3,12,15}\b'` = 0; the 265 `p->pad4` hits are another type whose pad4 sits at 0x4; St_SC03_118_80183{3C4,4DC} only used via `P` macros in ov_SC03_118_o0d.c, no pad access). engine_types.h: Ent_SC07_010_8017AE2C/EntA pad2->pad_E0, PHdr_80189340 pad1->pad_4, St_SC03_118_801833C4/801834DC pad1..4->pad_4/_DE/_E4/_E8. struct_types.h (arrived with md/main/headers batches): Mtx40 pad_20/pad_24, PrimX pad_34, Dst pad_C, FT3_16D78 pad_1E, FT4_16F18 pad_1E/pad_26, G4_80017778 pad_F/_17/_1F, GT4_80017930 pad_26/_32, PG3 pad_F/_17, Prim20 pad_1E, Struct800D2E20/Struct800D3104 pad15->pad_0. Scratch: `.run/P38/t51/c2/padfix.py`.
- `tools/type_census.py` controls() #3 prim_layout_class:
- old: `prim_files > 1000 and top_n == len(prim_defs)` (Prim_8016E7C8 in ~1,192 .c files, two spellings, one layout class).
- new: `len(prim_defs) == 1 and is_canon and size == 64` (post-relocation invariant). Premise was pre-relocation: pre-c2 copy (6869f8f26~1, ov_SC01_000_jr_8016AB6C.c) is byte-identical text to the canon def now in engine_types.h (v[4] SVECTOR + 6 s32 + u8 + pad[7] = 0x40), so no layout loss.
- selftest: `PRIM_FIXTURES` (two spellings, two files) + check "Prim spellings one layout 64" (distinct text_hash, both 64 B, equal layout_hash). Negative control on a scratch copy: mutating spelling 2 to `pad[3]; s32 x;` (same size, other layout) and to `pad[11]` (68 B) each -> 30/31 FAILED ['Prim spellings one layout 64'].
## Commands / results
- R22 (`make clean && make extract-all JOBS=16 && make check-all JOBS=16`, run.sh --bg/--wait) after each batch and after the pad rename: every run `extract-all: 217 extracted, 0 failed of 217`, `check-all: 218 passed, 0 failed of 218`.
- `.run/P38/t51/c2/hdrchk.sh` rc=0 after every apply.
- Census progression outside_canon/dead/pad_names: start 1684/10/1 -> after ov-block 642/11/11 -> md 284/30/14 -> main 48/35/28 -> headers 0/37/(0, race with pad edit) -> final (census5, tree = this commit) `dup_classes=200 outside_canon=0 dead=37 twins_stale=0 variants=0 pad_names=0 parse_error_decls=1106 controls=4/4`.
- `type_census.py --selftest` -> 31/31 checks OK.
- `restruct.py --audit-types` -> 3417 canonical definitions audited; 0 violations.
## For the expert
- dead grew 10 -> 37: locally-defined-but-unused defs relocated into canon now count as dead canon names. Not in this task's done-criteria; not touched.
- pad_names grew as batches moved defs into canon (1 -> 28); brief named 1, all 28 renamed to reach pad_names=0.
- Census --no-cache takes ~4.5-5 min: always run.sh --bg + --wait (a foreground call exceeds 285 s).
- dup_classes=200 unchanged (folding names is not this task's).
- docs/struct-map.md regenerated by census5, committed here.
+11 -11
View File
@@ -7379,7 +7379,7 @@ typedef struct {
s16 f4;
s16 f6;
} D_80195AF6_t;
struct Ent_SC07_010_8017AE2C { u8 pad[0xDC]; s32 unkDC; u8 pad2[0x1C]; u16 unkFC; };
struct Ent_SC07_010_8017AE2C { u8 pad[0xDC]; s32 unkDC; u8 pad_E0[0x1C]; u16 unkFC; };
typedef struct { s16 m[3][3]; s32 t[3]; } MTX_CF90;
typedef struct {
u8 pad_00[0x20];
@@ -7821,7 +7821,7 @@ struct Cnt {
struct Cnt_80187DE4 {
s32 c;
};
struct EntA { u8 pad[0xDC]; s32 unkDC; u8 pad2[0x1C]; u16 unkFC; };
struct EntA { u8 pad[0xDC]; s32 unkDC; u8 pad_E0[0x1C]; u16 unkFC; };
typedef struct {
u8 pad00[4];
u32 flags; /* 0x04 */
@@ -7998,7 +7998,7 @@ typedef struct {
u8 r3, g3, b3, p3; /* 0x1C..0x1F */
s16 x3, y3; /* 0x20, 0x22 */
} PG4_8018C960_80189EA0;
typedef struct { u8 pad0[3]; u8 len; u8 pad1[3]; u8 code; } PHdr_80189340;
typedef struct { u8 pad0[3]; u8 len; u8 pad_4[3]; u8 code; } PHdr_80189340;
typedef struct { u32 addr : 24; u32 len : 8; } PTag_8017DD28;
typedef struct { u32 addr : 24; u32 len : 8; } PTag_8018C960;
typedef struct { u32 addr : 24; u32 len : 8; } PTag_8018C960_801895C8;
@@ -8198,26 +8198,26 @@ struct St80180ACC { s16 a[3]; u16 b[3]; };
typedef struct {
u8 pad0[2];
union { s16 s; u16 u; } f02;
u8 pad1[0xD8];
u8 pad_4[0xD8];
union { s16 s; u16 u; } fDC;
u8 pad2[4];
u8 pad_DE[4];
s16 fE2;
u8 pad3[2];
u8 pad_E4[2];
s16 fE6;
u8 pad4[4];
u8 pad_E8[4];
u16 fEC;
u16 fEE;
} St_SC03_118_801833C4;
typedef struct {
u8 pad0[2];
union { s16 s; u16 u; } f02;
u8 pad1[0xD8];
u8 pad_4[0xD8];
union { s16 s; u16 u; } fDC;
u8 pad2[4];
u8 pad_DE[4];
s16 fE2;
u8 pad3[2];
u8 pad_E4[2];
s16 fE6;
u8 pad4[4];
u8 pad_E8[4];
s16 fEC;
s16 fEE;
} St_SC03_118_801834DC;
+26 -3
View File
@@ -1554,14 +1554,17 @@ def controls(defs_all, sites_all, extern_data, by_text, name_layouts):
return None
m32, m48 = size_of("MATRIX"), size_of("MATRIX_L48")
out["matrix_layouts"] = dict(ok=(m32 == 32 and m48 == 48), detail=f"MATRIX={m32} MATRIX_L48={m48}")
# 3. Prim_8016E7C8 (defined in ~1,192 .c files under two spellings): ONE layout class covers every definition
# 3. Prim_8016E7C8: ONE canonical definition, 64 B. Was (pre-T5.1.c2): "defined in ~1,192 .c files under two
# spellings, ONE layout class covers every definition" (prim_files > 1000); the census relocation left one canon
# def, so the multi-spelling case now lives in the selftest ("Prim spellings one layout 64").
prim_defs = [d for d in defs_all if "Prim_8016E7C8" in d["names"] or d["tag"] == "Prim_8016E7C8"]
prim_files = len({d["file"] for d in prim_defs})
prim_lh = collections.Counter(d["lhash"] for d in prim_defs)
prim_texts = len({d["text_hash"] for d in prim_defs})
top_lh, top_n = (prim_lh.most_common(1)[0] if prim_lh else (None, 0))
out["prim_layout_class"] = dict(ok=(prim_files > 1000 and top_n == len(prim_defs) and top_lh is not None),
detail=f"defs in {prim_files} files, {prim_texts} spellings, {top_n}/{len(prim_defs)} in one layout ({prim_defs[0]['size'] if prim_defs else None} B)")
out["prim_layout_class"] = dict(ok=(len(prim_defs) == 1 and prim_defs[0]["is_canon"] and prim_defs[0]["size"] == 64),
detail=f"defs in {prim_files} files, {prim_texts} spellings, {top_n}/{len(prim_defs)} in one layout ({prim_defs[0]['size'] if prim_defs else None} B)"
+ ("" if all(d["is_canon"] for d in prim_defs) else " (non-canon def present)"))
# 4. a TU-CONFLICT ledger row reproduced: D_800A651C is declared with more than one type across TUs
types = collections.Counter(e["type"] + e["dims"] for e in extern_data if e["name"] == "D_800A651C")
out["tu_conflict_D_800A651C"] = dict(ok=(len(types) > 1), detail=" | ".join(f"{t}:{n}" for t, n in types.most_common(5)))
@@ -1690,6 +1693,20 @@ void f(void) {
fr.a = 0;
}
'''
PRIM_FIXTURES = (r'''typedef struct { s16 vx, vy, vz, pad; } SVECTOR_8016E7C8;
typedef struct {
SVECTOR_8016E7C8 v[4]; /* 0x00 */
s32 f0, f1, f2, f3, f4, f5; /* 0x20..0x37 */
u8 f6; /* 0x38 */
u8 pad[7]; /* -> 0x40 */
} Prim_8016E7C8;
''', r'''typedef struct { short vx; short vy; short vz; short pad; } SVECTOR_8016E7C8;
typedef struct {
SVECTOR_8016E7C8 v[4];
s32 f0; s32 f1; s32 f2; s32 f3; s32 f4; s32 f5;
u8 f6; u8 pad[7];
} Prim_8016E7C8;
''') # the pre-T5.1.c2 live control: two spellings of Prim_8016E7C8 in two files, one layout class, 64 B
TWINS_FIXTURE = '''# Struct twins
## Twins
### aaaa:opaque
@@ -1735,6 +1752,12 @@ def selftest():
checks.append(("K&R-empty extern", any(e["name"] == "func_80002000" and e["params"] == "" for e in res["extern_fns"])))
checks.append(("typedef alias fnptr", any(a["name"] == "Handler" and a["fnptr"] for a in res["typedef_aliases"])))
checks.append(("params of f1", res["fndefs"][0]["pnames"] == ["a0", "b", "c"]))
prim = []
for i, txt in enumerate(PRIM_FIXTURES):
pres = walk_file(txt, f"src/ov_TEST/p{i}.c")
pr = Resolver({n: d for d in pres["definitions"] for n in d["names"] + ([d["tag"]] if d["tag"] else [])}).resolve_name("Prim_8016E7C8")
prim.append((pres["definitions"][-1]["text_hash"], pr[0], layout_hash(pr)))
checks.append(("Prim spellings one layout 64", prim[0][0] != prim[1][0] and prim[0][1] == prim[1][1] == 64 and prim[0][2] == prim[1][2]))
# T5.1: the --check-structs reading (liveness, allowlists, block frames)
cres = walk_file(CANON_FIXTURE, "src/shared/engine_types.h")
ures = walk_file(USE_FIXTURE, "src/ov_TEST/u.c")