T5.1.c6: type_census: layout hash keys packed/aligned(N) defs off natural alignment; +1 selftest

This commit is contained in:
Drew T
2026-09-30 15:03:29 -06:00
parent 1eab0c7d95
commit 343b718b21
2 changed files with 32 additions and 2 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# The struct map — the types the fleet's cast sites need (Phase 37 T1)
> **Generated by `tools/type_census.py` at `f7fde90cc2` (2026-09-30T20:48:50Z); 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 `1eab0c7d95` (2026-09-30T21:02: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.
**19124 types explain 498868 of 502988 dereference sites (99.2 %); 4120 sites have a base the census could not classify; 857 types carry a width conflict.** Merges by evidence: param 38876, assign 16783, arg 10825, field 9904, ret-of 4335, return 1524, assign& 1184, arg& 270.
+31 -1
View File
@@ -1127,6 +1127,29 @@ def structs_reading(results, defs_all, td_all, dup_rows, dup_full_names, twins_t
stale=sorted(set(twins) - twins_ok) + sorted(set(kept) - kept_ok))
ALIGNED_RX = re.compile(r"aligned\s*\(\s*(\d+)\s*\)")
def def_layout_hash(res, d, lay):
"""The def's layout hash: layout_hash(lay), re-keyed with the alignment when the def's own `packed` / `aligned(N)` moves it
off the natural one (P38 T5.1.c6: R22 proved packed and natural twins different layouts — a2e73 → packed_word 70/218,
0aaa → BlockData 216/218). An attribute that leaves the natural alignment keeps the plain hash (the twins ledger's keys)."""
lh = layout_hash(lay)
if lay is None or d["kind"] == "enum":
return lh
al = lay[1]
ma = ALIGNED_RX.search(d["attrs"] or "")
if ma:
al = max(al, int(ma.group(1)))
nat = lay[1]
if "packed" in d["attrs"]:
nlay = res.layout_of_fields(d["fields"], d["kind"])
nat = nlay[1] if nlay else nat
if al == nat:
return lh
return hashlib.sha1(json.dumps([lay[0], lay[2], "align", al]).encode()).hexdigest()[:12]
def run_census(jobs, use_cache=True, out_dir=OUT_DIR_DEFAULT, want_sites=False):
t0 = time.time()
w = walk_all(jobs, use_cache=use_cache, out_dir=out_dir)
@@ -1169,7 +1192,7 @@ def run_census(jobs, use_cache=True, out_dir=OUT_DIR_DEFAULT, want_sites=False):
else:
lay = res.layout_of_fields(d["fields"], d["kind"], packed=("packed" in d["attrs"]))
d["layout"] = lay
d["lhash"] = layout_hash(lay)
d["lhash"] = def_layout_hash(res, d, lay)
d["size"] = lay[0] if lay else None
if lay is None:
unknown_layout += 1
@@ -1758,6 +1781,13 @@ def selftest():
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.c6: packed and natural twins are two layouts; an attribute at the natural alignment is not
ares = walk_file("typedef struct { u32 w; } __attribute__((packed)) PkW;\ntypedef struct { u32 w; } NatW;\n"
"typedef struct { u32 w; } __attribute__((aligned(4))) AlW;\n", "src/ov_TEST/a.c")
ar = Resolver({n: d for d in ares["definitions"] for n in d["names"]})
ah = {d["names"][0]: def_layout_hash(ar, d, ar.layout_of_fields(d["fields"], d["kind"], packed=("packed" in d["attrs"])))
for d in ares["definitions"]}
checks.append(("packed twin apart, natural aligned() together", ah["PkW"] != ah["NatW"] and ah["AlW"] == ah["NatW"]))
# 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")