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:
@@ -83,6 +83,46 @@ fn callee_variadic_param(c: *cgen, callee: *syntax.node, nfixed_out: *i32) *synt
|
||||
return findvariadicparam(ps, nfixed_out);
|
||||
};
|
||||
|
||||
// calleecvariadic — true when the callee is a C-style variadic fn: a
|
||||
// bare `...` param (str == "...", distinct from the Hare-style `T...`
|
||||
// findvariadicparam keys on op == TK_ELLIPSIS). nfixed_out gets the
|
||||
// count of fixed params before the `...`. Mirror of cstage's
|
||||
// `cu->variadic` gate (cmd/w6c/cgen.c cgcall), which check.c:902 sets
|
||||
// ONLY for the bare-`...` form — the bare `...` adds no Tparam, so
|
||||
// nfixed is the fixed-param count. Drives the SysV §3.5.7 AL=XMM-count
|
||||
// emit and the C-default-promotion of an f32 variadic-tail arg to f64
|
||||
// (#14). nfixed_out cannot be nil.
|
||||
fn calleecvariadic(c: *cgen, callee: *syntax.node, nfixed_out: *i32) bool = {
|
||||
*nfixed_out = 0;
|
||||
if (callee == nil) { return false; };
|
||||
let ps: *syntax.node = nil;
|
||||
if (callee.kind == syntax.nkind.N_IDENT) {
|
||||
if (callee.str.len == 0) { return false; };
|
||||
ps = fnparamslookup(c, callee.str);
|
||||
} else { if (callee.kind == syntax.nkind.N_DOT) {
|
||||
if (callee.str.len == 0) { return false; };
|
||||
let cmod: str;
|
||||
cmod.ptr = nil; cmod.len = 0;
|
||||
if (callee.lhs != nil) {
|
||||
if (callee.lhs.kind == syntax.nkind.N_IDENT) {
|
||||
cmod = callee.lhs.str;
|
||||
};
|
||||
};
|
||||
ps = fnparamslookupmod(c, callee.str, cmod);
|
||||
}; };
|
||||
let p: *syntax.node = ps;
|
||||
for (p != nil) {
|
||||
if (p.kind == syntax.nkind.N_PARAM) {
|
||||
if (syntax.streq(p.str, "...")) {
|
||||
return true;
|
||||
};
|
||||
*nfixed_out += 1;
|
||||
};
|
||||
p = p.next;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
// ---- expression cgen -------------------------------------------------
|
||||
|
||||
// pushargsrev — recursively walks the arg list, evaluates rightmost
|
||||
@@ -155,11 +195,17 @@ fn argtaggedwidensz(c: *cgen, arg0: *syntax.node, param: *syntax.node) i32 = {
|
||||
return pslot;
|
||||
};
|
||||
|
||||
fn pushargsrev(c: *cgen, arg: *syntax.node, param: *syntax.node, memphase: bool) i32 = {
|
||||
// argidx is this arg's 0-based position in the call's arg list;
|
||||
// cvarnfixed is the fixed-param count of a C-variadic callee (-1 when
|
||||
// the callee is not C-variadic). An f32 arg in the variadic tail
|
||||
// (argidx >= cvarnfixed) is promoted to f64 here (#14), so its 8B stack
|
||||
// slot holds a real double for the pop side and the callee's
|
||||
// va_arg(double).
|
||||
fn pushargsrev(c: *cgen, arg: *syntax.node, param: *syntax.node, memphase: bool, argidx: i32, cvarnfixed: i32) i32 = {
|
||||
if (arg == nil) { return 0; };
|
||||
let nextparam: *syntax.node = nil;
|
||||
if (param != nil) { nextparam = param.next; };
|
||||
let rest: i32 = pushargsrev(c, arg.next, nextparam, memphase);
|
||||
let rest: i32 = pushargsrev(c, arg.next, nextparam, memphase, argidx + 1, cvarnfixed);
|
||||
// Family C (#35): peel tagged→tagged casts FIRST so every gate
|
||||
// below keys on the operand — an identity cast reduces to the
|
||||
// ident fast path, a widening cast trips the widen branch with
|
||||
@@ -992,6 +1038,14 @@ fn pushargsrev(c: *cgen, arg: *syntax.node, param: *syntax.node, memphase: bool)
|
||||
cgexpr(c, arg);
|
||||
let mov: str = "MOVSD";
|
||||
if (fk == 1) { mov = "MOVSS"; };
|
||||
// #14: an f32 in the C-variadic tail widens to f64 (C default
|
||||
// argument promotion) — CVTSS2SD in X0, then MOVSD spills a
|
||||
// full 8B double; the pop reloads MOVSD and counts it as one
|
||||
// SSE reg. Mirror cstage cmd/w6c/cgen.c promote_f32 push.
|
||||
if (cvarnfixed >= 0 && argidx >= cvarnfixed && fk == 1) {
|
||||
emitline("\tCVTSS2SD\tX0, X0\n");
|
||||
mov = "MOVSD";
|
||||
};
|
||||
emitline("\tSUBQ\t$8, SP\n");
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
|
||||
Reference in New Issue
Block a user