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:
@@ -8656,59 +8656,113 @@ export fn dotchainresolve(c: *cgen, n: *node,
|
||||
return false;
|
||||
};
|
||||
|
||||
// cgstructlitfillbp — fill a struct-typed slot from an N_STRUCTLIT
|
||||
// value into BP-relative memory at `bpoff`. Mirror of cstage cgen.c's
|
||||
// cg_structlit_fill_bp. Used by cglet, cgreturn N_STRUCTLIT, and
|
||||
// cgassign N_IDENT-lhs N_STRUCTLIT sites where the dst is BP-
|
||||
// relative.
|
||||
// cgstructlitfill — fill a struct-typed slot from an N_STRUCTLIT
|
||||
// value into one of three destination flavors. Mirror of cstage
|
||||
// cgen.c's cg_structlit_fill. Used by cglet, cgreturn N_STRUCTLIT,
|
||||
// cgassign N_IDENT-lhs N_STRUCTLIT (BP-rel) AND cgassign N_DOT-lhs
|
||||
// N_STRUCTLIT (BP-rel / via *struct local / via struct global) at
|
||||
// single-dot and chained-dot sites.
|
||||
//
|
||||
// The inline field-walk previously did `cgexpr(field.lhs); store AX
|
||||
// sized`. For struct-typed fields whose value is itself a nested
|
||||
// N_STRUCTLIT, cgexpr has no whole-struct-in-register convention —
|
||||
// it lands AX = first qword and the trailing bytes silently stay
|
||||
// zero. Selfhost source used write-by-field as a workaround; this
|
||||
// helper recurses cleanly so the workaround is no longer required.
|
||||
// Destination modes:
|
||||
// 0 = DST_BP — base = BP, no reload. Stores at disp+i(BP).
|
||||
// srcoff/srcname unused.
|
||||
// 1 = DST_PTR_LOCAL — base = BX, reloaded from srcoff(BP) before
|
||||
// the ELLIPSIS zero-fill loop and before EVERY
|
||||
// field store (cgexpr clobbers BX between
|
||||
// fields). Stores at disp+i(BX). srcname
|
||||
// unused.
|
||||
// 2 = DST_GLOBAL — base = BX, reloaded via `LEAQ srcname(SB),
|
||||
// BX` with the same cadence as DST_PTR_LOCAL.
|
||||
// srcoff unused.
|
||||
//
|
||||
// Sister branches (cgassign single-dot / via_ptr / global structlit
|
||||
// walks) keep their inline field-walk in v1 since their addressing
|
||||
// has additional BX-reload concerns; nested-STRUCTLIT silent-zero
|
||||
// still drops on those paths — separate follow-up task.
|
||||
// Param semantics (locked in here so the recursion contract is
|
||||
// clear):
|
||||
// - `disp` is the per-recursion accumulator — grows by `fi.foff`
|
||||
// as we descend into a nested struct-typed structlit field.
|
||||
// - `srcoff` (DST_PTR_LOCAL) and `srcname` (DST_GLOBAL) are
|
||||
// *constant* across the whole call tree — they identify the
|
||||
// root dst, which doesn't change with depth.
|
||||
// - `totsize` is also constant; pass the natural size for dot
|
||||
// sites (structnaturalsize) and si.totsize for BP-rel sites,
|
||||
// matching each site's pre-#18 zero-fill bound.
|
||||
//
|
||||
// Output is byte-identical to the prior inline code for any input
|
||||
// that has no nested-STRUCTLIT field (the only shape selfhost source
|
||||
// actually compiles today), preserving 995_self_rebuild byte-
|
||||
// identity.
|
||||
// Why a helper? The inline field-walk previously did
|
||||
// `cgexpr(field.lhs); store AX sized`. For struct-typed fields whose
|
||||
// value is itself a nested N_STRUCTLIT, cgexpr has no whole-struct-
|
||||
// in-register convention — it lands AX = first qword and the
|
||||
// trailing bytes silently stay zero. #17 fixed the BP-rel sites;
|
||||
// #18 extends the same recursion to the four cgassign N_DOT-lhs
|
||||
// structlit walks (single-dot via_ptr/global/local + chained
|
||||
// depth>=2).
|
||||
//
|
||||
// The non-BP modes emit a redundant BX reload at the start of each
|
||||
// recursive nested zero-fill / each recursive scalar store — this is
|
||||
// correctness-by-construction (BX is always freshly loaded right
|
||||
// before use), and the redundancy only fires on the nested-STRUCTLIT
|
||||
// shapes that didn't compile before. Byte-identity for the no-
|
||||
// nested case (the only shape selfhost source uses today) is
|
||||
// preserved because the existing inline code's reload-before-each-
|
||||
// store pattern matches the helper's per-store reload exactly.
|
||||
//
|
||||
// Graduation note (task #13): the scalar store currently uses the
|
||||
// explicit {1→MOVB, 4→MOVL, else MOVQ} dispatch to match cstage
|
||||
// byte-identically — cstage hasn't yet learned MOVW for fsz==2. Once
|
||||
// #13 aligns both stages, the dispatch can switch to fieldstoreop
|
||||
// which already returns MOVW where appropriate.
|
||||
fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||
fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *node,
|
||||
mode: i32, srcoff: i32, srcname: str,
|
||||
disp: i32, totsize: i32) void = {
|
||||
if (si == nil) { return; };
|
||||
let basereg: str = "BP";
|
||||
if (mode != 0) { basereg = "BX"; };
|
||||
if (lit.op == tkind.TK_ELLIPSIS) {
|
||||
// `..., ...` autofill — zero the entire slot first so
|
||||
// unmentioned fields read as 0. Uses si.totsize (slot-padded)
|
||||
// to match the inline pre-#17 pattern.
|
||||
let total: i32 = si.totsize;
|
||||
// unmentioned fields read as 0. Sized stores: 8/4/1. For
|
||||
// non-BP modes, reload BX once before the loop (cgexpr-free
|
||||
// region between iterations, so one reload is enough).
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
if (mode == 1) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(srcoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
if (mode == 2) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
let zi: i32 = 0;
|
||||
for (zi + 8 <= total) {
|
||||
for (zi + 8 <= totsize) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((bpoff + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
if (mode == 0) {
|
||||
emitoff((disp + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitdispreg((disp + zi): i64, basereg);
|
||||
emitline("\n");
|
||||
};
|
||||
zi += 8;
|
||||
};
|
||||
for (zi + 4 <= total) {
|
||||
for (zi + 4 <= totsize) {
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff((bpoff + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
if (mode == 0) {
|
||||
emitoff((disp + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitdispreg((disp + zi): i64, basereg);
|
||||
emitline("\n");
|
||||
};
|
||||
zi += 4;
|
||||
};
|
||||
for (zi < total) {
|
||||
for (zi < totsize) {
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff((bpoff + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
if (mode == 0) {
|
||||
emitoff((disp + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitdispreg((disp + zi): i64, basereg);
|
||||
emitline("\n");
|
||||
};
|
||||
zi += 1;
|
||||
};
|
||||
};
|
||||
@@ -8723,23 +8777,29 @@ fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||
// Tagged-union field: delegate to the shared
|
||||
// widening writer (handles str/scalar/struct
|
||||
// literal/ident payload + tagged-subset tag
|
||||
// remap). Mirrors cstage cg_structlit_fill_bp
|
||||
// and the pre-existing wwstage cgreturn
|
||||
// structlit dispatch; cglet's pre-#17 inline
|
||||
// lacked this branch but selfhost never
|
||||
// tripped it (no tagged-fields-in-let-
|
||||
// structlit in tree).
|
||||
// remap). For non-BP modes, reload BX first so
|
||||
// the widener sees a valid base reg.
|
||||
if (istaggedtype(c, fi.tnode)) {
|
||||
if (mode == 1) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(srcoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
if (mode == 2) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
cgwidentaggedstore(c, fi.tnode,
|
||||
fieldnode.lhs, "BP",
|
||||
bpoff + fi.foff, fi.fsz);
|
||||
fieldnode.lhs, basereg,
|
||||
disp + fi.foff, fi.fsz);
|
||||
fi = nil;
|
||||
} else {
|
||||
// Nested struct-typed structlit value: look up
|
||||
// the inner struct's metadata and recurse at the
|
||||
// field's offset. Pre-#17 the cgexpr-then-store
|
||||
// below would land AX = first qword and the rest
|
||||
// silently stayed zero.
|
||||
// field's offset. Pre-#17/#18 the cgexpr-then-
|
||||
// store below would land AX = first qword and
|
||||
// the rest silently stayed zero.
|
||||
let nested: bool = false;
|
||||
if (fieldnode.lhs != nil) {
|
||||
if (fieldnode.lhs.kind == nkind.N_STRUCTLIT) {
|
||||
@@ -8748,9 +8808,17 @@ fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||
if (primsize(fi.tnode.str) == 0) {
|
||||
let isi: *structinfo = structlookup(c, fi.tnode.str);
|
||||
if (isi != nil) {
|
||||
cgstructlitfillbp(c, isi,
|
||||
// Nested fill: pick the size
|
||||
// discipline matching the outer
|
||||
// site — dot sites pass natural
|
||||
// size, BP-rel sites pass
|
||||
// totsize. Mirror it.
|
||||
let inner_tot: i32 = isi.totsize;
|
||||
if (mode != 0) { inner_tot = structnaturalsize(isi); };
|
||||
cgstructlitfill(c, isi,
|
||||
fieldnode.lhs,
|
||||
bpoff + fi.foff);
|
||||
mode, srcoff, srcname,
|
||||
disp + fi.foff, inner_tot);
|
||||
nested = true;
|
||||
};
|
||||
};
|
||||
@@ -8762,24 +8830,39 @@ fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||
fi = nil;
|
||||
} else {
|
||||
cgexpr(c, fieldnode.lhs);
|
||||
// For non-BP modes, cgexpr just clobbered
|
||||
// BX; reload it before the store.
|
||||
if (mode == 1) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(srcoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
if (mode == 2) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitoff((bpoff + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
if (mode == 0) {
|
||||
emitoff((disp + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitdispreg((disp + fi.foff): i64, basereg);
|
||||
emitline("\n");
|
||||
};
|
||||
fi = nil;
|
||||
} else {
|
||||
// Explicit {1→MOVB, 4→MOVL, else MOVQ}
|
||||
// dispatch (not fieldstoreop) to match
|
||||
// cstage cg_structlit_fill_bp byte-
|
||||
// identically. wwstage's fieldstoreop
|
||||
// would return MOVW for fsz==2 which
|
||||
// cstage doesn't emit — tracked as task
|
||||
// #13. Until that lands, the helper
|
||||
// emits the cstage shape.
|
||||
// cstage byte-identically. wwstage's
|
||||
// fieldstoreop would return MOVW for
|
||||
// fsz==2 which cstage doesn't emit —
|
||||
// tracked as task #13.
|
||||
let fsz: i32 = fi.fsz;
|
||||
let op: str = "MOVQ";
|
||||
if (fsz == 1) { op = "MOVB"; };
|
||||
@@ -8787,8 +8870,13 @@ fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\tAX, ");
|
||||
emitoff((bpoff + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
if (mode == 0) {
|
||||
emitoff((disp + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitdispreg((disp + fi.foff): i64, basereg);
|
||||
emitline("\n");
|
||||
};
|
||||
fi = nil;
|
||||
};
|
||||
};
|
||||
@@ -8802,6 +8890,14 @@ fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||
};
|
||||
};
|
||||
|
||||
// Thin wrapper preserving the BP-rel call shape used by cglet,
|
||||
// cgreturn, and cgassign N_IDENT-lhs N_STRUCTLIT. Byte-identical to
|
||||
// the pre-#18 cgstructlitfillbp.
|
||||
fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||
if (si == nil) { return; };
|
||||
cgstructlitfill(c, si, lit, 0, 0, "", bpoff, si.totsize);
|
||||
};
|
||||
|
||||
// MODULE: wcc
|
||||
// selfhost/cmd/wcc/cgenexpr.ww — split out of cgen.ww.
|
||||
//
|
||||
@@ -12643,6 +12739,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
|
||||
@@ -12652,75 +12753,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;
|
||||
};
|
||||
};
|
||||
@@ -12936,6 +12970,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
|
||||
@@ -12945,66 +12983,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;
|
||||
};
|
||||
};
|
||||
@@ -13243,87 +13223,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
|
||||
@@ -13702,6 +13620,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
|
||||
@@ -13712,131 +13638,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;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2784,59 +2784,113 @@ export fn dotchainresolve(c: *cgen, n: *node,
|
||||
return false;
|
||||
};
|
||||
|
||||
// cgstructlitfillbp — fill a struct-typed slot from an N_STRUCTLIT
|
||||
// value into BP-relative memory at `bpoff`. Mirror of cstage cgen.c's
|
||||
// cg_structlit_fill_bp. Used by cglet, cgreturn N_STRUCTLIT, and
|
||||
// cgassign N_IDENT-lhs N_STRUCTLIT sites where the dst is BP-
|
||||
// relative.
|
||||
// cgstructlitfill — fill a struct-typed slot from an N_STRUCTLIT
|
||||
// value into one of three destination flavors. Mirror of cstage
|
||||
// cgen.c's cg_structlit_fill. Used by cglet, cgreturn N_STRUCTLIT,
|
||||
// cgassign N_IDENT-lhs N_STRUCTLIT (BP-rel) AND cgassign N_DOT-lhs
|
||||
// N_STRUCTLIT (BP-rel / via *struct local / via struct global) at
|
||||
// single-dot and chained-dot sites.
|
||||
//
|
||||
// The inline field-walk previously did `cgexpr(field.lhs); store AX
|
||||
// sized`. For struct-typed fields whose value is itself a nested
|
||||
// N_STRUCTLIT, cgexpr has no whole-struct-in-register convention —
|
||||
// it lands AX = first qword and the trailing bytes silently stay
|
||||
// zero. Selfhost source used write-by-field as a workaround; this
|
||||
// helper recurses cleanly so the workaround is no longer required.
|
||||
// Destination modes:
|
||||
// 0 = DST_BP — base = BP, no reload. Stores at disp+i(BP).
|
||||
// srcoff/srcname unused.
|
||||
// 1 = DST_PTR_LOCAL — base = BX, reloaded from srcoff(BP) before
|
||||
// the ELLIPSIS zero-fill loop and before EVERY
|
||||
// field store (cgexpr clobbers BX between
|
||||
// fields). Stores at disp+i(BX). srcname
|
||||
// unused.
|
||||
// 2 = DST_GLOBAL — base = BX, reloaded via `LEAQ srcname(SB),
|
||||
// BX` with the same cadence as DST_PTR_LOCAL.
|
||||
// srcoff unused.
|
||||
//
|
||||
// Sister branches (cgassign single-dot / via_ptr / global structlit
|
||||
// walks) keep their inline field-walk in v1 since their addressing
|
||||
// has additional BX-reload concerns; nested-STRUCTLIT silent-zero
|
||||
// still drops on those paths — separate follow-up task.
|
||||
// Param semantics (locked in here so the recursion contract is
|
||||
// clear):
|
||||
// - `disp` is the per-recursion accumulator — grows by `fi.foff`
|
||||
// as we descend into a nested struct-typed structlit field.
|
||||
// - `srcoff` (DST_PTR_LOCAL) and `srcname` (DST_GLOBAL) are
|
||||
// *constant* across the whole call tree — they identify the
|
||||
// root dst, which doesn't change with depth.
|
||||
// - `totsize` is also constant; pass the natural size for dot
|
||||
// sites (structnaturalsize) and si.totsize for BP-rel sites,
|
||||
// matching each site's pre-#18 zero-fill bound.
|
||||
//
|
||||
// Output is byte-identical to the prior inline code for any input
|
||||
// that has no nested-STRUCTLIT field (the only shape selfhost source
|
||||
// actually compiles today), preserving 995_self_rebuild byte-
|
||||
// identity.
|
||||
// Why a helper? The inline field-walk previously did
|
||||
// `cgexpr(field.lhs); store AX sized`. For struct-typed fields whose
|
||||
// value is itself a nested N_STRUCTLIT, cgexpr has no whole-struct-
|
||||
// in-register convention — it lands AX = first qword and the
|
||||
// trailing bytes silently stay zero. #17 fixed the BP-rel sites;
|
||||
// #18 extends the same recursion to the four cgassign N_DOT-lhs
|
||||
// structlit walks (single-dot via_ptr/global/local + chained
|
||||
// depth>=2).
|
||||
//
|
||||
// The non-BP modes emit a redundant BX reload at the start of each
|
||||
// recursive nested zero-fill / each recursive scalar store — this is
|
||||
// correctness-by-construction (BX is always freshly loaded right
|
||||
// before use), and the redundancy only fires on the nested-STRUCTLIT
|
||||
// shapes that didn't compile before. Byte-identity for the no-
|
||||
// nested case (the only shape selfhost source uses today) is
|
||||
// preserved because the existing inline code's reload-before-each-
|
||||
// store pattern matches the helper's per-store reload exactly.
|
||||
//
|
||||
// Graduation note (task #13): the scalar store currently uses the
|
||||
// explicit {1→MOVB, 4→MOVL, else MOVQ} dispatch to match cstage
|
||||
// byte-identically — cstage hasn't yet learned MOVW for fsz==2. Once
|
||||
// #13 aligns both stages, the dispatch can switch to fieldstoreop
|
||||
// which already returns MOVW where appropriate.
|
||||
fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||
fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *node,
|
||||
mode: i32, srcoff: i32, srcname: str,
|
||||
disp: i32, totsize: i32) void = {
|
||||
if (si == nil) { return; };
|
||||
let basereg: str = "BP";
|
||||
if (mode != 0) { basereg = "BX"; };
|
||||
if (lit.op == tkind.TK_ELLIPSIS) {
|
||||
// `..., ...` autofill — zero the entire slot first so
|
||||
// unmentioned fields read as 0. Uses si.totsize (slot-padded)
|
||||
// to match the inline pre-#17 pattern.
|
||||
let total: i32 = si.totsize;
|
||||
// unmentioned fields read as 0. Sized stores: 8/4/1. For
|
||||
// non-BP modes, reload BX once before the loop (cgexpr-free
|
||||
// region between iterations, so one reload is enough).
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
if (mode == 1) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(srcoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
if (mode == 2) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
let zi: i32 = 0;
|
||||
for (zi + 8 <= total) {
|
||||
for (zi + 8 <= totsize) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((bpoff + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
if (mode == 0) {
|
||||
emitoff((disp + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitdispreg((disp + zi): i64, basereg);
|
||||
emitline("\n");
|
||||
};
|
||||
zi += 8;
|
||||
};
|
||||
for (zi + 4 <= total) {
|
||||
for (zi + 4 <= totsize) {
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff((bpoff + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
if (mode == 0) {
|
||||
emitoff((disp + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitdispreg((disp + zi): i64, basereg);
|
||||
emitline("\n");
|
||||
};
|
||||
zi += 4;
|
||||
};
|
||||
for (zi < total) {
|
||||
for (zi < totsize) {
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff((bpoff + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
if (mode == 0) {
|
||||
emitoff((disp + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitdispreg((disp + zi): i64, basereg);
|
||||
emitline("\n");
|
||||
};
|
||||
zi += 1;
|
||||
};
|
||||
};
|
||||
@@ -2851,23 +2905,29 @@ fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||
// Tagged-union field: delegate to the shared
|
||||
// widening writer (handles str/scalar/struct
|
||||
// literal/ident payload + tagged-subset tag
|
||||
// remap). Mirrors cstage cg_structlit_fill_bp
|
||||
// and the pre-existing wwstage cgreturn
|
||||
// structlit dispatch; cglet's pre-#17 inline
|
||||
// lacked this branch but selfhost never
|
||||
// tripped it (no tagged-fields-in-let-
|
||||
// structlit in tree).
|
||||
// remap). For non-BP modes, reload BX first so
|
||||
// the widener sees a valid base reg.
|
||||
if (istaggedtype(c, fi.tnode)) {
|
||||
if (mode == 1) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(srcoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
if (mode == 2) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
cgwidentaggedstore(c, fi.tnode,
|
||||
fieldnode.lhs, "BP",
|
||||
bpoff + fi.foff, fi.fsz);
|
||||
fieldnode.lhs, basereg,
|
||||
disp + fi.foff, fi.fsz);
|
||||
fi = nil;
|
||||
} else {
|
||||
// Nested struct-typed structlit value: look up
|
||||
// the inner struct's metadata and recurse at the
|
||||
// field's offset. Pre-#17 the cgexpr-then-store
|
||||
// below would land AX = first qword and the rest
|
||||
// silently stayed zero.
|
||||
// field's offset. Pre-#17/#18 the cgexpr-then-
|
||||
// store below would land AX = first qword and
|
||||
// the rest silently stayed zero.
|
||||
let nested: bool = false;
|
||||
if (fieldnode.lhs != nil) {
|
||||
if (fieldnode.lhs.kind == nkind.N_STRUCTLIT) {
|
||||
@@ -2876,9 +2936,17 @@ fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||
if (primsize(fi.tnode.str) == 0) {
|
||||
let isi: *structinfo = structlookup(c, fi.tnode.str);
|
||||
if (isi != nil) {
|
||||
cgstructlitfillbp(c, isi,
|
||||
// Nested fill: pick the size
|
||||
// discipline matching the outer
|
||||
// site — dot sites pass natural
|
||||
// size, BP-rel sites pass
|
||||
// totsize. Mirror it.
|
||||
let inner_tot: i32 = isi.totsize;
|
||||
if (mode != 0) { inner_tot = structnaturalsize(isi); };
|
||||
cgstructlitfill(c, isi,
|
||||
fieldnode.lhs,
|
||||
bpoff + fi.foff);
|
||||
mode, srcoff, srcname,
|
||||
disp + fi.foff, inner_tot);
|
||||
nested = true;
|
||||
};
|
||||
};
|
||||
@@ -2890,24 +2958,39 @@ fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||
fi = nil;
|
||||
} else {
|
||||
cgexpr(c, fieldnode.lhs);
|
||||
// For non-BP modes, cgexpr just clobbered
|
||||
// BX; reload it before the store.
|
||||
if (mode == 1) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(srcoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
if (mode == 2) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitoff((bpoff + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
if (mode == 0) {
|
||||
emitoff((disp + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitdispreg((disp + fi.foff): i64, basereg);
|
||||
emitline("\n");
|
||||
};
|
||||
fi = nil;
|
||||
} else {
|
||||
// Explicit {1→MOVB, 4→MOVL, else MOVQ}
|
||||
// dispatch (not fieldstoreop) to match
|
||||
// cstage cg_structlit_fill_bp byte-
|
||||
// identically. wwstage's fieldstoreop
|
||||
// would return MOVW for fsz==2 which
|
||||
// cstage doesn't emit — tracked as task
|
||||
// #13. Until that lands, the helper
|
||||
// emits the cstage shape.
|
||||
// cstage byte-identically. wwstage's
|
||||
// fieldstoreop would return MOVW for
|
||||
// fsz==2 which cstage doesn't emit —
|
||||
// tracked as task #13.
|
||||
let fsz: i32 = fi.fsz;
|
||||
let op: str = "MOVQ";
|
||||
if (fsz == 1) { op = "MOVB"; };
|
||||
@@ -2915,8 +2998,13 @@ fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\tAX, ");
|
||||
emitoff((bpoff + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
if (mode == 0) {
|
||||
emitoff((disp + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitdispreg((disp + fi.foff): i64, basereg);
|
||||
emitline("\n");
|
||||
};
|
||||
fi = nil;
|
||||
};
|
||||
};
|
||||
@@ -2929,3 +3017,11 @@ fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||
fieldnode = fieldnode.next;
|
||||
};
|
||||
};
|
||||
|
||||
// Thin wrapper preserving the BP-rel call shape used by cglet,
|
||||
// cgreturn, and cgassign N_IDENT-lhs N_STRUCTLIT. Byte-identical to
|
||||
// the pre-#18 cgstructlitfillbp.
|
||||
fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||
if (si == nil) { return; };
|
||||
cgstructlitfill(c, si, lit, 0, 0, "", bpoff, si.totsize);
|
||||
};
|
||||
|
||||
@@ -8656,59 +8656,113 @@ export fn dotchainresolve(c: *cgen, n: *node,
|
||||
return false;
|
||||
};
|
||||
|
||||
// cgstructlitfillbp — fill a struct-typed slot from an N_STRUCTLIT
|
||||
// value into BP-relative memory at `bpoff`. Mirror of cstage cgen.c's
|
||||
// cg_structlit_fill_bp. Used by cglet, cgreturn N_STRUCTLIT, and
|
||||
// cgassign N_IDENT-lhs N_STRUCTLIT sites where the dst is BP-
|
||||
// relative.
|
||||
// cgstructlitfill — fill a struct-typed slot from an N_STRUCTLIT
|
||||
// value into one of three destination flavors. Mirror of cstage
|
||||
// cgen.c's cg_structlit_fill. Used by cglet, cgreturn N_STRUCTLIT,
|
||||
// cgassign N_IDENT-lhs N_STRUCTLIT (BP-rel) AND cgassign N_DOT-lhs
|
||||
// N_STRUCTLIT (BP-rel / via *struct local / via struct global) at
|
||||
// single-dot and chained-dot sites.
|
||||
//
|
||||
// The inline field-walk previously did `cgexpr(field.lhs); store AX
|
||||
// sized`. For struct-typed fields whose value is itself a nested
|
||||
// N_STRUCTLIT, cgexpr has no whole-struct-in-register convention —
|
||||
// it lands AX = first qword and the trailing bytes silently stay
|
||||
// zero. Selfhost source used write-by-field as a workaround; this
|
||||
// helper recurses cleanly so the workaround is no longer required.
|
||||
// Destination modes:
|
||||
// 0 = DST_BP — base = BP, no reload. Stores at disp+i(BP).
|
||||
// srcoff/srcname unused.
|
||||
// 1 = DST_PTR_LOCAL — base = BX, reloaded from srcoff(BP) before
|
||||
// the ELLIPSIS zero-fill loop and before EVERY
|
||||
// field store (cgexpr clobbers BX between
|
||||
// fields). Stores at disp+i(BX). srcname
|
||||
// unused.
|
||||
// 2 = DST_GLOBAL — base = BX, reloaded via `LEAQ srcname(SB),
|
||||
// BX` with the same cadence as DST_PTR_LOCAL.
|
||||
// srcoff unused.
|
||||
//
|
||||
// Sister branches (cgassign single-dot / via_ptr / global structlit
|
||||
// walks) keep their inline field-walk in v1 since their addressing
|
||||
// has additional BX-reload concerns; nested-STRUCTLIT silent-zero
|
||||
// still drops on those paths — separate follow-up task.
|
||||
// Param semantics (locked in here so the recursion contract is
|
||||
// clear):
|
||||
// - `disp` is the per-recursion accumulator — grows by `fi.foff`
|
||||
// as we descend into a nested struct-typed structlit field.
|
||||
// - `srcoff` (DST_PTR_LOCAL) and `srcname` (DST_GLOBAL) are
|
||||
// *constant* across the whole call tree — they identify the
|
||||
// root dst, which doesn't change with depth.
|
||||
// - `totsize` is also constant; pass the natural size for dot
|
||||
// sites (structnaturalsize) and si.totsize for BP-rel sites,
|
||||
// matching each site's pre-#18 zero-fill bound.
|
||||
//
|
||||
// Output is byte-identical to the prior inline code for any input
|
||||
// that has no nested-STRUCTLIT field (the only shape selfhost source
|
||||
// actually compiles today), preserving 995_self_rebuild byte-
|
||||
// identity.
|
||||
// Why a helper? The inline field-walk previously did
|
||||
// `cgexpr(field.lhs); store AX sized`. For struct-typed fields whose
|
||||
// value is itself a nested N_STRUCTLIT, cgexpr has no whole-struct-
|
||||
// in-register convention — it lands AX = first qword and the
|
||||
// trailing bytes silently stay zero. #17 fixed the BP-rel sites;
|
||||
// #18 extends the same recursion to the four cgassign N_DOT-lhs
|
||||
// structlit walks (single-dot via_ptr/global/local + chained
|
||||
// depth>=2).
|
||||
//
|
||||
// The non-BP modes emit a redundant BX reload at the start of each
|
||||
// recursive nested zero-fill / each recursive scalar store — this is
|
||||
// correctness-by-construction (BX is always freshly loaded right
|
||||
// before use), and the redundancy only fires on the nested-STRUCTLIT
|
||||
// shapes that didn't compile before. Byte-identity for the no-
|
||||
// nested case (the only shape selfhost source uses today) is
|
||||
// preserved because the existing inline code's reload-before-each-
|
||||
// store pattern matches the helper's per-store reload exactly.
|
||||
//
|
||||
// Graduation note (task #13): the scalar store currently uses the
|
||||
// explicit {1→MOVB, 4→MOVL, else MOVQ} dispatch to match cstage
|
||||
// byte-identically — cstage hasn't yet learned MOVW for fsz==2. Once
|
||||
// #13 aligns both stages, the dispatch can switch to fieldstoreop
|
||||
// which already returns MOVW where appropriate.
|
||||
fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||
fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *node,
|
||||
mode: i32, srcoff: i32, srcname: str,
|
||||
disp: i32, totsize: i32) void = {
|
||||
if (si == nil) { return; };
|
||||
let basereg: str = "BP";
|
||||
if (mode != 0) { basereg = "BX"; };
|
||||
if (lit.op == tkind.TK_ELLIPSIS) {
|
||||
// `..., ...` autofill — zero the entire slot first so
|
||||
// unmentioned fields read as 0. Uses si.totsize (slot-padded)
|
||||
// to match the inline pre-#17 pattern.
|
||||
let total: i32 = si.totsize;
|
||||
// unmentioned fields read as 0. Sized stores: 8/4/1. For
|
||||
// non-BP modes, reload BX once before the loop (cgexpr-free
|
||||
// region between iterations, so one reload is enough).
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
if (mode == 1) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(srcoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
if (mode == 2) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
let zi: i32 = 0;
|
||||
for (zi + 8 <= total) {
|
||||
for (zi + 8 <= totsize) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((bpoff + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
if (mode == 0) {
|
||||
emitoff((disp + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitdispreg((disp + zi): i64, basereg);
|
||||
emitline("\n");
|
||||
};
|
||||
zi += 8;
|
||||
};
|
||||
for (zi + 4 <= total) {
|
||||
for (zi + 4 <= totsize) {
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff((bpoff + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
if (mode == 0) {
|
||||
emitoff((disp + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitdispreg((disp + zi): i64, basereg);
|
||||
emitline("\n");
|
||||
};
|
||||
zi += 4;
|
||||
};
|
||||
for (zi < total) {
|
||||
for (zi < totsize) {
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff((bpoff + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
if (mode == 0) {
|
||||
emitoff((disp + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitdispreg((disp + zi): i64, basereg);
|
||||
emitline("\n");
|
||||
};
|
||||
zi += 1;
|
||||
};
|
||||
};
|
||||
@@ -8723,23 +8777,29 @@ fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||
// Tagged-union field: delegate to the shared
|
||||
// widening writer (handles str/scalar/struct
|
||||
// literal/ident payload + tagged-subset tag
|
||||
// remap). Mirrors cstage cg_structlit_fill_bp
|
||||
// and the pre-existing wwstage cgreturn
|
||||
// structlit dispatch; cglet's pre-#17 inline
|
||||
// lacked this branch but selfhost never
|
||||
// tripped it (no tagged-fields-in-let-
|
||||
// structlit in tree).
|
||||
// remap). For non-BP modes, reload BX first so
|
||||
// the widener sees a valid base reg.
|
||||
if (istaggedtype(c, fi.tnode)) {
|
||||
if (mode == 1) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(srcoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
if (mode == 2) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
cgwidentaggedstore(c, fi.tnode,
|
||||
fieldnode.lhs, "BP",
|
||||
bpoff + fi.foff, fi.fsz);
|
||||
fieldnode.lhs, basereg,
|
||||
disp + fi.foff, fi.fsz);
|
||||
fi = nil;
|
||||
} else {
|
||||
// Nested struct-typed structlit value: look up
|
||||
// the inner struct's metadata and recurse at the
|
||||
// field's offset. Pre-#17 the cgexpr-then-store
|
||||
// below would land AX = first qword and the rest
|
||||
// silently stayed zero.
|
||||
// field's offset. Pre-#17/#18 the cgexpr-then-
|
||||
// store below would land AX = first qword and
|
||||
// the rest silently stayed zero.
|
||||
let nested: bool = false;
|
||||
if (fieldnode.lhs != nil) {
|
||||
if (fieldnode.lhs.kind == nkind.N_STRUCTLIT) {
|
||||
@@ -8748,9 +8808,17 @@ fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||
if (primsize(fi.tnode.str) == 0) {
|
||||
let isi: *structinfo = structlookup(c, fi.tnode.str);
|
||||
if (isi != nil) {
|
||||
cgstructlitfillbp(c, isi,
|
||||
// Nested fill: pick the size
|
||||
// discipline matching the outer
|
||||
// site — dot sites pass natural
|
||||
// size, BP-rel sites pass
|
||||
// totsize. Mirror it.
|
||||
let inner_tot: i32 = isi.totsize;
|
||||
if (mode != 0) { inner_tot = structnaturalsize(isi); };
|
||||
cgstructlitfill(c, isi,
|
||||
fieldnode.lhs,
|
||||
bpoff + fi.foff);
|
||||
mode, srcoff, srcname,
|
||||
disp + fi.foff, inner_tot);
|
||||
nested = true;
|
||||
};
|
||||
};
|
||||
@@ -8762,24 +8830,39 @@ fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||
fi = nil;
|
||||
} else {
|
||||
cgexpr(c, fieldnode.lhs);
|
||||
// For non-BP modes, cgexpr just clobbered
|
||||
// BX; reload it before the store.
|
||||
if (mode == 1) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(srcoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
if (mode == 2) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitoff((bpoff + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
if (mode == 0) {
|
||||
emitoff((disp + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitdispreg((disp + fi.foff): i64, basereg);
|
||||
emitline("\n");
|
||||
};
|
||||
fi = nil;
|
||||
} else {
|
||||
// Explicit {1→MOVB, 4→MOVL, else MOVQ}
|
||||
// dispatch (not fieldstoreop) to match
|
||||
// cstage cg_structlit_fill_bp byte-
|
||||
// identically. wwstage's fieldstoreop
|
||||
// would return MOVW for fsz==2 which
|
||||
// cstage doesn't emit — tracked as task
|
||||
// #13. Until that lands, the helper
|
||||
// emits the cstage shape.
|
||||
// cstage byte-identically. wwstage's
|
||||
// fieldstoreop would return MOVW for
|
||||
// fsz==2 which cstage doesn't emit —
|
||||
// tracked as task #13.
|
||||
let fsz: i32 = fi.fsz;
|
||||
let op: str = "MOVQ";
|
||||
if (fsz == 1) { op = "MOVB"; };
|
||||
@@ -8787,8 +8870,13 @@ fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\tAX, ");
|
||||
emitoff((bpoff + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
if (mode == 0) {
|
||||
emitoff((disp + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitdispreg((disp + fi.foff): i64, basereg);
|
||||
emitline("\n");
|
||||
};
|
||||
fi = nil;
|
||||
};
|
||||
};
|
||||
@@ -8802,6 +8890,14 @@ fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||
};
|
||||
};
|
||||
|
||||
// Thin wrapper preserving the BP-rel call shape used by cglet,
|
||||
// cgreturn, and cgassign N_IDENT-lhs N_STRUCTLIT. Byte-identical to
|
||||
// the pre-#18 cgstructlitfillbp.
|
||||
fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *node, bpoff: i32) void = {
|
||||
if (si == nil) { return; };
|
||||
cgstructlitfill(c, si, lit, 0, 0, "", bpoff, si.totsize);
|
||||
};
|
||||
|
||||
// MODULE: wcc
|
||||
// selfhost/cmd/wcc/cgenexpr.ww — split out of cgen.ww.
|
||||
//
|
||||
@@ -12643,6 +12739,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
|
||||
@@ -12652,75 +12753,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;
|
||||
};
|
||||
};
|
||||
@@ -12936,6 +12970,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
|
||||
@@ -12945,66 +12983,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;
|
||||
};
|
||||
};
|
||||
@@ -13243,87 +13223,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
|
||||
@@ -13702,6 +13620,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
|
||||
@@ -13712,131 +13638,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