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:
2026-06-04 10:00:16 +09:00
parent 32063d0da0
commit cfc2985c61
6 changed files with 789 additions and 23 deletions

View File

@@ -6074,11 +6074,113 @@ cgexpr(Cg *c, Node *n, Local *locals)
if (fu && fu->kind == TY_TAGGED)
fatal("assign-resolver: tagged field not "
"wired (rule-7)");
/* C1.25 (#23): aggregate field STORE through the
* resolver — run_thread's 40B capture store
* `(*ts)[i].root_capture = capture{...}`. Dest address
* from cgplaceaddr (BX), source address in SI per rhs
* shape, then the #270-1b word-copy tail (SI)→(BX).
* Pre-C1 this was a SILENT no-op; C1 made it loud;
* this wires it (loud-first, wire-next). Compound on
* an aggregate is meaningless and stays loud. */
if (fu && (fu->kind == TY_STRUCT
|| fu->kind == TY_ARRAY
|| fu->kind == TY_TUPLE))
fatal("assign-resolver: aggregate field not "
"wired (rule-7)");
|| fu->kind == TY_TUPLE)) {
if (n->op != TK_ASSIGN)
fatal("assign-resolver: compound on "
"aggregate field not wired "
"(rule-7)");
if (n->rhs && n->rhs->kind == N_CALL) {
/* sret-class needs a runtime-RDI dest
* (the #234-tail deferral); the ≤24B
* reg-return receive is task #24. */
if (cg_sret_retsize(ft) > 0)
fatal("assign-resolver: sret "
"call into aggregate field "
"unwired (#234-tail/"
"rule-7)");
fatal("assign-resolver: call result "
"into aggregate field unwired "
"(task #24/rule-7)");
}
int placed = 0;
if (n->rhs && n->rhs->kind == N_STRUCTLIT
&& fu->kind == TY_STRUCT) {
/* @placescr — FRESH slot PER USE (the
* @slicescr discipline, NOT the cached
* @tagscr table: a cached slot is the
* #31 multi-live corruption trap; rob
* ruling). Funnel contract, #44
* discipline: this arm is the ONLY
* @placescr alloc site. Fill handles
* nested literals (#18), tagged
* fields, TK_ELLIPSIS autofill; the
* value sits in memory, so the
* resolver below may clobber AX/CX
* freely. */
int scr = local_alloc(c, &locals,
"@placescr", fsz, cg_frame);
cg_structlit_fill_bp(c, &locals, fu,
n->rhs, scr);
placed = cgplaceaddr(c, n->lhs, D_BX,
locals);
if (placed)
ins2(c, A_LEAQ,
amem(D_BP, scr),
areg(D_SI));
} else {
/* Addressable source — ident / global
* / N_DOT chain / deref — via the
* closed #265/#268 dispatch. Its
* N_INDEX arm clobbers BX, so the dest
* spills around it (the #270-1b
* order). Literal arrays/tuples have
* no storage address and stay loud. */
placed = cgplaceaddr(c, n->lhs, D_BX,
locals);
if (placed) {
ins1(c, A_PUSHQ, areg(D_BX));
if (!aggarg_srcaddr(c, n->rhs,
D_SI, locals))
fatal("assign-resolver"
": aggregate rhs "
"shape unwired "
"(rule-7)");
ins1(c, A_POPQ, areg(D_BX));
}
}
if (!placed)
fatal("unsupported assign target "
"shape");
int k = 0;
for (; k + 8 <= fsz; k += 8) {
ins2(c, A_MOVQ, amem(D_SI, k),
areg(D_AX));
ins2(c, A_MOVQ, areg(D_AX),
amem(D_BX, k));
}
if (k + 4 <= fsz) {
ins2(c, A_MOVL, amem(D_SI, k),
areg(D_AX));
ins2(c, A_MOVL, areg(D_AX),
amem(D_BX, k));
k += 4;
}
if (k + 2 <= fsz) {
ins2(c, A_MOVW, amem(D_SI, k),
areg(D_AX));
ins2(c, A_MOVW, areg(D_AX),
amem(D_BX, k));
k += 2;
}
if (k + 1 <= fsz) {
ins2(c, A_MOVB, amem(D_SI, k),
areg(D_AX));
ins2(c, A_MOVB, areg(D_AX),
amem(D_BX, k));
k += 1;
}
break;
}
if (fu && (fu->kind == TY_STR
|| fu->kind == TY_SLICE)) {
if (n->op != TK_ASSIGN)