w6c+wwstage: array return-by-value via the struct-return ABI (#267 fold-2)

Wire TY_ARRAY into the existing struct-return gates so arrays ride the
same reg-class (<=24B in AX:DX:CX) / sret-class (>24B) path the struct
return ABI already emits byte-identically. No new ABI machinery.

Both stages, uniform gate-widen:
- cg_sret_retsize / sretretsize: +TY_ARRAY (natural size sub.size*len,
  the type table) -> auto-enables sret send/recv + the >24B sret N_IDENT
  word-copy + return-forward, all keyed on the shared sret SSoT.
- cgreturn <=24B reg-send: +TY_ARRAY (N_IDENT scratch word-copy ->
  AX/DX/CX). reg-class return-forward rides the default cgexpr passthrough.
- let-init / assign <=24B recv: +TY_ARRAY (AX/DX/CX sized stores).

struct_float_class stays struct-only: pure-int element arrays only; no
pure-float-array-return consumer exists today.

949 +11 rows: reg-class 8/16/24B + sret-class 32B, [N]u32 and [N]u8,
at let-init/assign/return-forward, full-member readback, + a struct-
return regression control. All cstage-run + cs==ww byte-id.
This commit is contained in:
2026-06-02 12:00:58 +09:00
parent 35b517ca3e
commit ebbc3f98c2
7 changed files with 682 additions and 6 deletions

View File

@@ -311,6 +311,11 @@ cg_sret_retsize(Type *rt)
if (rt == NULL) return 0;
if (rt->kind == TY_STRUCT)
return (int)rt->size <= 24 ? 0 : (int)rt->size;
/* #267: arrays ride the struct-return ABI — same ≤24 reg / >24 sret
* split. Pure-int element arrays only; no float-array-return
* consumer exists, so struct_float_class stays struct-only. */
if (rt->kind == TY_ARRAY)
return (int)rt->size <= 24 ? 0 : (int)rt->size;
if (rt->kind == TY_TUPLE) {
int gptotal = 0, ssecount = 0, f32;
for (Tparam *p = rt->params; p; p = p->next) {
@@ -5453,7 +5458,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
* the receive side of #4's cgreturn (calls + literals).
* Sizes >24B and non-{0,1,2,4}-byte tails fall through
* to the existing scalar path. */
if (lu && lu->kind == TY_STRUCT
if (lu && (lu->kind == TY_STRUCT || lu->kind == TY_ARRAY)
&& (int)lu->size <= 24) {
int off = localfind(locals, n->lhs->str);
if (off != 0) {
@@ -8620,7 +8625,8 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
}
}
if (n->rhs && n->rhs->kind == N_CALL && lu
&& lu->kind == TY_STRUCT && sz <= 24
&& (lu->kind == TY_STRUCT || lu->kind == TY_ARRAY)
&& sz <= 24
&& (sz % 8 == 0 || sz % 8 == 1
|| sz % 8 == 2 || sz % 8 == 4)) {
cgexpr(c, n->rhs, *locals);
@@ -9171,7 +9177,7 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
ins0(c, A_RET);
break;
}
if (rt && rt->kind == TY_STRUCT
if (rt && (rt->kind == TY_STRUCT || rt->kind == TY_ARRAY)
&& (int)rt->size > 24
&& (n->lhs->kind == N_IDENT
|| n->lhs->kind == N_STRUCTLIT)) {
@@ -9182,9 +9188,12 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
* narrow field (e.g. bool@32 in a 33B struct
* padded to 40B) widened to an 8B MOVQ at the
* loop tail — diverged from wwstage's MOVB
* tail. Task #33, Class A. */
* tail. Task #33, Class A. An array (#267) has no
* fields; its natural size IS rt->size. */
int sz = 0;
for (Tfield *fl = rt->fields; fl; fl = fl->next) {
if (rt->kind == TY_ARRAY) {
sz = (int)rt->size;
} else for (Tfield *fl = rt->fields; fl; fl = fl->next) {
int end = (int)fl->offset + (int)(fl->type
? fl->type->size : 8);
if (end > sz) sz = end;
@@ -9260,7 +9269,8 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
* for the same rationale. The ≤24B register-return
* ABI uses the same TY_STRUCT gate. */
Type *rt = type_chase_named(cg_ret_type);
if (rt && rt->kind == TY_STRUCT && rt->size <= 24
if (rt && (rt->kind == TY_STRUCT || rt->kind == TY_ARRAY)
&& rt->size <= 24
&& (n->lhs->kind == N_IDENT
|| n->lhs->kind == N_STRUCTLIT)) {
int sz = (int)rt->size;