selfhost/cmd/wcc/cgenutil: collapse slotsize onto tinfo (#48, A.6.3d)
slotsize selected the slot-padded width for a frame slot via an AST
walker over TBANG / TPTR / TFN / TCHAN / TSLICE / TTUPLE / TTAGGED /
TNAME / TARRAY / TSTRUCT — duplicating the size + slot-pad math
tinfofornode already runs at check time. With A.6.2 stamping every
N_T* kind's n.type_, plus #61 A.5 splitting tinfo.slotsize from
tinfo.size, the dispatch collapses to a kind switch over the
populated tinfo:
- TY_VOID → 0
- TY_PTR/SLICE/CHAN/FN/STR/TAGGED → ti.size (size == slotsize for
these kinds)
- TY_STRUCT/TUPLE/ARRAY → ti.slotsize (slot-padded by check.ww's
TSTRUCT/TTUPLE/TARRAY arms with the same field-pad / stride
rules cgenutil's registerstruct uses)
- primitives → catch-all 8 (pad-to-8 lives at the read site, not
in ti.slotsize, so [N]i32 stride stays 4)
Ww-to-Hare divergence (deliberate, team consensus): Hare's struct
type carries only `size` (ref/harec/include/types.h:134-137); QBE
handles slot padding downstream. ww emits Plan 9 amd64 asm directly,
so slot-padding is part of the ww calling convention and belongs on
the type table. Cstage scatters pad-to-8 inline at ~30+ localoff
sites + the `(su->kind == TY_TAGGED) ? su->size : 16` tagged-spill
pattern at cmd/w6c/cgen.c:4850 / :5193 — that scattering would be a
rule-13 (no hardcoded size literals) violation in ww. Centralizing
on tinfo.slotsize IS the rule-13-compliant shape; #61 A.5 put it
there, and #48 just consumes it. Rule 9 is lib/-scoped; tinfo is
compiler-internal (already diverges via node.type_ as a checker
invariant Hare/harec lacks). Rule 10 covers inference power, not
data shape — byte-identity (994/995) holds at 133/133.
Body went from ~150 LOC AST walk to ~15 LOC tinfo dispatch. Two
nil guards (typn == nil, ti == nil) fall through to 8 — same outcome
as cstage's `(t && t->size > 0) ? sz : 8` defensive shape.
`c: *cgen` retained unused for callsite stability (localloadop
precedent, 68219a1).
Net -453 LOC across cgenutil.ww + two .combined.ww bundler regens.
Follow-up filed: grow cstage Type.slot_size symmetrically and
sizelint-ok-annotate the surviving inline 8s until ported.
This commit is contained in:
@@ -12155,190 +12155,39 @@ export fn letslotsize(c: *cgen, n: *node) i32 = {
|
||||
return 8;
|
||||
};
|
||||
|
||||
// #48 A.6.3d: AST walker retired. resolvewalk (check.ww:426-436) stamps
|
||||
// `n.type_` on every N_T* kind via tinfofornode, which folds TBANG
|
||||
// (inner unchanged, check.ww:1154-1161), TNAME alias chains
|
||||
// (resolvealias, check.ww:1128-1153), TARRAY/TPTR/TSLICE/TCHAN/TFN/
|
||||
// TENUM/TTUPLE/TSTRUCT/TTAGGED with size + slot-padded slotsize.
|
||||
//
|
||||
// cstage SSoT is distributed — there is no single slot_size(Type*).
|
||||
// Tagged slot follows cstage cmd/wcc/check.c:348 + :998 (tag (8) +
|
||||
// max variant payload rounded to 8); the wwstage TTAGGED arm at
|
||||
// check.ww:1296-1346 mirrors that layout. cgen.c:4850 / :5193 use
|
||||
// the same `(su->kind == TY_TAGGED) ? su->size : 16` pattern for the
|
||||
// match-spill slot. Pointer-and-narrow → 8 is the local-frame
|
||||
// convention encoded at every cstage `localoff(…, 8, …)` callsite
|
||||
// (cmd/w6c/cgen.c throughout); wwstage encodes the same pad-to-8 at
|
||||
// this read site so `[N]i32` stride stays 4 (natural) — moving it
|
||||
// into ti.slotsize would lift array stride to 8/elem.
|
||||
//
|
||||
// `c: *cgen` retained unused for callsite stability (localloadop
|
||||
// precedent, 68219a1).
|
||||
fn slotsize(c: *cgen, typn: *node) i32 = {
|
||||
// #61 audit §1.8 — A.3 / A.4 / A.5 fast-path expansion. Read the
|
||||
// slot-padded width off the populated type-expression node when the
|
||||
// kind matches the cstage natural-size SSoT. Coverage:
|
||||
// - pointer-like (PTR/CHAN/FN), slice, str, tagged: ti.size ===
|
||||
// ti.slotsize (slot already equals natural). TY_TAGGED is safe
|
||||
// now that tinfofornode folds `(*T | void)` to 8B (#61 A.3 step
|
||||
// 1, mirrors cmd/wcc/check.c:412-426).
|
||||
// - narrow scalars (BOOL/RUNE/I8..I32/U8..U32/ENUM/F32) pad UP to
|
||||
// 8 — cstage's let_emit_size (cmd/w6c/cgen.c:691-720) spills
|
||||
// every primitive into an 8B stack slot regardless of
|
||||
// tinfo.size. Pad-to-8 lives at the read site, not in
|
||||
// tinfo.slotsize, so `[N]i32` stride stays 4 (natural) — moving
|
||||
// the pad into ti.slotsize would lift array stride to 8/elem.
|
||||
// - TY_VOID returns 0 (mirrors the TNAME-"void" fallback arm,
|
||||
// same as #61 A.4).
|
||||
// - #61 A.5 adds: TY_STRUCT / TY_TUPLE / TY_ARRAY read ti.slotsize
|
||||
// (slot-padded). tinfofornode populates the slot total mirroring
|
||||
// cgenutil.ww registerstruct (size-derived align, nested struct
|
||||
// fields → si.totsize, final round to 8), and TARRAY threads
|
||||
// stride through sub.slotsize so `[N]Triplet` lifts to padded *
|
||||
// N. `size(T)` stays natural — split SSoT in tinfo.
|
||||
if (typn != nil && typn.type_ != nil) {
|
||||
if (typn == nil) { return 8; };
|
||||
let ti: *tinfo = typn.type_: *tinfo;
|
||||
if (ti == nil) { return 8; };
|
||||
let kk: tykind = ti.kind;
|
||||
if (kk == tykind.TY_VOID) { return 0; };
|
||||
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_VOID) {
|
||||
kk == tykind.TY_STR || kk == tykind.TY_TAGGED) {
|
||||
return ti.size: i32;
|
||||
};
|
||||
if (kk == tykind.TY_STRUCT || kk == tykind.TY_TUPLE) {
|
||||
if (ti.slotsize > 0u64) { return ti.slotsize: i32; };
|
||||
};
|
||||
if (kk == tykind.TY_ARRAY) {
|
||||
if (ti.alen > 0u64) { return ti.slotsize: 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 ||
|
||||
kk == tykind.TY_U8 || kk == tykind.TY_U16 ||
|
||||
kk == tykind.TY_U32 || kk == tykind.TY_U64 ||
|
||||
kk == tykind.TY_INT || kk == tykind.TY_UINT ||
|
||||
kk == tykind.TY_UINTPTR || kk == tykind.TY_ENUM ||
|
||||
kk == tykind.TY_F32 || kk == tykind.TY_F64) {
|
||||
return 8;
|
||||
};
|
||||
};
|
||||
if (typn == nil) { return 8; };
|
||||
let k: nkind = typn.kind;
|
||||
// `!T` carries T's memory layout; the error-tag bit lives in the
|
||||
// enclosing union's discriminant, not the variant payload. cstage
|
||||
// resolve_type N_TBANG copies the inner size (cmd/wcc/check.c:303-
|
||||
// 306); wwstage previously fell through to the catch-all 8, so a
|
||||
// bare `!void` variant sized to 8 instead of 0. (#48 part-A.)
|
||||
if (k == nkind.N_TBANG) { return slotsize(c, typn.lhs); };
|
||||
if (k == nkind.N_TPTR) { return 8; };
|
||||
if (k == nkind.N_TFN) { return 8; };
|
||||
if (k == nkind.N_TCHAN) { return 8; };
|
||||
if (k == nkind.N_TSLICE) { return tyslicesize(): i32; };
|
||||
if (k == nkind.N_TTUPLE) {
|
||||
// Sum element sizes. Mirrors C cgen which uses raw type
|
||||
// sizes; padding to 8 happens inside slotsize for primitives,
|
||||
// so a `(i64, str)` resolves to 8 + 16 = 24 (matches the C
|
||||
// cgen 24B init / positional-access layout).
|
||||
let total: i32 = 0;
|
||||
let p: *node = typn.list;
|
||||
for (p != nil) {
|
||||
total += slotsize(c, p.lhs);
|
||||
p = p.next;
|
||||
};
|
||||
return total;
|
||||
};
|
||||
if (k == nkind.N_TTAGGED){
|
||||
// Nullable `(*T | void)` collapses to a single 8B pointer.
|
||||
if (isnullabletype(typn)) { return 8; };
|
||||
// Slot = 8 (tag) + max(variant payload sizes), rounded up
|
||||
// to an 8-byte multiple so the reg-passing ABI (size/8
|
||||
// words) doesn't drop the last value register. Mirrors C
|
||||
// cgen's resolve_type for nkind.N_TTAGGED.
|
||||
let v: *node = typn.list;
|
||||
let maxsz: i32 = 0;
|
||||
for (v != nil) {
|
||||
let sz: i32 = slotsize(c, v);
|
||||
if (sz > maxsz) { maxsz = sz; };
|
||||
v = v.next;
|
||||
};
|
||||
let pad: i32 = (maxsz + 7) & ~7;
|
||||
return 8 + pad;
|
||||
};
|
||||
if (k == nkind.N_TNAME) {
|
||||
let nm: str = typn.str;
|
||||
// `void` is zero-sized per Hare design; cstage's ty_void.size = 0
|
||||
// at cmd/wcc/type.c:46. Pre-#48 wwstage fell through to the
|
||||
// catch-all 8, so (void | !void) sized as tag + 8 = 16 and let-
|
||||
// init through cgwidentaggedstore emitted a phantom DX spill at
|
||||
// slot+8 picking up the callee's stale-DX. Bypassing primsize's
|
||||
// `> 0` guard keeps the rest of the prim-pad-to-8 contract intact.
|
||||
if (streq(nm, "void")) { return 0; };
|
||||
if (streq(nm, "str")) { return primtypesize("str"): i32; };
|
||||
let ps: i32 = primsize(nm);
|
||||
if (ps > 0) {
|
||||
// Pad to 8 for stack slots — matches C cgen which spills
|
||||
// every primitive into an 8-byte slot.
|
||||
return 8;
|
||||
};
|
||||
// Named struct lookup.
|
||||
let si: *structinfo = structlookup(c, nm);
|
||||
if (si != nil) { return si.totsize; };
|
||||
// Type alias (`type foo = !str;` / `type foo = bar;`):
|
||||
// follow it so a tagged-union variant of a !str-aliased
|
||||
// error type contributes 16 bytes to the max payload
|
||||
// rather than 8 (the default).
|
||||
if (c != nil) {
|
||||
let aliased: *node = aliaslookup(c, nm);
|
||||
if (aliased != nil) {
|
||||
if (aliased.kind == nkind.N_TBANG) {
|
||||
return slotsize(c, aliased.lhs);
|
||||
};
|
||||
return slotsize(c, aliased);
|
||||
};
|
||||
};
|
||||
return 8;
|
||||
};
|
||||
if (k == nkind.N_TARRAY) {
|
||||
let lenn: *node = typn.rhs;
|
||||
let elemn: *node = typn.lhs;
|
||||
let elen: i64 = 1i64;
|
||||
if (lenn != nil) {
|
||||
if (lenn.kind == nkind.N_INTLIT) { elen = lenn.uval: i64; };
|
||||
};
|
||||
let esz: i32 = 8;
|
||||
if (elemn != nil) {
|
||||
if (elemn.kind == nkind.N_TNAME) {
|
||||
let en: str = elemn.str;
|
||||
// `str` is a composite primitive (ptr+len, 16B);
|
||||
// primsize returns 0 for it, so without this
|
||||
// explicit case a `[N]str` would slot 8B/elem,
|
||||
// collapsing the per-element stride and losing
|
||||
// every .len half.
|
||||
if (streq(en, "str")) { esz = primtypesize("str"): i32; };
|
||||
let ps: i32 = primsize(en);
|
||||
if (esz == 8) { if (ps > 0) { esz = ps; }
|
||||
else {
|
||||
// Named struct / aliased type: size off
|
||||
// the structinfo if present, else follow
|
||||
// the alias via aliaslookup so
|
||||
// `[N]formattable` reads the resolved
|
||||
// tagged slot (e.g. 24B for
|
||||
// `(i64|str|bool)`), not the fall-
|
||||
// through 8B.
|
||||
let si: *structinfo = structlookup(c, en);
|
||||
if (si != nil) { esz = si.totsize; }
|
||||
else { if (c != nil) {
|
||||
let al: *node = aliaslookup(c, en);
|
||||
if (al != nil) {
|
||||
esz = slotsize(c, al);
|
||||
};
|
||||
}; };
|
||||
}; };
|
||||
} else { if (elemn.kind == nkind.N_TTAGGED) {
|
||||
// Tagged-union element: full slot (8 tag +
|
||||
// padded max payload). Matches C cgen's
|
||||
// resolve_type for `[N]TAGGED`.
|
||||
esz = slotsize(c, elemn);
|
||||
} else { if (elemn.kind == nkind.N_TPTR) {
|
||||
esz = 8;
|
||||
} else { if (elemn.kind == nkind.N_TSTRUCT) {
|
||||
esz = slotsize(c, elemn);
|
||||
}; }; }; };
|
||||
};
|
||||
return (esz: i64 * elen): i32;
|
||||
};
|
||||
if (k == nkind.N_TSTRUCT) {
|
||||
// Inline anonymous struct — sum of field sizes.
|
||||
let f: *node = typn.list;
|
||||
let total: i32 = 0;
|
||||
for (f != nil) {
|
||||
if (f.kind == nkind.N_TFIELD) {
|
||||
total += slotsize(c, f.lhs);
|
||||
};
|
||||
f = f.next;
|
||||
};
|
||||
return total;
|
||||
if (kk == tykind.TY_STRUCT || kk == tykind.TY_TUPLE ||
|
||||
kk == tykind.TY_ARRAY) {
|
||||
return ti.slotsize: i32;
|
||||
};
|
||||
return 8;
|
||||
};
|
||||
|
||||
@@ -1783,190 +1783,39 @@ export fn letslotsize(c: *cgen, n: *node) i32 = {
|
||||
return 8;
|
||||
};
|
||||
|
||||
// #48 A.6.3d: AST walker retired. resolvewalk (check.ww:426-436) stamps
|
||||
// `n.type_` on every N_T* kind via tinfofornode, which folds TBANG
|
||||
// (inner unchanged, check.ww:1154-1161), TNAME alias chains
|
||||
// (resolvealias, check.ww:1128-1153), TARRAY/TPTR/TSLICE/TCHAN/TFN/
|
||||
// TENUM/TTUPLE/TSTRUCT/TTAGGED with size + slot-padded slotsize.
|
||||
//
|
||||
// cstage SSoT is distributed — there is no single slot_size(Type*).
|
||||
// Tagged slot follows cstage cmd/wcc/check.c:348 + :998 (tag (8) +
|
||||
// max variant payload rounded to 8); the wwstage TTAGGED arm at
|
||||
// check.ww:1296-1346 mirrors that layout. cgen.c:4850 / :5193 use
|
||||
// the same `(su->kind == TY_TAGGED) ? su->size : 16` pattern for the
|
||||
// match-spill slot. Pointer-and-narrow → 8 is the local-frame
|
||||
// convention encoded at every cstage `localoff(…, 8, …)` callsite
|
||||
// (cmd/w6c/cgen.c throughout); wwstage encodes the same pad-to-8 at
|
||||
// this read site so `[N]i32` stride stays 4 (natural) — moving it
|
||||
// into ti.slotsize would lift array stride to 8/elem.
|
||||
//
|
||||
// `c: *cgen` retained unused for callsite stability (localloadop
|
||||
// precedent, 68219a1).
|
||||
fn slotsize(c: *cgen, typn: *node) i32 = {
|
||||
// #61 audit §1.8 — A.3 / A.4 / A.5 fast-path expansion. Read the
|
||||
// slot-padded width off the populated type-expression node when the
|
||||
// kind matches the cstage natural-size SSoT. Coverage:
|
||||
// - pointer-like (PTR/CHAN/FN), slice, str, tagged: ti.size ===
|
||||
// ti.slotsize (slot already equals natural). TY_TAGGED is safe
|
||||
// now that tinfofornode folds `(*T | void)` to 8B (#61 A.3 step
|
||||
// 1, mirrors cmd/wcc/check.c:412-426).
|
||||
// - narrow scalars (BOOL/RUNE/I8..I32/U8..U32/ENUM/F32) pad UP to
|
||||
// 8 — cstage's let_emit_size (cmd/w6c/cgen.c:691-720) spills
|
||||
// every primitive into an 8B stack slot regardless of
|
||||
// tinfo.size. Pad-to-8 lives at the read site, not in
|
||||
// tinfo.slotsize, so `[N]i32` stride stays 4 (natural) — moving
|
||||
// the pad into ti.slotsize would lift array stride to 8/elem.
|
||||
// - TY_VOID returns 0 (mirrors the TNAME-"void" fallback arm,
|
||||
// same as #61 A.4).
|
||||
// - #61 A.5 adds: TY_STRUCT / TY_TUPLE / TY_ARRAY read ti.slotsize
|
||||
// (slot-padded). tinfofornode populates the slot total mirroring
|
||||
// cgenutil.ww registerstruct (size-derived align, nested struct
|
||||
// fields → si.totsize, final round to 8), and TARRAY threads
|
||||
// stride through sub.slotsize so `[N]Triplet` lifts to padded *
|
||||
// N. `size(T)` stays natural — split SSoT in tinfo.
|
||||
if (typn != nil && typn.type_ != nil) {
|
||||
if (typn == nil) { return 8; };
|
||||
let ti: *tinfo = typn.type_: *tinfo;
|
||||
if (ti == nil) { return 8; };
|
||||
let kk: tykind = ti.kind;
|
||||
if (kk == tykind.TY_VOID) { return 0; };
|
||||
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_VOID) {
|
||||
kk == tykind.TY_STR || kk == tykind.TY_TAGGED) {
|
||||
return ti.size: i32;
|
||||
};
|
||||
if (kk == tykind.TY_STRUCT || kk == tykind.TY_TUPLE) {
|
||||
if (ti.slotsize > 0u64) { return ti.slotsize: i32; };
|
||||
};
|
||||
if (kk == tykind.TY_ARRAY) {
|
||||
if (ti.alen > 0u64) { return ti.slotsize: 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 ||
|
||||
kk == tykind.TY_U8 || kk == tykind.TY_U16 ||
|
||||
kk == tykind.TY_U32 || kk == tykind.TY_U64 ||
|
||||
kk == tykind.TY_INT || kk == tykind.TY_UINT ||
|
||||
kk == tykind.TY_UINTPTR || kk == tykind.TY_ENUM ||
|
||||
kk == tykind.TY_F32 || kk == tykind.TY_F64) {
|
||||
return 8;
|
||||
};
|
||||
};
|
||||
if (typn == nil) { return 8; };
|
||||
let k: nkind = typn.kind;
|
||||
// `!T` carries T's memory layout; the error-tag bit lives in the
|
||||
// enclosing union's discriminant, not the variant payload. cstage
|
||||
// resolve_type N_TBANG copies the inner size (cmd/wcc/check.c:303-
|
||||
// 306); wwstage previously fell through to the catch-all 8, so a
|
||||
// bare `!void` variant sized to 8 instead of 0. (#48 part-A.)
|
||||
if (k == nkind.N_TBANG) { return slotsize(c, typn.lhs); };
|
||||
if (k == nkind.N_TPTR) { return 8; };
|
||||
if (k == nkind.N_TFN) { return 8; };
|
||||
if (k == nkind.N_TCHAN) { return 8; };
|
||||
if (k == nkind.N_TSLICE) { return tyslicesize(): i32; };
|
||||
if (k == nkind.N_TTUPLE) {
|
||||
// Sum element sizes. Mirrors C cgen which uses raw type
|
||||
// sizes; padding to 8 happens inside slotsize for primitives,
|
||||
// so a `(i64, str)` resolves to 8 + 16 = 24 (matches the C
|
||||
// cgen 24B init / positional-access layout).
|
||||
let total: i32 = 0;
|
||||
let p: *node = typn.list;
|
||||
for (p != nil) {
|
||||
total += slotsize(c, p.lhs);
|
||||
p = p.next;
|
||||
};
|
||||
return total;
|
||||
};
|
||||
if (k == nkind.N_TTAGGED){
|
||||
// Nullable `(*T | void)` collapses to a single 8B pointer.
|
||||
if (isnullabletype(typn)) { return 8; };
|
||||
// Slot = 8 (tag) + max(variant payload sizes), rounded up
|
||||
// to an 8-byte multiple so the reg-passing ABI (size/8
|
||||
// words) doesn't drop the last value register. Mirrors C
|
||||
// cgen's resolve_type for nkind.N_TTAGGED.
|
||||
let v: *node = typn.list;
|
||||
let maxsz: i32 = 0;
|
||||
for (v != nil) {
|
||||
let sz: i32 = slotsize(c, v);
|
||||
if (sz > maxsz) { maxsz = sz; };
|
||||
v = v.next;
|
||||
};
|
||||
let pad: i32 = (maxsz + 7) & ~7;
|
||||
return 8 + pad;
|
||||
};
|
||||
if (k == nkind.N_TNAME) {
|
||||
let nm: str = typn.str;
|
||||
// `void` is zero-sized per Hare design; cstage's ty_void.size = 0
|
||||
// at cmd/wcc/type.c:46. Pre-#48 wwstage fell through to the
|
||||
// catch-all 8, so (void | !void) sized as tag + 8 = 16 and let-
|
||||
// init through cgwidentaggedstore emitted a phantom DX spill at
|
||||
// slot+8 picking up the callee's stale-DX. Bypassing primsize's
|
||||
// `> 0` guard keeps the rest of the prim-pad-to-8 contract intact.
|
||||
if (streq(nm, "void")) { return 0; };
|
||||
if (streq(nm, "str")) { return primtypesize("str"): i32; };
|
||||
let ps: i32 = primsize(nm);
|
||||
if (ps > 0) {
|
||||
// Pad to 8 for stack slots — matches C cgen which spills
|
||||
// every primitive into an 8-byte slot.
|
||||
return 8;
|
||||
};
|
||||
// Named struct lookup.
|
||||
let si: *structinfo = structlookup(c, nm);
|
||||
if (si != nil) { return si.totsize; };
|
||||
// Type alias (`type foo = !str;` / `type foo = bar;`):
|
||||
// follow it so a tagged-union variant of a !str-aliased
|
||||
// error type contributes 16 bytes to the max payload
|
||||
// rather than 8 (the default).
|
||||
if (c != nil) {
|
||||
let aliased: *node = aliaslookup(c, nm);
|
||||
if (aliased != nil) {
|
||||
if (aliased.kind == nkind.N_TBANG) {
|
||||
return slotsize(c, aliased.lhs);
|
||||
};
|
||||
return slotsize(c, aliased);
|
||||
};
|
||||
};
|
||||
return 8;
|
||||
};
|
||||
if (k == nkind.N_TARRAY) {
|
||||
let lenn: *node = typn.rhs;
|
||||
let elemn: *node = typn.lhs;
|
||||
let elen: i64 = 1i64;
|
||||
if (lenn != nil) {
|
||||
if (lenn.kind == nkind.N_INTLIT) { elen = lenn.uval: i64; };
|
||||
};
|
||||
let esz: i32 = 8;
|
||||
if (elemn != nil) {
|
||||
if (elemn.kind == nkind.N_TNAME) {
|
||||
let en: str = elemn.str;
|
||||
// `str` is a composite primitive (ptr+len, 16B);
|
||||
// primsize returns 0 for it, so without this
|
||||
// explicit case a `[N]str` would slot 8B/elem,
|
||||
// collapsing the per-element stride and losing
|
||||
// every .len half.
|
||||
if (streq(en, "str")) { esz = primtypesize("str"): i32; };
|
||||
let ps: i32 = primsize(en);
|
||||
if (esz == 8) { if (ps > 0) { esz = ps; }
|
||||
else {
|
||||
// Named struct / aliased type: size off
|
||||
// the structinfo if present, else follow
|
||||
// the alias via aliaslookup so
|
||||
// `[N]formattable` reads the resolved
|
||||
// tagged slot (e.g. 24B for
|
||||
// `(i64|str|bool)`), not the fall-
|
||||
// through 8B.
|
||||
let si: *structinfo = structlookup(c, en);
|
||||
if (si != nil) { esz = si.totsize; }
|
||||
else { if (c != nil) {
|
||||
let al: *node = aliaslookup(c, en);
|
||||
if (al != nil) {
|
||||
esz = slotsize(c, al);
|
||||
};
|
||||
}; };
|
||||
}; };
|
||||
} else { if (elemn.kind == nkind.N_TTAGGED) {
|
||||
// Tagged-union element: full slot (8 tag +
|
||||
// padded max payload). Matches C cgen's
|
||||
// resolve_type for `[N]TAGGED`.
|
||||
esz = slotsize(c, elemn);
|
||||
} else { if (elemn.kind == nkind.N_TPTR) {
|
||||
esz = 8;
|
||||
} else { if (elemn.kind == nkind.N_TSTRUCT) {
|
||||
esz = slotsize(c, elemn);
|
||||
}; }; }; };
|
||||
};
|
||||
return (esz: i64 * elen): i32;
|
||||
};
|
||||
if (k == nkind.N_TSTRUCT) {
|
||||
// Inline anonymous struct — sum of field sizes.
|
||||
let f: *node = typn.list;
|
||||
let total: i32 = 0;
|
||||
for (f != nil) {
|
||||
if (f.kind == nkind.N_TFIELD) {
|
||||
total += slotsize(c, f.lhs);
|
||||
};
|
||||
f = f.next;
|
||||
};
|
||||
return total;
|
||||
if (kk == tykind.TY_STRUCT || kk == tykind.TY_TUPLE ||
|
||||
kk == tykind.TY_ARRAY) {
|
||||
return ti.slotsize: i32;
|
||||
};
|
||||
return 8;
|
||||
};
|
||||
|
||||
@@ -12155,190 +12155,39 @@ export fn letslotsize(c: *cgen, n: *node) i32 = {
|
||||
return 8;
|
||||
};
|
||||
|
||||
// #48 A.6.3d: AST walker retired. resolvewalk (check.ww:426-436) stamps
|
||||
// `n.type_` on every N_T* kind via tinfofornode, which folds TBANG
|
||||
// (inner unchanged, check.ww:1154-1161), TNAME alias chains
|
||||
// (resolvealias, check.ww:1128-1153), TARRAY/TPTR/TSLICE/TCHAN/TFN/
|
||||
// TENUM/TTUPLE/TSTRUCT/TTAGGED with size + slot-padded slotsize.
|
||||
//
|
||||
// cstage SSoT is distributed — there is no single slot_size(Type*).
|
||||
// Tagged slot follows cstage cmd/wcc/check.c:348 + :998 (tag (8) +
|
||||
// max variant payload rounded to 8); the wwstage TTAGGED arm at
|
||||
// check.ww:1296-1346 mirrors that layout. cgen.c:4850 / :5193 use
|
||||
// the same `(su->kind == TY_TAGGED) ? su->size : 16` pattern for the
|
||||
// match-spill slot. Pointer-and-narrow → 8 is the local-frame
|
||||
// convention encoded at every cstage `localoff(…, 8, …)` callsite
|
||||
// (cmd/w6c/cgen.c throughout); wwstage encodes the same pad-to-8 at
|
||||
// this read site so `[N]i32` stride stays 4 (natural) — moving it
|
||||
// into ti.slotsize would lift array stride to 8/elem.
|
||||
//
|
||||
// `c: *cgen` retained unused for callsite stability (localloadop
|
||||
// precedent, 68219a1).
|
||||
fn slotsize(c: *cgen, typn: *node) i32 = {
|
||||
// #61 audit §1.8 — A.3 / A.4 / A.5 fast-path expansion. Read the
|
||||
// slot-padded width off the populated type-expression node when the
|
||||
// kind matches the cstage natural-size SSoT. Coverage:
|
||||
// - pointer-like (PTR/CHAN/FN), slice, str, tagged: ti.size ===
|
||||
// ti.slotsize (slot already equals natural). TY_TAGGED is safe
|
||||
// now that tinfofornode folds `(*T | void)` to 8B (#61 A.3 step
|
||||
// 1, mirrors cmd/wcc/check.c:412-426).
|
||||
// - narrow scalars (BOOL/RUNE/I8..I32/U8..U32/ENUM/F32) pad UP to
|
||||
// 8 — cstage's let_emit_size (cmd/w6c/cgen.c:691-720) spills
|
||||
// every primitive into an 8B stack slot regardless of
|
||||
// tinfo.size. Pad-to-8 lives at the read site, not in
|
||||
// tinfo.slotsize, so `[N]i32` stride stays 4 (natural) — moving
|
||||
// the pad into ti.slotsize would lift array stride to 8/elem.
|
||||
// - TY_VOID returns 0 (mirrors the TNAME-"void" fallback arm,
|
||||
// same as #61 A.4).
|
||||
// - #61 A.5 adds: TY_STRUCT / TY_TUPLE / TY_ARRAY read ti.slotsize
|
||||
// (slot-padded). tinfofornode populates the slot total mirroring
|
||||
// cgenutil.ww registerstruct (size-derived align, nested struct
|
||||
// fields → si.totsize, final round to 8), and TARRAY threads
|
||||
// stride through sub.slotsize so `[N]Triplet` lifts to padded *
|
||||
// N. `size(T)` stays natural — split SSoT in tinfo.
|
||||
if (typn != nil && typn.type_ != nil) {
|
||||
if (typn == nil) { return 8; };
|
||||
let ti: *tinfo = typn.type_: *tinfo;
|
||||
if (ti == nil) { return 8; };
|
||||
let kk: tykind = ti.kind;
|
||||
if (kk == tykind.TY_VOID) { return 0; };
|
||||
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_VOID) {
|
||||
kk == tykind.TY_STR || kk == tykind.TY_TAGGED) {
|
||||
return ti.size: i32;
|
||||
};
|
||||
if (kk == tykind.TY_STRUCT || kk == tykind.TY_TUPLE) {
|
||||
if (ti.slotsize > 0u64) { return ti.slotsize: i32; };
|
||||
};
|
||||
if (kk == tykind.TY_ARRAY) {
|
||||
if (ti.alen > 0u64) { return ti.slotsize: 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 ||
|
||||
kk == tykind.TY_U8 || kk == tykind.TY_U16 ||
|
||||
kk == tykind.TY_U32 || kk == tykind.TY_U64 ||
|
||||
kk == tykind.TY_INT || kk == tykind.TY_UINT ||
|
||||
kk == tykind.TY_UINTPTR || kk == tykind.TY_ENUM ||
|
||||
kk == tykind.TY_F32 || kk == tykind.TY_F64) {
|
||||
return 8;
|
||||
};
|
||||
};
|
||||
if (typn == nil) { return 8; };
|
||||
let k: nkind = typn.kind;
|
||||
// `!T` carries T's memory layout; the error-tag bit lives in the
|
||||
// enclosing union's discriminant, not the variant payload. cstage
|
||||
// resolve_type N_TBANG copies the inner size (cmd/wcc/check.c:303-
|
||||
// 306); wwstage previously fell through to the catch-all 8, so a
|
||||
// bare `!void` variant sized to 8 instead of 0. (#48 part-A.)
|
||||
if (k == nkind.N_TBANG) { return slotsize(c, typn.lhs); };
|
||||
if (k == nkind.N_TPTR) { return 8; };
|
||||
if (k == nkind.N_TFN) { return 8; };
|
||||
if (k == nkind.N_TCHAN) { return 8; };
|
||||
if (k == nkind.N_TSLICE) { return tyslicesize(): i32; };
|
||||
if (k == nkind.N_TTUPLE) {
|
||||
// Sum element sizes. Mirrors C cgen which uses raw type
|
||||
// sizes; padding to 8 happens inside slotsize for primitives,
|
||||
// so a `(i64, str)` resolves to 8 + 16 = 24 (matches the C
|
||||
// cgen 24B init / positional-access layout).
|
||||
let total: i32 = 0;
|
||||
let p: *node = typn.list;
|
||||
for (p != nil) {
|
||||
total += slotsize(c, p.lhs);
|
||||
p = p.next;
|
||||
};
|
||||
return total;
|
||||
};
|
||||
if (k == nkind.N_TTAGGED){
|
||||
// Nullable `(*T | void)` collapses to a single 8B pointer.
|
||||
if (isnullabletype(typn)) { return 8; };
|
||||
// Slot = 8 (tag) + max(variant payload sizes), rounded up
|
||||
// to an 8-byte multiple so the reg-passing ABI (size/8
|
||||
// words) doesn't drop the last value register. Mirrors C
|
||||
// cgen's resolve_type for nkind.N_TTAGGED.
|
||||
let v: *node = typn.list;
|
||||
let maxsz: i32 = 0;
|
||||
for (v != nil) {
|
||||
let sz: i32 = slotsize(c, v);
|
||||
if (sz > maxsz) { maxsz = sz; };
|
||||
v = v.next;
|
||||
};
|
||||
let pad: i32 = (maxsz + 7) & ~7;
|
||||
return 8 + pad;
|
||||
};
|
||||
if (k == nkind.N_TNAME) {
|
||||
let nm: str = typn.str;
|
||||
// `void` is zero-sized per Hare design; cstage's ty_void.size = 0
|
||||
// at cmd/wcc/type.c:46. Pre-#48 wwstage fell through to the
|
||||
// catch-all 8, so (void | !void) sized as tag + 8 = 16 and let-
|
||||
// init through cgwidentaggedstore emitted a phantom DX spill at
|
||||
// slot+8 picking up the callee's stale-DX. Bypassing primsize's
|
||||
// `> 0` guard keeps the rest of the prim-pad-to-8 contract intact.
|
||||
if (streq(nm, "void")) { return 0; };
|
||||
if (streq(nm, "str")) { return primtypesize("str"): i32; };
|
||||
let ps: i32 = primsize(nm);
|
||||
if (ps > 0) {
|
||||
// Pad to 8 for stack slots — matches C cgen which spills
|
||||
// every primitive into an 8-byte slot.
|
||||
return 8;
|
||||
};
|
||||
// Named struct lookup.
|
||||
let si: *structinfo = structlookup(c, nm);
|
||||
if (si != nil) { return si.totsize; };
|
||||
// Type alias (`type foo = !str;` / `type foo = bar;`):
|
||||
// follow it so a tagged-union variant of a !str-aliased
|
||||
// error type contributes 16 bytes to the max payload
|
||||
// rather than 8 (the default).
|
||||
if (c != nil) {
|
||||
let aliased: *node = aliaslookup(c, nm);
|
||||
if (aliased != nil) {
|
||||
if (aliased.kind == nkind.N_TBANG) {
|
||||
return slotsize(c, aliased.lhs);
|
||||
};
|
||||
return slotsize(c, aliased);
|
||||
};
|
||||
};
|
||||
return 8;
|
||||
};
|
||||
if (k == nkind.N_TARRAY) {
|
||||
let lenn: *node = typn.rhs;
|
||||
let elemn: *node = typn.lhs;
|
||||
let elen: i64 = 1i64;
|
||||
if (lenn != nil) {
|
||||
if (lenn.kind == nkind.N_INTLIT) { elen = lenn.uval: i64; };
|
||||
};
|
||||
let esz: i32 = 8;
|
||||
if (elemn != nil) {
|
||||
if (elemn.kind == nkind.N_TNAME) {
|
||||
let en: str = elemn.str;
|
||||
// `str` is a composite primitive (ptr+len, 16B);
|
||||
// primsize returns 0 for it, so without this
|
||||
// explicit case a `[N]str` would slot 8B/elem,
|
||||
// collapsing the per-element stride and losing
|
||||
// every .len half.
|
||||
if (streq(en, "str")) { esz = primtypesize("str"): i32; };
|
||||
let ps: i32 = primsize(en);
|
||||
if (esz == 8) { if (ps > 0) { esz = ps; }
|
||||
else {
|
||||
// Named struct / aliased type: size off
|
||||
// the structinfo if present, else follow
|
||||
// the alias via aliaslookup so
|
||||
// `[N]formattable` reads the resolved
|
||||
// tagged slot (e.g. 24B for
|
||||
// `(i64|str|bool)`), not the fall-
|
||||
// through 8B.
|
||||
let si: *structinfo = structlookup(c, en);
|
||||
if (si != nil) { esz = si.totsize; }
|
||||
else { if (c != nil) {
|
||||
let al: *node = aliaslookup(c, en);
|
||||
if (al != nil) {
|
||||
esz = slotsize(c, al);
|
||||
};
|
||||
}; };
|
||||
}; };
|
||||
} else { if (elemn.kind == nkind.N_TTAGGED) {
|
||||
// Tagged-union element: full slot (8 tag +
|
||||
// padded max payload). Matches C cgen's
|
||||
// resolve_type for `[N]TAGGED`.
|
||||
esz = slotsize(c, elemn);
|
||||
} else { if (elemn.kind == nkind.N_TPTR) {
|
||||
esz = 8;
|
||||
} else { if (elemn.kind == nkind.N_TSTRUCT) {
|
||||
esz = slotsize(c, elemn);
|
||||
}; }; }; };
|
||||
};
|
||||
return (esz: i64 * elen): i32;
|
||||
};
|
||||
if (k == nkind.N_TSTRUCT) {
|
||||
// Inline anonymous struct — sum of field sizes.
|
||||
let f: *node = typn.list;
|
||||
let total: i32 = 0;
|
||||
for (f != nil) {
|
||||
if (f.kind == nkind.N_TFIELD) {
|
||||
total += slotsize(c, f.lhs);
|
||||
};
|
||||
f = f.next;
|
||||
};
|
||||
return total;
|
||||
if (kk == tykind.TY_STRUCT || kk == tykind.TY_TUPLE ||
|
||||
kk == tykind.TY_ARRAY) {
|
||||
return ti.slotsize: i32;
|
||||
};
|
||||
return 8;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user