w6c+wwstage: array global-aggregate-receive g = f() (#272 commit-2)

The caller-half of the global case: `g = mk()` into a GLOBAL array
stored only the first word — a ≤24B reg-return landed `MOVQ AX, g(SB)`
(8 of 24 bytes); a >24B sret-return hit the #220 sret-to-symbol gate
which was TY_STRUCT-only and fell through to the same truncation.

≤24B: the local aggregate-receive arm was `off != 0`-only, so a global
array fell to the scalar IDENT store. Add a global ARRAY arm — LEAQ
name(SB), DI then store the full+tail words from AX/DX/CX (an array is
never float-class, so AX/DX/CX is always the transport; no `g+8(SB)`
operand form exists). Mirrors the str/slice global arm.
>24B: add TY_ARRAY to the #220 sret-to-symbol gate (cg_sret_dest_sym /
sretdestnode) — the callee writes the whole array through RDI.

A ≤24B STRUCT global receive can be float-class (X0/X1, not AX/DX/CX),
so it is left at its pre-existing symmetric behaviour — no consumer.

949_aggret_source_run gains global_recv (c → 15) and global_recv_sret
(>24B → 22), both with per-row byte-id.
This commit is contained in:
2026-06-02 15:00:55 +09:00
parent 0d39129741
commit 9d81ba77b7
5 changed files with 249 additions and 50 deletions

View File

@@ -5625,11 +5625,12 @@ cgexpr(Cg *c, Node *n, Local *locals)
* global arm above (let_islet + LEAQ masym). The
* scalar fall-through below would emit a truncated
* 8-byte `MOVQ AX, g(SB)` and drop the struct body.
* Kept TY_STRUCT-only: a tuple-typed global reassign
* has no sret-to-symbol path in wwstage either, so
* leaving it to fall through keeps the stages aligned
* (rule-10). */
if (lu && lu->kind == TY_STRUCT
* Kept aggregate-only (struct + #272 array): a
* tuple-typed global reassign has no sret-to-symbol
* path in wwstage either, so leaving it to fall
* through keeps the stages aligned (rule-10). */
if (lu && (lu->kind == TY_STRUCT
|| lu->kind == TY_ARRAY)
&& let_islet(n->lhs->str)) {
cg_sret_dest_sym = n->lhs->str;
cgexpr(c, n->rhs, locals);
@@ -5687,6 +5688,35 @@ cgexpr(Cg *c, Node *n, Local *locals)
}
break;
}
} else if (lu->kind == TY_ARRAY
&& let_islet(n->lhs->str)
&& n->rhs && n->rhs->kind == N_CALL
&& n->op == TK_ASSIGN) {
/* #272: `g = f();` where g is a GLOBAL
* aggregate ≤24B. The callee leaves the result
* in AX/DX/CX (#272 reg-return); the scalar IDENT
* fall-through below would store only MOVQ AX,
* g(SB) = the first word. The asm has no `g+8(SB)`
* operand form, so LEAQ the symbol into DI and
* store the full+tail words. Mirrors the str/slice
* global arm above and the #220 sret-to-symbol path. */
int sz = (int)lu->size;
cgexpr(c, n->rhs, locals);
ins2(c, A_LEAQ, masym(c, n->lhs->str), areg(D_DI));
int regs[3] = { D_AX, D_DX, D_CX };
int full = sz / 8;
int tail = sz % 8;
for (int i = 0; i < full; i++)
ins2(c, A_MOVQ, areg(regs[i]),
amem(D_DI, i * 8));
if (tail > 0) {
int op = (tail == 4) ? A_MOVL
: (tail == 2) ? A_MOVW
: A_MOVB;
ins2(c, op, areg(regs[full]),
amem(D_DI, full * 8));
}
break;
}
}
}