cgen: accept an indexed source in aggregate element assignment
The a[i] = src copy loop enumerated ident/field/deref sources; an N_INDEX rhs fell to the scalar tail and truncated the element. Route it through the generic place-address funnel. Both stages.
This commit is contained in:
@@ -7295,8 +7295,9 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
* address, then word-copy esz bytes: the WRITE-twin of the
|
* address, then word-copy esz bytes: the WRITE-twin of the
|
||||||
* #268 let-init copy loop. Source shapes mirror that loop
|
* #268 let-init copy loop. Source shapes mirror that loop
|
||||||
* (ident local/global, N_DOT field via cg_dotchain_addr,
|
* (ident local/global, N_DOT field via cg_dotchain_addr,
|
||||||
* `*p` deref); struct-lit sources divert at the place_slit
|
* N_INDEX via cgplaceaddr, `*p` deref); struct-lit sources
|
||||||
* gate above (#20), array-lit dies loud (task #32), and a
|
* divert at the place_slit gate above (#20), array-lit dies
|
||||||
|
* loud (task #32), and a
|
||||||
* by-value call result still falls to the scalar tail —
|
* by-value call result still falls to the scalar tail —
|
||||||
* RAX-only store, task #31-G. */
|
* RAX-only store, task #31-G. */
|
||||||
if ((is_arr || is_sl || is_ptr) && n->op == TK_ASSIGN
|
if ((is_arr || is_sl || is_ptr) && n->op == TK_ASSIGN
|
||||||
@@ -7306,6 +7307,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
&& esz > 8
|
&& esz > 8
|
||||||
&& ((n->rhs->kind == N_IDENT)
|
&& ((n->rhs->kind == N_IDENT)
|
||||||
|| (n->rhs->kind == N_DOT)
|
|| (n->rhs->kind == N_DOT)
|
||||||
|
|| (n->rhs->kind == N_INDEX)
|
||||||
|| (n->rhs->kind == N_UN
|
|| (n->rhs->kind == N_UN
|
||||||
&& n->rhs->op == TK_STAR))) {
|
&& n->rhs->op == TK_STAR))) {
|
||||||
/* dest &a[i] → BX */
|
/* dest &a[i] → BX */
|
||||||
@@ -7358,8 +7360,11 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
ins2(c, A_LEAQ,
|
ins2(c, A_LEAQ,
|
||||||
masym(c, n->rhs->str),
|
masym(c, n->rhs->str),
|
||||||
areg(D_SI));
|
areg(D_SI));
|
||||||
} else {
|
} else if (n->rhs->kind == N_DOT) {
|
||||||
cg_dotchain_addr(c, n->rhs, D_SI, locals);
|
cg_dotchain_addr(c, n->rhs, D_SI, locals);
|
||||||
|
} else if (!cgplaceaddr(c, n->rhs, D_SI, locals)) {
|
||||||
|
fatal("indexed aggregate assignment source "
|
||||||
|
"unresolved");
|
||||||
}
|
}
|
||||||
ins1(c, A_POPQ, areg(D_BX)); /* dest */
|
ins1(c, A_POPQ, areg(D_BX)); /* dest */
|
||||||
int k = 0;
|
int k = 0;
|
||||||
|
|||||||
@@ -9591,15 +9591,17 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
|||||||
// truncation. Compute &a[i] (dest) and the rhs SOURCE
|
// truncation. Compute &a[i] (dest) and the rhs SOURCE
|
||||||
// address, then word-copy esz bytes: the WRITE-twin of
|
// address, then word-copy esz bytes: the WRITE-twin of
|
||||||
// the #268 let-init copy loop. Source shapes mirror that
|
// the #268 let-init copy loop. Source shapes mirror that
|
||||||
// loop (ident, N_DOT field via dotchainaddr, `*p`
|
// loop (ident, N_DOT field via dotchainaddr, N_INDEX via
|
||||||
// deref); struct-lit sources divert at the placeslit
|
// cgplaceaddr, `*p` deref); struct-lit sources divert at
|
||||||
// gate above (#20), array-lit dies loud (task #32), and
|
// the placeslit gate above (#20), array-lit dies loud
|
||||||
|
// (task #32), and
|
||||||
// a by-value call result still falls to the scalar tail
|
// a by-value call result still falls to the scalar tail
|
||||||
// — RAX-only store, task #31-G. esz>8
|
// — RAX-only store, task #31-G. esz>8
|
||||||
// non-str/non-slice IS a struct/array/tuple here (the
|
// non-str/non-slice IS a struct/array/tuple here (the
|
||||||
// tagged element already returned above; floats are ≤8).
|
// tagged element already returned above; floats are ≤8).
|
||||||
let aggsrc: bool = (n.rhs.kind == syntax.nkind.N_IDENT)
|
let aggsrc: bool = (n.rhs.kind == syntax.nkind.N_IDENT)
|
||||||
|| (n.rhs.kind == syntax.nkind.N_DOT)
|
|| (n.rhs.kind == syntax.nkind.N_DOT)
|
||||||
|
|| (n.rhs.kind == syntax.nkind.N_INDEX)
|
||||||
|| (n.rhs.kind == syntax.nkind.N_UN
|
|| (n.rhs.kind == syntax.nkind.N_UN
|
||||||
&& n.rhs.op == syntax.tkind.TK_STAR);
|
&& n.rhs.op == syntax.tkind.TK_STAR);
|
||||||
if (esz > 8 && !isstrtype(c, elemtn)
|
if (esz > 8 && !isstrtype(c, elemtn)
|
||||||
@@ -9663,9 +9665,15 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
|||||||
emitsymname(c, n.rhs.str);
|
emitsymname(c, n.rhs.str);
|
||||||
emitline("(SB), SI\n");
|
emitline("(SB), SI\n");
|
||||||
};
|
};
|
||||||
} else {
|
} else { if (n.rhs.kind == syntax.nkind.N_DOT) {
|
||||||
dotchainaddr(c, n.rhs, "SI");
|
dotchainaddr(c, n.rhs, "SI");
|
||||||
};};
|
} else {
|
||||||
|
if (!cgplaceaddr(c, n.rhs, "SI")) {
|
||||||
|
let msrc: str = "indexed aggregate assignment source unresolved\n";
|
||||||
|
os.write(2, msrc.ptr, msrc.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
};};};
|
||||||
emitline("\tPOPQ\tBX\n"); // dest
|
emitline("\tPOPQ\tBX\n"); // dest
|
||||||
let kc: i32 = 0;
|
let kc: i32 = 0;
|
||||||
for (kc + 8 <= esz) {
|
for (kc + 8 <= esz) {
|
||||||
|
|||||||
26
test/lang/aggregate_slice_swap_test.ww
Normal file
26
test/lang/aggregate_slice_swap_test.ww
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// Indexed aggregate assignment must copy from the source element's storage.
|
||||||
|
// Sorting uses this three-step swap on slices, so preserving only one word can
|
||||||
|
// silently duplicate records while both compiler stages still agree.
|
||||||
|
|
||||||
|
package aggregate_slice_swap_test;
|
||||||
|
|
||||||
|
type row = struct {
|
||||||
|
key: i64,
|
||||||
|
value: i64,
|
||||||
|
guard: i64,
|
||||||
|
};
|
||||||
|
|
||||||
|
@test fn distinct_after_swap() void = {
|
||||||
|
let rows: []row = [];
|
||||||
|
append(rows, row { key = 1, value = 11, guard = 111 });
|
||||||
|
append(rows, row { key = 2, value = 22, guard = 222 });
|
||||||
|
let t: row = rows[1];
|
||||||
|
rows[1] = rows[0];
|
||||||
|
rows[0] = t;
|
||||||
|
assert(rows[0].key == 2);
|
||||||
|
assert(rows[0].value == 22);
|
||||||
|
assert(rows[0].guard == 222);
|
||||||
|
assert(rows[1].key == 1);
|
||||||
|
assert(rows[1].value == 11);
|
||||||
|
assert(rows[1].guard == 111);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user