w6c+wwstage: &aggregate-array-element addressing + store/copy (#270-1)
The array-of-struct element store/copy family — one primitive (&(array element) for an AGGREGATE element, used as address, never deref/truncate) across three consumers. Both stages were symmetric-broken; converge on the runtime-correct full-address/full-copy (#263). (1a) `a[i].m[j] = v` (a:[N]struct) segfaulted: the `arr[i].field` arm computed &a[i] then DEREF'd it (loaded the struct's first 8 bytes as a value) for an `[N]T`-typed field → garbage base. Now an array-typed field of an array element leaves the field ADDRESS (the #135 read-side, applied to the array-element base). cgen.c arm + cgenexpr.ww cgdot N_INDEX-lhs branch. (1b) `a[i] = aggregateval` truncated the copy to an 8B MOVQ. New aggregate (struct/array/tuple >8B) element-store branch word-copies the element from the rhs source address (ident / N_DOT field / `*p` deref) — the WRITE-twin of the #268 let-init loop. cgen.c N_INDEX store + cgenexpr.ww cgassign. (3a) `let c = x.arr[i]` (N_DOT base) / `let c = a[i][j]` (nested) dropped the copy: the #268 let-init N_INDEX source-addr arm was N_IDENT-base- gated. Now computes &base[idx] via cg_dotbase_addr (N_DOT field) or the &abase[bidx] spine (nested N_IDENT-array base). cgen.c N_LET + cgenstmt.ww cglet. 949 rows: elemfield_store, elem_struct_store, elem_arr_store, letcopy_{dot,nest}_prim, letcopy_subarr (byteid=1); letcopy_{dot,nest}_ struct (byteid=0 — run-correct, byte-id blocked by the orthogonal value-nested-struct frame divergence #254). All 94 pass; test-unit 241 green.
This commit is contained in:
@@ -21994,6 +21994,24 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
} else {
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
};
|
||||
// #270-1a: an `[N]T`-typed field of an
|
||||
// array element (`a[i].m[j]`) — leave the
|
||||
// field's ADDRESS, a base for the outer
|
||||
// index, NEVER deref. AX holds &a[i]; the
|
||||
// field address is &a[i]+foff. The #135
|
||||
// read-side for `d.m[i]`, applied to an
|
||||
// array-element base. Without this an array
|
||||
// field fell to fieldloadop below and loaded
|
||||
// its first 8 bytes as a value → garbage
|
||||
// base → SEGFAULT in the outer index.
|
||||
if (tinfoisarray(fi.tnode.type_: *tinfo)) {
|
||||
if (fi.foff != 0) {
|
||||
emitline("\tADDQ\t$");
|
||||
emitint(fi.foff: i64);
|
||||
emitline(", AX\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (isstrtype(c, fi.tnode) || isslicetype(c, fi.tnode)) {
|
||||
// str/slice: the 3-word {ptr,len,cap}
|
||||
// slice header (#1). AX holds the
|
||||
@@ -24620,6 +24638,120 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
c.sretdestoff = 0;
|
||||
return;
|
||||
};
|
||||
// #270-1b: aggregate (struct/array/tuple >8B) element
|
||||
// STORE `a[i] = val`. The scalar store path below copies
|
||||
// only the first 8 bytes (tnodestoreop MOVQ) — a silent
|
||||
// truncation. Compute &a[i] (dest) and the rhs SOURCE
|
||||
// address, then word-copy esz bytes: the WRITE-twin of
|
||||
// the #268 let-init copy loop. Source shapes mirror that
|
||||
// loop (ident, N_DOT field via dotchainaddr, `*p`
|
||||
// deref); a by-value call result is the deferred #271, so
|
||||
// N_CALL/literal sources fall through unchanged. esz>8
|
||||
// non-str/non-slice IS a struct/array/tuple here (the
|
||||
// tagged element already returned above; floats are ≤8).
|
||||
let aggsrc: bool = (n.rhs.kind == nkind.N_IDENT)
|
||||
|| (n.rhs.kind == nkind.N_DOT)
|
||||
|| (n.rhs.kind == nkind.N_UN
|
||||
&& n.rhs.op == tkind.TK_STAR);
|
||||
if (esz > 8 && !isstrtype(c, elemtn)
|
||||
&& !isslicetype(c, elemtn) && aggsrc) {
|
||||
cgexpr(c, idx); // idx → AX
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
emitline("\tPUSHQ\tAX\n"); // scaled idx
|
||||
if (isglobalarr) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, globalname);
|
||||
emitline("(SB), BX\n");
|
||||
} else { if (isglobalptr) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymname(c, globalname);
|
||||
emitline("(SB), BX\n");
|
||||
} else { if (baselocal != nil) {
|
||||
let tn2: *node = baselocal.tnode;
|
||||
let isarr2: bool = false;
|
||||
if (tn2 != nil) { if (tn2.kind == nkind.N_TARRAY) { isarr2 = true; }; };
|
||||
if (isarr2) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(baselocal.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(baselocal.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
} else { if (dotbaseaddr(c, base, "BX")) {
|
||||
// N_DOT array-field base resolved inline.
|
||||
} else {
|
||||
cgexpr(c, base);
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
};};};};
|
||||
emitline("\tPOPQ\tAX\n"); // scaled idx
|
||||
emitline("\tADDQ\tAX, BX\n");
|
||||
emitline("\tPUSHQ\tBX\n"); // spill dest
|
||||
// rhs source address → SI
|
||||
if (n.rhs.kind == nkind.N_UN
|
||||
&& n.rhs.op == tkind.TK_STAR) {
|
||||
cgexpr(c, n.rhs.lhs);
|
||||
emitline("\tMOVQ\tAX, SI\n");
|
||||
} else { if (n.rhs.kind == nkind.N_IDENT) {
|
||||
let sl: *local = localfindnode(c, n.rhs.str);
|
||||
if (sl != nil) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(sl.off: i64);
|
||||
emitline("(BP), SI\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, n.rhs.str);
|
||||
emitline("(SB), SI\n");
|
||||
};
|
||||
} else {
|
||||
dotchainaddr(c, n.rhs, "SI");
|
||||
};};
|
||||
emitline("\tPOPQ\tBX\n"); // dest
|
||||
let kc: i32 = 0;
|
||||
for (kc + 8 <= esz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(kc: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(kc: i64);
|
||||
emitline("(BX)\n");
|
||||
kc += 8;
|
||||
};
|
||||
if (kc + 4 <= esz) {
|
||||
emitline("\tMOVL\t");
|
||||
emitoff(kc: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff(kc: i64);
|
||||
emitline("(BX)\n");
|
||||
kc += 4;
|
||||
};
|
||||
if (kc + 2 <= esz) {
|
||||
emitline("\tMOVW\t");
|
||||
emitoff(kc: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVW\tAX, ");
|
||||
emitoff(kc: i64);
|
||||
emitline("(BX)\n");
|
||||
kc += 2;
|
||||
};
|
||||
if (kc + 1 <= esz) {
|
||||
emitline("\tMOVB\t");
|
||||
emitoff(kc: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff(kc: i64);
|
||||
emitline("(BX)\n");
|
||||
kc += 1;
|
||||
};
|
||||
return;
|
||||
};
|
||||
cgexpr(c, n.rhs); // value → AX
|
||||
// str/slice: spill cap (CX) + len (BX) before
|
||||
// computing the index so the post-index store can
|
||||
@@ -29186,6 +29318,76 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tAX, SI\n");
|
||||
havesrc = true;
|
||||
};
|
||||
// #270-3a: the index BASE is an N_DOT
|
||||
// array-field (`x.arr[i]`) or a nested N_INDEX
|
||||
// (`a[i][j]`); the N_IDENT-base arm above missed
|
||||
// both, so the copy fell to the 8B truncation
|
||||
// below. Compute &base[idx]: scaled idx on the
|
||||
// stack, then &base via dotbaseaddr (N_DOT field
|
||||
// address) or the &abase[bidx] spine (nested
|
||||
// N_IDENT-array base), then add.
|
||||
if (!havesrc && base != nil
|
||||
&& (base.kind == nkind.N_DOT
|
||||
|| base.kind == nkind.N_INDEX)) {
|
||||
let esz2: i32 = 1;
|
||||
if (bu != nil && bu.sub != nil) {
|
||||
esz2 = bu.sub.size: i32;
|
||||
};
|
||||
cgexpr(c, idx);
|
||||
if (esz2 > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz2: i64);
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
let baseok: bool = false;
|
||||
if (base.kind == nkind.N_DOT) {
|
||||
if (dotbaseaddr(c, base, "AX")) {
|
||||
baseok = true;
|
||||
};
|
||||
} else {
|
||||
let ab: *node = base.lhs;
|
||||
let bidx: *node = base.rhs;
|
||||
let abu: *tinfo = nil;
|
||||
if (ab != nil) { abu = ab.type_: *tinfo; };
|
||||
for (abu != nil && abu.kind == tykind.TY_NAMED) {
|
||||
abu = abu.under;
|
||||
};
|
||||
if (ab != nil && ab.kind == nkind.N_IDENT
|
||||
&& abu != nil && abu.kind == tykind.TY_ARRAY) {
|
||||
let aesz: i32 = 1;
|
||||
if (abu.sub != nil) {
|
||||
aesz = abu.sub.size: i32;
|
||||
};
|
||||
cgexpr(c, bidx);
|
||||
if (aesz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(aesz: i64);
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
let abl: *local = localfindnode(c, ab.str);
|
||||
if (abl != nil) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(abl.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, ab.str);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
emitline("\tADDQ\tBX, AX\n");
|
||||
baseok = true;
|
||||
};
|
||||
};
|
||||
emitline("\tPOPQ\tBX\n");
|
||||
if (baseok) {
|
||||
emitline("\tADDQ\tBX, AX\n");
|
||||
emitline("\tMOVQ\tAX, SI\n");
|
||||
havesrc = true;
|
||||
};
|
||||
};
|
||||
}; };
|
||||
if (havesrc) {
|
||||
let k: i32 = 0;
|
||||
|
||||
@@ -2496,6 +2496,24 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
} else {
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
};
|
||||
// #270-1a: an `[N]T`-typed field of an
|
||||
// array element (`a[i].m[j]`) — leave the
|
||||
// field's ADDRESS, a base for the outer
|
||||
// index, NEVER deref. AX holds &a[i]; the
|
||||
// field address is &a[i]+foff. The #135
|
||||
// read-side for `d.m[i]`, applied to an
|
||||
// array-element base. Without this an array
|
||||
// field fell to fieldloadop below and loaded
|
||||
// its first 8 bytes as a value → garbage
|
||||
// base → SEGFAULT in the outer index.
|
||||
if (tinfoisarray(fi.tnode.type_: *tinfo)) {
|
||||
if (fi.foff != 0) {
|
||||
emitline("\tADDQ\t$");
|
||||
emitint(fi.foff: i64);
|
||||
emitline(", AX\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (isstrtype(c, fi.tnode) || isslicetype(c, fi.tnode)) {
|
||||
// str/slice: the 3-word {ptr,len,cap}
|
||||
// slice header (#1). AX holds the
|
||||
@@ -5122,6 +5140,120 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
c.sretdestoff = 0;
|
||||
return;
|
||||
};
|
||||
// #270-1b: aggregate (struct/array/tuple >8B) element
|
||||
// STORE `a[i] = val`. The scalar store path below copies
|
||||
// only the first 8 bytes (tnodestoreop MOVQ) — a silent
|
||||
// truncation. Compute &a[i] (dest) and the rhs SOURCE
|
||||
// address, then word-copy esz bytes: the WRITE-twin of
|
||||
// the #268 let-init copy loop. Source shapes mirror that
|
||||
// loop (ident, N_DOT field via dotchainaddr, `*p`
|
||||
// deref); a by-value call result is the deferred #271, so
|
||||
// N_CALL/literal sources fall through unchanged. esz>8
|
||||
// non-str/non-slice IS a struct/array/tuple here (the
|
||||
// tagged element already returned above; floats are ≤8).
|
||||
let aggsrc: bool = (n.rhs.kind == nkind.N_IDENT)
|
||||
|| (n.rhs.kind == nkind.N_DOT)
|
||||
|| (n.rhs.kind == nkind.N_UN
|
||||
&& n.rhs.op == tkind.TK_STAR);
|
||||
if (esz > 8 && !isstrtype(c, elemtn)
|
||||
&& !isslicetype(c, elemtn) && aggsrc) {
|
||||
cgexpr(c, idx); // idx → AX
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
emitline("\tPUSHQ\tAX\n"); // scaled idx
|
||||
if (isglobalarr) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, globalname);
|
||||
emitline("(SB), BX\n");
|
||||
} else { if (isglobalptr) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymname(c, globalname);
|
||||
emitline("(SB), BX\n");
|
||||
} else { if (baselocal != nil) {
|
||||
let tn2: *node = baselocal.tnode;
|
||||
let isarr2: bool = false;
|
||||
if (tn2 != nil) { if (tn2.kind == nkind.N_TARRAY) { isarr2 = true; }; };
|
||||
if (isarr2) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(baselocal.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(baselocal.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
} else { if (dotbaseaddr(c, base, "BX")) {
|
||||
// N_DOT array-field base resolved inline.
|
||||
} else {
|
||||
cgexpr(c, base);
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
};};};};
|
||||
emitline("\tPOPQ\tAX\n"); // scaled idx
|
||||
emitline("\tADDQ\tAX, BX\n");
|
||||
emitline("\tPUSHQ\tBX\n"); // spill dest
|
||||
// rhs source address → SI
|
||||
if (n.rhs.kind == nkind.N_UN
|
||||
&& n.rhs.op == tkind.TK_STAR) {
|
||||
cgexpr(c, n.rhs.lhs);
|
||||
emitline("\tMOVQ\tAX, SI\n");
|
||||
} else { if (n.rhs.kind == nkind.N_IDENT) {
|
||||
let sl: *local = localfindnode(c, n.rhs.str);
|
||||
if (sl != nil) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(sl.off: i64);
|
||||
emitline("(BP), SI\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, n.rhs.str);
|
||||
emitline("(SB), SI\n");
|
||||
};
|
||||
} else {
|
||||
dotchainaddr(c, n.rhs, "SI");
|
||||
};};
|
||||
emitline("\tPOPQ\tBX\n"); // dest
|
||||
let kc: i32 = 0;
|
||||
for (kc + 8 <= esz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(kc: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(kc: i64);
|
||||
emitline("(BX)\n");
|
||||
kc += 8;
|
||||
};
|
||||
if (kc + 4 <= esz) {
|
||||
emitline("\tMOVL\t");
|
||||
emitoff(kc: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff(kc: i64);
|
||||
emitline("(BX)\n");
|
||||
kc += 4;
|
||||
};
|
||||
if (kc + 2 <= esz) {
|
||||
emitline("\tMOVW\t");
|
||||
emitoff(kc: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVW\tAX, ");
|
||||
emitoff(kc: i64);
|
||||
emitline("(BX)\n");
|
||||
kc += 2;
|
||||
};
|
||||
if (kc + 1 <= esz) {
|
||||
emitline("\tMOVB\t");
|
||||
emitoff(kc: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff(kc: i64);
|
||||
emitline("(BX)\n");
|
||||
kc += 1;
|
||||
};
|
||||
return;
|
||||
};
|
||||
cgexpr(c, n.rhs); // value → AX
|
||||
// str/slice: spill cap (CX) + len (BX) before
|
||||
// computing the index so the post-index store can
|
||||
|
||||
@@ -1953,6 +1953,76 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tAX, SI\n");
|
||||
havesrc = true;
|
||||
};
|
||||
// #270-3a: the index BASE is an N_DOT
|
||||
// array-field (`x.arr[i]`) or a nested N_INDEX
|
||||
// (`a[i][j]`); the N_IDENT-base arm above missed
|
||||
// both, so the copy fell to the 8B truncation
|
||||
// below. Compute &base[idx]: scaled idx on the
|
||||
// stack, then &base via dotbaseaddr (N_DOT field
|
||||
// address) or the &abase[bidx] spine (nested
|
||||
// N_IDENT-array base), then add.
|
||||
if (!havesrc && base != nil
|
||||
&& (base.kind == nkind.N_DOT
|
||||
|| base.kind == nkind.N_INDEX)) {
|
||||
let esz2: i32 = 1;
|
||||
if (bu != nil && bu.sub != nil) {
|
||||
esz2 = bu.sub.size: i32;
|
||||
};
|
||||
cgexpr(c, idx);
|
||||
if (esz2 > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz2: i64);
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
let baseok: bool = false;
|
||||
if (base.kind == nkind.N_DOT) {
|
||||
if (dotbaseaddr(c, base, "AX")) {
|
||||
baseok = true;
|
||||
};
|
||||
} else {
|
||||
let ab: *node = base.lhs;
|
||||
let bidx: *node = base.rhs;
|
||||
let abu: *tinfo = nil;
|
||||
if (ab != nil) { abu = ab.type_: *tinfo; };
|
||||
for (abu != nil && abu.kind == tykind.TY_NAMED) {
|
||||
abu = abu.under;
|
||||
};
|
||||
if (ab != nil && ab.kind == nkind.N_IDENT
|
||||
&& abu != nil && abu.kind == tykind.TY_ARRAY) {
|
||||
let aesz: i32 = 1;
|
||||
if (abu.sub != nil) {
|
||||
aesz = abu.sub.size: i32;
|
||||
};
|
||||
cgexpr(c, bidx);
|
||||
if (aesz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(aesz: i64);
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
let abl: *local = localfindnode(c, ab.str);
|
||||
if (abl != nil) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(abl.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, ab.str);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
emitline("\tADDQ\tBX, AX\n");
|
||||
baseok = true;
|
||||
};
|
||||
};
|
||||
emitline("\tPOPQ\tBX\n");
|
||||
if (baseok) {
|
||||
emitline("\tADDQ\tBX, AX\n");
|
||||
emitline("\tMOVQ\tAX, SI\n");
|
||||
havesrc = true;
|
||||
};
|
||||
};
|
||||
}; };
|
||||
if (havesrc) {
|
||||
let k: i32 = 0;
|
||||
|
||||
@@ -21994,6 +21994,24 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
} else {
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
};
|
||||
// #270-1a: an `[N]T`-typed field of an
|
||||
// array element (`a[i].m[j]`) — leave the
|
||||
// field's ADDRESS, a base for the outer
|
||||
// index, NEVER deref. AX holds &a[i]; the
|
||||
// field address is &a[i]+foff. The #135
|
||||
// read-side for `d.m[i]`, applied to an
|
||||
// array-element base. Without this an array
|
||||
// field fell to fieldloadop below and loaded
|
||||
// its first 8 bytes as a value → garbage
|
||||
// base → SEGFAULT in the outer index.
|
||||
if (tinfoisarray(fi.tnode.type_: *tinfo)) {
|
||||
if (fi.foff != 0) {
|
||||
emitline("\tADDQ\t$");
|
||||
emitint(fi.foff: i64);
|
||||
emitline(", AX\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (isstrtype(c, fi.tnode) || isslicetype(c, fi.tnode)) {
|
||||
// str/slice: the 3-word {ptr,len,cap}
|
||||
// slice header (#1). AX holds the
|
||||
@@ -24620,6 +24638,120 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
c.sretdestoff = 0;
|
||||
return;
|
||||
};
|
||||
// #270-1b: aggregate (struct/array/tuple >8B) element
|
||||
// STORE `a[i] = val`. The scalar store path below copies
|
||||
// only the first 8 bytes (tnodestoreop MOVQ) — a silent
|
||||
// truncation. Compute &a[i] (dest) and the rhs SOURCE
|
||||
// address, then word-copy esz bytes: the WRITE-twin of
|
||||
// the #268 let-init copy loop. Source shapes mirror that
|
||||
// loop (ident, N_DOT field via dotchainaddr, `*p`
|
||||
// deref); a by-value call result is the deferred #271, so
|
||||
// N_CALL/literal sources fall through unchanged. esz>8
|
||||
// non-str/non-slice IS a struct/array/tuple here (the
|
||||
// tagged element already returned above; floats are ≤8).
|
||||
let aggsrc: bool = (n.rhs.kind == nkind.N_IDENT)
|
||||
|| (n.rhs.kind == nkind.N_DOT)
|
||||
|| (n.rhs.kind == nkind.N_UN
|
||||
&& n.rhs.op == tkind.TK_STAR);
|
||||
if (esz > 8 && !isstrtype(c, elemtn)
|
||||
&& !isslicetype(c, elemtn) && aggsrc) {
|
||||
cgexpr(c, idx); // idx → AX
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
emitline("\tPUSHQ\tAX\n"); // scaled idx
|
||||
if (isglobalarr) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, globalname);
|
||||
emitline("(SB), BX\n");
|
||||
} else { if (isglobalptr) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymname(c, globalname);
|
||||
emitline("(SB), BX\n");
|
||||
} else { if (baselocal != nil) {
|
||||
let tn2: *node = baselocal.tnode;
|
||||
let isarr2: bool = false;
|
||||
if (tn2 != nil) { if (tn2.kind == nkind.N_TARRAY) { isarr2 = true; }; };
|
||||
if (isarr2) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(baselocal.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(baselocal.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
} else { if (dotbaseaddr(c, base, "BX")) {
|
||||
// N_DOT array-field base resolved inline.
|
||||
} else {
|
||||
cgexpr(c, base);
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
};};};};
|
||||
emitline("\tPOPQ\tAX\n"); // scaled idx
|
||||
emitline("\tADDQ\tAX, BX\n");
|
||||
emitline("\tPUSHQ\tBX\n"); // spill dest
|
||||
// rhs source address → SI
|
||||
if (n.rhs.kind == nkind.N_UN
|
||||
&& n.rhs.op == tkind.TK_STAR) {
|
||||
cgexpr(c, n.rhs.lhs);
|
||||
emitline("\tMOVQ\tAX, SI\n");
|
||||
} else { if (n.rhs.kind == nkind.N_IDENT) {
|
||||
let sl: *local = localfindnode(c, n.rhs.str);
|
||||
if (sl != nil) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(sl.off: i64);
|
||||
emitline("(BP), SI\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, n.rhs.str);
|
||||
emitline("(SB), SI\n");
|
||||
};
|
||||
} else {
|
||||
dotchainaddr(c, n.rhs, "SI");
|
||||
};};
|
||||
emitline("\tPOPQ\tBX\n"); // dest
|
||||
let kc: i32 = 0;
|
||||
for (kc + 8 <= esz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(kc: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(kc: i64);
|
||||
emitline("(BX)\n");
|
||||
kc += 8;
|
||||
};
|
||||
if (kc + 4 <= esz) {
|
||||
emitline("\tMOVL\t");
|
||||
emitoff(kc: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff(kc: i64);
|
||||
emitline("(BX)\n");
|
||||
kc += 4;
|
||||
};
|
||||
if (kc + 2 <= esz) {
|
||||
emitline("\tMOVW\t");
|
||||
emitoff(kc: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVW\tAX, ");
|
||||
emitoff(kc: i64);
|
||||
emitline("(BX)\n");
|
||||
kc += 2;
|
||||
};
|
||||
if (kc + 1 <= esz) {
|
||||
emitline("\tMOVB\t");
|
||||
emitoff(kc: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff(kc: i64);
|
||||
emitline("(BX)\n");
|
||||
kc += 1;
|
||||
};
|
||||
return;
|
||||
};
|
||||
cgexpr(c, n.rhs); // value → AX
|
||||
// str/slice: spill cap (CX) + len (BX) before
|
||||
// computing the index so the post-index store can
|
||||
@@ -29186,6 +29318,76 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tAX, SI\n");
|
||||
havesrc = true;
|
||||
};
|
||||
// #270-3a: the index BASE is an N_DOT
|
||||
// array-field (`x.arr[i]`) or a nested N_INDEX
|
||||
// (`a[i][j]`); the N_IDENT-base arm above missed
|
||||
// both, so the copy fell to the 8B truncation
|
||||
// below. Compute &base[idx]: scaled idx on the
|
||||
// stack, then &base via dotbaseaddr (N_DOT field
|
||||
// address) or the &abase[bidx] spine (nested
|
||||
// N_IDENT-array base), then add.
|
||||
if (!havesrc && base != nil
|
||||
&& (base.kind == nkind.N_DOT
|
||||
|| base.kind == nkind.N_INDEX)) {
|
||||
let esz2: i32 = 1;
|
||||
if (bu != nil && bu.sub != nil) {
|
||||
esz2 = bu.sub.size: i32;
|
||||
};
|
||||
cgexpr(c, idx);
|
||||
if (esz2 > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz2: i64);
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
let baseok: bool = false;
|
||||
if (base.kind == nkind.N_DOT) {
|
||||
if (dotbaseaddr(c, base, "AX")) {
|
||||
baseok = true;
|
||||
};
|
||||
} else {
|
||||
let ab: *node = base.lhs;
|
||||
let bidx: *node = base.rhs;
|
||||
let abu: *tinfo = nil;
|
||||
if (ab != nil) { abu = ab.type_: *tinfo; };
|
||||
for (abu != nil && abu.kind == tykind.TY_NAMED) {
|
||||
abu = abu.under;
|
||||
};
|
||||
if (ab != nil && ab.kind == nkind.N_IDENT
|
||||
&& abu != nil && abu.kind == tykind.TY_ARRAY) {
|
||||
let aesz: i32 = 1;
|
||||
if (abu.sub != nil) {
|
||||
aesz = abu.sub.size: i32;
|
||||
};
|
||||
cgexpr(c, bidx);
|
||||
if (aesz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(aesz: i64);
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
let abl: *local = localfindnode(c, ab.str);
|
||||
if (abl != nil) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(abl.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, ab.str);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
emitline("\tADDQ\tBX, AX\n");
|
||||
baseok = true;
|
||||
};
|
||||
};
|
||||
emitline("\tPOPQ\tBX\n");
|
||||
if (baseok) {
|
||||
emitline("\tADDQ\tBX, AX\n");
|
||||
emitline("\tMOVQ\tAX, SI\n");
|
||||
havesrc = true;
|
||||
};
|
||||
};
|
||||
}; };
|
||||
if (havesrc) {
|
||||
let k: i32 = 0;
|
||||
|
||||
Reference in New Issue
Block a user