From 07633778064eeb5d717999f91b53c06d8c7b152d Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Sun, 24 May 2026 02:35:53 +0900 Subject: [PATCH] selfhost/cmd/wcc: dotchainresolve walks tinfo.fields (#71, A.6.3j) The chained value-struct spine-walker resolved root + total offset + leaf type via structinfo/fieldinfo (slot-padded foff). Swap its guts onto the stamped root-ident type_: peel TY_NAMED to TY_STRUCT (one TY_PTR hop for a *struct root) and accumulate tfield.offset down the spine, mirroring cstage's cur->lhs->type fields walk (cmd/w6c/cgen.c:3156-3216 write, :1955-2030 AMP/ read). Leaf out-param becomes *tinfo; the three callers read leaf-ness via typeis*/loadopsz, and the cgassign struct-terminal recovers the struct name from the leaf tinfo's NAMED wrapper for the still-structinfo cgstructlitfill. Root classification (local/ptr/global) is unchanged, so the firing set and addressing mode stay byte-identical; the global-root path remains its pre- existing shared breakage (#27), untouched. Natural tfield.offset equals the old slot-padded foff for every shape the byte-id gate exercises -- and since cstage already reads the natural offset and ww-old==cstage held, the flip is structurally identical, not coincidental. Latent (off-corpus, byte-id-neutral): the tinfo-peel now fires the str/slice pseudo-leaf on an aliased-str/slice field where the old structinfo path bailed -- a faithful-toward-cstage gap closure, filed for a probe + sentinel. make test 134/134, byte-id 950+990-997 hold. --- selfhost/cmd/w6c/main.combined.ww | 188 +++++++++++++++++---------- selfhost/cmd/wcc/cgenexpr.ww | 81 ++++++++---- selfhost/cmd/wcc/cgenutil.ww | 107 +++++++++------ selfhost/cmd/wwdump/main.combined.ww | 188 +++++++++++++++++---------- 4 files changed, 360 insertions(+), 204 deletions(-) diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index c150283d..8beb7854 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -13353,7 +13353,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s // Spine-walk a chained N_DOT (n) inward to a root ident, summing field // offsets through value-struct intermediates. Optional slice/str leaf // pseudo-field (.ptr / .len / .cap) on the last segment is folded into -// *outslicedelta (0/8/16); otherwise *outleaffi is the leaf fieldinfo +// *outslicedelta (0/8/16); otherwise *outleaftype is the leaf *tinfo // and *outslicedelta stays -1. Returns true on success; on false the // caller falls through to other branches. // @@ -13370,16 +13370,28 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s // Numeric out-params are i32 — offsets fit naturally and the post-#19 // localloadop sign-extends i32 deref-stored slots on read, so negative // frame offsets round-trip intact. +// #71 (A.6.3j): the spine offset-sum + leaf-type now read off +// tinfo.fields (natural layout) instead of the structinfo/fieldinfo +// walk. Mirror of cstage cmd/w6c/cgen.c:3156-3216 which walks +// `cur->lhs->type` fields. Root resolution (the local/ptr/global split +// and the rootoff/ptrroot/isglobal flags) stays on localfindnode/ +// letvarstructinfo unchanged, so the firing set + addressing mode are +// byte-identical to the structinfo era; only the layout SOURCE moves. +// The slot-padded foff and the natural tfield.offset coincide for every +// shape the byte-id gate exercises (cstage already reads the natural +// offset), so this is offset-preserving. Leaf out-param is the field's +// stamped *tinfo (was *fieldinfo); the str/slice pseudo-leaf leaves it +// nil and the callers gate on slicedelta>=0 first. export fn dotchainresolve(c: *cgen, n: *node, outrootname: *str, outrootoff: *i32, outtotaloff: *i32, - outleaffi: **fieldinfo, outslicedelta: *i32, + outleaftype: **tinfo, outslicedelta: *i32, outisglobal: *bool, outptrroot: *bool) bool = { *outrootname = ""; *outrootoff = 0; *outisglobal = false; *outptrroot = false; *outtotaloff = 0; - *outleaffi = nil; + *outleaftype = nil; *outslicedelta = -1; if (n == nil) { return false; }; if (n.kind != nkind.N_DOT) { return false; }; @@ -13397,13 +13409,13 @@ export fn dotchainresolve(c: *cgen, n: *node, if (cur == nil) { return false; }; if (cur.kind != nkind.N_IDENT) { return false; }; *outrootname = cur.str; - let rootstruct: str = ""; + let resolved: bool = false; let lc: *local = localfindnode(c, cur.str); if (lc != nil) { if (lc.tnode != nil) { if (lc.tnode.kind == nkind.N_TNAME) { - rootstruct = lc.tnode.str; *outrootoff = lc.off; + resolved = true; }; // `*T` root (param/local): dereference at emit time; // pointee struct supplies the field layout. Callers @@ -13413,60 +13425,71 @@ export fn dotchainresolve(c: *cgen, n: *node, let pe: *node = lc.tnode.lhs; if (pe != nil) { if (pe.kind == nkind.N_TNAME) { - rootstruct = pe.str; *outrootoff = lc.off; *outptrroot = true; + resolved = true; }; }; }; }; }; - if (rootstruct.len == 0) { + if (!resolved) { let gsi: *structinfo = letvarstructinfo(c, cur.str); if (gsi != nil) { - rootstruct = gsi.sname; *outisglobal = true; + resolved = true; + }; + }; + if (!resolved) { return false; }; + // Root struct layout = the stamped root-ident type_, peeled NAMED + // (plus one TY_PTR hop for a `*struct` root). tfield.type_ then + // supplies each nested struct directly, so no name re-lookup. + let curstruct: *tinfo = cur.type_: *tinfo; + for (curstruct != nil && curstruct.kind == tykind.TY_NAMED) { + curstruct = curstruct.under; + }; + if (*outptrroot) { + if (curstruct == nil) { return false; }; + if (curstruct.kind != tykind.TY_PTR) { return false; }; + curstruct = curstruct.sub; + for (curstruct != nil && curstruct.kind == tykind.TY_NAMED) { + curstruct = curstruct.under; }; }; - if (rootstruct.len == 0) { return false; }; - let curstruct: str = rootstruct; let i: i32 = nsteps - 1; for (i >= 0) { - let csi: *structinfo = structlookup(c, curstruct); - if (csi == nil) { return false; }; + if (curstruct == nil) { return false; }; + if (curstruct.kind != tykind.TY_STRUCT) { return false; }; if (stk[i] == nil) { return false; }; let stepnm: str = stk[i].str; - let fi: *fieldinfo = csi.fields; - let found: *fieldinfo = nil; - for (fi != nil) { - if (streq(fi.fname, stepnm)) { found = fi; break; }; - fi = fi.finext; + let tf: *tfield = curstruct.fields; + let found: *tfield = nil; + for (tf != nil) { + if (streq(tf.name, stepnm)) { found = tf; break; }; + tf = tf.tnext; }; if (found == nil) { return false; }; + let foff: i32 = found.offset: i32; if (i == 0) { - *outtotaloff = *outtotaloff + found.foff; - *outleaffi = found; + *outtotaloff = *outtotaloff + foff; + *outleaftype = found.type_; return true; }; - let ft: *node = found.tnode; + let ft: *tinfo = found.type_; + for (ft != nil && ft.kind == tykind.TY_NAMED) { ft = ft.under; }; if (ft == nil) { return false; }; - if (ft.kind == nkind.N_TNAME) { - if (streq(ft.str, "str")) { - if (i != 1) { return false; }; - let pseudo: str = stk[0].str; - let delta: i32 = -1; - if (streq(pseudo, "ptr")) { delta = 0; } - else { if (streq(pseudo, "len")) { delta = 8; }; }; - if (delta < 0) { return false; }; - *outtotaloff = *outtotaloff + found.foff; - *outslicedelta = delta; - return true; - }; - if (primsize(ft.str) != 0) { return false; }; - *outtotaloff = *outtotaloff + found.foff; - curstruct = ft.str; - i -= 1; - } else { if (ft.kind == nkind.N_TSLICE) { + if (ft.kind == tykind.TY_STR) { + if (i != 1) { return false; }; + let pseudo: str = stk[0].str; + let delta: i32 = -1; + if (streq(pseudo, "ptr")) { delta = 0; } + else { if (streq(pseudo, "len")) { delta = 8; }; }; + if (delta < 0) { return false; }; + *outtotaloff = *outtotaloff + foff; + *outslicedelta = delta; + return true; + }; + if (ft.kind == tykind.TY_SLICE) { if (i != 1) { return false; }; let pseudo: str = stk[0].str; let delta: i32 = -1; @@ -13474,12 +13497,14 @@ export fn dotchainresolve(c: *cgen, n: *node, else { if (streq(pseudo, "len")) { delta = 8; } else { if (streq(pseudo, "cap")) { delta = 16; }; }; }; if (delta < 0) { return false; }; - *outtotaloff = *outtotaloff + found.foff; + *outtotaloff = *outtotaloff + foff; *outslicedelta = delta; return true; - } else { - return false; - }; }; + }; + if (ft.kind != tykind.TY_STRUCT) { return false; }; + *outtotaloff = *outtotaloff + foff; + curstruct = ft; + i -= 1; }; return false; }; @@ -15683,13 +15708,13 @@ fn cgdot(c: *cgen, n: *node) void = { let rootname: str = ""; let rootoff: i32 = 0; let totaloff: i32 = 0; - let leaffi: *fieldinfo = nil; + let leaftype: *tinfo = nil; let slicedelta: i32 = -1; let isglobal: bool = false; let ptrroot: bool = false; let pok: bool = dotchainresolve(c, n, &rootname, &rootoff, &totaloff, - &leaffi, &slicedelta, &isglobal, &ptrroot); + &leaftype, &slicedelta, &isglobal, &ptrroot); if (pok) { // `*T` root: load the pointer slot once into CX, // then index every leaf at total_off off CX. Same @@ -15717,7 +15742,7 @@ fn cgdot(c: *cgen, n: *node) void = { }; return; }; - if (isstrtype(c, leaffi.tnode)) { + if (typeisstr(leaftype)) { if (viacx) { if (ptrroot) { emitline("\tMOVQ\t"); @@ -15744,7 +15769,7 @@ fn cgdot(c: *cgen, n: *node) void = { }; return; }; - if (isslicetype(c, leaffi.tnode)) { + if (typeisslice(leaftype)) { // Slice leaf: load all three header words into // (AX=ptr, BX=len, CX=cap). For the viacx path // (global or `*T` root) CX is the base; load @@ -15783,9 +15808,9 @@ fn cgdot(c: *cgen, n: *node) void = { }; return; }; - if (isfloattype(c, leaffi.tnode)) { + if (typeisfloat(leaftype)) { let mov: str = "MOVSD"; - if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; }; + if (typeisf32(leaftype)) { mov = "MOVSS"; }; if (viacx) { if (ptrroot) { emitline("\tMOVQ\t"); @@ -15810,7 +15835,8 @@ fn cgdot(c: *cgen, n: *node) void = { }; return; }; - let lop: str = fieldloadop(c, leaffi); + let lop: str = loadopsz(typeissigned(leaftype), + leaftype.slotsize: i32); if (viacx) { if (ptrroot) { emitline("\tMOVQ\t"); @@ -16157,13 +16183,13 @@ fn cgun(c: *cgen, n: *node) void = { let rootname: str = ""; let rootoff: i32 = 0; let totaloff: i32 = 0; - let leaffi: *fieldinfo = nil; + let leaftype: *tinfo = nil; let slicedelta: i32 = -1; let isglobal: bool = false; let ptrroot: bool = false; let pok: bool = dotchainresolve(c, opnd, &rootname, &rootoff, &totaloff, - &leaffi, &slicedelta, &isglobal, + &leaftype, &slicedelta, &isglobal, &ptrroot); // `&` through a `*T`-rooted chain is a // separate shape (would need MOVQ + LEAQ @@ -18839,13 +18865,13 @@ fn cgassign(c: *cgen, n: *node) void = { let rootname: str = ""; let rootoff: i32 = 0; let totaloff: i32 = 0; - let leaffi: *fieldinfo = nil; + let leaftype: *tinfo = nil; let slicedelta: i32 = -1; let isglobal: bool = false; let ptrroot: bool = false; let yok: bool = dotchainresolve(c, lhs, &rootname, &rootoff, &totaloff, - &leaffi, &slicedelta, &isglobal, &ptrroot); + &leaftype, &slicedelta, &isglobal, &ptrroot); if (yok) { // `*T` root and global share the CX-based emit: // loader runs AFTER cgexpr(rhs) so AX/BX/X0 stay @@ -18873,7 +18899,7 @@ fn cgassign(c: *cgen, n: *node) void = { }; return; }; - if (isstrtype(c, leaffi.tnode)) { + if (typeisstr(leaftype)) { cgexpr(c, n.rhs); if (viacx) { if (ptrroot) { @@ -18916,12 +18942,33 @@ fn cgassign(c: *cgen, n: *node) void = { // store; for ptrroot/global the dst addr is // reloaded into BX before each store so cgexpr // can clobber AX/BX between fields. + // #71: the struct-terminal cases below still drive the + // structinfo machinery (structnaturalsize / + // cgstructlitfill), so recover the struct NAME from the + // leaf tinfo's TY_NAMED wrapper. Peeled-TY_STRUCT + + // structlookup!=nil is byte-equal to the old `N_TNAME && + // primsize==0 && structlookup` guard: a named non-struct + // (tagged/alias) peels to a non-STRUCT kind, and + // structlookup decides struct-ness off the same declared + // name either way. + let leafstruct: bool = false; + let leafname: str = ""; + if (leaftype != nil) { + let lp: *tinfo = leaftype; + for (lp != nil && lp.kind == tykind.TY_NAMED) { + lp = lp.under; + }; + if (lp != nil) { + if (lp.kind == tykind.TY_STRUCT) { leafstruct = true; }; + }; + if (leaftype.kind == tykind.TY_NAMED) { + leafname = leaftype.name; + }; + }; if (n.rhs != nil && n.rhs.kind == nkind.N_CALL - && leaffi.tnode != nil - && leaffi.tnode.kind == nkind.N_TNAME - && primsize(leaffi.tnode.str) == 0) { - let lsi: *structinfo = structlookup(c, leaffi.tnode.str); + && leafstruct) { + let lsi: *structinfo = structlookup(c, leafname); if (lsi != nil) { // si.totsize is slot-padded (rounded to 8); // receive ABI needs the TYPE's natural size. @@ -19003,10 +19050,8 @@ fn cgassign(c: *cgen, n: *node) void = { // else → mode=0 (DST_BP), direct BP-rel, no reload. if (n.rhs != nil && n.rhs.kind == nkind.N_STRUCTLIT - && leaffi.tnode != nil - && leaffi.tnode.kind == nkind.N_TNAME - && primsize(leaffi.tnode.str) == 0) { - let lsi: *structinfo = structlookup(c, leaffi.tnode.str); + && leafstruct) { + let lsi: *structinfo = structlookup(c, leafname); if (lsi != nil) { // si.totsize is slot-padded (rounded to 8); // receive ABI needs the TYPE's natural size. @@ -19029,10 +19074,8 @@ fn cgassign(c: *cgen, n: *node) void = { }; if (n.rhs != nil && n.rhs.kind == nkind.N_IDENT - && leaffi.tnode != nil - && leaffi.tnode.kind == nkind.N_TNAME - && primsize(leaffi.tnode.str) == 0) { - let ssi: *structinfo = structlookup(c, leaffi.tnode.str); + && leafstruct) { + let ssi: *structinfo = structlookup(c, leafname); let srhs: *local = localfindnode(c, n.rhs.str); if (ssi != nil) { if (srhs != nil) { if (viacx) { @@ -19090,9 +19133,9 @@ fn cgassign(c: *cgen, n: *node) void = { return; };}; }; - if (isfloattype(c, leaffi.tnode)) { + if (typeisfloat(leaftype)) { let mov: str = "MOVSD"; - if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; }; + if (typeisf32(leaftype)) { mov = "MOVSS"; }; cgexpr(c, n.rhs); if (viacx) { if (ptrroot) { @@ -19118,7 +19161,16 @@ fn cgassign(c: *cgen, n: *node) void = { }; return; }; - let sop: str = fieldstoreop(c, leaffi); + // Scalar leaf store-op by size — the same size→op + // dispatch fieldstoreop used on the leaf fieldinfo, now + // keyed on the leaf tinfo's slot width (#71). + let sop: str = "MOVQ"; + if (leaftype != nil) { + let ssz: i32 = leaftype.slotsize: i32; + if (ssz == 1) { sop = "MOVB"; } + else { if (ssz == 2) { sop = "MOVW"; } + else { if (ssz == 4) { sop = "MOVL"; }; }; }; + }; cgexpr(c, n.rhs); if (viacx) { if (ptrroot) { diff --git a/selfhost/cmd/wcc/cgenexpr.ww b/selfhost/cmd/wcc/cgenexpr.ww index d91ed9b0..97ed61ca 100644 --- a/selfhost/cmd/wcc/cgenexpr.ww +++ b/selfhost/cmd/wcc/cgenexpr.ww @@ -1840,13 +1840,13 @@ fn cgdot(c: *cgen, n: *node) void = { let rootname: str = ""; let rootoff: i32 = 0; let totaloff: i32 = 0; - let leaffi: *fieldinfo = nil; + let leaftype: *tinfo = nil; let slicedelta: i32 = -1; let isglobal: bool = false; let ptrroot: bool = false; let pok: bool = dotchainresolve(c, n, &rootname, &rootoff, &totaloff, - &leaffi, &slicedelta, &isglobal, &ptrroot); + &leaftype, &slicedelta, &isglobal, &ptrroot); if (pok) { // `*T` root: load the pointer slot once into CX, // then index every leaf at total_off off CX. Same @@ -1874,7 +1874,7 @@ fn cgdot(c: *cgen, n: *node) void = { }; return; }; - if (isstrtype(c, leaffi.tnode)) { + if (typeisstr(leaftype)) { if (viacx) { if (ptrroot) { emitline("\tMOVQ\t"); @@ -1901,7 +1901,7 @@ fn cgdot(c: *cgen, n: *node) void = { }; return; }; - if (isslicetype(c, leaffi.tnode)) { + if (typeisslice(leaftype)) { // Slice leaf: load all three header words into // (AX=ptr, BX=len, CX=cap). For the viacx path // (global or `*T` root) CX is the base; load @@ -1940,9 +1940,9 @@ fn cgdot(c: *cgen, n: *node) void = { }; return; }; - if (isfloattype(c, leaffi.tnode)) { + if (typeisfloat(leaftype)) { let mov: str = "MOVSD"; - if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; }; + if (typeisf32(leaftype)) { mov = "MOVSS"; }; if (viacx) { if (ptrroot) { emitline("\tMOVQ\t"); @@ -1967,7 +1967,8 @@ fn cgdot(c: *cgen, n: *node) void = { }; return; }; - let lop: str = fieldloadop(c, leaffi); + let lop: str = loadopsz(typeissigned(leaftype), + leaftype.slotsize: i32); if (viacx) { if (ptrroot) { emitline("\tMOVQ\t"); @@ -2314,13 +2315,13 @@ fn cgun(c: *cgen, n: *node) void = { let rootname: str = ""; let rootoff: i32 = 0; let totaloff: i32 = 0; - let leaffi: *fieldinfo = nil; + let leaftype: *tinfo = nil; let slicedelta: i32 = -1; let isglobal: bool = false; let ptrroot: bool = false; let pok: bool = dotchainresolve(c, opnd, &rootname, &rootoff, &totaloff, - &leaffi, &slicedelta, &isglobal, + &leaftype, &slicedelta, &isglobal, &ptrroot); // `&` through a `*T`-rooted chain is a // separate shape (would need MOVQ + LEAQ @@ -4996,13 +4997,13 @@ fn cgassign(c: *cgen, n: *node) void = { let rootname: str = ""; let rootoff: i32 = 0; let totaloff: i32 = 0; - let leaffi: *fieldinfo = nil; + let leaftype: *tinfo = nil; let slicedelta: i32 = -1; let isglobal: bool = false; let ptrroot: bool = false; let yok: bool = dotchainresolve(c, lhs, &rootname, &rootoff, &totaloff, - &leaffi, &slicedelta, &isglobal, &ptrroot); + &leaftype, &slicedelta, &isglobal, &ptrroot); if (yok) { // `*T` root and global share the CX-based emit: // loader runs AFTER cgexpr(rhs) so AX/BX/X0 stay @@ -5030,7 +5031,7 @@ fn cgassign(c: *cgen, n: *node) void = { }; return; }; - if (isstrtype(c, leaffi.tnode)) { + if (typeisstr(leaftype)) { cgexpr(c, n.rhs); if (viacx) { if (ptrroot) { @@ -5073,12 +5074,33 @@ fn cgassign(c: *cgen, n: *node) void = { // store; for ptrroot/global the dst addr is // reloaded into BX before each store so cgexpr // can clobber AX/BX between fields. + // #71: the struct-terminal cases below still drive the + // structinfo machinery (structnaturalsize / + // cgstructlitfill), so recover the struct NAME from the + // leaf tinfo's TY_NAMED wrapper. Peeled-TY_STRUCT + + // structlookup!=nil is byte-equal to the old `N_TNAME && + // primsize==0 && structlookup` guard: a named non-struct + // (tagged/alias) peels to a non-STRUCT kind, and + // structlookup decides struct-ness off the same declared + // name either way. + let leafstruct: bool = false; + let leafname: str = ""; + if (leaftype != nil) { + let lp: *tinfo = leaftype; + for (lp != nil && lp.kind == tykind.TY_NAMED) { + lp = lp.under; + }; + if (lp != nil) { + if (lp.kind == tykind.TY_STRUCT) { leafstruct = true; }; + }; + if (leaftype.kind == tykind.TY_NAMED) { + leafname = leaftype.name; + }; + }; if (n.rhs != nil && n.rhs.kind == nkind.N_CALL - && leaffi.tnode != nil - && leaffi.tnode.kind == nkind.N_TNAME - && primsize(leaffi.tnode.str) == 0) { - let lsi: *structinfo = structlookup(c, leaffi.tnode.str); + && leafstruct) { + let lsi: *structinfo = structlookup(c, leafname); if (lsi != nil) { // si.totsize is slot-padded (rounded to 8); // receive ABI needs the TYPE's natural size. @@ -5160,10 +5182,8 @@ fn cgassign(c: *cgen, n: *node) void = { // else → mode=0 (DST_BP), direct BP-rel, no reload. if (n.rhs != nil && n.rhs.kind == nkind.N_STRUCTLIT - && leaffi.tnode != nil - && leaffi.tnode.kind == nkind.N_TNAME - && primsize(leaffi.tnode.str) == 0) { - let lsi: *structinfo = structlookup(c, leaffi.tnode.str); + && leafstruct) { + let lsi: *structinfo = structlookup(c, leafname); if (lsi != nil) { // si.totsize is slot-padded (rounded to 8); // receive ABI needs the TYPE's natural size. @@ -5186,10 +5206,8 @@ fn cgassign(c: *cgen, n: *node) void = { }; if (n.rhs != nil && n.rhs.kind == nkind.N_IDENT - && leaffi.tnode != nil - && leaffi.tnode.kind == nkind.N_TNAME - && primsize(leaffi.tnode.str) == 0) { - let ssi: *structinfo = structlookup(c, leaffi.tnode.str); + && leafstruct) { + let ssi: *structinfo = structlookup(c, leafname); let srhs: *local = localfindnode(c, n.rhs.str); if (ssi != nil) { if (srhs != nil) { if (viacx) { @@ -5247,9 +5265,9 @@ fn cgassign(c: *cgen, n: *node) void = { return; };}; }; - if (isfloattype(c, leaffi.tnode)) { + if (typeisfloat(leaftype)) { let mov: str = "MOVSD"; - if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; }; + if (typeisf32(leaftype)) { mov = "MOVSS"; }; cgexpr(c, n.rhs); if (viacx) { if (ptrroot) { @@ -5275,7 +5293,16 @@ fn cgassign(c: *cgen, n: *node) void = { }; return; }; - let sop: str = fieldstoreop(c, leaffi); + // Scalar leaf store-op by size — the same size→op + // dispatch fieldstoreop used on the leaf fieldinfo, now + // keyed on the leaf tinfo's slot width (#71). + let sop: str = "MOVQ"; + if (leaftype != nil) { + let ssz: i32 = leaftype.slotsize: i32; + if (ssz == 1) { sop = "MOVB"; } + else { if (ssz == 2) { sop = "MOVW"; } + else { if (ssz == 4) { sop = "MOVL"; }; }; }; + }; cgexpr(c, n.rhs); if (viacx) { if (ptrroot) { diff --git a/selfhost/cmd/wcc/cgenutil.ww b/selfhost/cmd/wcc/cgenutil.ww index ede21099..538f2961 100644 --- a/selfhost/cmd/wcc/cgenutil.ww +++ b/selfhost/cmd/wcc/cgenutil.ww @@ -2808,7 +2808,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s // Spine-walk a chained N_DOT (n) inward to a root ident, summing field // offsets through value-struct intermediates. Optional slice/str leaf // pseudo-field (.ptr / .len / .cap) on the last segment is folded into -// *outslicedelta (0/8/16); otherwise *outleaffi is the leaf fieldinfo +// *outslicedelta (0/8/16); otherwise *outleaftype is the leaf *tinfo // and *outslicedelta stays -1. Returns true on success; on false the // caller falls through to other branches. // @@ -2825,16 +2825,28 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s // Numeric out-params are i32 — offsets fit naturally and the post-#19 // localloadop sign-extends i32 deref-stored slots on read, so negative // frame offsets round-trip intact. +// #71 (A.6.3j): the spine offset-sum + leaf-type now read off +// tinfo.fields (natural layout) instead of the structinfo/fieldinfo +// walk. Mirror of cstage cmd/w6c/cgen.c:3156-3216 which walks +// `cur->lhs->type` fields. Root resolution (the local/ptr/global split +// and the rootoff/ptrroot/isglobal flags) stays on localfindnode/ +// letvarstructinfo unchanged, so the firing set + addressing mode are +// byte-identical to the structinfo era; only the layout SOURCE moves. +// The slot-padded foff and the natural tfield.offset coincide for every +// shape the byte-id gate exercises (cstage already reads the natural +// offset), so this is offset-preserving. Leaf out-param is the field's +// stamped *tinfo (was *fieldinfo); the str/slice pseudo-leaf leaves it +// nil and the callers gate on slicedelta>=0 first. export fn dotchainresolve(c: *cgen, n: *node, outrootname: *str, outrootoff: *i32, outtotaloff: *i32, - outleaffi: **fieldinfo, outslicedelta: *i32, + outleaftype: **tinfo, outslicedelta: *i32, outisglobal: *bool, outptrroot: *bool) bool = { *outrootname = ""; *outrootoff = 0; *outisglobal = false; *outptrroot = false; *outtotaloff = 0; - *outleaffi = nil; + *outleaftype = nil; *outslicedelta = -1; if (n == nil) { return false; }; if (n.kind != nkind.N_DOT) { return false; }; @@ -2852,13 +2864,13 @@ export fn dotchainresolve(c: *cgen, n: *node, if (cur == nil) { return false; }; if (cur.kind != nkind.N_IDENT) { return false; }; *outrootname = cur.str; - let rootstruct: str = ""; + let resolved: bool = false; let lc: *local = localfindnode(c, cur.str); if (lc != nil) { if (lc.tnode != nil) { if (lc.tnode.kind == nkind.N_TNAME) { - rootstruct = lc.tnode.str; *outrootoff = lc.off; + resolved = true; }; // `*T` root (param/local): dereference at emit time; // pointee struct supplies the field layout. Callers @@ -2868,60 +2880,71 @@ export fn dotchainresolve(c: *cgen, n: *node, let pe: *node = lc.tnode.lhs; if (pe != nil) { if (pe.kind == nkind.N_TNAME) { - rootstruct = pe.str; *outrootoff = lc.off; *outptrroot = true; + resolved = true; }; }; }; }; }; - if (rootstruct.len == 0) { + if (!resolved) { let gsi: *structinfo = letvarstructinfo(c, cur.str); if (gsi != nil) { - rootstruct = gsi.sname; *outisglobal = true; + resolved = true; + }; + }; + if (!resolved) { return false; }; + // Root struct layout = the stamped root-ident type_, peeled NAMED + // (plus one TY_PTR hop for a `*struct` root). tfield.type_ then + // supplies each nested struct directly, so no name re-lookup. + let curstruct: *tinfo = cur.type_: *tinfo; + for (curstruct != nil && curstruct.kind == tykind.TY_NAMED) { + curstruct = curstruct.under; + }; + if (*outptrroot) { + if (curstruct == nil) { return false; }; + if (curstruct.kind != tykind.TY_PTR) { return false; }; + curstruct = curstruct.sub; + for (curstruct != nil && curstruct.kind == tykind.TY_NAMED) { + curstruct = curstruct.under; }; }; - if (rootstruct.len == 0) { return false; }; - let curstruct: str = rootstruct; let i: i32 = nsteps - 1; for (i >= 0) { - let csi: *structinfo = structlookup(c, curstruct); - if (csi == nil) { return false; }; + if (curstruct == nil) { return false; }; + if (curstruct.kind != tykind.TY_STRUCT) { return false; }; if (stk[i] == nil) { return false; }; let stepnm: str = stk[i].str; - let fi: *fieldinfo = csi.fields; - let found: *fieldinfo = nil; - for (fi != nil) { - if (streq(fi.fname, stepnm)) { found = fi; break; }; - fi = fi.finext; + let tf: *tfield = curstruct.fields; + let found: *tfield = nil; + for (tf != nil) { + if (streq(tf.name, stepnm)) { found = tf; break; }; + tf = tf.tnext; }; if (found == nil) { return false; }; + let foff: i32 = found.offset: i32; if (i == 0) { - *outtotaloff = *outtotaloff + found.foff; - *outleaffi = found; + *outtotaloff = *outtotaloff + foff; + *outleaftype = found.type_; return true; }; - let ft: *node = found.tnode; + let ft: *tinfo = found.type_; + for (ft != nil && ft.kind == tykind.TY_NAMED) { ft = ft.under; }; if (ft == nil) { return false; }; - if (ft.kind == nkind.N_TNAME) { - if (streq(ft.str, "str")) { - if (i != 1) { return false; }; - let pseudo: str = stk[0].str; - let delta: i32 = -1; - if (streq(pseudo, "ptr")) { delta = 0; } - else { if (streq(pseudo, "len")) { delta = 8; }; }; - if (delta < 0) { return false; }; - *outtotaloff = *outtotaloff + found.foff; - *outslicedelta = delta; - return true; - }; - if (primsize(ft.str) != 0) { return false; }; - *outtotaloff = *outtotaloff + found.foff; - curstruct = ft.str; - i -= 1; - } else { if (ft.kind == nkind.N_TSLICE) { + if (ft.kind == tykind.TY_STR) { + if (i != 1) { return false; }; + let pseudo: str = stk[0].str; + let delta: i32 = -1; + if (streq(pseudo, "ptr")) { delta = 0; } + else { if (streq(pseudo, "len")) { delta = 8; }; }; + if (delta < 0) { return false; }; + *outtotaloff = *outtotaloff + foff; + *outslicedelta = delta; + return true; + }; + if (ft.kind == tykind.TY_SLICE) { if (i != 1) { return false; }; let pseudo: str = stk[0].str; let delta: i32 = -1; @@ -2929,12 +2952,14 @@ export fn dotchainresolve(c: *cgen, n: *node, else { if (streq(pseudo, "len")) { delta = 8; } else { if (streq(pseudo, "cap")) { delta = 16; }; }; }; if (delta < 0) { return false; }; - *outtotaloff = *outtotaloff + found.foff; + *outtotaloff = *outtotaloff + foff; *outslicedelta = delta; return true; - } else { - return false; - }; }; + }; + if (ft.kind != tykind.TY_STRUCT) { return false; }; + *outtotaloff = *outtotaloff + foff; + curstruct = ft; + i -= 1; }; return false; }; diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index ef1d1d73..bb1deb48 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -13353,7 +13353,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s // Spine-walk a chained N_DOT (n) inward to a root ident, summing field // offsets through value-struct intermediates. Optional slice/str leaf // pseudo-field (.ptr / .len / .cap) on the last segment is folded into -// *outslicedelta (0/8/16); otherwise *outleaffi is the leaf fieldinfo +// *outslicedelta (0/8/16); otherwise *outleaftype is the leaf *tinfo // and *outslicedelta stays -1. Returns true on success; on false the // caller falls through to other branches. // @@ -13370,16 +13370,28 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s // Numeric out-params are i32 — offsets fit naturally and the post-#19 // localloadop sign-extends i32 deref-stored slots on read, so negative // frame offsets round-trip intact. +// #71 (A.6.3j): the spine offset-sum + leaf-type now read off +// tinfo.fields (natural layout) instead of the structinfo/fieldinfo +// walk. Mirror of cstage cmd/w6c/cgen.c:3156-3216 which walks +// `cur->lhs->type` fields. Root resolution (the local/ptr/global split +// and the rootoff/ptrroot/isglobal flags) stays on localfindnode/ +// letvarstructinfo unchanged, so the firing set + addressing mode are +// byte-identical to the structinfo era; only the layout SOURCE moves. +// The slot-padded foff and the natural tfield.offset coincide for every +// shape the byte-id gate exercises (cstage already reads the natural +// offset), so this is offset-preserving. Leaf out-param is the field's +// stamped *tinfo (was *fieldinfo); the str/slice pseudo-leaf leaves it +// nil and the callers gate on slicedelta>=0 first. export fn dotchainresolve(c: *cgen, n: *node, outrootname: *str, outrootoff: *i32, outtotaloff: *i32, - outleaffi: **fieldinfo, outslicedelta: *i32, + outleaftype: **tinfo, outslicedelta: *i32, outisglobal: *bool, outptrroot: *bool) bool = { *outrootname = ""; *outrootoff = 0; *outisglobal = false; *outptrroot = false; *outtotaloff = 0; - *outleaffi = nil; + *outleaftype = nil; *outslicedelta = -1; if (n == nil) { return false; }; if (n.kind != nkind.N_DOT) { return false; }; @@ -13397,13 +13409,13 @@ export fn dotchainresolve(c: *cgen, n: *node, if (cur == nil) { return false; }; if (cur.kind != nkind.N_IDENT) { return false; }; *outrootname = cur.str; - let rootstruct: str = ""; + let resolved: bool = false; let lc: *local = localfindnode(c, cur.str); if (lc != nil) { if (lc.tnode != nil) { if (lc.tnode.kind == nkind.N_TNAME) { - rootstruct = lc.tnode.str; *outrootoff = lc.off; + resolved = true; }; // `*T` root (param/local): dereference at emit time; // pointee struct supplies the field layout. Callers @@ -13413,60 +13425,71 @@ export fn dotchainresolve(c: *cgen, n: *node, let pe: *node = lc.tnode.lhs; if (pe != nil) { if (pe.kind == nkind.N_TNAME) { - rootstruct = pe.str; *outrootoff = lc.off; *outptrroot = true; + resolved = true; }; }; }; }; }; - if (rootstruct.len == 0) { + if (!resolved) { let gsi: *structinfo = letvarstructinfo(c, cur.str); if (gsi != nil) { - rootstruct = gsi.sname; *outisglobal = true; + resolved = true; + }; + }; + if (!resolved) { return false; }; + // Root struct layout = the stamped root-ident type_, peeled NAMED + // (plus one TY_PTR hop for a `*struct` root). tfield.type_ then + // supplies each nested struct directly, so no name re-lookup. + let curstruct: *tinfo = cur.type_: *tinfo; + for (curstruct != nil && curstruct.kind == tykind.TY_NAMED) { + curstruct = curstruct.under; + }; + if (*outptrroot) { + if (curstruct == nil) { return false; }; + if (curstruct.kind != tykind.TY_PTR) { return false; }; + curstruct = curstruct.sub; + for (curstruct != nil && curstruct.kind == tykind.TY_NAMED) { + curstruct = curstruct.under; }; }; - if (rootstruct.len == 0) { return false; }; - let curstruct: str = rootstruct; let i: i32 = nsteps - 1; for (i >= 0) { - let csi: *structinfo = structlookup(c, curstruct); - if (csi == nil) { return false; }; + if (curstruct == nil) { return false; }; + if (curstruct.kind != tykind.TY_STRUCT) { return false; }; if (stk[i] == nil) { return false; }; let stepnm: str = stk[i].str; - let fi: *fieldinfo = csi.fields; - let found: *fieldinfo = nil; - for (fi != nil) { - if (streq(fi.fname, stepnm)) { found = fi; break; }; - fi = fi.finext; + let tf: *tfield = curstruct.fields; + let found: *tfield = nil; + for (tf != nil) { + if (streq(tf.name, stepnm)) { found = tf; break; }; + tf = tf.tnext; }; if (found == nil) { return false; }; + let foff: i32 = found.offset: i32; if (i == 0) { - *outtotaloff = *outtotaloff + found.foff; - *outleaffi = found; + *outtotaloff = *outtotaloff + foff; + *outleaftype = found.type_; return true; }; - let ft: *node = found.tnode; + let ft: *tinfo = found.type_; + for (ft != nil && ft.kind == tykind.TY_NAMED) { ft = ft.under; }; if (ft == nil) { return false; }; - if (ft.kind == nkind.N_TNAME) { - if (streq(ft.str, "str")) { - if (i != 1) { return false; }; - let pseudo: str = stk[0].str; - let delta: i32 = -1; - if (streq(pseudo, "ptr")) { delta = 0; } - else { if (streq(pseudo, "len")) { delta = 8; }; }; - if (delta < 0) { return false; }; - *outtotaloff = *outtotaloff + found.foff; - *outslicedelta = delta; - return true; - }; - if (primsize(ft.str) != 0) { return false; }; - *outtotaloff = *outtotaloff + found.foff; - curstruct = ft.str; - i -= 1; - } else { if (ft.kind == nkind.N_TSLICE) { + if (ft.kind == tykind.TY_STR) { + if (i != 1) { return false; }; + let pseudo: str = stk[0].str; + let delta: i32 = -1; + if (streq(pseudo, "ptr")) { delta = 0; } + else { if (streq(pseudo, "len")) { delta = 8; }; }; + if (delta < 0) { return false; }; + *outtotaloff = *outtotaloff + foff; + *outslicedelta = delta; + return true; + }; + if (ft.kind == tykind.TY_SLICE) { if (i != 1) { return false; }; let pseudo: str = stk[0].str; let delta: i32 = -1; @@ -13474,12 +13497,14 @@ export fn dotchainresolve(c: *cgen, n: *node, else { if (streq(pseudo, "len")) { delta = 8; } else { if (streq(pseudo, "cap")) { delta = 16; }; }; }; if (delta < 0) { return false; }; - *outtotaloff = *outtotaloff + found.foff; + *outtotaloff = *outtotaloff + foff; *outslicedelta = delta; return true; - } else { - return false; - }; }; + }; + if (ft.kind != tykind.TY_STRUCT) { return false; }; + *outtotaloff = *outtotaloff + foff; + curstruct = ft; + i -= 1; }; return false; }; @@ -15683,13 +15708,13 @@ fn cgdot(c: *cgen, n: *node) void = { let rootname: str = ""; let rootoff: i32 = 0; let totaloff: i32 = 0; - let leaffi: *fieldinfo = nil; + let leaftype: *tinfo = nil; let slicedelta: i32 = -1; let isglobal: bool = false; let ptrroot: bool = false; let pok: bool = dotchainresolve(c, n, &rootname, &rootoff, &totaloff, - &leaffi, &slicedelta, &isglobal, &ptrroot); + &leaftype, &slicedelta, &isglobal, &ptrroot); if (pok) { // `*T` root: load the pointer slot once into CX, // then index every leaf at total_off off CX. Same @@ -15717,7 +15742,7 @@ fn cgdot(c: *cgen, n: *node) void = { }; return; }; - if (isstrtype(c, leaffi.tnode)) { + if (typeisstr(leaftype)) { if (viacx) { if (ptrroot) { emitline("\tMOVQ\t"); @@ -15744,7 +15769,7 @@ fn cgdot(c: *cgen, n: *node) void = { }; return; }; - if (isslicetype(c, leaffi.tnode)) { + if (typeisslice(leaftype)) { // Slice leaf: load all three header words into // (AX=ptr, BX=len, CX=cap). For the viacx path // (global or `*T` root) CX is the base; load @@ -15783,9 +15808,9 @@ fn cgdot(c: *cgen, n: *node) void = { }; return; }; - if (isfloattype(c, leaffi.tnode)) { + if (typeisfloat(leaftype)) { let mov: str = "MOVSD"; - if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; }; + if (typeisf32(leaftype)) { mov = "MOVSS"; }; if (viacx) { if (ptrroot) { emitline("\tMOVQ\t"); @@ -15810,7 +15835,8 @@ fn cgdot(c: *cgen, n: *node) void = { }; return; }; - let lop: str = fieldloadop(c, leaffi); + let lop: str = loadopsz(typeissigned(leaftype), + leaftype.slotsize: i32); if (viacx) { if (ptrroot) { emitline("\tMOVQ\t"); @@ -16157,13 +16183,13 @@ fn cgun(c: *cgen, n: *node) void = { let rootname: str = ""; let rootoff: i32 = 0; let totaloff: i32 = 0; - let leaffi: *fieldinfo = nil; + let leaftype: *tinfo = nil; let slicedelta: i32 = -1; let isglobal: bool = false; let ptrroot: bool = false; let pok: bool = dotchainresolve(c, opnd, &rootname, &rootoff, &totaloff, - &leaffi, &slicedelta, &isglobal, + &leaftype, &slicedelta, &isglobal, &ptrroot); // `&` through a `*T`-rooted chain is a // separate shape (would need MOVQ + LEAQ @@ -18839,13 +18865,13 @@ fn cgassign(c: *cgen, n: *node) void = { let rootname: str = ""; let rootoff: i32 = 0; let totaloff: i32 = 0; - let leaffi: *fieldinfo = nil; + let leaftype: *tinfo = nil; let slicedelta: i32 = -1; let isglobal: bool = false; let ptrroot: bool = false; let yok: bool = dotchainresolve(c, lhs, &rootname, &rootoff, &totaloff, - &leaffi, &slicedelta, &isglobal, &ptrroot); + &leaftype, &slicedelta, &isglobal, &ptrroot); if (yok) { // `*T` root and global share the CX-based emit: // loader runs AFTER cgexpr(rhs) so AX/BX/X0 stay @@ -18873,7 +18899,7 @@ fn cgassign(c: *cgen, n: *node) void = { }; return; }; - if (isstrtype(c, leaffi.tnode)) { + if (typeisstr(leaftype)) { cgexpr(c, n.rhs); if (viacx) { if (ptrroot) { @@ -18916,12 +18942,33 @@ fn cgassign(c: *cgen, n: *node) void = { // store; for ptrroot/global the dst addr is // reloaded into BX before each store so cgexpr // can clobber AX/BX between fields. + // #71: the struct-terminal cases below still drive the + // structinfo machinery (structnaturalsize / + // cgstructlitfill), so recover the struct NAME from the + // leaf tinfo's TY_NAMED wrapper. Peeled-TY_STRUCT + + // structlookup!=nil is byte-equal to the old `N_TNAME && + // primsize==0 && structlookup` guard: a named non-struct + // (tagged/alias) peels to a non-STRUCT kind, and + // structlookup decides struct-ness off the same declared + // name either way. + let leafstruct: bool = false; + let leafname: str = ""; + if (leaftype != nil) { + let lp: *tinfo = leaftype; + for (lp != nil && lp.kind == tykind.TY_NAMED) { + lp = lp.under; + }; + if (lp != nil) { + if (lp.kind == tykind.TY_STRUCT) { leafstruct = true; }; + }; + if (leaftype.kind == tykind.TY_NAMED) { + leafname = leaftype.name; + }; + }; if (n.rhs != nil && n.rhs.kind == nkind.N_CALL - && leaffi.tnode != nil - && leaffi.tnode.kind == nkind.N_TNAME - && primsize(leaffi.tnode.str) == 0) { - let lsi: *structinfo = structlookup(c, leaffi.tnode.str); + && leafstruct) { + let lsi: *structinfo = structlookup(c, leafname); if (lsi != nil) { // si.totsize is slot-padded (rounded to 8); // receive ABI needs the TYPE's natural size. @@ -19003,10 +19050,8 @@ fn cgassign(c: *cgen, n: *node) void = { // else → mode=0 (DST_BP), direct BP-rel, no reload. if (n.rhs != nil && n.rhs.kind == nkind.N_STRUCTLIT - && leaffi.tnode != nil - && leaffi.tnode.kind == nkind.N_TNAME - && primsize(leaffi.tnode.str) == 0) { - let lsi: *structinfo = structlookup(c, leaffi.tnode.str); + && leafstruct) { + let lsi: *structinfo = structlookup(c, leafname); if (lsi != nil) { // si.totsize is slot-padded (rounded to 8); // receive ABI needs the TYPE's natural size. @@ -19029,10 +19074,8 @@ fn cgassign(c: *cgen, n: *node) void = { }; if (n.rhs != nil && n.rhs.kind == nkind.N_IDENT - && leaffi.tnode != nil - && leaffi.tnode.kind == nkind.N_TNAME - && primsize(leaffi.tnode.str) == 0) { - let ssi: *structinfo = structlookup(c, leaffi.tnode.str); + && leafstruct) { + let ssi: *structinfo = structlookup(c, leafname); let srhs: *local = localfindnode(c, n.rhs.str); if (ssi != nil) { if (srhs != nil) { if (viacx) { @@ -19090,9 +19133,9 @@ fn cgassign(c: *cgen, n: *node) void = { return; };}; }; - if (isfloattype(c, leaffi.tnode)) { + if (typeisfloat(leaftype)) { let mov: str = "MOVSD"; - if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; }; + if (typeisf32(leaftype)) { mov = "MOVSS"; }; cgexpr(c, n.rhs); if (viacx) { if (ptrroot) { @@ -19118,7 +19161,16 @@ fn cgassign(c: *cgen, n: *node) void = { }; return; }; - let sop: str = fieldstoreop(c, leaffi); + // Scalar leaf store-op by size — the same size→op + // dispatch fieldstoreop used on the leaf fieldinfo, now + // keyed on the leaf tinfo's slot width (#71). + let sop: str = "MOVQ"; + if (leaftype != nil) { + let ssz: i32 = leaftype.slotsize: i32; + if (ssz == 1) { sop = "MOVB"; } + else { if (ssz == 2) { sop = "MOVW"; } + else { if (ssz == 4) { sop = "MOVL"; }; }; }; + }; cgexpr(c, n.rhs); if (viacx) { if (ptrroot) {