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

@@ -5610,6 +5610,18 @@ fn desugarcallargs(c: *checker, n: *syntax.node) void = {
let nexta: *syntax.node = a.next;
if (param != nil) {
if (param.kind == syntax.nkind.N_PARAM) {
// C-style FFI variadic (bare `...`): str=="...",
// op != TK_ELLIPSIS, lhs == nil. The `...` absorbs
// every remaining arg untyped — mirror cstage
// check.c:1860 `if (!u->variadic) ...; continue`.
// MANDATORY: this param's lhs is nil, so the per-arg
// coercerunelit / checkarrlitfits / desugararrayslice
// calls below would deref nil on a rune-literal or
// array-literal variadic arg.
if (syntax.streq(param.str, "...")
&& param.op != syntax.tkind.TK_ELLIPSIS) {
break;
};
if (param.op != syntax.tkind.TK_ELLIPSIS) {
let atype: *syntax.node = exprtype(c, a, nil);
// #29: a rune literal narrowing into an integer param