wcc: converge let-IDENT memcpy, IDENT-assign recv, nested struct call-recv onto structabisize
Three more wwstage cgen sites still used unrounded structnaturalsize where cstage rounds via lu->size — pre-existing gate-blind cs!=ww latents the #169b reviewer surfaced: cgenstmt N_LET struct-IDENT memcpy (let p2: T = p1; twin cgen.c:7869), cgenexpr N_ASSIGN N_IDENT-lhs register RECV (s = mk(); twin cgen.c:4700-4737), and cgenutil's nested struct N_CALL recv inside cgstructlitfill (twin cgen.c:2121). Converge all three onto structabisize, completing the same-class closure started by #169 and continued by #169b. Also corrected the inline comment at cgenutil.ww:3273-3286 that wrongly claimed fl->type->size was natural (check.c:760 sets ABI). sretretsize at cgenutil.ww:1301 is gate-equivalent natural and is left alone. Probe 698 +3 rows (one per converged site) with cs==ww .s byte-cmp and a pre-fix-rebuild discriminator. 990-997 byte-id hold.
This commit is contained in:
@@ -17337,21 +17337,22 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *node,
|
|||||||
if (primsize(fi.tnode.str) == 0) {
|
if (primsize(fi.tnode.str) == 0) {
|
||||||
let csi: *structinfo = structlookup(c, fi.tnode.str);
|
let csi: *structinfo = structlookup(c, fi.tnode.str);
|
||||||
if (csi != nil) {
|
if (csi != nil) {
|
||||||
// Use the inner struct's
|
// Inner struct's ABI size
|
||||||
// NATURAL size (no 8B slot
|
// (maxalign-rounded) — cstage
|
||||||
// rounding) so MOVL/MOVW/
|
// reads fl->type->size at the
|
||||||
// MOVB tail dispatch matches
|
// nested-call branch
|
||||||
// cstage's fl->type->size
|
// (cgen.c:2121); check.c:760
|
||||||
// (which is natural per
|
// sets that to the
|
||||||
// check.c). fi.fsz here is
|
// maxalign-rounded ABI extent
|
||||||
|
// (NOT natural). fi.fsz is
|
||||||
// wwstage's slot-padded
|
// wwstage's slot-padded
|
||||||
// totsize — using it would
|
// totsize (round-to-8); the
|
||||||
// emit 2× MOVQ where cstage
|
// pre-#169 structnaturalsize
|
||||||
// emits MOVQ+MOVL for a
|
// shorts struct{i64,i32}
|
||||||
// 12B inner, etc. (task #15
|
// (natural 12, ABI 16) to
|
||||||
// territory; sidestepped
|
// MOVQ+MOVL where cstage
|
||||||
// locally.)
|
// writes MOVQ+MOVQ.
|
||||||
let cfsz: i32 = structnaturalsize(csi);
|
let cfsz: i32 = structabisize(csi);
|
||||||
let crem: i32 = cfsz - (cfsz / 8) * 8;
|
let crem: i32 = cfsz - (cfsz / 8) * 8;
|
||||||
if (cfsz <= 24) {
|
if (cfsz <= 24) {
|
||||||
if (crem == 0 || crem == 1
|
if (crem == 0 || crem == 1
|
||||||
@@ -24267,9 +24268,14 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
if (lcsname.len > 0) {
|
if (lcsname.len > 0) {
|
||||||
let lcsi: *structinfo = structlookup(c, lcsname);
|
let lcsi: *structinfo = structlookup(c, lcsname);
|
||||||
if (lcsi != nil) {
|
if (lcsi != nil) {
|
||||||
// si.totsize is slot-padded (rounded to 8);
|
// register RECV reads AX/DX/CX at 8-byte
|
||||||
// receive ABI needs the TYPE's natural size.
|
// granularity — size via structabisize (cstage
|
||||||
let lcnsz: i32 = structnaturalsize(lcsi);
|
// N_ASSIGN-IDENT branch sets sz = lu->size,
|
||||||
|
// cgen.c:4704; check.c:760 SSoT). The pre-
|
||||||
|
// #169 structnaturalsize shorts struct{i64,i32}
|
||||||
|
// (natural 12, ABI 16) to MOVQ+MOVL where
|
||||||
|
// cstage writes MOVQ+MOVQ.
|
||||||
|
let lcnsz: i32 = structabisize(lcsi);
|
||||||
if (n.op == tkind.TK_ASSIGN) {
|
if (n.op == tkind.TK_ASSIGN) {
|
||||||
if (n.rhs != nil
|
if (n.rhs != nil
|
||||||
&& n.rhs.kind == nkind.N_STRUCTLIT) {
|
&& n.rhs.kind == nkind.N_STRUCTLIT) {
|
||||||
@@ -25876,8 +25882,8 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
// Struct ident copy: `let p2: T = p1;` where T is a struct
|
// Struct ident copy: `let p2: T = p1;` where T is a struct
|
||||||
// >8B and rhs is a local ident. Per-qword MOVQ from src
|
// >8B and rhs is a local ident. Per-qword MOVQ from src
|
||||||
// slot to dst slot, with a sized tail (MOVL/MOVB) for
|
// slot to dst slot, with a sized tail (MOVL/MOVB) for
|
||||||
// natural sizes that aren't 8-aligned (e.g. `struct
|
// ABI sizes that aren't 8-aligned (e.g. `struct
|
||||||
// { i32, i32, i32 }` is 12B). Pre-fix this path fell
|
// { i32, i32, i32 }`, maxalign 4 → ABI 12B). Pre-fix this path fell
|
||||||
// through to `cgexpr + MOVQ AX, off(BP)` which stored
|
// through to `cgexpr + MOVQ AX, off(BP)` which stored
|
||||||
// only the first qword (and a stale BX for sz==16 lets
|
// only the first qword (and a stale BX for sz==16 lets
|
||||||
// via the str-init tail) — silent partial copy. Mirrors
|
// via the str-init tail) — silent partial copy. Mirrors
|
||||||
@@ -25891,7 +25897,13 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
if (sname.len > 0) {
|
if (sname.len > 0) {
|
||||||
let lsi: *structinfo = structlookup(c, sname);
|
let lsi: *structinfo = structlookup(c, sname);
|
||||||
if (lsi != nil) {
|
if (lsi != nil) {
|
||||||
let lsz: i32 = structnaturalsize(lsi);
|
// memcpy run sizes on the maxalign-rounded ABI
|
||||||
|
// size — cstage N_LET sets sz = lu->size
|
||||||
|
// (cgen.c:7533, struct-IDENT branch :7869),
|
||||||
|
// check.c:760 SSoT. structnaturalsize would
|
||||||
|
// short struct{i64,i32} (natural 12, ABI 16)
|
||||||
|
// to MOVQ+MOVL where cstage writes MOVQ+MOVQ.
|
||||||
|
let lsz: i32 = structabisize(lsi);
|
||||||
if (lsz > 8) {
|
if (lsz > 8) {
|
||||||
let lc: *local = localfindnode(c, rhs.str);
|
let lc: *local = localfindnode(c, rhs.str);
|
||||||
if (lc != nil) {
|
if (lc != nil) {
|
||||||
|
|||||||
@@ -6739,9 +6739,14 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
if (lcsname.len > 0) {
|
if (lcsname.len > 0) {
|
||||||
let lcsi: *structinfo = structlookup(c, lcsname);
|
let lcsi: *structinfo = structlookup(c, lcsname);
|
||||||
if (lcsi != nil) {
|
if (lcsi != nil) {
|
||||||
// si.totsize is slot-padded (rounded to 8);
|
// register RECV reads AX/DX/CX at 8-byte
|
||||||
// receive ABI needs the TYPE's natural size.
|
// granularity — size via structabisize (cstage
|
||||||
let lcnsz: i32 = structnaturalsize(lcsi);
|
// N_ASSIGN-IDENT branch sets sz = lu->size,
|
||||||
|
// cgen.c:4704; check.c:760 SSoT). The pre-
|
||||||
|
// #169 structnaturalsize shorts struct{i64,i32}
|
||||||
|
// (natural 12, ABI 16) to MOVQ+MOVL where
|
||||||
|
// cstage writes MOVQ+MOVQ.
|
||||||
|
let lcnsz: i32 = structabisize(lcsi);
|
||||||
if (n.op == tkind.TK_ASSIGN) {
|
if (n.op == tkind.TK_ASSIGN) {
|
||||||
if (n.rhs != nil
|
if (n.rhs != nil
|
||||||
&& n.rhs.kind == nkind.N_STRUCTLIT) {
|
&& n.rhs.kind == nkind.N_STRUCTLIT) {
|
||||||
|
|||||||
@@ -1358,8 +1358,8 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
// Struct ident copy: `let p2: T = p1;` where T is a struct
|
// Struct ident copy: `let p2: T = p1;` where T is a struct
|
||||||
// >8B and rhs is a local ident. Per-qword MOVQ from src
|
// >8B and rhs is a local ident. Per-qword MOVQ from src
|
||||||
// slot to dst slot, with a sized tail (MOVL/MOVB) for
|
// slot to dst slot, with a sized tail (MOVL/MOVB) for
|
||||||
// natural sizes that aren't 8-aligned (e.g. `struct
|
// ABI sizes that aren't 8-aligned (e.g. `struct
|
||||||
// { i32, i32, i32 }` is 12B). Pre-fix this path fell
|
// { i32, i32, i32 }`, maxalign 4 → ABI 12B). Pre-fix this path fell
|
||||||
// through to `cgexpr + MOVQ AX, off(BP)` which stored
|
// through to `cgexpr + MOVQ AX, off(BP)` which stored
|
||||||
// only the first qword (and a stale BX for sz==16 lets
|
// only the first qword (and a stale BX for sz==16 lets
|
||||||
// via the str-init tail) — silent partial copy. Mirrors
|
// via the str-init tail) — silent partial copy. Mirrors
|
||||||
@@ -1373,7 +1373,13 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
if (sname.len > 0) {
|
if (sname.len > 0) {
|
||||||
let lsi: *structinfo = structlookup(c, sname);
|
let lsi: *structinfo = structlookup(c, sname);
|
||||||
if (lsi != nil) {
|
if (lsi != nil) {
|
||||||
let lsz: i32 = structnaturalsize(lsi);
|
// memcpy run sizes on the maxalign-rounded ABI
|
||||||
|
// size — cstage N_LET sets sz = lu->size
|
||||||
|
// (cgen.c:7533, struct-IDENT branch :7869),
|
||||||
|
// check.c:760 SSoT. structnaturalsize would
|
||||||
|
// short struct{i64,i32} (natural 12, ABI 16)
|
||||||
|
// to MOVQ+MOVL where cstage writes MOVQ+MOVQ.
|
||||||
|
let lsz: i32 = structabisize(lsi);
|
||||||
if (lsz > 8) {
|
if (lsz > 8) {
|
||||||
let lc: *local = localfindnode(c, rhs.str);
|
let lc: *local = localfindnode(c, rhs.str);
|
||||||
if (lc != nil) {
|
if (lc != nil) {
|
||||||
|
|||||||
@@ -3270,21 +3270,22 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *node,
|
|||||||
if (primsize(fi.tnode.str) == 0) {
|
if (primsize(fi.tnode.str) == 0) {
|
||||||
let csi: *structinfo = structlookup(c, fi.tnode.str);
|
let csi: *structinfo = structlookup(c, fi.tnode.str);
|
||||||
if (csi != nil) {
|
if (csi != nil) {
|
||||||
// Use the inner struct's
|
// Inner struct's ABI size
|
||||||
// NATURAL size (no 8B slot
|
// (maxalign-rounded) — cstage
|
||||||
// rounding) so MOVL/MOVW/
|
// reads fl->type->size at the
|
||||||
// MOVB tail dispatch matches
|
// nested-call branch
|
||||||
// cstage's fl->type->size
|
// (cgen.c:2121); check.c:760
|
||||||
// (which is natural per
|
// sets that to the
|
||||||
// check.c). fi.fsz here is
|
// maxalign-rounded ABI extent
|
||||||
|
// (NOT natural). fi.fsz is
|
||||||
// wwstage's slot-padded
|
// wwstage's slot-padded
|
||||||
// totsize — using it would
|
// totsize (round-to-8); the
|
||||||
// emit 2× MOVQ where cstage
|
// pre-#169 structnaturalsize
|
||||||
// emits MOVQ+MOVL for a
|
// shorts struct{i64,i32}
|
||||||
// 12B inner, etc. (task #15
|
// (natural 12, ABI 16) to
|
||||||
// territory; sidestepped
|
// MOVQ+MOVL where cstage
|
||||||
// locally.)
|
// writes MOVQ+MOVQ.
|
||||||
let cfsz: i32 = structnaturalsize(csi);
|
let cfsz: i32 = structabisize(csi);
|
||||||
let crem: i32 = cfsz - (cfsz / 8) * 8;
|
let crem: i32 = cfsz - (cfsz / 8) * 8;
|
||||||
if (cfsz <= 24) {
|
if (cfsz <= 24) {
|
||||||
if (crem == 0 || crem == 1
|
if (crem == 0 || crem == 1
|
||||||
|
|||||||
@@ -17337,21 +17337,22 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *node,
|
|||||||
if (primsize(fi.tnode.str) == 0) {
|
if (primsize(fi.tnode.str) == 0) {
|
||||||
let csi: *structinfo = structlookup(c, fi.tnode.str);
|
let csi: *structinfo = structlookup(c, fi.tnode.str);
|
||||||
if (csi != nil) {
|
if (csi != nil) {
|
||||||
// Use the inner struct's
|
// Inner struct's ABI size
|
||||||
// NATURAL size (no 8B slot
|
// (maxalign-rounded) — cstage
|
||||||
// rounding) so MOVL/MOVW/
|
// reads fl->type->size at the
|
||||||
// MOVB tail dispatch matches
|
// nested-call branch
|
||||||
// cstage's fl->type->size
|
// (cgen.c:2121); check.c:760
|
||||||
// (which is natural per
|
// sets that to the
|
||||||
// check.c). fi.fsz here is
|
// maxalign-rounded ABI extent
|
||||||
|
// (NOT natural). fi.fsz is
|
||||||
// wwstage's slot-padded
|
// wwstage's slot-padded
|
||||||
// totsize — using it would
|
// totsize (round-to-8); the
|
||||||
// emit 2× MOVQ where cstage
|
// pre-#169 structnaturalsize
|
||||||
// emits MOVQ+MOVL for a
|
// shorts struct{i64,i32}
|
||||||
// 12B inner, etc. (task #15
|
// (natural 12, ABI 16) to
|
||||||
// territory; sidestepped
|
// MOVQ+MOVL where cstage
|
||||||
// locally.)
|
// writes MOVQ+MOVQ.
|
||||||
let cfsz: i32 = structnaturalsize(csi);
|
let cfsz: i32 = structabisize(csi);
|
||||||
let crem: i32 = cfsz - (cfsz / 8) * 8;
|
let crem: i32 = cfsz - (cfsz / 8) * 8;
|
||||||
if (cfsz <= 24) {
|
if (cfsz <= 24) {
|
||||||
if (crem == 0 || crem == 1
|
if (crem == 0 || crem == 1
|
||||||
@@ -24267,9 +24268,14 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
if (lcsname.len > 0) {
|
if (lcsname.len > 0) {
|
||||||
let lcsi: *structinfo = structlookup(c, lcsname);
|
let lcsi: *structinfo = structlookup(c, lcsname);
|
||||||
if (lcsi != nil) {
|
if (lcsi != nil) {
|
||||||
// si.totsize is slot-padded (rounded to 8);
|
// register RECV reads AX/DX/CX at 8-byte
|
||||||
// receive ABI needs the TYPE's natural size.
|
// granularity — size via structabisize (cstage
|
||||||
let lcnsz: i32 = structnaturalsize(lcsi);
|
// N_ASSIGN-IDENT branch sets sz = lu->size,
|
||||||
|
// cgen.c:4704; check.c:760 SSoT). The pre-
|
||||||
|
// #169 structnaturalsize shorts struct{i64,i32}
|
||||||
|
// (natural 12, ABI 16) to MOVQ+MOVL where
|
||||||
|
// cstage writes MOVQ+MOVQ.
|
||||||
|
let lcnsz: i32 = structabisize(lcsi);
|
||||||
if (n.op == tkind.TK_ASSIGN) {
|
if (n.op == tkind.TK_ASSIGN) {
|
||||||
if (n.rhs != nil
|
if (n.rhs != nil
|
||||||
&& n.rhs.kind == nkind.N_STRUCTLIT) {
|
&& n.rhs.kind == nkind.N_STRUCTLIT) {
|
||||||
@@ -25876,8 +25882,8 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
// Struct ident copy: `let p2: T = p1;` where T is a struct
|
// Struct ident copy: `let p2: T = p1;` where T is a struct
|
||||||
// >8B and rhs is a local ident. Per-qword MOVQ from src
|
// >8B and rhs is a local ident. Per-qword MOVQ from src
|
||||||
// slot to dst slot, with a sized tail (MOVL/MOVB) for
|
// slot to dst slot, with a sized tail (MOVL/MOVB) for
|
||||||
// natural sizes that aren't 8-aligned (e.g. `struct
|
// ABI sizes that aren't 8-aligned (e.g. `struct
|
||||||
// { i32, i32, i32 }` is 12B). Pre-fix this path fell
|
// { i32, i32, i32 }`, maxalign 4 → ABI 12B). Pre-fix this path fell
|
||||||
// through to `cgexpr + MOVQ AX, off(BP)` which stored
|
// through to `cgexpr + MOVQ AX, off(BP)` which stored
|
||||||
// only the first qword (and a stale BX for sz==16 lets
|
// only the first qword (and a stale BX for sz==16 lets
|
||||||
// via the str-init tail) — silent partial copy. Mirrors
|
// via the str-init tail) — silent partial copy. Mirrors
|
||||||
@@ -25891,7 +25897,13 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
if (sname.len > 0) {
|
if (sname.len > 0) {
|
||||||
let lsi: *structinfo = structlookup(c, sname);
|
let lsi: *structinfo = structlookup(c, sname);
|
||||||
if (lsi != nil) {
|
if (lsi != nil) {
|
||||||
let lsz: i32 = structnaturalsize(lsi);
|
// memcpy run sizes on the maxalign-rounded ABI
|
||||||
|
// size — cstage N_LET sets sz = lu->size
|
||||||
|
// (cgen.c:7533, struct-IDENT branch :7869),
|
||||||
|
// check.c:760 SSoT. structnaturalsize would
|
||||||
|
// short struct{i64,i32} (natural 12, ABI 16)
|
||||||
|
// to MOVQ+MOVL where cstage writes MOVQ+MOVQ.
|
||||||
|
let lsz: i32 = structabisize(lsi);
|
||||||
if (lsz > 8) {
|
if (lsz > 8) {
|
||||||
let lc: *local = localfindnode(c, rhs.str);
|
let lc: *local = localfindnode(c, rhs.str);
|
||||||
if (lc != nil) {
|
if (lc != nil) {
|
||||||
|
|||||||
@@ -242,6 +242,52 @@ static const struct row rows[] = {
|
|||||||
" return z.a + z.b + z.c;\n"
|
" return z.a + z.b + z.c;\n"
|
||||||
"};\n",
|
"};\n",
|
||||||
0 },
|
0 },
|
||||||
|
/* `let p2: T = p1;` ident memcpy. struct{i64,i32} natural 12,
|
||||||
|
* maxalign 8 → cstage N_LET sets sz = lu->size = 16 (cgen.c:7533,
|
||||||
|
* struct-IDENT branch :7869) and emits MOVQ+MOVQ for the per-qword
|
||||||
|
* copy. Pre-fix wwstage sized the run on structnaturalsize (12) →
|
||||||
|
* MOVQ+MOVL. The byte-id catches the 4B widen; both stages
|
||||||
|
* round-trip the value correctly because the high 4B of the rhs
|
||||||
|
* slot were zero-init'd by the source let. */
|
||||||
|
{ "let_ident_memcpy_pair_i64_i32",
|
||||||
|
"type pt = struct { a: i64, b: i32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let p1: pt = pt { a = 7i64, b = 9i32 };\n"
|
||||||
|
" let p2: pt = p1;\n"
|
||||||
|
" return p2.b - 9;\n"
|
||||||
|
"};\n",
|
||||||
|
0 },
|
||||||
|
/* `s = mk();` ident-lhs register RECV (sibling of dot_recv_call_*).
|
||||||
|
* struct{i64,i32} natural 12, maxalign 8 → cstage N_ASSIGN-IDENT
|
||||||
|
* sets sz = lu->size = 16 (cgen.c:4704) and stores AX/DX with
|
||||||
|
* MOVQ+MOVQ. Pre-fix wwstage used structnaturalsize (12) →
|
||||||
|
* MOVQ+MOVL. byte-id discriminator regardless of value readback. */
|
||||||
|
{ "assign_ident_call_recv_pair_i64_i32",
|
||||||
|
"type pt = struct { a: i64, b: i32 };\n"
|
||||||
|
"fn mk() pt = { return pt { a = 7i64, b = 9i32 }; };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let s: pt = pt { a = 0i64, b = 0i32 };\n"
|
||||||
|
" s = mk();\n"
|
||||||
|
" return s.b - 9;\n"
|
||||||
|
"};\n",
|
||||||
|
0 },
|
||||||
|
/* Nested struct N_CALL inside a structlit field: the outer literal
|
||||||
|
* has field `v` whose type is struct{i64,i32}; the value is mk().
|
||||||
|
* cstage's cg_structlit_fill reads fl->type->size at the nested-
|
||||||
|
* call branch (cgen.c:2121) — per check.c:760 that's the maxalign-
|
||||||
|
* rounded ABI extent (16). Pre-fix wwstage cgstructlitfill used
|
||||||
|
* structnaturalsize (12) → MOVQ+MOVL where cstage emits MOVQ+MOVQ.
|
||||||
|
* mk returns via a structlit so the producer side stays out of the
|
||||||
|
* comparison; only the nested-call-recv ladder shifts. */
|
||||||
|
{ "structlit_nested_call_recv_pair_i64_i32",
|
||||||
|
"type pt = struct { a: i64, b: i32 };\n"
|
||||||
|
"type box = struct { v: pt };\n"
|
||||||
|
"fn mk() pt = { return pt { a = 7i64, b = 9i32 }; };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let b: box = box { v = mk() };\n"
|
||||||
|
" return b.v.b - 9;\n"
|
||||||
|
"};\n",
|
||||||
|
0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|||||||
Reference in New Issue
Block a user