test: port 989_ffivariadic_run to ww; retire the C carrier

This commit is contained in:
2026-08-08 15:49:14 +09:00
parent ee793a8407
commit e511cdd5a2
3 changed files with 135 additions and 263 deletions

View File

@@ -0,0 +1,127 @@
package ffivariadic_test;
// SysV variadic-FFI AL contract, runtime-gated on both driver stages.
// Port of the retired native carrier test/wcc/989_ffivariadic_run.c;
// every assertion preserved.
//
// A ww caller of a C variadic fn (`@symbol("fixture") fn fixture(n:
// i64, ...) f64;`) must set AL to the count of XMM regs used for the
// variadic FLOAT args: the C callee gates its xmm-save-area stores on
// `test %al,%al`, so a wrong AL makes va_arg(double) read garbage.
// The pre-fix bug hardcoded AL=0 (`XORQ AX,AX`) — correct only for a
// zero-float call. Ref SysV §3.5.7, ref/qbe/amd64/sysv.c:384.
//
// Byte-id can never see AL correctness, so each row builds a ww
// caller against the host-cc-built va_arg(double) summer
// (test/wcc/data/ffivariadic/fixture.c archived as
// out/ffivariadic/libffifix.a, self-contained: zero relocs/undef
// syms, no libc, so w6l links it hermetically) and asserts the
// runtime sum via the binary's exit code, on BOTH `ww` and `ww_ww`.
//
// NON-VACUITY: fixture(2,1.0,2.0) is vacuous on this box — with AL=0
// the two skipped xmm slots alias stale stack already holding
// 1.0/2.0. At >=3 floats the coincidence breaks deterministically,
// so every f64 row uses >=3 floats and reverting the fix FAILS here.
//
// f32 rows (#14): C default argument promotion widens each f32 arg
// to f64. f32three — exact values, unpromoted MOVSS leaves stale
// high 4B, sum misses 7.0. f32inexact — 0.1 is INEXACT in f32; the
// expected side `(x: f64)` is the SAME var widened at runtime, so
// equality holds iff the f32-ROUNDED value was promoted (not the f64
// literal, not garbage); single f32 arg also lands the fi==1
// boundary. f32mixed — only the f32 args promote; the native-f64 arg
// passes at width, never double-promoted.
//
// Dropped C machinery, not assertions: the ww_ww-absent skip gate
// (the Make target declares both drivers) and the unlink/rmdir
// accounting (testenv.clean asserts the removal).
import os;
import os.exec;
import strings;
import testenv;
import time;
fn fail(label: str, why: str) void = {
let m: str = strings.concat("ffivariadic FAIL: ", label, " -- ",
why, "\n");
os.write(2, m.ptr, m.len: u64);
assert(false);
};
fn tmo() time.duration = {
return (240i64 * (time.second: i64)): time.duration;
};
// -1 encodes an abnormal (non-EXIT) termination, never a valid code.
fn runcode(dir: str, name: str, argv: []str) i32 = {
let co: testenv.commandout;
testenv.runcommand(dir, dir, name, argv, tmo(), &co);
if (co.termination != exec.termination.EXIT) { return -1; };
return co.code;
};
fn caller(call: str, want: str) str = {
return strings.concat(
"package main;\n",
"@symbol(\"fixture\") fn fixture(n: i64, ...) f64;\n",
"export fn main() int = {\n",
"\tlet r: f64 = ", call, ";\n",
"\tif (r == ", want, ") { return 0; };\n",
"\treturn 1;\n",
"};\n");
};
@test fn variadic_al() void = {
let labels: []str = ["three", "five", "eight", "f32three",
"f32inexact", "f32mixed"];
let srcs: []str = [
caller("fixture(3, 1.0, 2.0, 3.0)", "6.0"),
caller("fixture(5, 1.0, 2.0, 3.0, 4.0, 5.0)", "15.0"),
caller("fixture(8, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0)",
"36.0"),
caller("fixture(3, 1.5: f32, 2.5: f32, 3.0: f32)", "7.0"),
strings.concat(
"package main;\n",
"@symbol(\"fixture\") fn fixture(n: i64, ...) f64;\n",
"export fn main() int = {\n",
"\tlet x: f32 = 0.1: f32;\n",
"\tlet r: f64 = fixture(1, x);\n",
"\tif (r == (x: f64)) { return 0; };\n",
"\treturn 1;\n",
"};\n"),
caller("fixture(3, 1.5: f32, 2.0, 3.5: f32)", "7.0"),
];
let libdir: str = strings.concat(testenv.repo(), "/out/ffivariadic");
let drvs: []str = ["ww", "ww_ww"];
let td: str = testenv.fresh();
let i: i32 = 0;
for (i < labels.len) {
let src: str = strings.concat(td, "/", labels[i], ".ww");
testenv.writefile(src, srcs[i]);
let s: i32 = 0;
for (s < 2) {
// "out." prefix: a bare <label>.<drv> stem collides with
// the <label>.ww SOURCE name on the cstage driver leg
let out: str = strings.concat(td, "/out.", labels[i], ".",
drvs[s]);
let av: []str = [testenv.driver(drvs[s]), "build",
strings.concat("-L", libdir), "-lffifix", "-o", out,
src];
if (runcode(td, strings.concat("build_", labels[i], "_",
drvs[s]), av) != 0) {
fail(labels[i], strings.concat(drvs[s],
" build failed (-lffifix link)"));
};
let rav: []str = [out];
if (runcode(td, strings.concat("run_", labels[i], "_",
drvs[s]), rav) != 0) {
fail(labels[i], strings.concat(drvs[s],
" wrong variadic sum (AL/promotion contract)"));
};
s += 1;
};
i += 1;
};
testenv.clean(td);
};