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:
2026-06-02 12:49:25 +09:00
parent 33bd2b1054
commit 6f18f42a4a
6 changed files with 897 additions and 0 deletions

View File

@@ -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;