cgen: copy all eightbytes when a non-call aggregate assigns into a field of an indexed element (#11b)
The arr[i].f=src legacy assign block enumerated scalar field-type arms then fell to a 1-word scalar default, so a non-call aggregate source (ident/dot/index) cgexpr'd only its first word into AX and stored one eightbyte — silent on BOTH stages (byte-id blind). The non-indexed bases (local/deref/chained/global) reach the general assign resolver's canonical aggargsrcaddr+aggcopy; the indexed arm short-circuited before it. Route the indexed base through the block's own proven &arr[i] spine into the same aggargsrcaddr+aggcopy emitters (DRY — no third copy), dual-site symmetric. Unlike #11's in-cap arm, the source is a memory address so aggcopy is a pure memcpy: float bits and the sub-8 tail transport verbatim, no loud-stop needed. Did not fall through to the general resolver because its cgplaceaddr N_INDEX arm rejects a *[N]S (TY_PTR) base (latent resolver gap, filed separately). Contained to the indexed base + non-call aggregate-field rhs; value-asserting pins redden under each stage's independent revert.
This commit is contained in:
@@ -5982,6 +5982,57 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* wholly in AX; the scalar default's
|
||||
* single store is the correct receive. */
|
||||
}
|
||||
/* #11b: a NON-call AGGREGATE source into an
|
||||
* aggregate field of an indexed element
|
||||
* `arr[i].f = src` (src an ident / .g / index).
|
||||
* The scalar default below loads only the
|
||||
* source's FIRST word into AX and stores ONE
|
||||
* word — dropping the rest (a SILENT both-stage
|
||||
* member drop, the non-call twin of the #11
|
||||
* in-cap CALL arm above; byte-id blind). Unlike
|
||||
* #11's GP AX/DX/CX cursor the source is a
|
||||
* MEMORY address, so the shared mem-to-mem
|
||||
* cg_aggcopy transports EVERY byte: a sub-8
|
||||
* tail and float bits copy verbatim, so NO
|
||||
* tail/float/over-cap loud-stop is needed here
|
||||
* (those #11 stops were register-cursor
|
||||
* artefacts). Reuse the block's own &arr[i]
|
||||
* spine (proven for [N]S / *[N]S / []S by the
|
||||
* sibling arms) -> BX + foff, then funnel
|
||||
* through aggarg_srcaddr (src -> SI) +
|
||||
* cg_aggcopy — the ONE copy emitter the non-
|
||||
* indexed bases use (DRY). fsz natural
|
||||
* (ft->size). Mirrors wwstage cgenexpr.ww. */
|
||||
if (n->op == TK_ASSIGN && n->rhs
|
||||
&& n->rhs->kind != N_CALL && fu
|
||||
&& (fu->kind == TY_STRUCT || fu->kind == TY_ARRAY
|
||||
|| fu->kind == TY_TUPLE) && fsz > 8) {
|
||||
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));
|
||||
}
|
||||
if (is_arr)
|
||||
ins2(c, A_LEAQ, amem(D_BP, off), areg(D_BX));
|
||||
else
|
||||
ins2(c, A_MOVQ, amem(D_BP, off), areg(D_BX));
|
||||
ins2(c, A_ADDQ, areg(D_AX), areg(D_BX));
|
||||
if (viaptr)
|
||||
ins2(c, A_MOVQ, amem(D_BX, 0), areg(D_BX));
|
||||
if (foff != 0)
|
||||
ins2(c, A_ADDQ, aimm(foff), areg(D_BX));
|
||||
/* spill dest across the source-address
|
||||
* resolution (the #270-1b order:
|
||||
* aggarg_srcaddr clobbers BX). */
|
||||
ins1(c, A_PUSHQ, areg(D_BX));
|
||||
if (!aggarg_srcaddr(c, n->rhs, D_SI, locals))
|
||||
fatal("#11b: aggregate field receive "
|
||||
"arr[i].f=src - source shape unwired "
|
||||
"(rule-7)");
|
||||
ins1(c, A_POPQ, areg(D_BX));
|
||||
cg_aggcopy(c, fsz);
|
||||
break;
|
||||
}
|
||||
if (n->op == TK_ASSIGN) {
|
||||
cgexpr(c, n->rhs, locals);
|
||||
ins1(c, A_PUSHQ,
|
||||
|
||||
Reference in New Issue
Block a user