cgen+test: copy struct >8B local-ident rhs in N_LET (#32)
let p2: T = p1; where T is a struct >8B and rhs is a local ident
silently dropped most of the copy. Cstage's N_LET fell past every
specialized rhs branch (str/tuple/tagged/structlit/call) without
matching the bare-ident case, then past the sz==8 fallback (false)
to the no-rhs zero-init (false: rhs present), emitting zero
instructions — the dest slot read fresh-stack zeros. Wwstage's
cglet fell to cgexpr+MOVQ AX which loads only the first qword
(cgident shape for struct ident), and for sz==16 slots the
str-init tail then stored a stale BX into +8. Reads after the
let saw whatever the stack held: silent partial copy.
Both stages now byte-copy src slot → dst slot per qword with
a sized tail (MOVL/MOVB) for natural sizes not 8-aligned.
Mirrors cg_widen_tagged_store's struct-ident payload copy.
744_letcopy_struct pins the four struct shapes (3×i32, i32+str,
i32+[]u8, i32+tagged) on asm-presence in both stages, cmp -s
byte-id, and runtime exit code via both drivers.
Scope: only N_IDENT rhs at the local-ident-found path. Filed as
siblings (no in-tree consumer today, bootstrap byte-id proves it):
- N_DOT / N_INDEX / N_UN(deref) struct rhs.
- Top-level (non-local) struct ident rhs.
- TY_TUPLE same-shape ident-copy bug.
Row (a) uses tri{a=11, b=22, c=33} structlit init for p1 to
isolate this fix from STATUS-3 #15/#26c (no-rhs zero-init sz=12
vs sz=16 slot-padded divergence between stages, separate task).
Row (d) runtime check uses only p2.a to isolate from match-on-
tagged-field scrutinee spill divergence (same task).
118/118 ok. ww2 == ww3 == ww4 byte-id holds.
This commit is contained in:
@@ -17320,6 +17320,62 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// Struct ident copy: `let p2: T = p1;` where T is a struct
|
||||
// >8B and rhs is a local ident. Per-qword MOVQ from src
|
||||
// slot to dst slot, with a sized tail (MOVL/MOVB) for
|
||||
// natural sizes that aren't 8-aligned (e.g. `struct
|
||||
// { i32, i32, i32 }` is 12B). Pre-fix this path fell
|
||||
// through to `cgexpr + MOVQ AX, off(BP)` which stored
|
||||
// only the first qword (and a stale BX for sz==16 lets
|
||||
// via the str-init tail) — silent partial copy. Mirrors
|
||||
// cstage cgen.c N_LET struct-ident branch (Task #32).
|
||||
if (rhs.kind == nkind.N_IDENT) {
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TNAME) { sname = tn.str; };
|
||||
};
|
||||
if (sname.len > 0) {
|
||||
let lsi: *structinfo = structlookup(c, sname);
|
||||
if (lsi != nil) {
|
||||
let lsz: i32 = structnaturalsize(lsi);
|
||||
if (lsz > 8) {
|
||||
let lc: *local = localfindnode(c, rhs.str);
|
||||
if (lc != nil) {
|
||||
let soff: i32 = lc.off;
|
||||
let ki: i32 = 0;
|
||||
for (ki + 8 <= lsz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((soff + ki): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((off + ki): i64);
|
||||
emitline("(BP)\n");
|
||||
ki += 8;
|
||||
};
|
||||
if (ki < lsz) {
|
||||
let tail: i32 = lsz - ki;
|
||||
let lop: str = "MOVQ";
|
||||
if (tail == 4) { lop = "MOVL"; }
|
||||
else { if (tail == 1) { lop = "MOVB"; }; };
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
emitoff((soff + ki): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\tAX, ");
|
||||
emitoff((off + ki): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
cgexpr(c, rhs);
|
||||
// Float local: cgexpr leaves the value in X0. Spill via
|
||||
// MOVSS (f32, 4B) or MOVSD (f64, 8B).
|
||||
|
||||
@@ -817,6 +817,62 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// Struct ident copy: `let p2: T = p1;` where T is a struct
|
||||
// >8B and rhs is a local ident. Per-qword MOVQ from src
|
||||
// slot to dst slot, with a sized tail (MOVL/MOVB) for
|
||||
// natural sizes that aren't 8-aligned (e.g. `struct
|
||||
// { i32, i32, i32 }` is 12B). Pre-fix this path fell
|
||||
// through to `cgexpr + MOVQ AX, off(BP)` which stored
|
||||
// only the first qword (and a stale BX for sz==16 lets
|
||||
// via the str-init tail) — silent partial copy. Mirrors
|
||||
// cstage cgen.c N_LET struct-ident branch (Task #32).
|
||||
if (rhs.kind == nkind.N_IDENT) {
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TNAME) { sname = tn.str; };
|
||||
};
|
||||
if (sname.len > 0) {
|
||||
let lsi: *structinfo = structlookup(c, sname);
|
||||
if (lsi != nil) {
|
||||
let lsz: i32 = structnaturalsize(lsi);
|
||||
if (lsz > 8) {
|
||||
let lc: *local = localfindnode(c, rhs.str);
|
||||
if (lc != nil) {
|
||||
let soff: i32 = lc.off;
|
||||
let ki: i32 = 0;
|
||||
for (ki + 8 <= lsz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((soff + ki): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((off + ki): i64);
|
||||
emitline("(BP)\n");
|
||||
ki += 8;
|
||||
};
|
||||
if (ki < lsz) {
|
||||
let tail: i32 = lsz - ki;
|
||||
let lop: str = "MOVQ";
|
||||
if (tail == 4) { lop = "MOVL"; }
|
||||
else { if (tail == 1) { lop = "MOVB"; }; };
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
emitoff((soff + ki): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\tAX, ");
|
||||
emitoff((off + ki): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
cgexpr(c, rhs);
|
||||
// Float local: cgexpr leaves the value in X0. Spill via
|
||||
// MOVSS (f32, 4B) or MOVSD (f64, 8B).
|
||||
|
||||
@@ -17320,6 +17320,62 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// Struct ident copy: `let p2: T = p1;` where T is a struct
|
||||
// >8B and rhs is a local ident. Per-qword MOVQ from src
|
||||
// slot to dst slot, with a sized tail (MOVL/MOVB) for
|
||||
// natural sizes that aren't 8-aligned (e.g. `struct
|
||||
// { i32, i32, i32 }` is 12B). Pre-fix this path fell
|
||||
// through to `cgexpr + MOVQ AX, off(BP)` which stored
|
||||
// only the first qword (and a stale BX for sz==16 lets
|
||||
// via the str-init tail) — silent partial copy. Mirrors
|
||||
// cstage cgen.c N_LET struct-ident branch (Task #32).
|
||||
if (rhs.kind == nkind.N_IDENT) {
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TNAME) { sname = tn.str; };
|
||||
};
|
||||
if (sname.len > 0) {
|
||||
let lsi: *structinfo = structlookup(c, sname);
|
||||
if (lsi != nil) {
|
||||
let lsz: i32 = structnaturalsize(lsi);
|
||||
if (lsz > 8) {
|
||||
let lc: *local = localfindnode(c, rhs.str);
|
||||
if (lc != nil) {
|
||||
let soff: i32 = lc.off;
|
||||
let ki: i32 = 0;
|
||||
for (ki + 8 <= lsz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((soff + ki): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((off + ki): i64);
|
||||
emitline("(BP)\n");
|
||||
ki += 8;
|
||||
};
|
||||
if (ki < lsz) {
|
||||
let tail: i32 = lsz - ki;
|
||||
let lop: str = "MOVQ";
|
||||
if (tail == 4) { lop = "MOVL"; }
|
||||
else { if (tail == 1) { lop = "MOVB"; }; };
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
emitoff((soff + ki): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\tAX, ");
|
||||
emitoff((off + ki): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
cgexpr(c, rhs);
|
||||
// Float local: cgexpr leaves the value in X0. Spill via
|
||||
// MOVSS (f32, 4B) or MOVSD (f64, 8B).
|
||||
|
||||
Reference in New Issue
Block a user