w6c: promote f32 arg to double in C-variadic tail (#14)

C99 6.5.2.2p6 requires float->double promotion for floats in the
variadic region of a call; w6c emitted MOVSS (4B), so a C variadic's
va_arg(double) read 8B of garbage. Promote at push (CVTSS2SD) so the
8B slot holds a real double, covering the XMM-reg and >8-float spill
cases uniformly; fi/AL XMM-count is unchanged.

cstage only; the wwstage twin rides C2 (#10), which adds wwstage's
C-variadic-call codegen.
This commit is contained in:
2026-06-21 11:14:14 +09:00
parent 294f4c93fd
commit 8f0ce09f2a
2 changed files with 78 additions and 5 deletions

View File

@@ -79,6 +79,52 @@ static const struct row rows[] = {
" return 1;\n"
"};\n",
0 },
/* 3 f32 args (#14): C default arg promotion widens each to f64, so
* the callee's va_arg(double) reads 1.5+2.5+3.0 == 7.0. Values are
* exact in f32, so the f64 compare is exact. Unpromoted (MOVSS, 4B)
* the high 4B of each 8B slot is stale stack, so va_arg(double)
* pulls garbage and the sum misses 7.0 — this row FAILS pre-fix. */
{ "f32three",
"package main;\n"
"@symbol(\"fixture\") fn fixture(n: i64, ...) f64;\n"
"export fn main() int = {\n"
" let r: f64 = fixture(3, 1.5: f32, 2.5: f32, 3.0: f32);\n"
" if (r == 7.0) { return 0; };\n"
" return 1;\n"
"};\n",
0 },
/* Sharper than f32three: 0.1 is INEXACT in f32, so the f32-rounded
* value differs from the f64 literal 0.1. The expected side `(x: f64)`
* is the SAME f32 var widened at runtime (CVTSS2SD), so equality holds
* iff the variadic arg carried the f32-rounded value promoted to f64 —
* NOT the original f64 literal and NOT stale-high-bit garbage. Both a
* no-promote (MOVSS) and a hypothetical direct-f64 pass would miss it.
* Single f32 arg also exercises the fi==1 boundary. */
{ "f32inexact",
"package main;\n"
"@symbol(\"fixture\") fn fixture(n: i64, ...) f64;\n"
"export fn main() int = {\n"
" let x: f32 = 0.1: f32;\n"
" let r: f64 = fixture(1, x);\n"
" if (r == (x: f64)) { return 0; };\n"
" return 1;\n"
"};\n",
0 },
/* Mixed f32 + f64 in one variadic call: only the f32 args (1.5, 3.5)
* promote; the f64 arg (2.0) passes at its native width and must NOT be
* double-promoted or skewed. 1.5+2.0+3.5 == 7.0, exact in both widths. */
{ "f32mixed",
"package main;\n"
"@symbol(\"fixture\") fn fixture(n: i64, ...) f64;\n"
"export fn main() int = {\n"
" let r: f64 = fixture(3, 1.5: f32, 2.0, 3.5: f32);\n"
" if (r == 7.0) { return 0; };\n"
" return 1;\n"
"};\n",
0 },
};
/* run_build — build+run `src` via cstage `driver`, linking libffifix.a from