diff --git a/cmd/w6c/cgen.c b/cmd/w6c/cgen.c index cf524b94..6b5ae535 100644 --- a/cmd/w6c/cgen.c +++ b/cmd/w6c/cgen.c @@ -705,9 +705,8 @@ let_emit_size(Type *t) case TY_F64: return 8; /* MOVSD loads/stores 8B via LEAQ+indir. */ case TY_STR: - return 16; /* {ptr, len}; literal-strlit init NYI. */ case TY_SLICE: - return 24; /* {ptr, len, cap}; no init only. */ + return (int)u->size; /* #43: ty_str / ty_slice SSoT. */ case TY_STRUCT: return (int)u->size; /* zero-init only; field reads/ * scalar-field writes only. */ @@ -4988,10 +4987,10 @@ cgexpr(Cg *c, Node *n, Local *locals) amem(D_BP, voff)); } } else { + /* #43: route through Type.size SSoT rather + * than re-asserting 16/24 for str/slice. */ int bsz = 8; - if (bu && bu->kind == TY_STR) bsz = 16; - else if (bu && bu->kind == TY_SLICE) bsz = 24; - else if (bu) bsz = (int)bu->size; + if (bu) bsz = (int)bu->size; if (bsz <= 0) bsz = 8; /* local_alloc to dodge name-collision * dedup — a 16B str bind shadowing an @@ -6350,13 +6349,14 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame) * collapsed the struct/slice/tagged sizing arms to the 8B * fallback, and the slot under-allocated the local. */ Type *lu = type_chase_named(lt); + /* #43: every composite kind already has its byte size cached in + * lu->size; route through it instead of re-asserting 16/24 for + * str/slice and re-reading for the others. */ int sz = 8; - if (lu && lu->kind == TY_ARRAY) sz = (int)lu->size; - else if (lu && lu->kind == TY_SLICE) sz = 24; - else if (lu && lu->kind == TY_STR) sz = 16; - else if (lu && lu->kind == TY_STRUCT) sz = (int)lu->size; - else if (lu && lu->kind == TY_TUPLE) sz = (int)lu->size; - else if (lu && lu->kind == TY_TAGGED) sz = (int)lu->size; + if (lu && (lu->kind == TY_ARRAY || lu->kind == TY_SLICE + || lu->kind == TY_STR || lu->kind == TY_STRUCT + || lu->kind == TY_TUPLE || lu->kind == TY_TAGGED)) + sz = (int)lu->size; int off = localoff(c, locals, n->str, sz, frame); int isf = cg_isfloat(lt); int isf32 = type_isf32(lt); @@ -6433,8 +6433,10 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame) break; } } - /* str initialiser: cgexpr produces (AX=ptr, BX=len). */ - if (n->rhs && type_isstr(lt) && sz == 16) { + /* str initialiser: cgexpr produces (AX=ptr, BX=len). + * #43: gate width via ty_str->size so a future str-layout + * bump (#1) propagates without touching this site. */ + if (n->rhs && type_isstr(lt) && sz == (int)ty_str->size) { cgexpr(c, n->rhs, *locals); ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off + 0)); ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, off + 8)); @@ -7763,7 +7765,8 @@ emit_lets(Cg *c, FILE *out, Node *file) /* str literal init: bake the interned label's address * into the ptr half via a DATAR reloc, set the len half * inline. */ - if (sz == 16 && r != NULL && r->kind == N_STRLIT + /* #43: gate via ty_str->size so #1 propagates. */ + if (sz == (int)ty_str->size && r != NULL && r->kind == N_STRLIT && r->strlen > 0) { const char *lab = intern_strlit(c, r->str, r->strlen); const char *sym = mod_mangle(c, d->str); @@ -7916,7 +7919,7 @@ let_pre_intern(Cg *c, Node *file) if (file == NULL) return; for (Node *d = file->list; d; d = d->next) { if (d->kind != N_LET) continue; - if (let_emit_size(d->type) != 16) continue; + if (let_emit_size(d->type) != (int)ty_str->size) continue; Node *r = d->rhs; while (r != NULL && r->kind == N_CAST) r = r->lhs; if (r == NULL || r->kind != N_STRLIT) continue; diff --git a/lib/shlex/shlex.ww b/lib/shlex/shlex.ww index c6708abe..b1287870 100644 --- a/lib/shlex/shlex.ww +++ b/lib/shlex/shlex.ww @@ -139,7 +139,7 @@ fn dupstr(s: str) str = { fn appendstr(slice: *[]str, item: str) void = { let newlen: i32 = slice.len + 1; slice.len = newlen; - rtensure(slice: *void, 16u64); + rtensure(slice: *void, size(str): u64); let dst: *str = &slice.ptr[newlen - 1]; dst.ptr = item.ptr; dst.len = item.len; @@ -159,7 +159,7 @@ fn freepartial(slice: []str) void = { i += 1; }; if (slice.cap > 0) { - os.free(slice.ptr: *void, (slice.cap: u64) * 16u64); + os.free(slice.ptr: *void, (slice.cap: u64) * size(str): u64); }; }; diff --git a/lib/strings/strings.ww b/lib/strings/strings.ww index 2178d557..e2c1f724 100644 --- a/lib/strings/strings.ww +++ b/lib/strings/strings.ww @@ -159,7 +159,7 @@ export fn freeall(s: []str) void = { i += 1; }; if (s.cap > 0) { - os.free(s.ptr: *void, (s.cap: u64) * 16u64); + os.free(s.ptr: *void, (s.cap: u64) * size(str): u64); }; }; @@ -762,7 +762,7 @@ export fn remaining_tokens(s: *tokenizer) str = { fn appendstr(slice: *[]str, item: str) void = { let newlen: i32 = slice.len + 1; slice.len = newlen; - rtensure(slice: *void, 16u64); + rtensure(slice: *void, size(str): u64); let dst: *str = &slice.ptr[newlen - 1]; dst.ptr = item.ptr; dst.len = item.len; diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index d6e0bcf0..3d3ccf87 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -1996,7 +1996,7 @@ export fn freeall(s: []str) void = { i += 1; }; if (s.cap > 0) { - os.free(s.ptr: *void, (s.cap: u64) * 16u64); + os.free(s.ptr: *void, (s.cap: u64) * size(str): u64); }; }; @@ -2599,7 +2599,7 @@ export fn remaining_tokens(s: *tokenizer) str = { fn appendstr(slice: *[]str, item: str) void = { let newlen: i32 = slice.len + 1; slice.len = newlen; - rtensure(slice: *void, 16u64); + rtensure(slice: *void, size(str): u64); let dst: *str = &slice.ptr[newlen - 1]; dst.ptr = item.ptr; dst.len = item.len; @@ -7612,6 +7612,29 @@ fn mktname(c: *checker, nm: str) *node = { return n; }; +// #43: SSoT for primitive type byte sizes. astsize's N_TNAME-primitive +// arm and every wwstage cgen size walker (slotsize/fieldsize/letemit- +// size/elemsizeof/paramfieldsize) consult this table so a future +// ty_str.size bump (#1) lands in one place. Returns -1 for non-prim +// names; callers fall back to alias/struct/enum lookup. Cstage's +// equivalent SSoT is cmd/wcc/type.c:46-79 (ty_void/ty_bool/.../ty_str). +fn primtypesize(nm: str) i64 = { + if (streq(nm, "void")) { return 0i64; }; + if (streq(nm, "bool")) { return 1i64; }; + if (streq(nm, "i8") || streq(nm, "u8")) { return 1i64; }; + if (streq(nm, "i16") || streq(nm, "u16")) { return 2i64; }; + if (streq(nm, "i32") || streq(nm, "u32") || streq(nm, "f32") || streq(nm, "rune")) { return 4i64; }; + if (streq(nm, "i64") || streq(nm, "u64") || streq(nm, "f64")) { return 8i64; }; + if (streq(nm, "int") || streq(nm, "uint") || streq(nm, "uintptr")) { return 8i64; }; + if (streq(nm, "str")) { return 16i64; }; + return -1i64; +}; + +// #43: SSoT for slice header size (ptr+len+cap = 24B today). Mirrors +// cstage cmd/wcc/type.c:103 (ty_slice->size = 24). Bumping a slice's +// header layout in #34 touches only this constant. +fn tyslicesize() i64 = { return 24i64; }; + // #42: AST-level layout helpers for the size(T)/align(T)/offset(e.f) // fold. Mirror cstage resolve_type's size/align computation // (cmd/wcc/check.c:286-528) on AST nodes — wwstage check.ww never @@ -7673,7 +7696,7 @@ fn astsize(c: *checker, t: *node) i64 = { let k: nkind = t.kind; if (k == nkind.N_TBANG) { return astsize(c, t.lhs); }; if (k == nkind.N_TPTR) { return 8i64; }; - if (k == nkind.N_TSLICE) { return 24i64; }; + if (k == nkind.N_TSLICE) { return tyslicesize(); }; if (k == nkind.N_TCHAN) { return 8i64; }; if (k == nkind.N_TFN) { return 8i64; }; if (k == nkind.N_TARRAY) { @@ -7725,13 +7748,8 @@ fn astsize(c: *checker, t: *node) i64 = { }; if (k == nkind.N_TNAME) { let nm: str = t.str; - if (streq(nm, "void")) { return 0i64; }; - if (streq(nm, "bool")) { return 1i64; }; - if (streq(nm, "i8") || streq(nm, "u8")) { return 1i64; }; - if (streq(nm, "i16") || streq(nm, "u16")) { return 2i64; }; - if (streq(nm, "i32") || streq(nm, "u32") || streq(nm, "f32") || streq(nm, "rune")) { return 4i64; }; - if (streq(nm, "i64") || streq(nm, "u64") || streq(nm, "f64") || streq(nm, "int") || streq(nm, "uint") || streq(nm, "uintptr")) { return 8i64; }; - if (streq(nm, "str")) { return 16i64; }; + let ps: i64 = primtypesize(nm); + if (ps >= 0i64) { return ps; }; let resolved: *node = resolvealias(c, t); if (resolved != nil && resolved != t) { return astsize(c, resolved); @@ -9854,7 +9872,7 @@ fn indexbaseesz(c: *cgen, base: *node) i32 = { let elem: *node = ft.lhs; if (elem != nil) { if (elem.kind == nkind.N_TNAME) { - if (streq(elem.str, "str")) { return 16; }; + if (streq(elem.str, "str")) { return primtypesize("str"): i32; }; let ps: i32 = primsize(elem.str); if (ps > 0) { return ps; }; // Pointer to named struct: indexing @@ -9962,11 +9980,11 @@ fn elemsizeof(t: *node) i32 = { // `*[]T`: stride is the slice header (24B). Hare-faithful — a // pointer-to-slice is a 1D array of slices, not of T. Mirrors the // cstage check.c default `*U → U` path for U=[]T (slice element). - if (elem.kind == nkind.N_TSLICE) { return 24; }; + if (elem.kind == nkind.N_TSLICE) { return tyslicesize(): i32; }; if (elem.kind == nkind.N_TNAME) { let nm: str = elem.str; // str element is 16B (ptr+len). primsize returns 0 for it. - if (streq(nm, "str")) { return 16; }; + if (streq(nm, "str")) { return primtypesize("str"): i32; }; let ps: i32 = primsize(nm); if (ps > 0) { return ps; }; }; @@ -10561,7 +10579,7 @@ export fn letslotsize(c: *cgen, n: *node) i32 = { // (ptr+len) — primsize returns 0 for // it, so it'd slot 8B without this. if (streq(elemn.str, "str")) { - esz = 16; + esz = primtypesize("str"): i32; } else { let ps: i32 = primsize(elemn.str); if (ps > 0) { esz = ps; }; @@ -10610,7 +10628,7 @@ fn slotsize(c: *cgen, typn: *node) i32 = { 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 24; }; + 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, @@ -10650,7 +10668,7 @@ fn slotsize(c: *cgen, typn: *node) i32 = { // 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 16; }; + 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 @@ -10691,7 +10709,7 @@ fn slotsize(c: *cgen, typn: *node) i32 = { // explicit case a `[N]str` would slot 8B/elem, // collapsing the per-element stride and losing // every .len half. - if (streq(en, "str")) { esz = 16; }; + if (streq(en, "str")) { esz = primtypesize("str"): i32; }; let ps: i32 = primsize(en); if (esz == 8) { if (ps > 0) { esz = ps; } else { @@ -10749,7 +10767,7 @@ fn fieldsize(c: *cgen, tnode: *node) i32 = { if (k == nkind.N_TTAGGED){ return slotsize(c, tnode); }; if (k == nkind.N_TNAME) { let nm: str = tnode.str; - if (streq(nm, "str")) { return 16; }; + if (streq(nm, "str")) { return primtypesize("str"): i32; }; let ps: i32 = primsize(nm); if (ps > 0) { return ps; }; let si: *structinfo = structlookup(c, nm); @@ -10776,7 +10794,7 @@ fn fieldsize(c: *cgen, tnode: *node) i32 = { return 8; }; if (k == nkind.N_TPTR) { return 8; }; - if (k == nkind.N_TSLICE) { return 24; }; + if (k == nkind.N_TSLICE) { return tyslicesize(): i32; }; if (k == nkind.N_TARRAY) { // Same shape as slotsize's TARRAY branch. let lenn: *node = tnode.rhs; @@ -19139,7 +19157,7 @@ fn cglet(c: *cgen, n: *node) void = { if (elemn != nil) { if (elemn.kind == nkind.N_TNAME) { if (streq(elemn.str, "str")) { - esz = 16; + esz = primtypesize("str"): i32; isstrel = true; } else { let ps: i32 = primsize(elemn.str); @@ -19624,8 +19642,8 @@ fn cgmlet(c: *cgen, n: *node) void = { if (s0_is_str != s1_is_str) { let sz0: i32 = 8; let sz1: i32 = 8; - if (s0_is_str) { sz0 = 16; }; - if (s1_is_str) { sz1 = 16; }; + if (s0_is_str) { sz0 = primtypesize("str"): i32; }; + if (s1_is_str) { sz1 = primtypesize("str"): i32; }; let off0: i32 = localadd(c, l0.str, sz0, t0); let off1: i32 = localadd(c, l1.str, sz1, t1); if (s0_is_str) { @@ -19682,7 +19700,7 @@ fn paramfieldsize(t: *node) i32 = { if (k == nkind.N_TCHAN) { return 8; }; if (k == nkind.N_TNAME) { let nm: str = t.str; - if (streq(nm, "str")) { return 16; }; + if (streq(nm, "str")) { return primtypesize("str"): i32; }; let ps: i32 = primsize(nm); if (ps > 0) { return ps; }; }; @@ -20054,7 +20072,7 @@ fn cgfnparams(c: *cgen, params: *node) void = { if (p.op == tkind.TK_ELLIPSIS) { let tn: *node = slicewrap(c, p.lhs); if (idx + 3 <= 6) { - let off: i32 = localadd(c, nm, 24, tn); + let off: i32 = localadd(c, nm, tyslicesize(): i32, tn); emitline("\tMOVQ\t"); emitline(argregname(idx)); emitline(", "); @@ -20078,7 +20096,7 @@ fn cgfnparams(c: *cgen, params: *node) void = { // at the ABI boundary (the call site synthesises a // 24B descriptor and pushes ptr/len/cap), so this // mirrors the slice branch at cgendecl.ww:518. - let off: i32 = localadd(c, nm, 24, tn); + let off: i32 = localadd(c, nm, tyslicesize(): i32, tn); let regs_left: i32 = 6 - idx; let w: i32 = 0; for (w < regs_left) { @@ -20178,7 +20196,7 @@ fn cgfnparams(c: *cgen, params: *node) void = { };}; } else { if (isslicetype(c, p.lhs)) { if (idx + 3 <= 6) { - let off: i32 = localadd(c, nm, 24, p.lhs); + let off: i32 = localadd(c, nm, tyslicesize(): i32, p.lhs); emitline("\tMOVQ\t"); emitline(argregname(idx)); emitline(", "); @@ -20202,7 +20220,7 @@ fn cgfnparams(c: *cgen, params: *node) void = { // 440-469. Caller's pushargsrev greedy-fills the // remaining argregs (ptr,len,cap order), the tail // spills to +16+stkcursor*8(BP). - let off: i32 = localadd(c, nm, 24, p.lhs); + let off: i32 = localadd(c, nm, tyslicesize(): i32, p.lhs); let regs_left: i32 = 6 - idx; let w: i32 = 0; for (w < regs_left) { @@ -21426,7 +21444,7 @@ fn letemitsize(c: *cgen, d: *node) i32 = { let t: *node = d.lhs; for (t != nil) { if (t.kind == nkind.N_TPTR) { return 8; }; - if (t.kind == nkind.N_TSLICE) { return 24; }; + if (t.kind == nkind.N_TSLICE) { return tyslicesize(): i32; }; if (t.kind == nkind.N_TARRAY) { let lenn: *node = t.rhs; let elemn: *node = t.lhs; @@ -21448,7 +21466,7 @@ fn letemitsize(c: *cgen, d: *node) i32 = { if (letscalarprim(nm)) { return 8; }; let fsz: i32 = letfloatprim(nm); if (fsz > 0) { return fsz; }; - if (streq(nm, "str")) { return 16; }; + if (streq(nm, "str")) { return primtypesize("str"): i32; }; let si: *structinfo = structlookup(c, nm); if (si != nil) { return si.totsize; }; let next: *node = aliaslookup(c, nm); @@ -21774,7 +21792,7 @@ fn emitletdataw(c: *cgen, file: *node) void = { emitline("\"\n"); }; }; - if (sz == 16 && !issg) { + if (sz == primtypesize("str"): i32 && !issg) { let r: *node = d.rhs; for (r != nil) { if (r.kind != nkind.N_CAST) { break; }; @@ -21830,7 +21848,8 @@ fn emitletdataw(c: *cgen, file: *node) void = { emitsymname(c, nm); emitline("(SB),\""); let i: i32 = 0; - for (i < 16) { + let szstr: i32 = primtypesize("str"): i32; + for (i < szstr) { emitdatawbyte(0u8); i += 1; }; @@ -21838,7 +21857,7 @@ fn emitletdataw(c: *cgen, file: *node) void = { }; }; }; - if (sz == 24 && !issg) { + if (sz == tyslicesize(): i32 && !issg) { // Slice: zero-init only (no slice-literal // syntax to honour). Any rhs other than // `nil` is skipped → undefined symbol at @@ -21860,7 +21879,8 @@ fn emitletdataw(c: *cgen, file: *node) void = { emitsymname(c, nm); emitline("(SB),\""); let i: i32 = 0; - for (i < 24) { + let szsl: i32 = tyslicesize(): i32; + for (i < szsl) { emitdatawbyte(0u8); i += 1; }; diff --git a/selfhost/cmd/wcc/cgen.ww b/selfhost/cmd/wcc/cgen.ww index 72b95367..8838493a 100644 --- a/selfhost/cmd/wcc/cgen.ww +++ b/selfhost/cmd/wcc/cgen.ww @@ -965,7 +965,7 @@ fn letemitsize(c: *cgen, d: *node) i32 = { let t: *node = d.lhs; for (t != nil) { if (t.kind == nkind.N_TPTR) { return 8; }; - if (t.kind == nkind.N_TSLICE) { return 24; }; + if (t.kind == nkind.N_TSLICE) { return tyslicesize(): i32; }; if (t.kind == nkind.N_TARRAY) { let lenn: *node = t.rhs; let elemn: *node = t.lhs; @@ -987,7 +987,7 @@ fn letemitsize(c: *cgen, d: *node) i32 = { if (letscalarprim(nm)) { return 8; }; let fsz: i32 = letfloatprim(nm); if (fsz > 0) { return fsz; }; - if (streq(nm, "str")) { return 16; }; + if (streq(nm, "str")) { return primtypesize("str"): i32; }; let si: *structinfo = structlookup(c, nm); if (si != nil) { return si.totsize; }; let next: *node = aliaslookup(c, nm); @@ -1313,7 +1313,7 @@ fn emitletdataw(c: *cgen, file: *node) void = { emitline("\"\n"); }; }; - if (sz == 16 && !issg) { + if (sz == primtypesize("str"): i32 && !issg) { let r: *node = d.rhs; for (r != nil) { if (r.kind != nkind.N_CAST) { break; }; @@ -1369,7 +1369,8 @@ fn emitletdataw(c: *cgen, file: *node) void = { emitsymname(c, nm); emitline("(SB),\""); let i: i32 = 0; - for (i < 16) { + let szstr: i32 = primtypesize("str"): i32; + for (i < szstr) { emitdatawbyte(0u8); i += 1; }; @@ -1377,7 +1378,7 @@ fn emitletdataw(c: *cgen, file: *node) void = { }; }; }; - if (sz == 24 && !issg) { + if (sz == tyslicesize(): i32 && !issg) { // Slice: zero-init only (no slice-literal // syntax to honour). Any rhs other than // `nil` is skipped → undefined symbol at @@ -1399,7 +1400,8 @@ fn emitletdataw(c: *cgen, file: *node) void = { emitsymname(c, nm); emitline("(SB),\""); let i: i32 = 0; - for (i < 24) { + let szsl: i32 = tyslicesize(): i32; + for (i < szsl) { emitdatawbyte(0u8); i += 1; }; diff --git a/selfhost/cmd/wcc/cgendecl.ww b/selfhost/cmd/wcc/cgendecl.ww index 9c9cca49..84a25d83 100644 --- a/selfhost/cmd/wcc/cgendecl.ww +++ b/selfhost/cmd/wcc/cgendecl.ww @@ -46,7 +46,7 @@ fn cgfnparams(c: *cgen, params: *node) void = { if (p.op == tkind.TK_ELLIPSIS) { let tn: *node = slicewrap(c, p.lhs); if (idx + 3 <= 6) { - let off: i32 = localadd(c, nm, 24, tn); + let off: i32 = localadd(c, nm, tyslicesize(): i32, tn); emitline("\tMOVQ\t"); emitline(argregname(idx)); emitline(", "); @@ -70,7 +70,7 @@ fn cgfnparams(c: *cgen, params: *node) void = { // at the ABI boundary (the call site synthesises a // 24B descriptor and pushes ptr/len/cap), so this // mirrors the slice branch at cgendecl.ww:518. - let off: i32 = localadd(c, nm, 24, tn); + let off: i32 = localadd(c, nm, tyslicesize(): i32, tn); let regs_left: i32 = 6 - idx; let w: i32 = 0; for (w < regs_left) { @@ -170,7 +170,7 @@ fn cgfnparams(c: *cgen, params: *node) void = { };}; } else { if (isslicetype(c, p.lhs)) { if (idx + 3 <= 6) { - let off: i32 = localadd(c, nm, 24, p.lhs); + let off: i32 = localadd(c, nm, tyslicesize(): i32, p.lhs); emitline("\tMOVQ\t"); emitline(argregname(idx)); emitline(", "); @@ -194,7 +194,7 @@ fn cgfnparams(c: *cgen, params: *node) void = { // 440-469. Caller's pushargsrev greedy-fills the // remaining argregs (ptr,len,cap order), the tail // spills to +16+stkcursor*8(BP). - let off: i32 = localadd(c, nm, 24, p.lhs); + let off: i32 = localadd(c, nm, tyslicesize(): i32, p.lhs); let regs_left: i32 = 6 - idx; let w: i32 = 0; for (w < regs_left) { diff --git a/selfhost/cmd/wcc/cgenstmt.ww b/selfhost/cmd/wcc/cgenstmt.ww index b248bd75..7f53443b 100644 --- a/selfhost/cmd/wcc/cgenstmt.ww +++ b/selfhost/cmd/wcc/cgenstmt.ww @@ -759,7 +759,7 @@ fn cglet(c: *cgen, n: *node) void = { if (elemn != nil) { if (elemn.kind == nkind.N_TNAME) { if (streq(elemn.str, "str")) { - esz = 16; + esz = primtypesize("str"): i32; isstrel = true; } else { let ps: i32 = primsize(elemn.str); @@ -1244,8 +1244,8 @@ fn cgmlet(c: *cgen, n: *node) void = { if (s0_is_str != s1_is_str) { let sz0: i32 = 8; let sz1: i32 = 8; - if (s0_is_str) { sz0 = 16; }; - if (s1_is_str) { sz1 = 16; }; + if (s0_is_str) { sz0 = primtypesize("str"): i32; }; + if (s1_is_str) { sz1 = primtypesize("str"): i32; }; let off0: i32 = localadd(c, l0.str, sz0, t0); let off1: i32 = localadd(c, l1.str, sz1, t1); if (s0_is_str) { @@ -1302,7 +1302,7 @@ fn paramfieldsize(t: *node) i32 = { if (k == nkind.N_TCHAN) { return 8; }; if (k == nkind.N_TNAME) { let nm: str = t.str; - if (streq(nm, "str")) { return 16; }; + if (streq(nm, "str")) { return primtypesize("str"): i32; }; let ps: i32 = primsize(nm); if (ps > 0) { return ps; }; }; diff --git a/selfhost/cmd/wcc/cgenutil.ww b/selfhost/cmd/wcc/cgenutil.ww index 11977d75..1e6005bf 100644 --- a/selfhost/cmd/wcc/cgenutil.ww +++ b/selfhost/cmd/wcc/cgenutil.ww @@ -1196,7 +1196,7 @@ fn indexbaseesz(c: *cgen, base: *node) i32 = { let elem: *node = ft.lhs; if (elem != nil) { if (elem.kind == nkind.N_TNAME) { - if (streq(elem.str, "str")) { return 16; }; + if (streq(elem.str, "str")) { return primtypesize("str"): i32; }; let ps: i32 = primsize(elem.str); if (ps > 0) { return ps; }; // Pointer to named struct: indexing @@ -1304,11 +1304,11 @@ fn elemsizeof(t: *node) i32 = { // `*[]T`: stride is the slice header (24B). Hare-faithful — a // pointer-to-slice is a 1D array of slices, not of T. Mirrors the // cstage check.c default `*U → U` path for U=[]T (slice element). - if (elem.kind == nkind.N_TSLICE) { return 24; }; + if (elem.kind == nkind.N_TSLICE) { return tyslicesize(): i32; }; if (elem.kind == nkind.N_TNAME) { let nm: str = elem.str; // str element is 16B (ptr+len). primsize returns 0 for it. - if (streq(nm, "str")) { return 16; }; + if (streq(nm, "str")) { return primtypesize("str"): i32; }; let ps: i32 = primsize(nm); if (ps > 0) { return ps; }; }; @@ -1903,7 +1903,7 @@ export fn letslotsize(c: *cgen, n: *node) i32 = { // (ptr+len) — primsize returns 0 for // it, so it'd slot 8B without this. if (streq(elemn.str, "str")) { - esz = 16; + esz = primtypesize("str"): i32; } else { let ps: i32 = primsize(elemn.str); if (ps > 0) { esz = ps; }; @@ -1952,7 +1952,7 @@ fn slotsize(c: *cgen, typn: *node) i32 = { 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 24; }; + 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, @@ -1992,7 +1992,7 @@ fn slotsize(c: *cgen, typn: *node) i32 = { // 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 16; }; + 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 @@ -2033,7 +2033,7 @@ fn slotsize(c: *cgen, typn: *node) i32 = { // explicit case a `[N]str` would slot 8B/elem, // collapsing the per-element stride and losing // every .len half. - if (streq(en, "str")) { esz = 16; }; + if (streq(en, "str")) { esz = primtypesize("str"): i32; }; let ps: i32 = primsize(en); if (esz == 8) { if (ps > 0) { esz = ps; } else { @@ -2091,7 +2091,7 @@ fn fieldsize(c: *cgen, tnode: *node) i32 = { if (k == nkind.N_TTAGGED){ return slotsize(c, tnode); }; if (k == nkind.N_TNAME) { let nm: str = tnode.str; - if (streq(nm, "str")) { return 16; }; + if (streq(nm, "str")) { return primtypesize("str"): i32; }; let ps: i32 = primsize(nm); if (ps > 0) { return ps; }; let si: *structinfo = structlookup(c, nm); @@ -2118,7 +2118,7 @@ fn fieldsize(c: *cgen, tnode: *node) i32 = { return 8; }; if (k == nkind.N_TPTR) { return 8; }; - if (k == nkind.N_TSLICE) { return 24; }; + if (k == nkind.N_TSLICE) { return tyslicesize(): i32; }; if (k == nkind.N_TARRAY) { // Same shape as slotsize's TARRAY branch. let lenn: *node = tnode.rhs; diff --git a/selfhost/cmd/wcc/check.ww b/selfhost/cmd/wcc/check.ww index d8dae28a..2700c151 100644 --- a/selfhost/cmd/wcc/check.ww +++ b/selfhost/cmd/wcc/check.ww @@ -618,6 +618,29 @@ fn mktname(c: *checker, nm: str) *node = { return n; }; +// #43: SSoT for primitive type byte sizes. astsize's N_TNAME-primitive +// arm and every wwstage cgen size walker (slotsize/fieldsize/letemit- +// size/elemsizeof/paramfieldsize) consult this table so a future +// ty_str.size bump (#1) lands in one place. Returns -1 for non-prim +// names; callers fall back to alias/struct/enum lookup. Cstage's +// equivalent SSoT is cmd/wcc/type.c:46-79 (ty_void/ty_bool/.../ty_str). +fn primtypesize(nm: str) i64 = { + if (streq(nm, "void")) { return 0i64; }; + if (streq(nm, "bool")) { return 1i64; }; + if (streq(nm, "i8") || streq(nm, "u8")) { return 1i64; }; + if (streq(nm, "i16") || streq(nm, "u16")) { return 2i64; }; + if (streq(nm, "i32") || streq(nm, "u32") || streq(nm, "f32") || streq(nm, "rune")) { return 4i64; }; + if (streq(nm, "i64") || streq(nm, "u64") || streq(nm, "f64")) { return 8i64; }; + if (streq(nm, "int") || streq(nm, "uint") || streq(nm, "uintptr")) { return 8i64; }; + if (streq(nm, "str")) { return 16i64; }; + return -1i64; +}; + +// #43: SSoT for slice header size (ptr+len+cap = 24B today). Mirrors +// cstage cmd/wcc/type.c:103 (ty_slice->size = 24). Bumping a slice's +// header layout in #34 touches only this constant. +fn tyslicesize() i64 = { return 24i64; }; + // #42: AST-level layout helpers for the size(T)/align(T)/offset(e.f) // fold. Mirror cstage resolve_type's size/align computation // (cmd/wcc/check.c:286-528) on AST nodes — wwstage check.ww never @@ -679,7 +702,7 @@ fn astsize(c: *checker, t: *node) i64 = { let k: nkind = t.kind; if (k == nkind.N_TBANG) { return astsize(c, t.lhs); }; if (k == nkind.N_TPTR) { return 8i64; }; - if (k == nkind.N_TSLICE) { return 24i64; }; + if (k == nkind.N_TSLICE) { return tyslicesize(); }; if (k == nkind.N_TCHAN) { return 8i64; }; if (k == nkind.N_TFN) { return 8i64; }; if (k == nkind.N_TARRAY) { @@ -731,13 +754,8 @@ fn astsize(c: *checker, t: *node) i64 = { }; if (k == nkind.N_TNAME) { let nm: str = t.str; - if (streq(nm, "void")) { return 0i64; }; - if (streq(nm, "bool")) { return 1i64; }; - if (streq(nm, "i8") || streq(nm, "u8")) { return 1i64; }; - if (streq(nm, "i16") || streq(nm, "u16")) { return 2i64; }; - if (streq(nm, "i32") || streq(nm, "u32") || streq(nm, "f32") || streq(nm, "rune")) { return 4i64; }; - if (streq(nm, "i64") || streq(nm, "u64") || streq(nm, "f64") || streq(nm, "int") || streq(nm, "uint") || streq(nm, "uintptr")) { return 8i64; }; - if (streq(nm, "str")) { return 16i64; }; + let ps: i64 = primtypesize(nm); + if (ps >= 0i64) { return ps; }; let resolved: *node = resolvealias(c, t); if (resolved != nil && resolved != t) { return astsize(c, resolved); diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 5b700f74..b7bd0c72 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -1996,7 +1996,7 @@ export fn freeall(s: []str) void = { i += 1; }; if (s.cap > 0) { - os.free(s.ptr: *void, (s.cap: u64) * 16u64); + os.free(s.ptr: *void, (s.cap: u64) * size(str): u64); }; }; @@ -2599,7 +2599,7 @@ export fn remaining_tokens(s: *tokenizer) str = { fn appendstr(slice: *[]str, item: str) void = { let newlen: i32 = slice.len + 1; slice.len = newlen; - rtensure(slice: *void, 16u64); + rtensure(slice: *void, size(str): u64); let dst: *str = &slice.ptr[newlen - 1]; dst.ptr = item.ptr; dst.len = item.len; @@ -7612,6 +7612,29 @@ fn mktname(c: *checker, nm: str) *node = { return n; }; +// #43: SSoT for primitive type byte sizes. astsize's N_TNAME-primitive +// arm and every wwstage cgen size walker (slotsize/fieldsize/letemit- +// size/elemsizeof/paramfieldsize) consult this table so a future +// ty_str.size bump (#1) lands in one place. Returns -1 for non-prim +// names; callers fall back to alias/struct/enum lookup. Cstage's +// equivalent SSoT is cmd/wcc/type.c:46-79 (ty_void/ty_bool/.../ty_str). +fn primtypesize(nm: str) i64 = { + if (streq(nm, "void")) { return 0i64; }; + if (streq(nm, "bool")) { return 1i64; }; + if (streq(nm, "i8") || streq(nm, "u8")) { return 1i64; }; + if (streq(nm, "i16") || streq(nm, "u16")) { return 2i64; }; + if (streq(nm, "i32") || streq(nm, "u32") || streq(nm, "f32") || streq(nm, "rune")) { return 4i64; }; + if (streq(nm, "i64") || streq(nm, "u64") || streq(nm, "f64")) { return 8i64; }; + if (streq(nm, "int") || streq(nm, "uint") || streq(nm, "uintptr")) { return 8i64; }; + if (streq(nm, "str")) { return 16i64; }; + return -1i64; +}; + +// #43: SSoT for slice header size (ptr+len+cap = 24B today). Mirrors +// cstage cmd/wcc/type.c:103 (ty_slice->size = 24). Bumping a slice's +// header layout in #34 touches only this constant. +fn tyslicesize() i64 = { return 24i64; }; + // #42: AST-level layout helpers for the size(T)/align(T)/offset(e.f) // fold. Mirror cstage resolve_type's size/align computation // (cmd/wcc/check.c:286-528) on AST nodes — wwstage check.ww never @@ -7673,7 +7696,7 @@ fn astsize(c: *checker, t: *node) i64 = { let k: nkind = t.kind; if (k == nkind.N_TBANG) { return astsize(c, t.lhs); }; if (k == nkind.N_TPTR) { return 8i64; }; - if (k == nkind.N_TSLICE) { return 24i64; }; + if (k == nkind.N_TSLICE) { return tyslicesize(); }; if (k == nkind.N_TCHAN) { return 8i64; }; if (k == nkind.N_TFN) { return 8i64; }; if (k == nkind.N_TARRAY) { @@ -7725,13 +7748,8 @@ fn astsize(c: *checker, t: *node) i64 = { }; if (k == nkind.N_TNAME) { let nm: str = t.str; - if (streq(nm, "void")) { return 0i64; }; - if (streq(nm, "bool")) { return 1i64; }; - if (streq(nm, "i8") || streq(nm, "u8")) { return 1i64; }; - if (streq(nm, "i16") || streq(nm, "u16")) { return 2i64; }; - if (streq(nm, "i32") || streq(nm, "u32") || streq(nm, "f32") || streq(nm, "rune")) { return 4i64; }; - if (streq(nm, "i64") || streq(nm, "u64") || streq(nm, "f64") || streq(nm, "int") || streq(nm, "uint") || streq(nm, "uintptr")) { return 8i64; }; - if (streq(nm, "str")) { return 16i64; }; + let ps: i64 = primtypesize(nm); + if (ps >= 0i64) { return ps; }; let resolved: *node = resolvealias(c, t); if (resolved != nil && resolved != t) { return astsize(c, resolved); @@ -9854,7 +9872,7 @@ fn indexbaseesz(c: *cgen, base: *node) i32 = { let elem: *node = ft.lhs; if (elem != nil) { if (elem.kind == nkind.N_TNAME) { - if (streq(elem.str, "str")) { return 16; }; + if (streq(elem.str, "str")) { return primtypesize("str"): i32; }; let ps: i32 = primsize(elem.str); if (ps > 0) { return ps; }; // Pointer to named struct: indexing @@ -9962,11 +9980,11 @@ fn elemsizeof(t: *node) i32 = { // `*[]T`: stride is the slice header (24B). Hare-faithful — a // pointer-to-slice is a 1D array of slices, not of T. Mirrors the // cstage check.c default `*U → U` path for U=[]T (slice element). - if (elem.kind == nkind.N_TSLICE) { return 24; }; + if (elem.kind == nkind.N_TSLICE) { return tyslicesize(): i32; }; if (elem.kind == nkind.N_TNAME) { let nm: str = elem.str; // str element is 16B (ptr+len). primsize returns 0 for it. - if (streq(nm, "str")) { return 16; }; + if (streq(nm, "str")) { return primtypesize("str"): i32; }; let ps: i32 = primsize(nm); if (ps > 0) { return ps; }; }; @@ -10561,7 +10579,7 @@ export fn letslotsize(c: *cgen, n: *node) i32 = { // (ptr+len) — primsize returns 0 for // it, so it'd slot 8B without this. if (streq(elemn.str, "str")) { - esz = 16; + esz = primtypesize("str"): i32; } else { let ps: i32 = primsize(elemn.str); if (ps > 0) { esz = ps; }; @@ -10610,7 +10628,7 @@ fn slotsize(c: *cgen, typn: *node) i32 = { 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 24; }; + 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, @@ -10650,7 +10668,7 @@ fn slotsize(c: *cgen, typn: *node) i32 = { // 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 16; }; + 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 @@ -10691,7 +10709,7 @@ fn slotsize(c: *cgen, typn: *node) i32 = { // explicit case a `[N]str` would slot 8B/elem, // collapsing the per-element stride and losing // every .len half. - if (streq(en, "str")) { esz = 16; }; + if (streq(en, "str")) { esz = primtypesize("str"): i32; }; let ps: i32 = primsize(en); if (esz == 8) { if (ps > 0) { esz = ps; } else { @@ -10749,7 +10767,7 @@ fn fieldsize(c: *cgen, tnode: *node) i32 = { if (k == nkind.N_TTAGGED){ return slotsize(c, tnode); }; if (k == nkind.N_TNAME) { let nm: str = tnode.str; - if (streq(nm, "str")) { return 16; }; + if (streq(nm, "str")) { return primtypesize("str"): i32; }; let ps: i32 = primsize(nm); if (ps > 0) { return ps; }; let si: *structinfo = structlookup(c, nm); @@ -10776,7 +10794,7 @@ fn fieldsize(c: *cgen, tnode: *node) i32 = { return 8; }; if (k == nkind.N_TPTR) { return 8; }; - if (k == nkind.N_TSLICE) { return 24; }; + if (k == nkind.N_TSLICE) { return tyslicesize(): i32; }; if (k == nkind.N_TARRAY) { // Same shape as slotsize's TARRAY branch. let lenn: *node = tnode.rhs; @@ -19139,7 +19157,7 @@ fn cglet(c: *cgen, n: *node) void = { if (elemn != nil) { if (elemn.kind == nkind.N_TNAME) { if (streq(elemn.str, "str")) { - esz = 16; + esz = primtypesize("str"): i32; isstrel = true; } else { let ps: i32 = primsize(elemn.str); @@ -19624,8 +19642,8 @@ fn cgmlet(c: *cgen, n: *node) void = { if (s0_is_str != s1_is_str) { let sz0: i32 = 8; let sz1: i32 = 8; - if (s0_is_str) { sz0 = 16; }; - if (s1_is_str) { sz1 = 16; }; + if (s0_is_str) { sz0 = primtypesize("str"): i32; }; + if (s1_is_str) { sz1 = primtypesize("str"): i32; }; let off0: i32 = localadd(c, l0.str, sz0, t0); let off1: i32 = localadd(c, l1.str, sz1, t1); if (s0_is_str) { @@ -19682,7 +19700,7 @@ fn paramfieldsize(t: *node) i32 = { if (k == nkind.N_TCHAN) { return 8; }; if (k == nkind.N_TNAME) { let nm: str = t.str; - if (streq(nm, "str")) { return 16; }; + if (streq(nm, "str")) { return primtypesize("str"): i32; }; let ps: i32 = primsize(nm); if (ps > 0) { return ps; }; }; @@ -20054,7 +20072,7 @@ fn cgfnparams(c: *cgen, params: *node) void = { if (p.op == tkind.TK_ELLIPSIS) { let tn: *node = slicewrap(c, p.lhs); if (idx + 3 <= 6) { - let off: i32 = localadd(c, nm, 24, tn); + let off: i32 = localadd(c, nm, tyslicesize(): i32, tn); emitline("\tMOVQ\t"); emitline(argregname(idx)); emitline(", "); @@ -20078,7 +20096,7 @@ fn cgfnparams(c: *cgen, params: *node) void = { // at the ABI boundary (the call site synthesises a // 24B descriptor and pushes ptr/len/cap), so this // mirrors the slice branch at cgendecl.ww:518. - let off: i32 = localadd(c, nm, 24, tn); + let off: i32 = localadd(c, nm, tyslicesize(): i32, tn); let regs_left: i32 = 6 - idx; let w: i32 = 0; for (w < regs_left) { @@ -20178,7 +20196,7 @@ fn cgfnparams(c: *cgen, params: *node) void = { };}; } else { if (isslicetype(c, p.lhs)) { if (idx + 3 <= 6) { - let off: i32 = localadd(c, nm, 24, p.lhs); + let off: i32 = localadd(c, nm, tyslicesize(): i32, p.lhs); emitline("\tMOVQ\t"); emitline(argregname(idx)); emitline(", "); @@ -20202,7 +20220,7 @@ fn cgfnparams(c: *cgen, params: *node) void = { // 440-469. Caller's pushargsrev greedy-fills the // remaining argregs (ptr,len,cap order), the tail // spills to +16+stkcursor*8(BP). - let off: i32 = localadd(c, nm, 24, p.lhs); + let off: i32 = localadd(c, nm, tyslicesize(): i32, p.lhs); let regs_left: i32 = 6 - idx; let w: i32 = 0; for (w < regs_left) { @@ -21426,7 +21444,7 @@ fn letemitsize(c: *cgen, d: *node) i32 = { let t: *node = d.lhs; for (t != nil) { if (t.kind == nkind.N_TPTR) { return 8; }; - if (t.kind == nkind.N_TSLICE) { return 24; }; + if (t.kind == nkind.N_TSLICE) { return tyslicesize(): i32; }; if (t.kind == nkind.N_TARRAY) { let lenn: *node = t.rhs; let elemn: *node = t.lhs; @@ -21448,7 +21466,7 @@ fn letemitsize(c: *cgen, d: *node) i32 = { if (letscalarprim(nm)) { return 8; }; let fsz: i32 = letfloatprim(nm); if (fsz > 0) { return fsz; }; - if (streq(nm, "str")) { return 16; }; + if (streq(nm, "str")) { return primtypesize("str"): i32; }; let si: *structinfo = structlookup(c, nm); if (si != nil) { return si.totsize; }; let next: *node = aliaslookup(c, nm); @@ -21774,7 +21792,7 @@ fn emitletdataw(c: *cgen, file: *node) void = { emitline("\"\n"); }; }; - if (sz == 16 && !issg) { + if (sz == primtypesize("str"): i32 && !issg) { let r: *node = d.rhs; for (r != nil) { if (r.kind != nkind.N_CAST) { break; }; @@ -21830,7 +21848,8 @@ fn emitletdataw(c: *cgen, file: *node) void = { emitsymname(c, nm); emitline("(SB),\""); let i: i32 = 0; - for (i < 16) { + let szstr: i32 = primtypesize("str"): i32; + for (i < szstr) { emitdatawbyte(0u8); i += 1; }; @@ -21838,7 +21857,7 @@ fn emitletdataw(c: *cgen, file: *node) void = { }; }; }; - if (sz == 24 && !issg) { + if (sz == tyslicesize(): i32 && !issg) { // Slice: zero-init only (no slice-literal // syntax to honour). Any rhs other than // `nil` is skipped → undefined symbol at @@ -21860,7 +21879,8 @@ fn emitletdataw(c: *cgen, file: *node) void = { emitsymname(c, nm); emitline("(SB),\""); let i: i32 = 0; - for (i < 24) { + let szsl: i32 = tyslicesize(): i32; + for (i < szsl) { emitdatawbyte(0u8); i += 1; }; diff --git a/selfhost/test/smoke.combined.ww b/selfhost/test/smoke.combined.ww index 40191000..404336e2 100644 --- a/selfhost/test/smoke.combined.ww +++ b/selfhost/test/smoke.combined.ww @@ -1887,7 +1887,7 @@ export fn freeall(s: []str) void = { i += 1; }; if (s.cap > 0) { - os.free(s.ptr: *void, (s.cap: u64) * 16u64); + os.free(s.ptr: *void, (s.cap: u64) * size(str): u64); }; }; @@ -2490,7 +2490,7 @@ export fn remaining_tokens(s: *tokenizer) str = { fn appendstr(slice: *[]str, item: str) void = { let newlen: i32 = slice.len + 1; slice.len = newlen; - rtensure(slice: *void, 16u64); + rtensure(slice: *void, size(str): u64); let dst: *str = &slice.ptr[newlen - 1]; dst.ptr = item.ptr; dst.len = item.len;