wwstage: C-FFI variadic call codegen parity with cstage (#10)

Mirror cstage's C-variadic call handling in the ww self-host: parse a
bare `...` param (decl.ww), skip param-keyed desugar for it to avoid a
nil-deref (check.ww), and emit AL = XMM-reg count plus CVTSS2SD
promotion of f32 args in the variadic tail (cgenutil.ww, cgenexpr.ww).
Closes the cat-A wwstage silent miscompile (AL=0, unpromoted f32 tail).

Parse/check/cgen are one atomic align-up (parse alone miscompiles, so
not bisect-splittable). 989_ffivariadic now runs dual-stage (cstage ww
+ wwstage ww_ww), 12/12; w6c==w6c_ww byte-identical. Byte-id alone is
blind here (the bootstrap calls no float-bearing C variadic), so the
ww_ww runtime rows are the real net.
This commit is contained in:
2026-06-21 11:50:18 +09:00
parent 8f0ce09f2a
commit c814856550
6 changed files with 161 additions and 17 deletions

View File

@@ -119,6 +119,18 @@ fn parseparams(p: *parser) *node = {
let pf: str = p.curfile;
let pl: i32 = p.curline;
let pc: i32 = p.curcol;
// C-style FFI variadic: a bare `...` param (no name/type),
// the last param. Tagged by str == "..." (distinct from the
// Hare-style `T...` marked on n.op below). Mirrors cmd/wcc/
// parse.c:113-122; check.c keys cu->variadic on this form.
if (p.curkind == tkind.TK_ELLIPSIS) {
advance(p);
let cv: *node = newnode(nkind.N_PARAM, pf, pl, pc);
cv.str = "...";
if (head == nil) { head = cv; tail = cv; }
else { tail.next = cv; tail = cv; };
break;
};
let n: *node = newnode(nkind.N_PARAM, pf, pl, pc);
// Param form: (IDENT|'_') ':' type. Anonymous-type-only params
// (used in fn type expressions) aren't yet wired here.