w6c+selfhost: cgen N_ASSIGN TY_STRUCT field branch (closes #25)
Parallel to TY_STR/TY_SLICE branches at cgen.c:1939/1962. Word-copy from src slot to field+k*8 via AX, MOVL/MOVB ragged tail. N_IDENT rhs only — struct-call-result and struct-literal rhs are different code paths, filed as task #27. Symmetric in N_ASSIGN field case AND spine-walker terminal; mirrored in wwstage cgassign four sub-shapes (*struct base, direct local, global, spine terminal).
This commit is contained in:
@@ -1978,6 +1978,43 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
/* struct-typed field, struct-ident rhs: cgexpr cannot
|
||||||
|
* materialise a whole struct value in registers, so
|
||||||
|
* word-copy from the rhs slot directly to the dest
|
||||||
|
* field. Other rhs shapes (struct-returning call,
|
||||||
|
* struct literal) stay broken at the cgexpr level —
|
||||||
|
* tracked as #27. */
|
||||||
|
if (n->op == TK_ASSIGN && str_fu
|
||||||
|
&& str_fu->kind == TY_STRUCT
|
||||||
|
&& n->rhs && n->rhs->kind == N_IDENT
|
||||||
|
&& localfind(locals, n->rhs->str) != 0) {
|
||||||
|
int soff = localfind(locals, n->rhs->str);
|
||||||
|
int ssz = (int)str_fu->size;
|
||||||
|
if (via_ptr)
|
||||||
|
ins2(c, A_MOVQ, amem(D_BP, boff), areg(D_BX));
|
||||||
|
else if (is_global)
|
||||||
|
ins2(c, A_LEAQ, masym(c, base->str), areg(D_BX));
|
||||||
|
int k = 0;
|
||||||
|
while (k + 8 <= ssz) {
|
||||||
|
ins2(c, A_MOVQ, amem(D_BP, soff + k), areg(D_AX));
|
||||||
|
if (via_ptr || is_global)
|
||||||
|
ins2(c, A_MOVQ, areg(D_AX), amem(D_BX, foff + k));
|
||||||
|
else
|
||||||
|
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, boff + foff + k));
|
||||||
|
k += 8;
|
||||||
|
}
|
||||||
|
if (k < ssz) {
|
||||||
|
int tail = ssz - k;
|
||||||
|
int lop = (tail == 4) ? A_MOVL
|
||||||
|
: (tail == 1 ? A_MOVB : A_MOVQ);
|
||||||
|
ins2(c, lop, amem(D_BP, soff + k), areg(D_AX));
|
||||||
|
if (via_ptr || is_global)
|
||||||
|
ins2(c, lop, areg(D_AX), amem(D_BX, foff + k));
|
||||||
|
else
|
||||||
|
ins2(c, lop, areg(D_AX), amem(D_BP, boff + foff + k));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
/* compound: load current value into BX */
|
/* compound: load current value into BX */
|
||||||
if (n->op != TK_ASSIGN) {
|
if (n->op != TK_ASSIGN) {
|
||||||
if (via_ptr) {
|
if (via_ptr) {
|
||||||
@@ -2557,6 +2594,53 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
/* TY_STRUCT terminal: word-copy the rhs slot onto
|
||||||
|
* base+total_off. Only N_IDENT rhs is wired —
|
||||||
|
* other shapes route through #27. */
|
||||||
|
if (fu && fu->kind == TY_STRUCT
|
||||||
|
&& n->rhs && n->rhs->kind == N_IDENT
|
||||||
|
&& localfind(locals, n->rhs->str) != 0) {
|
||||||
|
int soff = localfind(locals, n->rhs->str);
|
||||||
|
int ssz = fsz;
|
||||||
|
if (via_cx) {
|
||||||
|
if (ptr_root)
|
||||||
|
ins2(c, A_MOVQ,
|
||||||
|
amem(D_BP, base_disp),
|
||||||
|
areg(D_CX));
|
||||||
|
else
|
||||||
|
ins2(c, A_LEAQ,
|
||||||
|
masym(c, cur->str),
|
||||||
|
areg(D_CX));
|
||||||
|
}
|
||||||
|
int k = 0;
|
||||||
|
while (k + 8 <= ssz) {
|
||||||
|
ins2(c, A_MOVQ,
|
||||||
|
amem(D_BP, soff + k),
|
||||||
|
areg(D_AX));
|
||||||
|
if (via_cx)
|
||||||
|
ins2(c, A_MOVQ, areg(D_AX),
|
||||||
|
amem(D_CX, total_off + k));
|
||||||
|
else
|
||||||
|
ins2(c, A_MOVQ, areg(D_AX),
|
||||||
|
amem(D_BP, base_disp + total_off + k));
|
||||||
|
k += 8;
|
||||||
|
}
|
||||||
|
if (k < ssz) {
|
||||||
|
int tail = ssz - k;
|
||||||
|
int lop = (tail == 4) ? A_MOVL
|
||||||
|
: (tail == 1 ? A_MOVB : A_MOVQ);
|
||||||
|
ins2(c, lop,
|
||||||
|
amem(D_BP, soff + k),
|
||||||
|
areg(D_AX));
|
||||||
|
if (via_cx)
|
||||||
|
ins2(c, lop, areg(D_AX),
|
||||||
|
amem(D_CX, total_off + k));
|
||||||
|
else
|
||||||
|
ins2(c, lop, areg(D_AX),
|
||||||
|
amem(D_BP, base_disp + total_off + k));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
int sf32 = 0;
|
int sf32 = 0;
|
||||||
if (fld_isfloat(leaf_type, &sf32)) {
|
if (fld_isfloat(leaf_type, &sf32)) {
|
||||||
int mov = sf32 ? A_MOVSS : A_MOVSD;
|
int mov = sf32 ? A_MOVSS : A_MOVSD;
|
||||||
|
|||||||
@@ -11619,6 +11619,53 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
for (fi != nil) {
|
for (fi != nil) {
|
||||||
let fn_: str = fi.fname;
|
let fn_: str = fi.fname;
|
||||||
if (streq(fn_, fld)) {
|
if (streq(fn_, fld)) {
|
||||||
|
// 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.
|
||||||
|
if (n.op == tkind.TK_ASSIGN
|
||||||
|
&& n.rhs != nil
|
||||||
|
&& n.rhs.kind == nkind.N_IDENT
|
||||||
|
&& fi.tnode != nil
|
||||||
|
&& fi.tnode.kind == nkind.N_TNAME
|
||||||
|
&& primsize(fi.tnode.str) == 0) {
|
||||||
|
let ssi: *structinfo = structlookup(c, fi.tnode.str);
|
||||||
|
let srhs: *local = localfindnode(c, n.rhs.str);
|
||||||
|
if (ssi != nil) { if (srhs != nil) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
let ssz: i32 = ssi.totsize;
|
||||||
|
let k: i32 = 0;
|
||||||
|
for (k + 8 <= ssz) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitdispreg((fi.foff + k): i64, "BX");
|
||||||
|
emitline("\n");
|
||||||
|
k += 8;
|
||||||
|
};
|
||||||
|
if (k < ssz) {
|
||||||
|
let tail: i32 = ssz - k;
|
||||||
|
let lop: str = "MOVQ";
|
||||||
|
if (tail == 4) { lop = "MOVL"; }
|
||||||
|
else { if (tail == 1) { lop = "MOVB"; }; };
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\tAX, ");
|
||||||
|
emitdispreg((fi.foff + k): i64, "BX");
|
||||||
|
emitline("\n");
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
};};
|
||||||
|
};
|
||||||
if (n.op != tkind.TK_ASSIGN) {
|
if (n.op != tkind.TK_ASSIGN) {
|
||||||
// compound: load current value
|
// compound: load current value
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
@@ -11722,6 +11769,49 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
for (fi != nil) {
|
for (fi != nil) {
|
||||||
let fn_: str = fi.fname;
|
let fn_: str = fi.fname;
|
||||||
if (streq(fn_, fld)) {
|
if (streq(fn_, fld)) {
|
||||||
|
// struct-typed field, struct-ident rhs:
|
||||||
|
// word-copy direct, skipping cgexpr (no
|
||||||
|
// register convention for a whole struct
|
||||||
|
// value). #27 covers non-ident rhs.
|
||||||
|
if (n.op == tkind.TK_ASSIGN
|
||||||
|
&& n.rhs != nil
|
||||||
|
&& n.rhs.kind == nkind.N_IDENT
|
||||||
|
&& fi.tnode != nil
|
||||||
|
&& fi.tnode.kind == nkind.N_TNAME
|
||||||
|
&& primsize(fi.tnode.str) == 0) {
|
||||||
|
let ssi: *structinfo = structlookup(c, fi.tnode.str);
|
||||||
|
let srhs: *local = localfindnode(c, n.rhs.str);
|
||||||
|
if (ssi != nil) { if (srhs != nil) {
|
||||||
|
let ssz: i32 = ssi.totsize;
|
||||||
|
let k: i32 = 0;
|
||||||
|
for (k + 8 <= ssz) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitoff((lc.off + fi.foff + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
k += 8;
|
||||||
|
};
|
||||||
|
if (k < ssz) {
|
||||||
|
let tail: i32 = ssz - k;
|
||||||
|
let lop: str = "MOVQ";
|
||||||
|
if (tail == 4) { lop = "MOVL"; }
|
||||||
|
else { if (tail == 1) { lop = "MOVB"; }; };
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\tAX, ");
|
||||||
|
emitoff((lc.off + fi.foff + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
};};
|
||||||
|
};
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
// str field: cgexpr left (AX=ptr, BX=len);
|
// str field: cgexpr left (AX=ptr, BX=len);
|
||||||
// store both halves at +0/+8. Without this,
|
// store both halves at +0/+8. Without this,
|
||||||
@@ -11861,6 +11951,52 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
let fi: *fieldinfo = si.fields;
|
let fi: *fieldinfo = si.fields;
|
||||||
for (fi != nil) {
|
for (fi != nil) {
|
||||||
if (streq(fi.fname, fld)) {
|
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.
|
||||||
|
if (n.op == tkind.TK_ASSIGN
|
||||||
|
&& n.rhs != nil
|
||||||
|
&& n.rhs.kind == nkind.N_IDENT
|
||||||
|
&& fi.tnode != nil
|
||||||
|
&& fi.tnode.kind == nkind.N_TNAME
|
||||||
|
&& primsize(fi.tnode.str) == 0) {
|
||||||
|
let ssi: *structinfo = structlookup(c, fi.tnode.str);
|
||||||
|
let srhs: *local = localfindnode(c, n.rhs.str);
|
||||||
|
if (ssi != nil) { if (srhs != nil) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, bn);
|
||||||
|
emitline("(SB), BX\n");
|
||||||
|
let ssz: i32 = ssi.totsize;
|
||||||
|
let k: i32 = 0;
|
||||||
|
for (k + 8 <= ssz) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitdispreg((fi.foff + k): i64, "BX");
|
||||||
|
emitline("\n");
|
||||||
|
k += 8;
|
||||||
|
};
|
||||||
|
if (k < ssz) {
|
||||||
|
let tail: i32 = ssz - k;
|
||||||
|
let lop: str = "MOVQ";
|
||||||
|
if (tail == 4) { lop = "MOVL"; }
|
||||||
|
else { if (tail == 1) { lop = "MOVB"; }; };
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\tAX, ");
|
||||||
|
emitdispreg((fi.foff + k): i64, "BX");
|
||||||
|
emitline("\n");
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
};};
|
||||||
|
};
|
||||||
if (n.op == tkind.TK_ASSIGN) {
|
if (n.op == tkind.TK_ASSIGN) {
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (isstrtype(c, fi.tnode)) {
|
if (isstrtype(c, fi.tnode)) {
|
||||||
@@ -12105,6 +12241,74 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
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.
|
||||||
|
if (n.rhs != nil
|
||||||
|
&& n.rhs.kind == nkind.N_IDENT
|
||||||
|
&& leaffi.tnode != nil
|
||||||
|
&& leaffi.tnode.kind == nkind.N_TNAME
|
||||||
|
&& primsize(leaffi.tnode.str) == 0) {
|
||||||
|
let ssi: *structinfo = structlookup(c, leaffi.tnode.str);
|
||||||
|
let srhs: *local = localfindnode(c, n.rhs.str);
|
||||||
|
if (ssi != nil) { if (srhs != nil) {
|
||||||
|
if (viacx) {
|
||||||
|
if (ptrroot) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
|
};
|
||||||
|
let ssz: i32 = ssi.totsize;
|
||||||
|
let k: i32 = 0;
|
||||||
|
for (k + 8 <= ssz) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
if (viacx) {
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitdispreg((totaloff + k): i64, "CX");
|
||||||
|
emitline("\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitoff((rootoff + totaloff + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
};
|
||||||
|
k += 8;
|
||||||
|
};
|
||||||
|
if (k < ssz) {
|
||||||
|
let tail: i32 = ssz - k;
|
||||||
|
let lop: str = "MOVQ";
|
||||||
|
if (tail == 4) { lop = "MOVL"; }
|
||||||
|
else { if (tail == 1) { lop = "MOVB"; }; };
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
if (viacx) {
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\tAX, ");
|
||||||
|
emitdispreg((totaloff + k): i64, "CX");
|
||||||
|
emitline("\n");
|
||||||
|
} else {
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\tAX, ");
|
||||||
|
emitoff((rootoff + totaloff + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
};};
|
||||||
|
};
|
||||||
if (isfloattype(c, leaffi.tnode)) {
|
if (isfloattype(c, leaffi.tnode)) {
|
||||||
let mov: str = "MOVSD";
|
let mov: str = "MOVSD";
|
||||||
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
||||||
|
|||||||
@@ -3571,6 +3571,53 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
for (fi != nil) {
|
for (fi != nil) {
|
||||||
let fn_: str = fi.fname;
|
let fn_: str = fi.fname;
|
||||||
if (streq(fn_, fld)) {
|
if (streq(fn_, fld)) {
|
||||||
|
// 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.
|
||||||
|
if (n.op == tkind.TK_ASSIGN
|
||||||
|
&& n.rhs != nil
|
||||||
|
&& n.rhs.kind == nkind.N_IDENT
|
||||||
|
&& fi.tnode != nil
|
||||||
|
&& fi.tnode.kind == nkind.N_TNAME
|
||||||
|
&& primsize(fi.tnode.str) == 0) {
|
||||||
|
let ssi: *structinfo = structlookup(c, fi.tnode.str);
|
||||||
|
let srhs: *local = localfindnode(c, n.rhs.str);
|
||||||
|
if (ssi != nil) { if (srhs != nil) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
let ssz: i32 = ssi.totsize;
|
||||||
|
let k: i32 = 0;
|
||||||
|
for (k + 8 <= ssz) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitdispreg((fi.foff + k): i64, "BX");
|
||||||
|
emitline("\n");
|
||||||
|
k += 8;
|
||||||
|
};
|
||||||
|
if (k < ssz) {
|
||||||
|
let tail: i32 = ssz - k;
|
||||||
|
let lop: str = "MOVQ";
|
||||||
|
if (tail == 4) { lop = "MOVL"; }
|
||||||
|
else { if (tail == 1) { lop = "MOVB"; }; };
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\tAX, ");
|
||||||
|
emitdispreg((fi.foff + k): i64, "BX");
|
||||||
|
emitline("\n");
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
};};
|
||||||
|
};
|
||||||
if (n.op != tkind.TK_ASSIGN) {
|
if (n.op != tkind.TK_ASSIGN) {
|
||||||
// compound: load current value
|
// compound: load current value
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
@@ -3674,6 +3721,49 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
for (fi != nil) {
|
for (fi != nil) {
|
||||||
let fn_: str = fi.fname;
|
let fn_: str = fi.fname;
|
||||||
if (streq(fn_, fld)) {
|
if (streq(fn_, fld)) {
|
||||||
|
// struct-typed field, struct-ident rhs:
|
||||||
|
// word-copy direct, skipping cgexpr (no
|
||||||
|
// register convention for a whole struct
|
||||||
|
// value). #27 covers non-ident rhs.
|
||||||
|
if (n.op == tkind.TK_ASSIGN
|
||||||
|
&& n.rhs != nil
|
||||||
|
&& n.rhs.kind == nkind.N_IDENT
|
||||||
|
&& fi.tnode != nil
|
||||||
|
&& fi.tnode.kind == nkind.N_TNAME
|
||||||
|
&& primsize(fi.tnode.str) == 0) {
|
||||||
|
let ssi: *structinfo = structlookup(c, fi.tnode.str);
|
||||||
|
let srhs: *local = localfindnode(c, n.rhs.str);
|
||||||
|
if (ssi != nil) { if (srhs != nil) {
|
||||||
|
let ssz: i32 = ssi.totsize;
|
||||||
|
let k: i32 = 0;
|
||||||
|
for (k + 8 <= ssz) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitoff((lc.off + fi.foff + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
k += 8;
|
||||||
|
};
|
||||||
|
if (k < ssz) {
|
||||||
|
let tail: i32 = ssz - k;
|
||||||
|
let lop: str = "MOVQ";
|
||||||
|
if (tail == 4) { lop = "MOVL"; }
|
||||||
|
else { if (tail == 1) { lop = "MOVB"; }; };
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\tAX, ");
|
||||||
|
emitoff((lc.off + fi.foff + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
};};
|
||||||
|
};
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
// str field: cgexpr left (AX=ptr, BX=len);
|
// str field: cgexpr left (AX=ptr, BX=len);
|
||||||
// store both halves at +0/+8. Without this,
|
// store both halves at +0/+8. Without this,
|
||||||
@@ -3813,6 +3903,52 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
let fi: *fieldinfo = si.fields;
|
let fi: *fieldinfo = si.fields;
|
||||||
for (fi != nil) {
|
for (fi != nil) {
|
||||||
if (streq(fi.fname, fld)) {
|
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.
|
||||||
|
if (n.op == tkind.TK_ASSIGN
|
||||||
|
&& n.rhs != nil
|
||||||
|
&& n.rhs.kind == nkind.N_IDENT
|
||||||
|
&& fi.tnode != nil
|
||||||
|
&& fi.tnode.kind == nkind.N_TNAME
|
||||||
|
&& primsize(fi.tnode.str) == 0) {
|
||||||
|
let ssi: *structinfo = structlookup(c, fi.tnode.str);
|
||||||
|
let srhs: *local = localfindnode(c, n.rhs.str);
|
||||||
|
if (ssi != nil) { if (srhs != nil) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, bn);
|
||||||
|
emitline("(SB), BX\n");
|
||||||
|
let ssz: i32 = ssi.totsize;
|
||||||
|
let k: i32 = 0;
|
||||||
|
for (k + 8 <= ssz) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitdispreg((fi.foff + k): i64, "BX");
|
||||||
|
emitline("\n");
|
||||||
|
k += 8;
|
||||||
|
};
|
||||||
|
if (k < ssz) {
|
||||||
|
let tail: i32 = ssz - k;
|
||||||
|
let lop: str = "MOVQ";
|
||||||
|
if (tail == 4) { lop = "MOVL"; }
|
||||||
|
else { if (tail == 1) { lop = "MOVB"; }; };
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\tAX, ");
|
||||||
|
emitdispreg((fi.foff + k): i64, "BX");
|
||||||
|
emitline("\n");
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
};};
|
||||||
|
};
|
||||||
if (n.op == tkind.TK_ASSIGN) {
|
if (n.op == tkind.TK_ASSIGN) {
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (isstrtype(c, fi.tnode)) {
|
if (isstrtype(c, fi.tnode)) {
|
||||||
@@ -4057,6 +4193,74 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
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.
|
||||||
|
if (n.rhs != nil
|
||||||
|
&& n.rhs.kind == nkind.N_IDENT
|
||||||
|
&& leaffi.tnode != nil
|
||||||
|
&& leaffi.tnode.kind == nkind.N_TNAME
|
||||||
|
&& primsize(leaffi.tnode.str) == 0) {
|
||||||
|
let ssi: *structinfo = structlookup(c, leaffi.tnode.str);
|
||||||
|
let srhs: *local = localfindnode(c, n.rhs.str);
|
||||||
|
if (ssi != nil) { if (srhs != nil) {
|
||||||
|
if (viacx) {
|
||||||
|
if (ptrroot) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
|
};
|
||||||
|
let ssz: i32 = ssi.totsize;
|
||||||
|
let k: i32 = 0;
|
||||||
|
for (k + 8 <= ssz) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
if (viacx) {
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitdispreg((totaloff + k): i64, "CX");
|
||||||
|
emitline("\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitoff((rootoff + totaloff + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
};
|
||||||
|
k += 8;
|
||||||
|
};
|
||||||
|
if (k < ssz) {
|
||||||
|
let tail: i32 = ssz - k;
|
||||||
|
let lop: str = "MOVQ";
|
||||||
|
if (tail == 4) { lop = "MOVL"; }
|
||||||
|
else { if (tail == 1) { lop = "MOVB"; }; };
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
if (viacx) {
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\tAX, ");
|
||||||
|
emitdispreg((totaloff + k): i64, "CX");
|
||||||
|
emitline("\n");
|
||||||
|
} else {
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\tAX, ");
|
||||||
|
emitoff((rootoff + totaloff + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
};};
|
||||||
|
};
|
||||||
if (isfloattype(c, leaffi.tnode)) {
|
if (isfloattype(c, leaffi.tnode)) {
|
||||||
let mov: str = "MOVSD";
|
let mov: str = "MOVSD";
|
||||||
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
||||||
|
|||||||
@@ -11619,6 +11619,53 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
for (fi != nil) {
|
for (fi != nil) {
|
||||||
let fn_: str = fi.fname;
|
let fn_: str = fi.fname;
|
||||||
if (streq(fn_, fld)) {
|
if (streq(fn_, fld)) {
|
||||||
|
// 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.
|
||||||
|
if (n.op == tkind.TK_ASSIGN
|
||||||
|
&& n.rhs != nil
|
||||||
|
&& n.rhs.kind == nkind.N_IDENT
|
||||||
|
&& fi.tnode != nil
|
||||||
|
&& fi.tnode.kind == nkind.N_TNAME
|
||||||
|
&& primsize(fi.tnode.str) == 0) {
|
||||||
|
let ssi: *structinfo = structlookup(c, fi.tnode.str);
|
||||||
|
let srhs: *local = localfindnode(c, n.rhs.str);
|
||||||
|
if (ssi != nil) { if (srhs != nil) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(lc.off: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
let ssz: i32 = ssi.totsize;
|
||||||
|
let k: i32 = 0;
|
||||||
|
for (k + 8 <= ssz) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitdispreg((fi.foff + k): i64, "BX");
|
||||||
|
emitline("\n");
|
||||||
|
k += 8;
|
||||||
|
};
|
||||||
|
if (k < ssz) {
|
||||||
|
let tail: i32 = ssz - k;
|
||||||
|
let lop: str = "MOVQ";
|
||||||
|
if (tail == 4) { lop = "MOVL"; }
|
||||||
|
else { if (tail == 1) { lop = "MOVB"; }; };
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\tAX, ");
|
||||||
|
emitdispreg((fi.foff + k): i64, "BX");
|
||||||
|
emitline("\n");
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
};};
|
||||||
|
};
|
||||||
if (n.op != tkind.TK_ASSIGN) {
|
if (n.op != tkind.TK_ASSIGN) {
|
||||||
// compound: load current value
|
// compound: load current value
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
@@ -11722,6 +11769,49 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
for (fi != nil) {
|
for (fi != nil) {
|
||||||
let fn_: str = fi.fname;
|
let fn_: str = fi.fname;
|
||||||
if (streq(fn_, fld)) {
|
if (streq(fn_, fld)) {
|
||||||
|
// struct-typed field, struct-ident rhs:
|
||||||
|
// word-copy direct, skipping cgexpr (no
|
||||||
|
// register convention for a whole struct
|
||||||
|
// value). #27 covers non-ident rhs.
|
||||||
|
if (n.op == tkind.TK_ASSIGN
|
||||||
|
&& n.rhs != nil
|
||||||
|
&& n.rhs.kind == nkind.N_IDENT
|
||||||
|
&& fi.tnode != nil
|
||||||
|
&& fi.tnode.kind == nkind.N_TNAME
|
||||||
|
&& primsize(fi.tnode.str) == 0) {
|
||||||
|
let ssi: *structinfo = structlookup(c, fi.tnode.str);
|
||||||
|
let srhs: *local = localfindnode(c, n.rhs.str);
|
||||||
|
if (ssi != nil) { if (srhs != nil) {
|
||||||
|
let ssz: i32 = ssi.totsize;
|
||||||
|
let k: i32 = 0;
|
||||||
|
for (k + 8 <= ssz) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitoff((lc.off + fi.foff + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
k += 8;
|
||||||
|
};
|
||||||
|
if (k < ssz) {
|
||||||
|
let tail: i32 = ssz - k;
|
||||||
|
let lop: str = "MOVQ";
|
||||||
|
if (tail == 4) { lop = "MOVL"; }
|
||||||
|
else { if (tail == 1) { lop = "MOVB"; }; };
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\tAX, ");
|
||||||
|
emitoff((lc.off + fi.foff + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
};};
|
||||||
|
};
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
// str field: cgexpr left (AX=ptr, BX=len);
|
// str field: cgexpr left (AX=ptr, BX=len);
|
||||||
// store both halves at +0/+8. Without this,
|
// store both halves at +0/+8. Without this,
|
||||||
@@ -11861,6 +11951,52 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
let fi: *fieldinfo = si.fields;
|
let fi: *fieldinfo = si.fields;
|
||||||
for (fi != nil) {
|
for (fi != nil) {
|
||||||
if (streq(fi.fname, fld)) {
|
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.
|
||||||
|
if (n.op == tkind.TK_ASSIGN
|
||||||
|
&& n.rhs != nil
|
||||||
|
&& n.rhs.kind == nkind.N_IDENT
|
||||||
|
&& fi.tnode != nil
|
||||||
|
&& fi.tnode.kind == nkind.N_TNAME
|
||||||
|
&& primsize(fi.tnode.str) == 0) {
|
||||||
|
let ssi: *structinfo = structlookup(c, fi.tnode.str);
|
||||||
|
let srhs: *local = localfindnode(c, n.rhs.str);
|
||||||
|
if (ssi != nil) { if (srhs != nil) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, bn);
|
||||||
|
emitline("(SB), BX\n");
|
||||||
|
let ssz: i32 = ssi.totsize;
|
||||||
|
let k: i32 = 0;
|
||||||
|
for (k + 8 <= ssz) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitdispreg((fi.foff + k): i64, "BX");
|
||||||
|
emitline("\n");
|
||||||
|
k += 8;
|
||||||
|
};
|
||||||
|
if (k < ssz) {
|
||||||
|
let tail: i32 = ssz - k;
|
||||||
|
let lop: str = "MOVQ";
|
||||||
|
if (tail == 4) { lop = "MOVL"; }
|
||||||
|
else { if (tail == 1) { lop = "MOVB"; }; };
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\tAX, ");
|
||||||
|
emitdispreg((fi.foff + k): i64, "BX");
|
||||||
|
emitline("\n");
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
};};
|
||||||
|
};
|
||||||
if (n.op == tkind.TK_ASSIGN) {
|
if (n.op == tkind.TK_ASSIGN) {
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (isstrtype(c, fi.tnode)) {
|
if (isstrtype(c, fi.tnode)) {
|
||||||
@@ -12105,6 +12241,74 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
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.
|
||||||
|
if (n.rhs != nil
|
||||||
|
&& n.rhs.kind == nkind.N_IDENT
|
||||||
|
&& leaffi.tnode != nil
|
||||||
|
&& leaffi.tnode.kind == nkind.N_TNAME
|
||||||
|
&& primsize(leaffi.tnode.str) == 0) {
|
||||||
|
let ssi: *structinfo = structlookup(c, leaffi.tnode.str);
|
||||||
|
let srhs: *local = localfindnode(c, n.rhs.str);
|
||||||
|
if (ssi != nil) { if (srhs != nil) {
|
||||||
|
if (viacx) {
|
||||||
|
if (ptrroot) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(rootoff: i64);
|
||||||
|
emitline("(BP), CX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, rootname);
|
||||||
|
emitline("(SB), CX\n");
|
||||||
|
};
|
||||||
|
};
|
||||||
|
let ssz: i32 = ssi.totsize;
|
||||||
|
let k: i32 = 0;
|
||||||
|
for (k + 8 <= ssz) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
if (viacx) {
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitdispreg((totaloff + k): i64, "CX");
|
||||||
|
emitline("\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitoff((rootoff + totaloff + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
};
|
||||||
|
k += 8;
|
||||||
|
};
|
||||||
|
if (k < ssz) {
|
||||||
|
let tail: i32 = ssz - k;
|
||||||
|
let lop: str = "MOVQ";
|
||||||
|
if (tail == 4) { lop = "MOVL"; }
|
||||||
|
else { if (tail == 1) { lop = "MOVB"; }; };
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\t");
|
||||||
|
emitoff((srhs.off + k): i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
if (viacx) {
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\tAX, ");
|
||||||
|
emitdispreg((totaloff + k): i64, "CX");
|
||||||
|
emitline("\n");
|
||||||
|
} else {
|
||||||
|
emitline("\t");
|
||||||
|
emitline(lop);
|
||||||
|
emitline("\tAX, ");
|
||||||
|
emitoff((rootoff + totaloff + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
};};
|
||||||
|
};
|
||||||
if (isfloattype(c, leaffi.tnode)) {
|
if (isfloattype(c, leaffi.tnode)) {
|
||||||
let mov: str = "MOVSD";
|
let mov: str = "MOVSD";
|
||||||
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
|
||||||
|
|||||||
@@ -276,6 +276,153 @@ static const struct row rows[] = {
|
|||||||
" return 42;\n"
|
" return 42;\n"
|
||||||
"};\n",
|
"};\n",
|
||||||
42 },
|
42 },
|
||||||
|
/* Whole-struct rhs to struct field (task #25). cgen N_ASSIGN had
|
||||||
|
* special-cases for TY_STR (2 words) and TY_SLICE (3 words) but no
|
||||||
|
* TY_STRUCT branch — the fallthrough fldstoreop wrote only AX,
|
||||||
|
* dropping every trailing word. Fixed by a word-copy branch that
|
||||||
|
* runs MOVQ from rhs slot to dest field, plus a 4/1-byte ragged
|
||||||
|
* tail. The 16B row is the BORDER case: same total size as TY_STR
|
||||||
|
* but must NOT take the str branch (str field has 16B header; this
|
||||||
|
* is a 2-word struct value, structurally identical but type-
|
||||||
|
* dispatched). Distinct byte values per word so a stray store at
|
||||||
|
* the wrong offset surfaces as a misvalue, not coincidental zero. */
|
||||||
|
{ "struct_field_value_write_16B",
|
||||||
|
"type inner = struct { a: i64, b: i64 };\n"
|
||||||
|
"type outer = struct { i: inner, mark: i32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let o: inner;\n"
|
||||||
|
" o.a = 0x1122334455667788i64;\n"
|
||||||
|
" o.b = 0x99aabbccddeeff00i64;\n"
|
||||||
|
" let x: outer;\n"
|
||||||
|
" x.mark = 0x55;\n"
|
||||||
|
" x.i = o;\n"
|
||||||
|
" if (x.i.a != 0x1122334455667788i64) { return 1; };\n"
|
||||||
|
" if (x.i.b != 0x99aabbccddeeff00i64) { return 2; };\n"
|
||||||
|
" if (x.mark != 0x55) { return 3; };\n"
|
||||||
|
" return 42;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* 24B struct rhs (the original #25 probe case). Three words at +0/+8/+16
|
||||||
|
* exercise the word-copy loop with no ragged tail. Distinct byte
|
||||||
|
* patterns pin each word independently. */
|
||||||
|
{ "struct_field_value_write_24B",
|
||||||
|
"type inner = struct { a: i64, b: i64, c: i64 };\n"
|
||||||
|
"type outer = struct { i: inner, mark: i32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let o: inner;\n"
|
||||||
|
" o.a = 0x0a0a0a0a0a0a0a0ai64;\n"
|
||||||
|
" o.b = 0x1b1b1b1b1b1b1b1bi64;\n"
|
||||||
|
" o.c = 0x2c2c2c2c2c2c2c2ci64;\n"
|
||||||
|
" let x: outer;\n"
|
||||||
|
" x.mark = 7;\n"
|
||||||
|
" x.i = o;\n"
|
||||||
|
" if (x.i.a != 0x0a0a0a0a0a0a0a0ai64) { return 1; };\n"
|
||||||
|
" if (x.i.b != 0x1b1b1b1b1b1b1b1bi64) { return 2; };\n"
|
||||||
|
" if (x.i.c != 0x2c2c2c2c2c2c2c2ci64) { return 3; };\n"
|
||||||
|
" if (x.mark != 7) { return 4; };\n"
|
||||||
|
" return 42;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* 32B struct rhs — four-word copy, no ragged tail. Trailing mark in
|
||||||
|
* the outer struct catches a stray fifth-word store. */
|
||||||
|
{ "struct_field_value_write_32B",
|
||||||
|
"type inner = struct { a: i64, b: i64, c: i64, d: i64 };\n"
|
||||||
|
"type outer = struct { i: inner, mark: i32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let o: inner;\n"
|
||||||
|
" o.a = 0x0a0a0a0a0a0a0a0ai64;\n"
|
||||||
|
" o.b = 0x1b1b1b1b1b1b1b1bi64;\n"
|
||||||
|
" o.c = 0x2c2c2c2c2c2c2c2ci64;\n"
|
||||||
|
" o.d = 0x3d3d3d3d3d3d3d3di64;\n"
|
||||||
|
" let x: outer;\n"
|
||||||
|
" x.mark = 0x33;\n"
|
||||||
|
" x.i = o;\n"
|
||||||
|
" if (x.i.a != 0x0a0a0a0a0a0a0a0ai64) { return 1; };\n"
|
||||||
|
" if (x.i.b != 0x1b1b1b1b1b1b1b1bi64) { return 2; };\n"
|
||||||
|
" if (x.i.c != 0x2c2c2c2c2c2c2c2ci64) { return 3; };\n"
|
||||||
|
" if (x.i.d != 0x3d3d3d3d3d3d3d3di64) { return 4; };\n"
|
||||||
|
" if (x.mark != 0x33) { return 5; };\n"
|
||||||
|
" return 42;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* Nested struct field write through chained N_DOT spine
|
||||||
|
* (`b.inner.deep = other_deep`). Confirms #22's spine walker
|
||||||
|
* handles a TY_STRUCT terminal, not just scalar/str/slice. The
|
||||||
|
* 24B inner struct payload must survive both the spine walk and
|
||||||
|
* the word-copy. */
|
||||||
|
{ "struct_field_value_write_nested_chain",
|
||||||
|
"type deep = struct { a: i64, b: i64, c: i64 };\n"
|
||||||
|
"type mid = struct { d: deep, mark: i32 };\n"
|
||||||
|
"type outer = struct { m: mid, tag: i32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let other: deep;\n"
|
||||||
|
" other.a = 0x0a0a0a0a0a0a0a0ai64;\n"
|
||||||
|
" other.b = 0x1b1b1b1b1b1b1b1bi64;\n"
|
||||||
|
" other.c = 0x2c2c2c2c2c2c2c2ci64;\n"
|
||||||
|
" let v: outer;\n"
|
||||||
|
" v.m.mark = 5;\n"
|
||||||
|
" v.tag = 9;\n"
|
||||||
|
" v.m.d = other;\n"
|
||||||
|
" if (v.m.d.a != 0x0a0a0a0a0a0a0a0ai64) { return 1; };\n"
|
||||||
|
" if (v.m.d.b != 0x1b1b1b1b1b1b1b1bi64) { return 2; };\n"
|
||||||
|
" if (v.m.d.c != 0x2c2c2c2c2c2c2c2ci64) { return 3; };\n"
|
||||||
|
" if (v.m.mark != 5) { return 4; };\n"
|
||||||
|
" if (v.tag != 9) { return 5; };\n"
|
||||||
|
" return 42;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* Whole-struct rhs to a struct field via a *struct base — exercises
|
||||||
|
* the via_ptr arm of the new TY_STRUCT branch. Stages the dest addr
|
||||||
|
* in BX (MOVQ off(BP),BX) before iterating, then writes at fi.foff+k(BX). */
|
||||||
|
{ "struct_field_value_write_via_ptr",
|
||||||
|
"type inner = struct { a: i64, b: i64, c: i64 };\n"
|
||||||
|
"type outer = struct { i: inner, mark: i32 };\n"
|
||||||
|
"fn fill(x: *outer) void = {\n"
|
||||||
|
" let o: inner;\n"
|
||||||
|
" o.a = 0x0a0a0a0a0a0a0a0ai64;\n"
|
||||||
|
" o.b = 0x1b1b1b1b1b1b1b1bi64;\n"
|
||||||
|
" o.c = 0x2c2c2c2c2c2c2c2ci64;\n"
|
||||||
|
" x.mark = 7;\n"
|
||||||
|
" x.i = o;\n"
|
||||||
|
"};\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let x: outer;\n"
|
||||||
|
" fill(&x);\n"
|
||||||
|
" if (x.i.a != 0x0a0a0a0a0a0a0a0ai64) { return 1; };\n"
|
||||||
|
" if (x.i.b != 0x1b1b1b1b1b1b1b1bi64) { return 2; };\n"
|
||||||
|
" if (x.i.c != 0x2c2c2c2c2c2c2c2ci64) { return 3; };\n"
|
||||||
|
" if (x.mark != 7) { return 4; };\n"
|
||||||
|
" return 42;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* Ragged tail — 12B inner struct (3 i32 fields, maxalign=4, no 8B
|
||||||
|
* round-up) lands the word-copy loop at k=8 with 4 bytes left and
|
||||||
|
* pins the MOVL tail branch. Tail-only structs are reachable for
|
||||||
|
* any TY_STRUCT whose maxalign < 8: this is the smallest such
|
||||||
|
* shape. The trailing i32 mark in the outer struct catches a stray
|
||||||
|
* MOVQ tail (which would overwrite +4 of the mark slot). The 5/3/2
|
||||||
|
* byte tails (struct of just i8 fields) fall back to MOVQ in cgen
|
||||||
|
* and would overwrite past the field boundary; the language permits
|
||||||
|
* align==1 structs but they're not exercised here — filed as a
|
||||||
|
* sub-followup if a real callsite surfaces. */
|
||||||
|
{ "struct_field_value_write_ragged_tail_12B",
|
||||||
|
"type inner = struct { a: i32, b: i32, c: i32 };\n"
|
||||||
|
"type outer = struct { i: inner, mark: i32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let o: inner;\n"
|
||||||
|
" o.a = 0x11223344;\n"
|
||||||
|
" o.b = 0x55667788;\n"
|
||||||
|
" o.c = 0x29aabbcc;\n"
|
||||||
|
" let x: outer;\n"
|
||||||
|
" x.mark = 0x33;\n"
|
||||||
|
" x.i = o;\n"
|
||||||
|
" if (x.i.a != 0x11223344) { return 1; };\n"
|
||||||
|
" if (x.i.b != 0x55667788) { return 2; };\n"
|
||||||
|
" if (x.i.c != 0x29aabbcc) { return 3; };\n"
|
||||||
|
" if (x.mark != 0x33) { return 4; };\n"
|
||||||
|
" return 42;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|||||||
Reference in New Issue
Block a user