w6c+wwstage: aggregate let-init copy for ident-array/N_DOT/N_INDEX rhs (#268 fold-1b) — close addressable-rhs copy family

#265 fold-1 landed the deref-rhs aggregate copy as one slot→slot memcpy
loop fed from a source address in SI. fold-1b adds the remaining
addressable-rhs source-address setups, all routed into that SAME loop:

  - array IDENT `let c: [N]T = s`  — LEAQ the source slot into SI.
    Pre-fix both stages truncated to the 8B scalar tail.
  - N_DOT field `let c: A = o.i`   — cg_dotchain_addr / dotchainaddr
    (#253) lands &(o.i) in SI. Pre-fix truncated to 8B.
  - N_INDEX element `let c: A = a[i]` — the &base[i] spine (#252:
    scaled index + LEAQ base) lands the element address in SI. Pre-fix
    scalar-loaded the element address as a value → segfault.

Size (the #254 non-slot-padded ABI extent) comes from the declared let
type for every shape (lu->size / structabisize|tinfo.size), independent
of the rhs; only the per-rhs address setup differs. The deref arm
becomes one branch of the unified arm. Struct-IDENT keeps its own #32
slot-copy arm above (unchanged). With those, the whole addressable-rhs
let-init-copy family is closed by construction: struct-ident / array-
ident / deref / N_DOT / N_INDEX all full-copy, both stages byte-identical
(rule-10).

949 gains 9 full-readback rows (every member written distinct + summed,
so a partial copy fails): array-ident 16B/32B + 12B(MOVL)/11B(MOVW+MOVB)
tails; N_DOT struct-field 16B + array-field 32B + 11B-tail struct field;
N_INDEX struct element 16B/32B. The N_INDEX source array is populated
through a `*inner` to `&a[i]` (the #135/#252 store path) because the
array-of-struct element direct store (`a[i].m[j]=v` / `a[i]=s` / struct-
array literal) segfaults on a SEPARATE pre-existing bug, reported
alongside this fold. w6c+wwdump combined.ww regen (#110). 70/70 949,
test-unit 241, sizelint, smoke green.
This commit is contained in:
2026-06-02 11:22:01 +09:00
parent dfa9771f42
commit bb2f4e1dfe
5 changed files with 517 additions and 140 deletions

View File

@@ -8763,48 +8763,110 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
break;
}
}
/* #265 fold-1: aggregate deref-rhs let-init
* `let c: T = *p` (T a struct or array, >8B). Neither
* the scalar tail below (one 8B word) nor a missing arm
* (cstage dropped the copy entirely) materialised the
* whole aggregate. cgexpr(rhs->lhs) leaves the SOURCE
* ADDRESS in AX (a `*p` ident loads the pointer value;
* `*(&s)` LEAQs the slot); memcpy sz bytes slot→slot via
* SI — a MOVQ run plus a sized MOVL/MOVW/MOVB tail. N is
* lu->size (sz), the #254 non-slot-padded ABI extent. Both
* stages emit this identical sequence (rule-10); the by-
* value RETURN ABI is fold-2 (#267). */
if (n->rhs && n->rhs->kind == N_UN
&& n->rhs->op == TK_STAR && lu
/* #265 fold-1/1b (#268): aggregate let-init copy from an
* ADDRESSABLE rhs. The whole family converges on ONE memcpy
* loop fed by a per-rhs source-address setup: `*p` (deref,
* fold-1), an array ident `= s` (struct-ident is the #32 arm
* above), an N_DOT field `= o.i`, an N_INDEX element `= a[i]`
* — T a struct or array >8B. Each shape lands the SOURCE
* ADDRESS in SI; the loop copies sz bytes (lu->size, the #254
* non-slot-padded ABI extent) slot→slot — a MOVQ run plus a
* sized MOVL/MOVW/MOVB tail. Pre-fix every non-deref shape was
* wrong: array-ident/N_DOT truncated to the 8B scalar tail
* below; N_INDEX scalar-loaded the element address as a value
* (segfault). Both stages emit the identical sequence
* (rule-10); the by-value RETURN ABI is fold-2 (#267). The
* source-addr setups reuse closed machinery: LEAQ-slot (ident),
* the deref operand (cgexpr), cg_dotchain_addr (#253, N_DOT),
* the &base[i] spine (#252, N_INDEX). */
if (n->rhs && lu
&& (lu->kind == TY_STRUCT || lu->kind == TY_ARRAY)
&& sz > 8) {
cgexpr(c, n->rhs->lhs, *locals);
ins2(c, A_MOVQ, areg(D_AX), areg(D_SI));
int k = 0;
for (; k + 8 <= sz; k += 8) {
ins2(c, A_MOVQ, amem(D_SI, k), areg(D_AX));
ins2(c, A_MOVQ, areg(D_AX),
amem(D_BP, off + k));
int havesrc = 0;
if (n->rhs->kind == N_UN && n->rhs->op == TK_STAR) {
cgexpr(c, n->rhs->lhs, *locals);
ins2(c, A_MOVQ, areg(D_AX), areg(D_SI));
havesrc = 1;
} else if (n->rhs->kind == N_IDENT) {
int soff = localfind(*locals, n->rhs->str);
if (soff != 0) {
ins2(c, A_LEAQ, amem(D_BP, soff),
areg(D_SI));
havesrc = 1;
} else if (let_islet(n->rhs->str)
|| def_isarraydef(n->rhs->str)) {
ins2(c, A_LEAQ, masym(c, n->rhs->str),
areg(D_SI));
havesrc = 1;
}
} else if (n->rhs->kind == N_DOT) {
if (cg_dotchain_addr(c, n->rhs, D_SI, *locals))
havesrc = 1;
} else if (n->rhs->kind == N_INDEX) {
Node *base = n->rhs->lhs;
Node *idx = n->rhs->rhs;
Type *bt = base ? base->type : NULL;
Type *bu = (bt && bt->kind == TY_NAMED)
? bt->under : bt;
if (base && base->kind == N_IDENT && bu
&& bu->kind == TY_ARRAY) {
int esz = (bu->sub)
? (int)bu->sub->size : 1;
cgexpr(c, idx, *locals);
if (esz > 1) {
ins2(c, A_MOVQ, aimm(esz),
areg(D_CX));
ins2(c, A_IMULQ, areg(D_CX),
areg(D_AX));
}
int boff = localfind(*locals,
base->str);
if (boff != 0)
ins2(c, A_LEAQ,
amem(D_BP, boff),
areg(D_BX));
else
ins2(c, A_LEAQ,
masym(c, base->str),
areg(D_BX));
ins2(c, A_ADDQ, areg(D_BX),
areg(D_AX));
ins2(c, A_MOVQ, areg(D_AX),
areg(D_SI));
havesrc = 1;
}
}
if (k + 4 <= sz) {
ins2(c, A_MOVL, amem(D_SI, k), areg(D_AX));
ins2(c, A_MOVL, areg(D_AX),
amem(D_BP, off + k));
k += 4;
if (havesrc) {
int k = 0;
for (; k + 8 <= sz; k += 8) {
ins2(c, A_MOVQ, amem(D_SI, k),
areg(D_AX));
ins2(c, A_MOVQ, areg(D_AX),
amem(D_BP, off + k));
}
if (k + 4 <= sz) {
ins2(c, A_MOVL, amem(D_SI, k),
areg(D_AX));
ins2(c, A_MOVL, areg(D_AX),
amem(D_BP, off + k));
k += 4;
}
if (k + 2 <= sz) {
ins2(c, A_MOVW, amem(D_SI, k),
areg(D_AX));
ins2(c, A_MOVW, areg(D_AX),
amem(D_BP, off + k));
k += 2;
}
if (k + 1 <= sz) {
ins2(c, A_MOVB, amem(D_SI, k),
areg(D_AX));
ins2(c, A_MOVB, areg(D_AX),
amem(D_BP, off + k));
k += 1;
}
break;
}
if (k + 2 <= sz) {
ins2(c, A_MOVW, amem(D_SI, k), areg(D_AX));
ins2(c, A_MOVW, areg(D_AX),
amem(D_BP, off + k));
k += 2;
}
if (k + 1 <= sz) {
ins2(c, A_MOVB, amem(D_SI, k), areg(D_AX));
ins2(c, A_MOVB, areg(D_AX),
amem(D_BP, off + k));
k += 1;
}
break;
}
if (n->rhs && sz == 8) {
cgexpr(c, n->rhs, *locals);