cstage+selfhost+test: cgen N_ASSIGN whole-STRUCT (call+structlit, 5 sites)
Receive side of #4's cgreturn ABI (aee8149) for TY_STRUCT lvalues of size <=24B. Producer materialises rhs into AX=bytes[0..7], DX=[8..15], CX=[16..23], zero-padded to 24B; receive sites here read the regs and write only `declared sz` bytes — MOVQ for full 8B chunks plus a sized tail (MOVL/MOVW/MOVB) by the *declared* struct size. ASYMMETRY: do NOT mirror the sender's three uniform MOVQs, else trailing 1..7B chunks overrun the next local slot. Tail chunks in {3,5,6,7} are unreachable under WW struct align rules (size%align==0) and fall through. Five sites wired in each stage (cstage cgen.c, wwstage cgenexpr.ww + cgenstmt.ww), call-result + structlit rhs at each: - N_LET `let s: T = bar()` / `= T{...}` cgenstmt cglet - N_ASSIGN N_IDENT-lhs `s = bar()` / `= T{...}` cgenexpr cgassign - N_ASSIGN single-DOT local-base `o.f = ...` - N_ASSIGN single-DOT ptr-base auto-deref `p.f = ...` - N_ASSIGN single-DOT global-base `g.f = ...` - N_ASSIGN chained-DOT depth>=2 `o.m.in = ...` (The four dot-flavors share one shape pattern, hence "5 sites".) Where the dst addr needs scratch (ptr-base/global-base/via_cx), it is loaded into BX after the call so CX stays as the third value word; for structlit field-walks BX is reloaded before each store since cgexpr clobbers AX/BX between fields. wwstage needed a new `structnaturalsize(si)` helper (cgenutil.ww): si.totsize is mis-named — it's slot-padded to 8 by registerstruct for stack-slot use, while the receive ABI wants the type's natural size (max(foff+fsz)). Splitting si.totsize into naturalsize + slotsize is tracked as the wwstage struct sizing follow-up (task #15); until that lands, the helper recovers the natural size at receive sites. Test 701_cgassign_struct.c (18 rows, 3 checks each — cstage value, wwstage value, asm byte-identity), wired in Makefile after 698. The headline ASYMMETRY case is the 20B `{i32×5}` row: sender pads to 24B via three MOVQs, receiver writes MOVQ AX +0, MOVQ DX +8, MOVL CX +16. A regression to a MOVQ tail there overruns 4B past the slot and flips the exit-code check. smoke.combined.ww is the auto-regen ride-along of strings.freeall landing in714d089(worker-shlex). Pre-existing gaps surfaced and tracked separately (not fixed here, out of scope): - task #16: silent drop of `(*p).f = ...` explicit-deref dot lhs. - task #17: silent zero of nested STRUCTLIT field in N_LET / N_ASSIGN initializer — the field_chain and field_global test rows use explicit field writes (`o.m.t = 10i64;`) rather than nested literals as a fixture-level workaround. - task #9: module-name-mangle for fn labels avoided in the field_global_call fixture by `let g: outer;` (no init). make test: 59/59. 994_w6c_ww + 995_self_rebuild PASS — bootstrap byte-identity is the load-bearing proof for this commit's scope.
This commit is contained in:
@@ -3745,11 +3745,148 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
n.rhs, "BX", fi.foff, fsz);
|
||||
return;
|
||||
};
|
||||
// struct-typed field, struct-ident rhs through
|
||||
// *struct base: cgexpr can't materialise a whole
|
||||
// struct value, so word-copy from the rhs slot
|
||||
// to *(struct_ptr + fi.foff). Other rhs shapes
|
||||
// (call result, struct literal) tracked as #27.
|
||||
// struct-typed field via *struct base — three
|
||||
// rhs shapes (call/structlit added with #5;
|
||||
// closes #27 marker here):
|
||||
// N_IDENT: word-copy from rhs slot.
|
||||
// N_CALL: cgexpr → AX/DX/CX per #4's cgreturn
|
||||
// ABI; load *struct ptr into BX after the
|
||||
// call, sized stores per natural struct size.
|
||||
// N_STRUCTLIT: field-walk; reload BX before
|
||||
// each store so cgexpr can clobber AX/BX.
|
||||
// si.totsize is slot-padded; use
|
||||
// structnaturalsize for the type-size query.
|
||||
if (n.op == tkind.TK_ASSIGN
|
||||
&& n.rhs != nil
|
||||
&& n.rhs.kind == nkind.N_CALL
|
||||
&& 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 (ssz <= 24) {
|
||||
let tlm: i32 = ssz - (ssz / 8) * 8;
|
||||
if (tlm == 0 || tlm == 1
|
||||
|| tlm == 2 || tlm == 4) {
|
||||
cgexpr(c, n.rhs);
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
let full: i32 = ssz / 8;
|
||||
let i: i32 = 0;
|
||||
for (i < full) {
|
||||
let reg: str = "AX";
|
||||
if (i == 1) { reg = "DX"; };
|
||||
if (i == 2) { reg = "CX"; };
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(reg);
|
||||
emitline(", ");
|
||||
emitdispreg((fi.foff + i * 8): i64, "BX");
|
||||
emitline("\n");
|
||||
i += 1;
|
||||
};
|
||||
if (tlm > 0) {
|
||||
let top: str = "MOVB";
|
||||
if (tlm == 4) { top = "MOVL"; };
|
||||
if (tlm == 2) { top = "MOVW"; };
|
||||
let treg: str = "AX";
|
||||
if (full == 1) { treg = "DX"; };
|
||||
if (full == 2) { treg = "CX"; };
|
||||
emitline("\t");
|
||||
emitline(top);
|
||||
emitline("\t");
|
||||
emitline(treg);
|
||||
emitline(", ");
|
||||
emitdispreg((fi.foff + full * 8): i64, "BX");
|
||||
emitline("\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
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("\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;
|
||||
};
|
||||
return;
|
||||
};
|
||||
};
|
||||
if (n.op == tkind.TK_ASSIGN
|
||||
&& n.rhs != nil
|
||||
&& n.rhs.kind == nkind.N_IDENT
|
||||
@@ -3906,10 +4043,134 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
n.rhs, "BP", lc.off + fi.foff, fsz);
|
||||
return;
|
||||
};
|
||||
// struct-typed field, struct-ident rhs:
|
||||
// word-copy direct, skipping cgexpr (no
|
||||
// register convention for a whole struct
|
||||
// value). #27 covers non-ident rhs.
|
||||
// struct-typed field on a direct struct
|
||||
// local — three rhs shapes (call/structlit
|
||||
// added with #5; closes #27 marker here):
|
||||
// N_IDENT: word-copy from rhs slot.
|
||||
// N_CALL: cgexpr → AX/DX/CX; sized stores
|
||||
// directly at (lc.off+fi.foff)(BP).
|
||||
// N_STRUCTLIT: field-walk; each inner
|
||||
// field stored at +fi.foff+inner_foff(BP).
|
||||
// BP-rel direct, no addr scratch needed.
|
||||
if (n.op == tkind.TK_ASSIGN
|
||||
&& n.rhs != nil
|
||||
&& n.rhs.kind == nkind.N_CALL
|
||||
&& 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 (ssz <= 24) {
|
||||
let tlm: i32 = ssz - (ssz / 8) * 8;
|
||||
if (tlm == 0 || tlm == 1
|
||||
|| tlm == 2 || tlm == 4) {
|
||||
cgexpr(c, n.rhs);
|
||||
let full: i32 = ssz / 8;
|
||||
let i: i32 = 0;
|
||||
for (i < full) {
|
||||
let reg: str = "AX";
|
||||
if (i == 1) { reg = "DX"; };
|
||||
if (i == 2) { reg = "CX"; };
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(reg);
|
||||
emitline(", ");
|
||||
emitoff((lc.off + fi.foff + i * 8): i64);
|
||||
emitline("(BP)\n");
|
||||
i += 1;
|
||||
};
|
||||
if (tlm > 0) {
|
||||
let top: str = "MOVB";
|
||||
if (tlm == 4) { top = "MOVL"; };
|
||||
if (tlm == 2) { top = "MOVW"; };
|
||||
let treg: str = "AX";
|
||||
if (full == 1) { treg = "DX"; };
|
||||
if (full == 2) { treg = "CX"; };
|
||||
emitline("\t");
|
||||
emitline(top);
|
||||
emitline("\t");
|
||||
emitline(treg);
|
||||
emitline(", ");
|
||||
emitoff((lc.off + fi.foff + full * 8): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
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");
|
||||
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;
|
||||
};
|
||||
return;
|
||||
};
|
||||
};
|
||||
if (n.op == tkind.TK_ASSIGN
|
||||
&& n.rhs != nil
|
||||
&& n.rhs.kind == nkind.N_IDENT
|
||||
@@ -4088,10 +4349,144 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
if (streq(fi.fname, fld)) {
|
||||
// struct-typed field, struct-ident rhs on global
|
||||
// struct base: word-copy direct. Skips cgexpr —
|
||||
// no register convention for a whole struct value.
|
||||
// #27 covers non-ident rhs.
|
||||
// struct-typed field on a global struct base —
|
||||
// three rhs shapes (call/structlit added with
|
||||
// #5; closes #27 marker here):
|
||||
// N_IDENT: word-copy from rhs slot.
|
||||
// N_CALL: cgexpr → AX/DX/CX; LEAQ base into BX
|
||||
// after call, sized stores per natural size.
|
||||
// N_STRUCTLIT: field-walk; reload BX per store.
|
||||
if (n.op == tkind.TK_ASSIGN
|
||||
&& n.rhs != nil
|
||||
&& n.rhs.kind == nkind.N_CALL
|
||||
&& 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 (ssz <= 24) {
|
||||
let tlm: i32 = ssz - (ssz / 8) * 8;
|
||||
if (tlm == 0 || tlm == 1
|
||||
|| tlm == 2 || tlm == 4) {
|
||||
cgexpr(c, n.rhs);
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, bn);
|
||||
emitline("(SB), BX\n");
|
||||
let full: i32 = ssz / 8;
|
||||
let i: i32 = 0;
|
||||
for (i < full) {
|
||||
let reg: str = "AX";
|
||||
if (i == 1) { reg = "DX"; };
|
||||
if (i == 2) { reg = "CX"; };
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(reg);
|
||||
emitline(", ");
|
||||
emitdispreg((fi.foff + i * 8): i64, "BX");
|
||||
emitline("\n");
|
||||
i += 1;
|
||||
};
|
||||
if (tlm > 0) {
|
||||
let top: str = "MOVB";
|
||||
if (tlm == 4) { top = "MOVL"; };
|
||||
if (tlm == 2) { top = "MOVW"; };
|
||||
let treg: str = "AX";
|
||||
if (full == 1) { treg = "DX"; };
|
||||
if (full == 2) { treg = "CX"; };
|
||||
emitline("\t");
|
||||
emitline(top);
|
||||
emitline("\t");
|
||||
emitline(treg);
|
||||
emitline(", ");
|
||||
emitdispreg((fi.foff + full * 8): i64, "BX");
|
||||
emitline("\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
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;
|
||||
};
|
||||
};
|
||||
if (n.op == tkind.TK_ASSIGN
|
||||
&& n.rhs != nil
|
||||
&& n.rhs.kind == nkind.N_IDENT
|
||||
@@ -4378,11 +4773,236 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
// TY_STRUCT terminal: word-copy the rhs slot onto
|
||||
// base+totaloff. Only N_IDENT rhs is wired — other
|
||||
// shapes (call result, struct literal) route through
|
||||
// #27. cgexpr is skipped (no whole-struct register
|
||||
// convention); reads come straight from the rhs slot.
|
||||
// TY_STRUCT terminal: three rhs shapes:
|
||||
// - N_IDENT: word-copy from the rhs local slot
|
||||
// (cgexpr is skipped — no whole-struct register
|
||||
// convention for an arbitrary local).
|
||||
// - N_CALL (added with #5): cgexpr leaves the
|
||||
// value in AX/DX/CX per #4's cgreturn ABI; sized
|
||||
// stores write only the declared field size.
|
||||
// cgreturn touches only AX/DX/CX so for
|
||||
// ptrroot/global we load the dst addr into BX
|
||||
// (not CX) after the call to keep CX as the
|
||||
// third value word.
|
||||
// - N_STRUCTLIT (added with #5): field-by-field
|
||||
// store; for ptrroot/global the dst addr is
|
||||
// reloaded into BX before each store so cgexpr
|
||||
// can clobber AX/BX between fields.
|
||||
if (n.rhs != nil
|
||||
&& n.rhs.kind == nkind.N_CALL
|
||||
&& leaffi.tnode != nil
|
||||
&& leaffi.tnode.kind == nkind.N_TNAME
|
||||
&& primsize(leaffi.tnode.str) == 0) {
|
||||
let lsi: *structinfo = structlookup(c, leaffi.tnode.str);
|
||||
if (lsi != nil) {
|
||||
// si.totsize is slot-padded (rounded to 8);
|
||||
// receive ABI needs the TYPE's natural size.
|
||||
let lsz: i32 = structnaturalsize(lsi);
|
||||
if (lsz <= 24) {
|
||||
let tlm: i32 = lsz - (lsz / 8) * 8;
|
||||
if (tlm == 0 || tlm == 1
|
||||
|| tlm == 2 || tlm == 4) {
|
||||
cgexpr(c, n.rhs);
|
||||
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 full: i32 = lsz / 8;
|
||||
let i: i32 = 0;
|
||||
for (i < full) {
|
||||
let reg: str = "AX";
|
||||
if (i == 1) { reg = "DX"; };
|
||||
if (i == 2) { reg = "CX"; };
|
||||
if (viacx) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(reg);
|
||||
emitline(", ");
|
||||
emitdispreg((totaloff + i * 8): i64, "BX");
|
||||
emitline("\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(reg);
|
||||
emitline(", ");
|
||||
emitoff((rootoff + totaloff + i * 8): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
if (tlm > 0) {
|
||||
let top: str = "MOVB";
|
||||
if (tlm == 4) { top = "MOVL"; };
|
||||
if (tlm == 2) { top = "MOVW"; };
|
||||
let treg: str = "AX";
|
||||
if (full == 1) { treg = "DX"; };
|
||||
if (full == 2) { treg = "CX"; };
|
||||
if (viacx) {
|
||||
emitline("\t");
|
||||
emitline(top);
|
||||
emitline("\t");
|
||||
emitline(treg);
|
||||
emitline(", ");
|
||||
emitdispreg((totaloff + full * 8): i64, "BX");
|
||||
emitline("\n");
|
||||
} else {
|
||||
emitline("\t");
|
||||
emitline(top);
|
||||
emitline("\t");
|
||||
emitline(treg);
|
||||
emitline(", ");
|
||||
emitoff((rootoff + totaloff + full * 8): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
};
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (n.rhs != nil
|
||||
&& n.rhs.kind == nkind.N_STRUCTLIT
|
||||
&& leaffi.tnode != nil
|
||||
&& leaffi.tnode.kind == nkind.N_TNAME
|
||||
&& primsize(leaffi.tnode.str) == 0) {
|
||||
let lsi: *structinfo = structlookup(c, leaffi.tnode.str);
|
||||
if (lsi != nil) {
|
||||
// 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 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;
|
||||
};
|
||||
return;
|
||||
};
|
||||
};
|
||||
if (n.rhs != nil
|
||||
&& n.rhs.kind == nkind.N_IDENT
|
||||
&& leaffi.tnode != nil
|
||||
@@ -4801,6 +5421,150 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
lcf = isfloattype(c, lcn.tnode);
|
||||
lcf32 = isf32type(c, lcn.tnode);
|
||||
};
|
||||
// Struct-typed local reassignment: `s = expr;` where s
|
||||
// is a TY_STRUCT local of size <=24B. Two rhs shapes
|
||||
// (mirrors cglet's N_STRUCTLIT and the call-result
|
||||
// receive branch):
|
||||
// - N_STRUCTLIT: walk fields, store at off+foff
|
||||
// directly. ASYMMETRY-safe (no register copy from
|
||||
// the caller; values come from cgexpr).
|
||||
// - N_CALL: cgexpr → AX/DX/CX, sized stores per the
|
||||
// declared struct size — MOVQ for full 8B chunks
|
||||
// plus MOVL/MOVW/MOVB tail. See cglet receive
|
||||
// site for the ASYMMETRY rationale.
|
||||
// Struct-IDENT word-copy rhs (s = p) is left unwired;
|
||||
// #5 is scoped to receive-side of #4 (calls + literals).
|
||||
// fsz dispatch uses the explicit {1→MOVB, 4→MOVL, else
|
||||
// MOVQ} pattern (not fieldstoreop) to match cstage
|
||||
// cgen.c N_ASSIGN byte-identically — wwstage's
|
||||
// fieldstoreop returns MOVW for fsz==2 which cstage
|
||||
// doesn't emit (tracked separately as the cstage/
|
||||
// wwstage MOVW divergence task).
|
||||
if (lcn != nil) {
|
||||
let lctn: *node = lcn.tnode;
|
||||
let lcsname: str;
|
||||
lcsname.ptr = nil; lcsname.len = 0;
|
||||
if (lctn != nil) {
|
||||
if (lctn.kind == nkind.N_TNAME) {
|
||||
lcsname = lctn.str;
|
||||
};
|
||||
};
|
||||
if (lcsname.len > 0) {
|
||||
let lcsi: *structinfo = structlookup(c, lcsname);
|
||||
if (lcsi != nil) {
|
||||
// si.totsize is slot-padded (rounded to 8);
|
||||
// receive ABI needs the TYPE's natural size.
|
||||
let lcnsz: i32 = structnaturalsize(lcsi);
|
||||
if (n.op == tkind.TK_ASSIGN) {
|
||||
if (n.rhs != nil
|
||||
&& n.rhs.kind == nkind.N_STRUCTLIT) {
|
||||
let lcsz: i32 = lcnsz;
|
||||
if (n.rhs.op == tkind.TK_ELLIPSIS) {
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
let zi: i32 = 0;
|
||||
for (zi + 8 <= lcsz) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((off + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
zi += 8;
|
||||
};
|
||||
for (zi + 4 <= lcsz) {
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff((off + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
zi += 4;
|
||||
};
|
||||
for (zi < lcsz) {
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff((off + 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 fi: *fieldinfo = lcsi.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";
|
||||
};
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitoff((off + 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"; };
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\tAX, ");
|
||||
emitoff((off + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
fi = nil;
|
||||
};
|
||||
} else {
|
||||
fi = fi.finext;
|
||||
};
|
||||
};
|
||||
};
|
||||
fldn = fldn.next;
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (n.rhs != nil
|
||||
&& n.rhs.kind == nkind.N_CALL) {
|
||||
let lcsz: i32 = lcnsz;
|
||||
if (lcsz <= 24) {
|
||||
let tlm: i32 = lcsz - (lcsz / 8) * 8;
|
||||
if (tlm == 0 || tlm == 1
|
||||
|| tlm == 2 || tlm == 4) {
|
||||
cgexpr(c, n.rhs);
|
||||
let full: i32 = lcsz / 8;
|
||||
let i: i32 = 0;
|
||||
for (i < full) {
|
||||
let reg: str = "AX";
|
||||
if (i == 1) { reg = "DX"; };
|
||||
if (i == 2) { reg = "CX"; };
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(reg);
|
||||
emitline(", ");
|
||||
emitoff((off + i * 8): i64);
|
||||
emitline("(BP)\n");
|
||||
i += 1;
|
||||
};
|
||||
if (tlm > 0) {
|
||||
let top: str = "MOVB";
|
||||
if (tlm == 4) { top = "MOVL"; };
|
||||
if (tlm == 2) { top = "MOVW"; };
|
||||
let treg: str = "AX";
|
||||
if (full == 1) { treg = "DX"; };
|
||||
if (full == 2) { treg = "CX"; };
|
||||
emitline("\t");
|
||||
emitline(top);
|
||||
emitline("\t");
|
||||
emitline(treg);
|
||||
emitline(", ");
|
||||
emitoff((off + full * 8): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// Float-typed local: rhs lands in X0; store via MOVSD/
|
||||
// MOVSS, no AX shuffle. Compound (+= -= *= /=) loads
|
||||
// slot into X1, combines into X1, stores X1 back —
|
||||
|
||||
Reference in New Issue
Block a user