wcc+w6c_ww: aggregate-field stores via cgplaceaddr (C1.25)
Wire struct/array/tuple field STORE through the C1 assign-resolver (task #23): structlit rhs materialises into a FRESH-per-use @placescr slot (the @slicescr discipline — a cached slot is the #31 multi-live trap) then word-copies to the resolved address; addressable rhs (ident/global/dot/deref) sources via aggarg_srcaddr with the dest spilled around the dispatch (#270-1b order). Kept loud: compound on aggregate, sret call rhs (#234-tail), <=24B call rhs (task #24), unaddressable literal rhs, ww-only anonymous-struct structinfo miss. test/wcc/805: +6 rows (40B structlit incl ... autofill, ident+deref source, nested literal, [3]u8 MOVW/MOVB and [3]u32 MOVL tails, two same-size stores in one fn pinning fresh-per-use) +3 exact-text reject rows. Tuple-field row blocked by the pre-existing tuple param/let-init word-2 drops (tasks #32/#33, documented in-row). p7b_capstore_only graduates byte-id and runs; p7_composed builds and runs on cstage, wwstage stays behind the pre-existing #29 asserttyped bail (verified identical at master with w6c_ww).
This commit is contained in:
@@ -14,10 +14,15 @@
|
||||
* selfhost/cmd/wcc/cgenexpr.ww); each call-site keeps its own
|
||||
* load/store emission, and every N_DOT lvalue the resolver can't
|
||||
* address now dies LOUD ("unsupported assign target shape") instead of
|
||||
* silently dropping (rule 7). Field kinds the resolver arm does not
|
||||
* wire yet (float / tagged / aggregate / str-slice compound) hard-stop
|
||||
* with their own diagnostics — the reject rows pin the exact text on
|
||||
* BOTH stages. Non-DOT lvalue tail residue is task #22.
|
||||
* silently dropping (rule 7). C1.25 (#23) wires the aggregate field
|
||||
* STORE on top of the resolver: literal rhs materialises into a FRESH
|
||||
* per-use @placescr slot then word-copies to the resolved address;
|
||||
* addressable rhs (ident/global/dot/deref) takes its source address
|
||||
* straight from the #265/#268 dispatch. Field kinds the resolver arm
|
||||
* does not wire yet (float / tagged / aggregate-compound / str-slice
|
||||
* compound / call-rhs into aggregate) hard-stop with their own
|
||||
* diagnostics — the reject rows pin the exact text on BOTH stages.
|
||||
* Non-DOT lvalue tail residue is task #22.
|
||||
*
|
||||
* row | shape | want
|
||||
* --------------------+----------------------------------------+------
|
||||
@@ -36,7 +41,17 @@
|
||||
* neutral_ident_bases | x.f / a[i].f / p.f = and += (untouched | 51
|
||||
* | enumerated arms — runtime-pins the |
|
||||
* | resolver's asm-neutrality claim) |
|
||||
* reject_aggregate | struct-typed field store | BUILD_FAIL
|
||||
* agg_structlit_40b | C1.25: 40B capture literal store via | 52
|
||||
* | fresh @placescr + word-copy, incl ... |
|
||||
* agg_from_ident_deref| C1.25: aggregate from ident + *p rhs | 53
|
||||
* agg_nested_lit | C1.25: nested struct literal (#18) | 54
|
||||
* agg_array_field_odd | C1.25: [3]u8 field, MOVW/MOVB tails | 55
|
||||
* agg_fresh_two_stores| C1.25: two same-size structlit stores | 56
|
||||
* | in ONE fn — distinct @placescr slots |
|
||||
* agg_array_field_movl| C1.25: [3]u32 field, MOVQ+MOVL tail | 57
|
||||
* reject_agg_compound | compound on aggregate field | BUILD_FAIL
|
||||
* reject_agg_call_sret| >24B call rhs (#234-tail) | BUILD_FAIL
|
||||
* reject_agg_call_reg | ≤24B call rhs (task #24) | BUILD_FAIL
|
||||
* reject_float | f64 field store | BUILD_FAIL
|
||||
* reject_tagged | tagged-union field store | BUILD_FAIL
|
||||
* reject_str_compound | (*ts)[i].name += — str/slice compound | BUILD_FAIL
|
||||
@@ -363,19 +378,245 @@ static const struct row rows[] = {
|
||||
/* The fold-2b 40B capture store (p7b/p7_composed) — wired in a
|
||||
* follow-up resolver commit; until then it must die LOUD, never
|
||||
* the pre-C1 silent drop. */
|
||||
{ "reject_aggregate",
|
||||
/* C1.25 (#23): the run_thread 40B capture store, graduated from
|
||||
* BUILD_FAIL (C1's loud boundary) to wired. Literal rhs goes
|
||||
* through a FRESH per-use @placescr materialise + word-copy; the
|
||||
* `...` setter pins the autofill zero loop; neighbour element +
|
||||
* sibling field readbacks pin the stride and copy length. */
|
||||
/* Readback is RAW bytes over ts.ptr (the 804 tagged_56b
|
||||
* precedent): a depth-2 read behind an index (ts[1].cap.start)
|
||||
* is the F4 walker gap (task #6) and link-fails today on the
|
||||
* cgen.c:9038 global-leaf fallback — typed readbacks graduate
|
||||
* with the C2 read-walker. Layout: t={pc@0,cap@8}, size 48;
|
||||
* capture={content(str ptr@0,len@8,cap@16),start@24,end@32}.
|
||||
* Elem1 base 48 → content.len byte 64, start 80, end 88;
|
||||
* elem0 → 16/32/40. Stored values fit one byte. */
|
||||
{ "agg_structlit_40b",
|
||||
"package main;\n"
|
||||
"type capture = struct { content: str, start: size, end: size };\n"
|
||||
"type t = struct { pc: size, cap: capture };\n"
|
||||
"fn setcap(ts: *[]t, i: size) void = {\n"
|
||||
"\t(*ts)[i].cap = capture { content = \"x\", start = 1, end = 2 };\n"
|
||||
"\t(*ts)[i].cap = capture { content = \"hit\", start = 2, end = 4 };\n"
|
||||
"};\n"
|
||||
"fn clearcap(ts: *[]t, i: size) void = {\n"
|
||||
"\t(*ts)[i].cap = capture { content = \"z\", ... };\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet z: capture = capture { content = \"\", start = 0, end = 0 };\n"
|
||||
"\tlet ts: []t = [];\n"
|
||||
"\tappend(ts, t { pc = 7, cap = z });\n"
|
||||
"\tappend(ts, t { pc = 8, cap = z });\n"
|
||||
"\tsetcap(&ts, 1);\n"
|
||||
"\tlet bp: *u8 = ts.ptr: *u8;\n"
|
||||
"\tif (bp[64] != 3u8) { return 1; };\n"
|
||||
"\tif (bp[80] != 2u8) { return 2; };\n"
|
||||
"\tif (bp[88] != 4u8) { return 3; };\n"
|
||||
"\tif (ts[1].pc != 8) { return 4; };\n"
|
||||
"\tif (bp[16] != 0u8) { return 5; };\n"
|
||||
"\tif (ts[0].pc != 7) { return 6; };\n"
|
||||
"\tclearcap(&ts, 1);\n"
|
||||
"\tif (bp[64] != 1u8) { return 7; };\n"
|
||||
"\tif (bp[80] != 0u8) { return 8; };\n"
|
||||
"\tif (bp[88] != 0u8) { return 9; };\n"
|
||||
"\treturn 52;\n"
|
||||
"};\n",
|
||||
52, NULL },
|
||||
|
||||
/* Addressable aggregate sources via the #265/#268 dispatch:
|
||||
* local ident (LEAQ slot) and deref (*pc). Raw-byte readback per
|
||||
* the agg_structlit_40b layout note (F4 blocks typed depth-2). */
|
||||
{ "agg_from_ident_deref",
|
||||
"package main;\n"
|
||||
"type capture = struct { content: str, start: size, end: size };\n"
|
||||
"type t = struct { pc: size, cap: capture };\n"
|
||||
"fn copycap(ts: *[]t, i: size, src: capture) void = {\n"
|
||||
"\t(*ts)[i].cap = src;\n"
|
||||
"};\n"
|
||||
"fn derefcap(ts: *[]t, i: size, pc: *capture) void = {\n"
|
||||
"\t(*ts)[i].cap = *pc;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet z: capture = capture { content = \"\", start = 0, end = 0 };\n"
|
||||
"\tlet ts: []t = [];\n"
|
||||
"\tappend(ts, t { pc = 7, cap = z });\n"
|
||||
"\tappend(ts, t { pc = 8, cap = z });\n"
|
||||
"\tlet a: capture = capture { content = \"ab\", start = 1, end = 3 };\n"
|
||||
"\tcopycap(&ts, 1, a);\n"
|
||||
"\tlet bp: *u8 = ts.ptr: *u8;\n"
|
||||
"\tif (bp[64] != 2u8) { return 1; };\n"
|
||||
"\tif (bp[80] != 1u8) { return 2; };\n"
|
||||
"\tif (bp[88] != 3u8) { return 3; };\n"
|
||||
"\tlet b: capture = capture { content = \"wxyz\", start = 5, end = 9 };\n"
|
||||
"\tderefcap(&ts, 0, &b);\n"
|
||||
"\tif (bp[16] != 4u8) { return 4; };\n"
|
||||
"\tif (bp[32] != 5u8) { return 5; };\n"
|
||||
"\tif (bp[40] != 9u8) { return 6; };\n"
|
||||
"\tif (bp[80] != 1u8) { return 7; };\n"
|
||||
"\treturn 53;\n"
|
||||
"};\n",
|
||||
53, NULL },
|
||||
|
||||
/* Nested struct-typed literal field inside the stored literal —
|
||||
* the #18 recursion through cg_structlit_fill must land the inner
|
||||
* bytes in the @placescr image before the copy. */
|
||||
{ "agg_nested_lit",
|
||||
"package main;\n"
|
||||
"type inner = struct { x: i64, y: i64 };\n"
|
||||
"type big = struct { a: i64, ib: inner };\n"
|
||||
"type t = struct { pc: size, bg: big };\n"
|
||||
"fn setbg(ts: *[]t, i: size) void = {\n"
|
||||
"\t(*ts)[i].bg = big { a = 1, ib = inner { x = 2, y = 3 } };\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet z: big = big { a = 0, ib = inner { x = 0, y = 0 } };\n"
|
||||
"\tlet ts: []t = [];\n"
|
||||
"\tappend(ts, t { pc = 7, bg = z });\n"
|
||||
"\tsetbg(&ts, 0);\n"
|
||||
"\tlet bp: *u8 = ts.ptr: *u8;\n"
|
||||
"\tif (bp[8] != 1u8) { return 1; };\n"
|
||||
"\tif (bp[16] != 2u8) { return 2; };\n"
|
||||
"\tif (bp[24] != 3u8) { return 3; };\n"
|
||||
"\tif (ts[0].pc != 7) { return 4; };\n"
|
||||
"\treturn 54;\n"
|
||||
"};\n",
|
||||
54, NULL },
|
||||
|
||||
/* [3]u8 array field from an ident source — TY_ARRAY aggregate arm
|
||||
* + the MOVW/MOVB copy tails (fsz=3). */
|
||||
{ "agg_array_field_odd",
|
||||
"package main;\n"
|
||||
"type t = struct { pc: size, arr: [3]u8 };\n"
|
||||
"fn setarr(ts: *[]t, i: size, src: [3]u8) void = {\n"
|
||||
"\t(*ts)[i].arr = src;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet a3: [3]u8 = [9u8, 8u8, 7u8];\n"
|
||||
"\tlet z3: [3]u8 = [0u8, 0u8, 0u8];\n"
|
||||
"\tlet ts: []t = [];\n"
|
||||
"\tappend(ts, t { pc = 5, arr = z3 });\n"
|
||||
"\tsetarr(&ts, 0, a3);\n"
|
||||
"\tif (ts[0].arr[0] != 9u8) { return 1; };\n"
|
||||
"\tif (ts[0].arr[1] != 8u8) { return 2; };\n"
|
||||
"\tif (ts[0].arr[2] != 7u8) { return 3; };\n"
|
||||
"\tif (ts[0].pc != 5) { return 4; };\n"
|
||||
"\treturn 55;\n"
|
||||
"};\n",
|
||||
55, NULL },
|
||||
|
||||
/* Two same-size structlit stores in ONE fn — each must get its
|
||||
* own FRESH @placescr slot and both must land (the #31 multi-
|
||||
* live discipline pin). The stronger shape — a structlit whose
|
||||
* FIELD expr recurses into a same-size aggregate assign, the
|
||||
* fresh-per-use rationale proper — is not constructible today:
|
||||
* match-expr (the only stmt-carrying expr) is a parse reject in
|
||||
* field position; fresh-per-use stays defensive (rob ruling). */
|
||||
{ "agg_fresh_two_stores",
|
||||
"package main;\n"
|
||||
"type capture = struct { content: str, start: size, end: size };\n"
|
||||
"type t = struct { pc: size, cap: capture };\n"
|
||||
"fn settwo(ts: *[]t) void = {\n"
|
||||
"\t(*ts)[0].cap = capture { content = \"aa\", start = 1, end = 2 };\n"
|
||||
"\t(*ts)[1].cap = capture { content = \"bbb\", start = 3, end = 4 };\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet z: capture = capture { content = \"\", start = 0, end = 0 };\n"
|
||||
"\tlet ts: []t = [];\n"
|
||||
"\tappend(ts, t { pc = 7, cap = z });\n"
|
||||
"\tappend(ts, t { pc = 8, cap = z });\n"
|
||||
"\tsettwo(&ts);\n"
|
||||
"\tlet bp: *u8 = ts.ptr: *u8;\n"
|
||||
"\tif (bp[16] != 2u8) { return 1; };\n"
|
||||
"\tif (bp[32] != 1u8) { return 2; };\n"
|
||||
"\tif (bp[40] != 2u8) { return 3; };\n"
|
||||
"\tif (bp[64] != 3u8) { return 4; };\n"
|
||||
"\tif (bp[80] != 3u8) { return 5; };\n"
|
||||
"\tif (bp[88] != 4u8) { return 6; };\n"
|
||||
"\treturn 56;\n"
|
||||
"};\n",
|
||||
56, NULL },
|
||||
|
||||
/* [3]u32 field (fsz=12) — the MOVL copy tail, untouched by the
|
||||
* 40B (all-MOVQ) and [3]u8 (MOVW+MOVB) rows.
|
||||
*
|
||||
* Matrix honesty, TY_TUPLE: the arm wires tuple fields and a
|
||||
* local-ident tuple source stores correctly (cstage-verified),
|
||||
* but a dual-stage row is blocked by three PRE-EXISTING tuple
|
||||
* gaps outside this arm: by-value tuple param drops word 2 at
|
||||
* runtime (both stages, master too), wwstage tuple-let init
|
||||
* `let a: (i64,i64) = (3,4)` drops the word-2 store (cs≠ww),
|
||||
* and `a.0 =` element assign is unwired (loud). Tuple-field row
|
||||
* graduates with that family (filed). */
|
||||
{ "agg_array_field_movl",
|
||||
"package main;\n"
|
||||
"type t = struct { pc: size, arr: [3]u32 };\n"
|
||||
"fn setarr(ts: *[]t, i: size, src: [3]u32) void = {\n"
|
||||
"\t(*ts)[i].arr = src;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet a3: [3]u32 = [9u32, 8u32, 7u32];\n"
|
||||
"\tlet z3: [3]u32 = [0u32, 0u32, 0u32];\n"
|
||||
"\tlet ts: []t = [];\n"
|
||||
"\tappend(ts, t { pc = 5, arr = z3 });\n"
|
||||
"\tsetarr(&ts, 0, a3);\n"
|
||||
"\tif (ts[0].arr[0] != 9u32) { return 1; };\n"
|
||||
"\tif (ts[0].arr[1] != 8u32) { return 2; };\n"
|
||||
"\tif (ts[0].arr[2] != 7u32) { return 3; };\n"
|
||||
"\tif (ts[0].pc != 5) { return 4; };\n"
|
||||
"\treturn 57;\n"
|
||||
"};\n",
|
||||
57, NULL },
|
||||
|
||||
/* Compound on an aggregate field is meaningless — stays loud. */
|
||||
{ "reject_agg_compound",
|
||||
"package main;\n"
|
||||
"type capture = struct { content: str, start: size, end: size };\n"
|
||||
"type t = struct { pc: size, cap: capture };\n"
|
||||
"fn addcap(ts: *[]t, i: size, src: capture) void = {\n"
|
||||
"\t(*ts)[i].cap += src;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet ts: []t = [];\n"
|
||||
"\tlet a: capture = capture { content = \"\", start = 0, end = 0 };\n"
|
||||
"\taddcap(&ts, 0, a);\n"
|
||||
"\treturn 0;\n"
|
||||
"};\n",
|
||||
BUILD_FAIL,
|
||||
"assign-resolver: compound on aggregate field not wired (rule-7)" },
|
||||
|
||||
/* sret-class call rhs (>24B return) needs a runtime-RDI dest —
|
||||
* the #234-tail deferral. */
|
||||
{ "reject_agg_call_sret",
|
||||
"package main;\n"
|
||||
"type capture = struct { content: str, start: size, end: size };\n"
|
||||
"type t = struct { pc: size, cap: capture };\n"
|
||||
"fn mk() capture = {\n"
|
||||
"\treturn capture { content = \"x\", start = 1, end = 2 };\n"
|
||||
"};\n"
|
||||
"fn setcap(ts: *[]t, i: size) void = { (*ts)[i].cap = mk(); };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet ts: []t = [];\n"
|
||||
"\tsetcap(&ts, 0);\n"
|
||||
"\treturn 0;\n"
|
||||
"};\n",
|
||||
BUILD_FAIL, "assign-resolver: aggregate field not wired (rule-7)" },
|
||||
BUILD_FAIL,
|
||||
"assign-resolver: sret call into aggregate field unwired "
|
||||
"(#234-tail/rule-7)" },
|
||||
|
||||
/* ≤24B reg-return call rhs — deferred, task #24. */
|
||||
{ "reject_agg_call_reg",
|
||||
"package main;\n"
|
||||
"type pair = struct { a: i64, b: i64 };\n"
|
||||
"type t = struct { pc: size, pr: pair };\n"
|
||||
"fn mk() pair = { return pair { a = 1, b = 2 }; };\n"
|
||||
"fn setpr(ts: *[]t, i: size) void = { (*ts)[i].pr = mk(); };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet ts: []t = [];\n"
|
||||
"\tsetpr(&ts, 0);\n"
|
||||
"\treturn 0;\n"
|
||||
"};\n",
|
||||
BUILD_FAIL,
|
||||
"assign-resolver: call result into aggregate field unwired "
|
||||
"(task #24/rule-7)" },
|
||||
|
||||
{ "reject_float",
|
||||
"package main;\n"
|
||||
|
||||
Reference in New Issue
Block a user