selfhost/cmd/wcc: TARRAY struct-stride + cache-bind resolved body (Phase A.4)
A.3 left wwstage slotsize at 134 fallback hits. Per-kind breakdown:
N_TARRAY 33 + N_TNAME 101 (of which 71 resolve to TY_STRUCT, 3 to
module-name quirks, 27 already had tinfo populated and were spurious
fallbacks via missed cache hits).
tinfofornode N_TNAME: existing arm already reached the resolved body
via aliaslookup → tinfofornode recursion (reviewer-61a3's "isn't
reaching body" hypothesis disproved by per-name instrumentation). A.4
binds the resolved-body node into the cache too — mirrors A.2's
TSTRUCT/TFN/TTUPLE/TTAGGED cycle-break pattern so future calls on
either node short-circuit.
tinfofornode N_TARRAY: when sub.kind == TY_STRUCT, round sub.size up
to 8 before stride. Mirrors registerstruct's slot-padded element
stride (cgenutil.ww:2156-2165 / :2233). Primitive elements stay
natural (slotsize's TARRAY walker also keeps them natural).
slotsize fast-path adds TY_VOID (size 0) and TY_ARRAY (gated on
alen > 0 so `[_]T` keeps routing through letslotsize). TY_STRUCT
deferred to A.5: tinfofornode TSTRUCT uses per-field natural-align
so size(T) stays natural at user level, but registerstruct uses
size-derived align with nested structs slot-padded — diverges on
ragged-tail shapes (`{inner=3*i32, mark: i32}` gives natural=16 vs
totsize=24). Proper A.5 design is a tinfo.slotsize SSoT distinct
from tinfo.size.
Module-name TNAME quirks (`let l: lex;` where lex is both a struct
and the imported module): resolvealias short-circuits on SK_MOD,
n.type_ stays nil, falls through to AST walker which structlookups
correctly. 3 hits in tree. A.5 work alongside TSTRUCT.
Post-A.4 fallback: wwdump 134→45, w6a 17→12, w6l 6→6, ww 12→11
(reviewer also measured w6c at 40). Total 169→74 across the corpus
(56% reduction). All 74 are TNAME → TY_STRUCT or module-name quirks.
131/131 + 994 + 995 + bootstrap (ww2==ww3==ww4) byte-identical.
This commit is contained in:
@@ -1941,8 +1941,8 @@ export fn letslotsize(c: *cgen, n: *node) i32 = {
|
||||
};
|
||||
|
||||
fn slotsize(c: *cgen, typn: *node) i32 = {
|
||||
// #61 audit §1.8 — A.3 fast-path expansion. Read tinfo.size off the
|
||||
// populated type-expression node when the kind matches the cstage
|
||||
// #61 audit §1.8 — A.3 / A.4 fast-path expansion. Read tinfo.size off
|
||||
// the populated type-expression node when the kind matches the cstage
|
||||
// natural-size SSoT. Coverage:
|
||||
// - pointer-like (PTR/CHAN/FN), slice, str, tagged
|
||||
// return ti.size directly (size already encodes the slot).
|
||||
@@ -1953,17 +1953,45 @@ fn slotsize(c: *cgen, typn: *node) i32 = {
|
||||
// primitive into an 8B stack slot regardless of tinfo.size.
|
||||
// Padding lives at the read site, not in tinfo.size, so size(T)
|
||||
// stays a faithful natural-width SSoT.
|
||||
// TUPLE / TSTRUCT / TARRAY still flow through the AST-walker fallback
|
||||
// because cgen's per-field stride contract (registerstruct.totsize
|
||||
// rounding, [N]T element-size walk) lives there, not in tinfo.size.
|
||||
// - #61 A.4 adds:
|
||||
// · TY_VOID returns 0 (mirrors the TNAME-"void" fallback arm).
|
||||
// Closes 56 N_TNAME-"void" / void-aliased (utf8.invalid,
|
||||
// overflow, done, more, ...) fallback hits — tinfofornode
|
||||
// now caches both the TNAME and its resolved body so future
|
||||
// calls on either land in the cache.
|
||||
// · TY_ARRAY returns ti.size when alen > 0 — tinfofornode's
|
||||
// TARRAY arm now applies the same struct-element round-to-8
|
||||
// cgen's fieldsize uses (#61 A.4 step 2), so [N]Struct's
|
||||
// stride matches. [_]T (alen=0) still routes through the
|
||||
// AST walker; letslotsize patches the length there.
|
||||
// TUPLE still flows through the fallback — slotsize sums raw element
|
||||
// sizes there, while tinfofornode TTUPLE mirrors cstage's natural
|
||||
// sum, and the two diverge for `(i32, str)`-style mixes (slot vs
|
||||
// natural per-element padding).
|
||||
//
|
||||
// TY_STRUCT deferred to A.5: tinfofornode TSTRUCT uses per-field
|
||||
// natural-align for offsets (mirroring cstage's resolve_type +
|
||||
// astsize, so `size(T)` stays natural), but registerstruct uses
|
||||
// size-derived alignment with nested structs slot-padded to 8
|
||||
// (cgenutil.ww:2178-2207). The two agree for the common 8B-aligned
|
||||
// shapes but diverge for "ragged tail" structs like
|
||||
// `struct { inner: struct{i32,i32,i32}, mark: i32 }` where the
|
||||
// inner struct's natural align (4) drops below cgen's nested-
|
||||
// struct contract (aln=8 for sz>=8). Closing the gap without
|
||||
// touching `size(T)`'s natural-width contract needs a separate
|
||||
// SSoT (e.g. tinfo.slotsize) — filed as A.5.
|
||||
if (typn != nil && typn.type_ != nil) {
|
||||
let ti: *tinfo = typn.type_: *tinfo;
|
||||
let kk: tykind = ti.kind;
|
||||
if (kk == tykind.TY_PTR || kk == tykind.TY_SLICE ||
|
||||
kk == tykind.TY_CHAN || kk == tykind.TY_FN ||
|
||||
kk == tykind.TY_STR || kk == tykind.TY_TAGGED) {
|
||||
kk == tykind.TY_STR || kk == tykind.TY_TAGGED ||
|
||||
kk == tykind.TY_VOID) {
|
||||
return ti.size: i32;
|
||||
};
|
||||
if (kk == tykind.TY_ARRAY) {
|
||||
if (ti.alen > 0u64) { return ti.size: i32; };
|
||||
};
|
||||
if (kk == tykind.TY_BOOL || kk == tykind.TY_RUNE ||
|
||||
kk == tykind.TY_I8 || kk == tykind.TY_I16 ||
|
||||
kk == tykind.TY_I32 || kk == tykind.TY_I64 ||
|
||||
|
||||
Reference in New Issue
Block a user