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