128 lines
4.6 KiB
Plaintext
128 lines
4.6 KiB
Plaintext
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);
|
|
};
|