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

@@ -7500,6 +7500,69 @@ fn cgassign(c: *cgen, n: *node) void = {
};
};
};
// #267: array return-by-value RECV — `c = mk()` where c is
// an array local. Arrays ride the struct reg/sret recv path.
// >24B sret keys on callsretsize (the shared SSoT, c's slot
// IS the prealloc dest); ≤24B arrives in AX/DX/CX, sized
// stores. Array natural size (tinfo.size = sub.size*len)
// mirrors cstage lu->size. No structfloatclass (pure-int
// element arrays).
if (lcn != nil && n.op == tkind.TK_ASSIGN
&& n.rhs != nil && n.rhs.kind == nkind.N_CALL) {
let acati: *tinfo = nil;
if (lcn.tnode != nil) { acati = lcn.tnode.type_: *tinfo; };
for (acati != nil && acati.kind == tykind.TY_NAMED) {
acati = acati.under;
};
if (acati != nil && acati.kind == tykind.TY_ARRAY) {
let lcsz: i32 = acati.size: i32;
if (lcsz > 24) {
let rscs: i32 = callsretsize(c, n.rhs);
if (rscs > 0) {
c.sretdestoff = off;
cgexpr(c, n.rhs);
c.sretdestoff = 0;
return;
};
};
if (lcsz <= 24) {
let tlm: i32 = lcsz - (lcsz / 8) * 8;
if (tlm == 0 || tlm == 1
|| tlm == 2 || tlm == 4) {
cgexpr(c, n.rhs);
let full: i32 = lcsz / 8;
let i: i32 = 0;
for (i < full) {
let reg: str = "AX";
if (i == 1) { reg = "DX"; };
if (i == 2) { reg = "CX"; };
emitline("\tMOVQ\t");
emitline(reg);
emitline(", ");
emitoff((off + i * 8): i64);
emitline("(BP)\n");
i += 1;
};
if (tlm > 0) {
let top: str = "MOVB";
if (tlm == 4) { top = "MOVL"; };
if (tlm == 2) { top = "MOVW"; };
let treg: str = "AX";
if (full == 1) { treg = "DX"; };
if (full == 2) { treg = "CX"; };
emitline("\t");
emitline(top);
emitline("\t");
emitline(treg);
emitline(", ");
emitoff((off + full * 8): i64);
emitline("(BP)\n");
};
return;
};
};
};
};
// Float-typed local: rhs lands in X0; store via MOVSD/
// MOVSS, no AX shuffle. Compound (+= -= *= /=) loads
// slot into X1, combines into X1, stores X1 back —