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

@@ -1057,6 +1057,107 @@ static const struct row rows[] = {
" a[1][1] = 88;\n"
" return (a[1][1] - a[0][1]): i32;\n"
"};\n", 48, 1 },
/* #270-1a: `a[i].m[j] = v` — array-of-struct element field, then
* index INTO that field. The `arr[i].field` read/write 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 → SEGFAULT on the
* outer store. Fix: an array-typed field of an array element leaves
* the field ADDRESS (the #135 read-side, applied to the array-element
* base). Multiple cells written then read back; byteid=1. */
{ "elemfield_store",
"package main;\n"
"type inner = struct { m: [4]u32 };\n"
"export fn main() i32 = {\n"
" let a: [3]inner;\n"
" a[0].m[1] = 5u32;\n"
" a[2].m[3] = 7u32;\n"
" a[2].m[0] = 9u32;\n"
" return (a[0].m[1] + a[2].m[3] + a[2].m[0]): i32;\n"
"};\n", 21, 1 },
/* #270-1b: `a[i] = aggregateval` — whole-element STORE. The scalar
* store path copied only the first 8 bytes (fldstoreop MOVQ). Fix:
* an aggregate (struct/array >8B) element store word-copies the
* element from the rhs source address (WRITE-twin of the #268
* let-init loop). Struct element + array element, full readback;
* byteid=1. */
/* 16B struct (slot == natural) keeps byteid=1: a struct whose
* natural size is NOT an 8-multiple trips the orthogonal
* elemsizeofc slot-vs-natural array-stride divergence (cstage strides
* by sub->size, wwstage by slotsize) — see letcopy_dot_struct. */
{ "elem_struct_store",
"package main;\n"
"type inner = struct { a: u32, b: u32, c: u32, d: u32 };\n"
"export fn main() i32 = {\n"
" let arr: [3]inner;\n"
" let v: inner; v.a=10u32; v.b=20u32; v.c=30u32; v.d=40u32;\n"
" arr[2] = v;\n"
" return (arr[2].a + arr[2].b + arr[2].c + arr[2].d): i32;\n"
"};\n", 100, 1 },
{ "elem_arr_store",
"package main;\n"
"export fn main() i32 = {\n"
" let arr: [2][4]u32;\n"
" let s: [4]u32; s[0]=1u32; s[1]=2u32; s[2]=3u32; s[3]=4u32;\n"
" arr[1] = s;\n"
" return (arr[1][0]+arr[1][1]+arr[1][2]+arr[1][3]): i32;\n"
"};\n", 10, 1 },
/* #270-3a: aggregate let-init COPY whose index base is an N_DOT
* array-field (`x.arr[i]`) or a nested N_INDEX (`a[i][j]`) — the
* let-init N_INDEX source-addr arm was N_IDENT-base-gated (#268
* residual), so both fell to the 8B truncation. Fix computes
* &base[idx] via cg_dotbase_addr (N_DOT) / the &abase[bidx] spine
* (nested). Primitive-element rows are byteid=1; the value-struct
* rows below run-correct but trip the orthogonal value-nested-struct
* frame divergence (#254), so byteid=0 (same carve-out as chain_val_*
* above). */
{ "letcopy_dot_prim",
"package main;\n"
"type box = struct { arr: [2][4]u32 };\n"
"export fn main() i32 = {\n"
" let s: [4]u32; s[0]=1u32; s[1]=2u32; s[2]=3u32; s[3]=4u32;\n"
" let x: box;\n"
" x.arr[1] = s;\n"
" let c: [4]u32 = x.arr[1];\n"
" return (c[0]+c[1]+c[2]+c[3]): i32;\n"
"};\n", 10, 1 },
{ "letcopy_nest_prim",
"package main;\n"
"export fn main() i32 = {\n"
" let a: [2][2][4]u32;\n"
" let s: [4]u32; s[0]=2u32; s[1]=4u32; s[2]=6u32; s[3]=8u32;\n"
" a[1][0] = s;\n"
" let c: [4]u32 = a[1][0];\n"
" return (c[0]+c[1]+c[2]+c[3]): i32;\n"
"};\n", 20, 1 },
{ "letcopy_subarr",
"package main;\n"
"export fn main() i32 = {\n"
" let a: [2][3]u32;\n"
" a[1][0] = 5u32; a[1][1] = 6u32; a[1][2] = 7u32;\n"
" let c: [3]u32 = a[1];\n"
" return (c[0] + c[1] + c[2]): i32;\n"
"};\n", 18, 1 },
{ "letcopy_dot_struct",
"package main;\n"
"type inner = struct { a: u32, b: u32, c: u32 };\n"
"type box = struct { arr: [3]inner };\n"
"export fn main() i32 = {\n"
" let x: box;\n"
" let v: inner; v.a = 10u32; v.b = 20u32; v.c = 30u32;\n"
" x.arr[1] = v;\n"
" let c: inner = x.arr[1];\n"
" return (c.a + c.b + c.c): i32;\n"
"};\n", 60, 0 },
{ "letcopy_nest_struct",
"package main;\n"
"type inner = struct { a: u32, b: u32, c: u32 };\n"
"export fn main() i32 = {\n"
" let a: [2][2]inner;\n"
" let v: inner; v.a = 11u32; v.b = 22u32; v.c = 33u32;\n"
" a[1][0] = v;\n"
" let c: inner = a[1][0];\n"
" return (c.a + c.b + c.c): i32;\n"
"};\n", 66, 0 },
{ NULL, NULL, 0, 0 }
};