cstage+selfhost+test: extend structlit-fill helper to N_ASSIGN N_DOT lhs (4 flavors)
Sister fix to #17. The BP-rel helper from #17 covered N_LET / N_ASSIGN N_IDENT-lhs / N_RETURN; the four N_ASSIGN N_DOT-lhs structlit walks still went through the inline `cgexpr(field.lhs); store-AX-sized` shape and silently dropped trailing bytes when a struct-typed field's value was itself an N_STRUCTLIT. Affected dot flavors: single-dot via_ptr / global / BP-rel and the chained-dot walker (depth >= 2, all three root flavors). Extend `cg_structlit_fill_bp` / `cgstructlitfillbp` into `cg_structlit_fill` / `cgstructlitfill` taking a destination mode (DST_BP / DST_PTR_LOCAL / DST_GLOBAL = 0/1/2), srcoff (PTR_LOCAL), srcname (GLOBAL), and disp accumulator. `disp` grows by foff on descent; srcoff/srcname stay constant across the call tree. The pre-#17 wrappers are preserved byte-identically by delegating with mode=DST_BP — 995_self_rebuild byte-identity holds for the no- nested-STRUCTLIT case that selfhost source actually uses. The non-BP modes reload BX before the ELLIPSIS zero-fill loop AND before every field store (tagged, scalar, and the cgexpr leaf). This is correctness-by-construction — cgexpr clobbers BX between fields, and the redundant reload only fires on shapes that didn't compile before. The four dot-flavor sites in each stage now compute their dst mode + disp and call the shared helper (reducing each from ~80-130 inline lines to ~5-12 lines of dispatch). Stage signature asymmetry: cstage threads Local** for cgexpr; ww- stage takes explicit totsize because #15 (split totsize into naturalsize + slotsize) is still pending and the dot sites need structnaturalsize while the BP-rel sites need si.totsize. Both asymmetries are documented in the helper docstrings. 704 covers 8 rows (24 checks: 8 cstage exits, 8 wwstage exits, 8 cstage-vs-wwstage .s byte-identity diffs): 6 dst-flavors (single- dot local/ptr/global, chained-dot local/ptr/global) plus single- local 3-deep and single-ptr 3-deep to pin disp threading through the helper's recursion and through DST_PTR_LOCAL BX reloads. The nested struct-typed CALL rhs in field-walks has the same shape as the STRUCTLIT bug fixed here but the helper only handles STRUCTLIT — tracked as task #20.
This commit is contained in:
@@ -3838,6 +3838,11 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// #18: delegate to cgstructlitfill so a nested struct-
|
||||
// typed structlit value recurses instead of dropping
|
||||
// its trailing bytes. mode=1 (DST_PTR_LOCAL) reloads BX
|
||||
// from lc.off(BP) before zero-fill and before every
|
||||
// field store.
|
||||
if (n.op == tkind.TK_ASSIGN
|
||||
&& n.rhs != nil
|
||||
&& n.rhs.kind == nkind.N_STRUCTLIT
|
||||
@@ -3847,75 +3852,8 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
let ssi: *structinfo = structlookup(c, fi.tnode.str);
|
||||
if (ssi != nil) {
|
||||
let ssz: i32 = structnaturalsize(ssi);
|
||||
if (n.rhs.op == tkind.TK_ELLIPSIS) {
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
let zi: i32 = 0;
|
||||
for (zi + 8 <= ssz) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg((fi.foff + zi): i64, "BX");
|
||||
emitline("\n");
|
||||
zi += 8;
|
||||
};
|
||||
for (zi + 4 <= ssz) {
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitdispreg((fi.foff + zi): i64, "BX");
|
||||
emitline("\n");
|
||||
zi += 4;
|
||||
};
|
||||
for (zi < ssz) {
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitdispreg((fi.foff + zi): i64, "BX");
|
||||
emitline("\n");
|
||||
zi += 1;
|
||||
};
|
||||
};
|
||||
let fldn: *node = n.rhs.list;
|
||||
for (fldn != nil) {
|
||||
if (fldn.kind == nkind.N_FIELD) {
|
||||
let fnm: str = fldn.str;
|
||||
let ifi: *fieldinfo = ssi.fields;
|
||||
for (ifi != nil) {
|
||||
if (streq(ifi.fname, fnm)) {
|
||||
cgexpr(c, fldn.lhs);
|
||||
if (isfloattype(c, ifi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, ifi.tnode)) {
|
||||
mov = "MOVSS";
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitdispreg((fi.foff + ifi.foff): i64, "BX");
|
||||
emitline("\n");
|
||||
ifi = nil;
|
||||
} else {
|
||||
let ifsz: i32 = ifi.fsz;
|
||||
let op: str = "MOVQ";
|
||||
if (ifsz == 1) { op = "MOVB"; };
|
||||
if (ifsz == 4) { op = "MOVL"; };
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\tAX, ");
|
||||
emitdispreg((fi.foff + ifi.foff): i64, "BX");
|
||||
emitline("\n");
|
||||
ifi = nil;
|
||||
};
|
||||
} else {
|
||||
ifi = ifi.finext;
|
||||
};
|
||||
};
|
||||
};
|
||||
fldn = fldn.next;
|
||||
};
|
||||
cgstructlitfill(c, ssi, n.rhs, 1, lc.off, "",
|
||||
fi.foff, ssz);
|
||||
return;
|
||||
};
|
||||
};
|
||||
@@ -4131,6 +4069,10 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// #18: delegate to cgstructlitfill so a nested struct-
|
||||
// typed structlit value recurses instead of dropping
|
||||
// its trailing bytes. mode=0 (DST_BP) — direct BP-rel,
|
||||
// no BX reload.
|
||||
if (n.op == tkind.TK_ASSIGN
|
||||
&& n.rhs != nil
|
||||
&& n.rhs.kind == nkind.N_STRUCTLIT
|
||||
@@ -4140,66 +4082,8 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
let ssi: *structinfo = structlookup(c, fi.tnode.str);
|
||||
if (ssi != nil) {
|
||||
let ssz: i32 = structnaturalsize(ssi);
|
||||
if (n.rhs.op == tkind.TK_ELLIPSIS) {
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
let zi: i32 = 0;
|
||||
for (zi + 8 <= ssz) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((lc.off + fi.foff + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
zi += 8;
|
||||
};
|
||||
for (zi + 4 <= ssz) {
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff((lc.off + fi.foff + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
zi += 4;
|
||||
};
|
||||
for (zi < ssz) {
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff((lc.off + fi.foff + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
zi += 1;
|
||||
};
|
||||
};
|
||||
let fldn: *node = n.rhs.list;
|
||||
for (fldn != nil) {
|
||||
if (fldn.kind == nkind.N_FIELD) {
|
||||
let fnm: str = fldn.str;
|
||||
let ifi: *fieldinfo = ssi.fields;
|
||||
for (ifi != nil) {
|
||||
if (streq(ifi.fname, fnm)) {
|
||||
cgexpr(c, fldn.lhs);
|
||||
if (isfloattype(c, ifi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, ifi.tnode)) {
|
||||
mov = "MOVSS";
|
||||
};
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitoff((lc.off + fi.foff + ifi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
ifi = nil;
|
||||
} else {
|
||||
let ifsz: i32 = ifi.fsz;
|
||||
let op: str = "MOVQ";
|
||||
if (ifsz == 1) { op = "MOVB"; };
|
||||
if (ifsz == 4) { op = "MOVL"; };
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\tAX, ");
|
||||
emitoff((lc.off + fi.foff + ifi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
ifi = nil;
|
||||
};
|
||||
} else {
|
||||
ifi = ifi.finext;
|
||||
};
|
||||
};
|
||||
};
|
||||
fldn = fldn.next;
|
||||
};
|
||||
cgstructlitfill(c, ssi, n.rhs, 0, 0, "",
|
||||
lc.off + fi.foff, ssz);
|
||||
return;
|
||||
};
|
||||
};
|
||||
@@ -4438,87 +4322,25 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
if (n.op == tkind.TK_ASSIGN
|
||||
&& n.rhs != nil
|
||||
&& n.rhs.kind == nkind.N_STRUCTLIT
|
||||
&& fi.tnode != nil
|
||||
&& fi.tnode.kind == nkind.N_TNAME
|
||||
&& primsize(fi.tnode.str) == 0) {
|
||||
let ssi: *structinfo = structlookup(c, fi.tnode.str);
|
||||
if (ssi != nil) {
|
||||
let ssz: i32 = structnaturalsize(ssi);
|
||||
if (n.rhs.op == tkind.TK_ELLIPSIS) {
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, bn);
|
||||
emitline("(SB), BX\n");
|
||||
let zi: i32 = 0;
|
||||
for (zi + 8 <= ssz) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg((fi.foff + zi): i64, "BX");
|
||||
emitline("\n");
|
||||
zi += 8;
|
||||
};
|
||||
for (zi + 4 <= ssz) {
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitdispreg((fi.foff + zi): i64, "BX");
|
||||
emitline("\n");
|
||||
zi += 4;
|
||||
};
|
||||
for (zi < ssz) {
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitdispreg((fi.foff + zi): i64, "BX");
|
||||
emitline("\n");
|
||||
zi += 1;
|
||||
};
|
||||
};
|
||||
let fldn: *node = n.rhs.list;
|
||||
for (fldn != nil) {
|
||||
if (fldn.kind == nkind.N_FIELD) {
|
||||
let fnm: str = fldn.str;
|
||||
let ifi: *fieldinfo = ssi.fields;
|
||||
for (ifi != nil) {
|
||||
if (streq(ifi.fname, fnm)) {
|
||||
cgexpr(c, fldn.lhs);
|
||||
if (isfloattype(c, ifi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, ifi.tnode)) {
|
||||
mov = "MOVSS";
|
||||
};
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, bn);
|
||||
emitline("(SB), BX\n");
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitdispreg((fi.foff + ifi.foff): i64, "BX");
|
||||
emitline("\n");
|
||||
ifi = nil;
|
||||
} else {
|
||||
let ifsz: i32 = ifi.fsz;
|
||||
let op: str = "MOVQ";
|
||||
if (ifsz == 1) { op = "MOVB"; };
|
||||
if (ifsz == 4) { op = "MOVL"; };
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, bn);
|
||||
emitline("(SB), BX\n");
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\tAX, ");
|
||||
emitdispreg((fi.foff + ifi.foff): i64, "BX");
|
||||
emitline("\n");
|
||||
ifi = nil;
|
||||
};
|
||||
} else {
|
||||
ifi = ifi.finext;
|
||||
};
|
||||
};
|
||||
};
|
||||
fldn = fldn.next;
|
||||
};
|
||||
return;
|
||||
};
|
||||
// #18: delegate to cgstructlitfill so a nested struct-
|
||||
// typed structlit value recurses instead of dropping
|
||||
// its trailing bytes. mode=2 (DST_GLOBAL) reloads BX
|
||||
// via LEAQ bn(SB) before zero-fill and before every
|
||||
// field store.
|
||||
if (n.op == tkind.TK_ASSIGN
|
||||
&& n.rhs != nil
|
||||
&& n.rhs.kind == nkind.N_STRUCTLIT
|
||||
&& fi.tnode != nil
|
||||
&& fi.tnode.kind == nkind.N_TNAME
|
||||
&& primsize(fi.tnode.str) == 0) {
|
||||
let ssi: *structinfo = structlookup(c, fi.tnode.str);
|
||||
if (ssi != nil) {
|
||||
let ssz: i32 = structnaturalsize(ssi);
|
||||
cgstructlitfill(c, ssi, n.rhs, 2, 0, bn,
|
||||
fi.foff, ssz);
|
||||
return;
|
||||
};
|
||||
};
|
||||
if (n.op == tkind.TK_ASSIGN
|
||||
&& n.rhs != nil
|
||||
&& n.rhs.kind == nkind.N_IDENT
|
||||
@@ -4897,6 +4719,14 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// #18: delegate to cgstructlitfill so a nested struct-
|
||||
// typed structlit value recurses instead of dropping
|
||||
// its trailing bytes. mode picks the dst flavor:
|
||||
// ptrroot → mode=1 (DST_PTR_LOCAL), reload BX from
|
||||
// rootoff(BP).
|
||||
// isglobal → mode=2 (DST_GLOBAL), reload BX via
|
||||
// LEAQ rootname(SB).
|
||||
// else → mode=0 (DST_BP), direct BP-rel, no reload.
|
||||
if (n.rhs != nil
|
||||
&& n.rhs.kind == nkind.N_STRUCTLIT
|
||||
&& leaffi.tnode != nil
|
||||
@@ -4907,131 +4737,19 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
// si.totsize is slot-padded (rounded to 8);
|
||||
// receive ABI needs the TYPE's natural size.
|
||||
let lsz: i32 = structnaturalsize(lsi);
|
||||
if (n.rhs.op == tkind.TK_ELLIPSIS) {
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
if (viacx) {
|
||||
if (ptrroot) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
let zi: i32 = 0;
|
||||
for (zi + 8 <= lsz) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg((totaloff + zi): i64, "BX");
|
||||
emitline("\n");
|
||||
zi += 8;
|
||||
};
|
||||
for (zi + 4 <= lsz) {
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitdispreg((totaloff + zi): i64, "BX");
|
||||
emitline("\n");
|
||||
zi += 4;
|
||||
};
|
||||
for (zi < lsz) {
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitdispreg((totaloff + zi): i64, "BX");
|
||||
emitline("\n");
|
||||
zi += 1;
|
||||
};
|
||||
} else {
|
||||
let zi: i32 = 0;
|
||||
for (zi + 8 <= lsz) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((rootoff + totaloff + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
zi += 8;
|
||||
};
|
||||
for (zi + 4 <= lsz) {
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff((rootoff + totaloff + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
zi += 4;
|
||||
};
|
||||
for (zi < lsz) {
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff((rootoff + totaloff + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
zi += 1;
|
||||
};
|
||||
};
|
||||
let dmode: i32 = 0;
|
||||
let ddisp: i32 = rootoff + totaloff;
|
||||
if (ptrroot) {
|
||||
dmode = 1;
|
||||
ddisp = totaloff;
|
||||
};
|
||||
let fldn: *node = n.rhs.list;
|
||||
for (fldn != nil) {
|
||||
if (fldn.kind == nkind.N_FIELD) {
|
||||
let fnm: str = fldn.str;
|
||||
let fi: *fieldinfo = lsi.fields;
|
||||
for (fi != nil) {
|
||||
if (streq(fi.fname, fnm)) {
|
||||
cgexpr(c, fldn.lhs);
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) {
|
||||
mov = "MOVSS";
|
||||
};
|
||||
if (viacx) {
|
||||
if (ptrroot) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitdispreg((totaloff + fi.foff): i64, "BX");
|
||||
emitline("\n");
|
||||
} else {
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitoff((rootoff + totaloff + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
fi = nil;
|
||||
} else {
|
||||
let fsz: i32 = fi.fsz;
|
||||
let op: str = "MOVQ";
|
||||
if (fsz == 1) { op = "MOVB"; };
|
||||
if (fsz == 4) { op = "MOVL"; };
|
||||
if (viacx) {
|
||||
if (ptrroot) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\tAX, ");
|
||||
emitdispreg((totaloff + fi.foff): i64, "BX");
|
||||
emitline("\n");
|
||||
} else {
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\tAX, ");
|
||||
emitoff((rootoff + totaloff + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
fi = nil;
|
||||
};
|
||||
} else {
|
||||
fi = fi.finext;
|
||||
};
|
||||
};
|
||||
};
|
||||
fldn = fldn.next;
|
||||
if (isglobal) {
|
||||
dmode = 2;
|
||||
ddisp = totaloff;
|
||||
};
|
||||
cgstructlitfill(c, lsi, n.rhs,
|
||||
dmode, rootoff, rootname,
|
||||
ddisp, lsz);
|
||||
return;
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user