test: migrate Fam11 float value tests to @test, keep ABI-conformance pins (#5-C4)
fold-2 chunk C4 (drew's Fam8-13 plan): 13 float value-row C drivers re-homed. 11 migrate to test/lang/*_test.ww @test row-tables (exact IEEE-bit asserts); 1 float-overflow reject row -> a runww //ww:error carrier. 956_tuprecv_f64 slims to a w6c_ww asserttyped pin (20 value rows -> @test; the stamp dimension can't be a value/byte-id @test) -- mutation-proven non-vacuous (break #121 stamp -> RED 6/6 -> restore -> GREEN) + an in-test vacuity self-check. 946_structparam/structret stay whole: their SSE register-class .s-grep (SysV ABI conformance, #165/#171a) is the genuine defect-guard, not @test-expressible. Float was the predicted SSE-cursor byte-id hotspot -- zero fresh cs!=ww surfaced; 951_f64cgen (cstage-only before) byte-ids clean. LANGBYTEID floor 82->93; test count 384->374 (10 deleted drivers; 956 + the 2 946 kept).
This commit is contained in:
56
test/lang/arr_float_call_index_test.ww
Normal file
56
test/lang/arr_float_call_index_test.ww
Normal file
@@ -0,0 +1,56 @@
|
||||
// arr_float_call_index_test — float arr[i]= with X0-clobbering index, migrated
|
||||
// from test/wcc/916_arr_float_call_index_run.c (#5-C4, #125). In the `arr[i] = v`
|
||||
// ASSIGN path, when the element type is float and the INDEX sub-expression
|
||||
// clobbers X0 (e.g. a fn-call index), the value was LOST pre-fix: both stages
|
||||
// PUSHed AX (junk for floats), never spilled X0 across the idx/base eval, then
|
||||
// re-emitted MOVSS/MOVSD X0,(BX) using the already-clobbered X0. The fix spills
|
||||
// X0 around the idx/base eval for float elements (SUBQ/MOVSD ...(SP)). Broken
|
||||
// byte-identically between stages (pre-existing, exposed by #122) — T1 run + T2
|
||||
// byte-id. The non-call-index rows are regression guards (lit/localvar/arith
|
||||
// indices don't clobber X0; worked pre-fix).
|
||||
|
||||
package arr_float_call_index_test;
|
||||
|
||||
fn geti(x: f64) i32 = {
|
||||
let y: f64 = x + 1.0;
|
||||
return (y: i32);
|
||||
};
|
||||
|
||||
// int-arg call: avoids a pre-call f32-arg-push MOVSD-vs-MOVSS sibling divergence;
|
||||
// the body's f64 arith still clobbers X0.
|
||||
fn getj(seed: i32) i32 = {
|
||||
let y: f64 = (seed: f64) + 1.0;
|
||||
return (y: i32);
|
||||
};
|
||||
|
||||
@test fn f64_call_index() void = {
|
||||
let a: [4]f64 = [99.0, 99.0, 99.0, 99.0];
|
||||
a[geti(1.0)] = 1.5f64;
|
||||
assert((a[2]: i32) == 1);
|
||||
};
|
||||
|
||||
@test fn f32_call_index() void = {
|
||||
let a: [4]f32 = [99.0f32, 99.0f32, 99.0f32, 99.0f32];
|
||||
a[getj(1)] = 1.5f32;
|
||||
assert((a[2]: i32) == 1);
|
||||
};
|
||||
|
||||
@test fn f64_lit_index() void = {
|
||||
let a: [4]f64 = [99.0, 99.0, 99.0, 99.0];
|
||||
a[2] = 1.5f64;
|
||||
assert((a[2]: i32) == 1);
|
||||
};
|
||||
|
||||
@test fn f64_localvar_index() void = {
|
||||
let a: [4]f64 = [99.0, 99.0, 99.0, 99.0];
|
||||
let k: i32 = 2;
|
||||
a[k] = 1.5f64;
|
||||
assert((a[2]: i32) == 1);
|
||||
};
|
||||
|
||||
@test fn f64_arith_index() void = {
|
||||
let a: [4]f64 = [99.0, 99.0, 99.0, 99.0];
|
||||
let k: i32 = 1;
|
||||
a[k + 1] = 1.5f64;
|
||||
assert((a[2]: i32) == 1);
|
||||
};
|
||||
56
test/lang/f32arg_test.ww
Normal file
56
test/lang/f32arg_test.ww
Normal file
@@ -0,0 +1,56 @@
|
||||
// f32arg_test — f32 function-argument spill width, migrated from
|
||||
// test/wcc/907_f32arg_run.c (#5-C4, #143). Passing an f32 as a function argument
|
||||
// must spill through the stack at f32 width (MOVSS, 4-byte), not MOVSD (8-byte).
|
||||
// cstage lowered the arg-push/pop with a hardcoded MOVSD; wwstage already split
|
||||
// f32/f64 via exprfloatkind. The bug was a pure cs!=ww byte-id break (the callee
|
||||
// reads its f32 param via MOVSS regardless), so T2 byte-id is the primary net;
|
||||
// the T1 cstage run also asserts the end-to-end XMM round-trip value.
|
||||
|
||||
package f32arg_test;
|
||||
|
||||
fn takef32(x: f32) f32 = { return x; };
|
||||
|
||||
fn id(x: f32) f32 = { return x; };
|
||||
|
||||
fn add3(a: f32, b: f32, c: f32) f32 = { return a + b + c; };
|
||||
|
||||
fn mix(a: f32, b: f64, c: f32) f64 = { return a: f64 + b + c: f64; };
|
||||
|
||||
fn ten(a: f32, b: f32, c: f32, d: f32, e: f32, f: f32, g: f32, h: f32, i: f32, j: f32) f32 = {
|
||||
return a + b + c + d + e + f + g + h + i + j;
|
||||
};
|
||||
|
||||
@test fn single_var() void = {
|
||||
let a: f32 = 2.5f32;
|
||||
let r: f32 = takef32(a);
|
||||
assert(r: i32 == 2);
|
||||
};
|
||||
|
||||
@test fn literal_arg() void = {
|
||||
let r: f32 = id(3.0f32);
|
||||
assert(r: i32 == 3);
|
||||
};
|
||||
|
||||
@test fn multi_f32() void = {
|
||||
let x: f32 = 1.5f32;
|
||||
let y: f32 = 2.5f32;
|
||||
let z: f32 = 4.0f32;
|
||||
let r: f32 = add3(x, y, z);
|
||||
assert(r: i32 == 8);
|
||||
};
|
||||
|
||||
// f32 + f64 MIX: the f64 arg stays MOVSD, the f32 args MOVSS, per-arg agree.
|
||||
@test fn mixed_f32_f64() void = {
|
||||
let p: f32 = 1.5f32;
|
||||
let q: f64 = 10.0;
|
||||
let s: f32 = 0.5f32;
|
||||
let r: f64 = mix(p, q, s);
|
||||
assert(r: i32 == 12);
|
||||
};
|
||||
|
||||
// STACK-passed f32: 8 ride X0..X7, args 9+10 stay on the stack (fpidx >= 8).
|
||||
@test fn stack_passed() void = {
|
||||
let v: f32 = 1.0f32;
|
||||
let r: f32 = ten(v, v, v, v, v, v, v, v, v, v);
|
||||
assert(r: i32 == 10);
|
||||
};
|
||||
43
test/lang/f32lit_test.ww
Normal file
43
test/lang/f32lit_test.ww
Normal file
@@ -0,0 +1,43 @@
|
||||
// f32lit_test — f32-suffixed literal narrowing, migrated from
|
||||
// test/wcc/964_f32lit_run.c (#5-C4, #104 fold-1). An f32-typed float literal
|
||||
// must narrow to single precision (CVTSD2SS at the materialise site) before the
|
||||
// f32 consumer reads it; pre-fix both stages materialised it as a 64-bit double
|
||||
// in X0 and the downstream MOVSS read the low 4 bytes (garbage; 0.0f for clean
|
||||
// values, which is why 0.0 coincidentally survived). Covers the f32 suffix
|
||||
// (1.0f32) and the no-decimal N_INTLIT-float arm (8f32). Both stages emit
|
||||
// byte-identical asm, so byte-id alone never catches a reintroduction — the T1
|
||||
// cstage run is the live net; T2 byte-id rides along.
|
||||
|
||||
package f32lit_test;
|
||||
|
||||
fn g() f32 = { return 2.5f32; };
|
||||
|
||||
@test fn bare_value() void = {
|
||||
let x: f32 = 1.0f32;
|
||||
assert(x: f64 == 1.0);
|
||||
};
|
||||
|
||||
@test fn arith() void = {
|
||||
let a: f32 = 1.5f32;
|
||||
let b: f32 = 2.5f32;
|
||||
let s: f32 = a + b;
|
||||
assert(s: f64 == 4.0);
|
||||
};
|
||||
|
||||
@test fn return_arith() void = {
|
||||
let r: f32 = g() + 1.5f32;
|
||||
assert(r: i32 == 4);
|
||||
};
|
||||
|
||||
@test fn intlit_f32_arm() void = {
|
||||
let y: f32 = 8f32;
|
||||
assert(y: i32 == 8);
|
||||
};
|
||||
|
||||
// genuine single-rounding: 2^24 + 1 is NOT representable in f32 and rounds back
|
||||
// to 2^24 (round-to-even); if the add ran in double it would be 16777217.0.
|
||||
@test fn single_round() void = {
|
||||
let big: f32 = 16777216.0f32;
|
||||
let r: f32 = big + 1.0f32;
|
||||
assert(r: f64 == 16777216.0);
|
||||
};
|
||||
40
test/lang/f32stamp_test.ww
Normal file
40
test/lang/f32stamp_test.ww
Normal file
@@ -0,0 +1,40 @@
|
||||
// f32stamp_test — un-suffixed f32-context literal checker-stamp, migrated from
|
||||
// test/wcc/965_f32stamp_run.c (#5-C4, #104 fold-2). An UN-suffixed float literal
|
||||
// in an f32 context (`let x: f32 = 1.0`, `return 1.0` from an f32 fn) must be
|
||||
// stamped f32 by the checker so fold-1's cgen narrow fires; without the stamp it
|
||||
// stays ty_untyped_float, materialises as a 64-bit double, and the f32 consumer
|
||||
// reads the low 4 bytes (0.0f for clean values). SCOPE (#104 fold-2): the stamp
|
||||
// fires at let-init and return ONLY — binop/unary/assign/call-arg/struct-field
|
||||
// are deferred to #120, so this pins bare let-init / return literals exclusively.
|
||||
// Both stages byte-identical; T1 cstage run is the live net, T2 byte-id rides.
|
||||
|
||||
package f32stamp_test;
|
||||
|
||||
fn g() f32 = { return 2.0; };
|
||||
|
||||
fn h() f32 = { return 4.0; };
|
||||
|
||||
@test fn let_one() void = {
|
||||
let x: f32 = 1.0;
|
||||
assert(x: f64 == 1.0);
|
||||
};
|
||||
|
||||
@test fn let_decimal() void = {
|
||||
let y: f32 = 8.0;
|
||||
assert(y: i32 == 8);
|
||||
};
|
||||
|
||||
@test fn let_frac() void = {
|
||||
let p: f32 = 0.5;
|
||||
assert(p: f64 == 0.5);
|
||||
};
|
||||
|
||||
@test fn return_bare() void = {
|
||||
assert(g(): i32 == 2);
|
||||
};
|
||||
|
||||
@test fn return_then_let() void = {
|
||||
let r: f32 = h();
|
||||
assert(r: f64 == 4.0);
|
||||
assert(r: i32 == 4);
|
||||
};
|
||||
143
test/lang/f64cgen_test.ww
Normal file
143
test/lang/f64cgen_test.ww
Normal file
@@ -0,0 +1,143 @@
|
||||
// f64cgen_test — f64/f32 deref-load + NaN relop, migrated from
|
||||
// test/wcc/951_f64cgen_run.c (#5-C4, #96 + #97). Two GATE-BLIND f64 codegen
|
||||
// bugs (both stages byte-identical before+after the fix, so byte-id never
|
||||
// catches a reintroduction — the T1 cstage run is the live net):
|
||||
// #96 — f64/f32 deref-load must MOVSD/MOVSS into X0, not MOVQ into AX.
|
||||
// #97 — f64/f32 compare must consult PF (parity) for IEEE-754 NaN: with a NaN
|
||||
// operand `!=` is true, the other five relops false.
|
||||
// The C driver was cstage-ONLY (no byte-id leg — ww_ww run was broken #95);
|
||||
// this migration ADDS the T2 cs==ww byte-id assertion for the first time.
|
||||
|
||||
package f64cgen_test;
|
||||
|
||||
fn deref(p: *f64) f64 = { return *p; };
|
||||
|
||||
fn zero() f64 = { return 0.0; };
|
||||
|
||||
fn one() f64 = { return 1.0; };
|
||||
|
||||
fn z() f64 = { return 0.0; };
|
||||
|
||||
fn z32() f32 = { return 0.0; };
|
||||
|
||||
fn one32() f32 = { return 1.0; };
|
||||
|
||||
fn tobits(f: f64) u64 = { return *((&f): *u64); };
|
||||
|
||||
fn frombits(b: u64) f64 = { return *((&b): *f64); };
|
||||
|
||||
// #96 deref-load: dirty X0 with junk before the call so X0-retention can't mask
|
||||
// a broken return load; `*px` must reach X0 for the MULSD.
|
||||
@test fn deref_basic() void = {
|
||||
let x: f64 = 7.5;
|
||||
let y: f64 = 1.25;
|
||||
let px: *f64 = &x;
|
||||
let junk: f64 = y * 2.0;
|
||||
assert(junk == 2.5);
|
||||
let r: f64 = deref(px);
|
||||
assert(r == 7.5);
|
||||
let q: f64 = *px * 2.0;
|
||||
assert(q == 15.0);
|
||||
};
|
||||
|
||||
// #96 value propagation through an f64-returning fn then truncated to i32.
|
||||
@test fn deref_valueprop() void = {
|
||||
let x: f64 = 42.0;
|
||||
let r: f64 = deref(&x);
|
||||
assert(r: i32 == 42);
|
||||
};
|
||||
|
||||
@test fn arith_deref() void = {
|
||||
let x: f64 = 7.5;
|
||||
let p: *f64 = &x;
|
||||
let q: f64 = *p * 2.0;
|
||||
assert(q: i32 == 15);
|
||||
};
|
||||
|
||||
// #96 f64frombits round-trip: reinterpret a u64 bit pattern as f64.
|
||||
@test fn frombits_roundtrip() void = {
|
||||
let bits: u64 = 0x4045000000000000u64;
|
||||
let f: f64 = *((&bits): *f64);
|
||||
assert(f: i32 == 42);
|
||||
};
|
||||
|
||||
// #96 copysign(5.0, -1.0) == -5.0 built from tobits/frombits reinterprets.
|
||||
@test fn copysign() void = {
|
||||
let x: f64 = 5.0;
|
||||
let y: f64 = -1.0;
|
||||
let mag: u64 = tobits(x) & 0x7fffffffffffffffu64;
|
||||
let sgn: u64 = tobits(y) & 0x8000000000000000u64;
|
||||
let r: f64 = frombits(mag | sgn);
|
||||
assert(!(r > 0.0));
|
||||
assert(r < -4.5);
|
||||
assert(r > -5.5);
|
||||
};
|
||||
|
||||
// #97 full NaN relop sweep (f64, UCOMISD). Runtime NaN via 0.0/0.0 through
|
||||
// opaque fns so the checker can't const-fold it.
|
||||
@test fn nan_sweep() void = {
|
||||
let z: f64 = zero();
|
||||
let nan: f64 = z / z;
|
||||
let x: f64 = one();
|
||||
assert(nan != nan);
|
||||
assert(!(nan == nan));
|
||||
assert(!(nan == x));
|
||||
assert(!(nan < x));
|
||||
assert(!(nan <= x));
|
||||
assert(!(nan > x));
|
||||
assert(!(nan >= x));
|
||||
assert(nan != x);
|
||||
assert(!(x != x));
|
||||
assert(x == x);
|
||||
assert(x < 2.0);
|
||||
assert(x <= 1.0);
|
||||
assert(2.0 > x);
|
||||
assert(1.0 >= x);
|
||||
assert(!(x > 2.0));
|
||||
};
|
||||
|
||||
// #97 value propagation: of the 6 relops against NaN, exactly one (`!=`) is true.
|
||||
@test fn nan_count() void = {
|
||||
let nan: f64 = z() / z();
|
||||
let x: f64 = 1.0;
|
||||
let n: i32 = 0;
|
||||
if (nan == x) { n += 1; };
|
||||
if (nan != x) { n += 1; };
|
||||
if (nan < x) { n += 1; };
|
||||
if (nan <= x) { n += 1; };
|
||||
if (nan > x) { n += 1; };
|
||||
if (nan >= x) { n += 1; };
|
||||
assert(n == 1);
|
||||
};
|
||||
|
||||
// #97 f32 path (UCOMISS): NaN unordered rules, plus ordered f64 relops correct.
|
||||
@test fn f32_nan_ordered() void = {
|
||||
let z: f32 = z32();
|
||||
let nan: f32 = z / z;
|
||||
let x: f32 = one32();
|
||||
assert(nan != nan);
|
||||
assert(!(nan == nan));
|
||||
assert(!(nan < x));
|
||||
assert(!(nan >= x));
|
||||
let a: f64 = 2.0;
|
||||
let b: f64 = 3.0;
|
||||
assert(a < b);
|
||||
assert(!(a > b));
|
||||
assert(a <= a);
|
||||
assert(b >= a);
|
||||
assert(a == 2.0);
|
||||
assert(!(a != 2.0));
|
||||
assert(!(b < a));
|
||||
assert(b > a);
|
||||
};
|
||||
|
||||
// #97 `>`/`>=` left-bare arm with runtime-built operands (JA/JAE template
|
||||
// unchanged by the fix — guards the untouched arm).
|
||||
@test fn gt_only() void = {
|
||||
let a: f64 = z() + 2.0;
|
||||
let b: f64 = z() + 3.0;
|
||||
assert(b > a);
|
||||
assert(!(a > b));
|
||||
assert(b >= a);
|
||||
assert(a >= a);
|
||||
};
|
||||
55
test/lang/f64xmm_test.ww
Normal file
55
test/lang/f64xmm_test.ww
Normal file
@@ -0,0 +1,55 @@
|
||||
// f64xmm_test — f64 XMM materialise + tuple .0 compare, migrated from
|
||||
// test/wcc/955_f64xmm_run.c (#5-C4, #103). Two GATE-BLIND faces (both stages
|
||||
// emitted byte-identical-but-wrong asm, so byte-id never catches a reintroduction
|
||||
// — the T1 cstage run is the live net):
|
||||
// FACE X — a no-decimal float-typed integer literal (`0f64`, `8f64`) is an
|
||||
// N_INTLIT carrying float TYPE; the integer-immediate path stranded it in
|
||||
// AX, so `n == 0f64` compared a stale X0 and `(8f64 * 10.0): i32` read
|
||||
// garbage. Fixed by routing the float-typed N_INTLIT through the float-
|
||||
// constant-in-X0 emit (+ the wwstage exprfloatkind N_INTLIT arm).
|
||||
// FACE Z — a tuple positional f64 field read (`r.0`, r:(f64,i64)) loaded via
|
||||
// the integer op into AX, so `r.0 == 0.0` was wrongly true. Fixed by a
|
||||
// fld_isfloat branch -> MOVSD/MOVSS into X0.
|
||||
|
||||
package f64xmm_test;
|
||||
|
||||
fn g0(n: f64) i32 = { if (n == 0f64) { return 1; }; return 0; };
|
||||
|
||||
fn gneg(n: f64) i32 = { if (n == -8f64) { return 1; }; return 0; };
|
||||
|
||||
fn norm(n: f64) (f64, i64) = { return (n, 0); };
|
||||
|
||||
@test fn x_cmp_false() void = {
|
||||
assert(g0(8.0) == 0);
|
||||
};
|
||||
|
||||
@test fn x_cmp_true() void = {
|
||||
assert(g0(0.0) == 1);
|
||||
};
|
||||
|
||||
@test fn x_arith() void = {
|
||||
assert((8f64 * 10.0): i32 == 80);
|
||||
};
|
||||
|
||||
// `-8f64` is N_UN(TK_MINUS) over a float-typed N_INTLIT — the wwstage
|
||||
// exprfloatkind must recurse through the unary into the N_INTLIT-float arm.
|
||||
@test fn x_neg_cmp_true() void = {
|
||||
assert(gneg(-8.0) == 1);
|
||||
};
|
||||
|
||||
@test fn x_neg_cmp_false() void = {
|
||||
assert(gneg(8.0) == 0);
|
||||
};
|
||||
|
||||
// FACE Z: r.0 == 0.0 with r = (8.0, 0) must be false (bug: wrongly true).
|
||||
@test fn z_tuple_field() void = {
|
||||
const r = norm(8.0);
|
||||
assert(r.0 != 0.0);
|
||||
};
|
||||
|
||||
// CONTROL: the let-bound spelling was already correct and must stay correct.
|
||||
@test fn z_letbound_control() void = {
|
||||
const r = norm(8.0);
|
||||
const m = r.0;
|
||||
assert(m != 0.0);
|
||||
};
|
||||
56
test/lang/f9_float_test.ww
Normal file
56
test/lang/f9_float_test.ww
Normal file
@@ -0,0 +1,56 @@
|
||||
// f9_float_test — F9 argument widen/drain ABI (float), migrated from
|
||||
// test/wcc/949_f9_float_run.c (#5-C4, #30/#48/#49). The cgcall push/drain of a
|
||||
// CONCRETE arg widened into a tagged-union param slot, where a float is involved:
|
||||
// #30 — a float arg AFTER a widened (i64|void) arg under-drained by one GP word
|
||||
// (the f64 read the box's leftover payload).
|
||||
// #48 — the widened arg IS an f64 source: the float arm ate the TAG word into
|
||||
// X0 and the payload landed in DI as the tag.
|
||||
// #49 (#263) — a runtime f64 widened into (f64|void) whose arm reads the
|
||||
// payload: the widen-PUSH did PUSHQ AX while the f64 sat in X0.
|
||||
// All align wwstage UP to cstage's precomputed widen handling; T1 run + T2
|
||||
// byte-id. Register-cursor invariant (rob): the values survive only if tag/
|
||||
// payload land in the right GP regs AND the float in the right XMM.
|
||||
|
||||
package f9_float_test;
|
||||
|
||||
fn f(a: (i64 | void), b: f64) f64 = { return b; };
|
||||
|
||||
fn g(v: (i64 | f64)) i32 = {
|
||||
match (v) {
|
||||
case let n: i64 =>
|
||||
return 1;
|
||||
case let d: f64 =>
|
||||
return 2;
|
||||
};
|
||||
return 9;
|
||||
};
|
||||
|
||||
type fv = (f64 | void);
|
||||
|
||||
fn mk(x: f64) f64 = { return x + 1.5; };
|
||||
|
||||
fn take(v: fv) i32 = {
|
||||
match (v) {
|
||||
case let d: f64 => {
|
||||
if (d == 2.5) { return 0; };
|
||||
return 1;
|
||||
};
|
||||
case void => { return 2; };
|
||||
};
|
||||
return 3;
|
||||
};
|
||||
|
||||
@test fn widen_then_float() void = {
|
||||
let r: f64 = f(7, 1.5);
|
||||
assert(r == 1.5);
|
||||
};
|
||||
|
||||
@test fn float_source_widen() void = {
|
||||
let d: f64 = 3.5;
|
||||
assert(g(d) == 2);
|
||||
};
|
||||
|
||||
@test fn runtime_float_widen_payload() void = {
|
||||
let d: f64 = mk(1.0);
|
||||
assert(take(d) == 0);
|
||||
};
|
||||
44
test/lang/floatarr_test.ww
Normal file
44
test/lang/floatarr_test.ww
Normal file
@@ -0,0 +1,44 @@
|
||||
// floatarr_test — float array-element load/store, migrated from
|
||||
// test/wcc/946_floatarr_run.c (#5-C4, #119 + #122). A float ARRAY ELEMENT load
|
||||
// must land in X0 (MOVSS/MOVSD), not the integer register file (MOVQ -> AX); the
|
||||
// element STORE (array-lit init, arr[i]=, and [v...] repeat-fill) must route FROM
|
||||
// X0 via MOVSS/MOVSD, not write the raw double low-bits via MOVL AX. Pre-fix the
|
||||
// f64 elements loaded into AX while the consumer's ADDSD read a stale X0, and f32
|
||||
// slots read back garbage. The N_INDEX result type is checker-stamped (dodging
|
||||
// the #121 unstamped trap). Both stages byte-identical; T1 run + T2 byte-id.
|
||||
|
||||
package floatarr_test;
|
||||
|
||||
@test fn f64_arith() void = {
|
||||
let a: [3]f64 = [1.5, 2.5, 9.0];
|
||||
assert(a[0] + a[1] == 4.0);
|
||||
};
|
||||
|
||||
@test fn f64_trunc() void = {
|
||||
let a: [3]f64 = [1.5, 2.5, 9.0];
|
||||
assert(a[0]: i32 == 1);
|
||||
};
|
||||
|
||||
@test fn f64_elem2() void = {
|
||||
let a: [3]f64 = [1.5, 2.5, 9.0];
|
||||
assert(a[2] == 9.0);
|
||||
};
|
||||
|
||||
@test fn f32_arith() void = {
|
||||
let b: [2]f32 = [1.5f32, 2.5f32];
|
||||
assert((b[0] + b[1]): f64 == 4.0);
|
||||
};
|
||||
|
||||
// arr[i]= index store (#122) into a [0.0f32,0.0f32]-init array.
|
||||
@test fn f32_index_store() void = {
|
||||
let b: [2]f32 = [0.0f32, 0.0f32];
|
||||
b[0] = 1.5f32;
|
||||
b[1] = 2.5f32;
|
||||
assert((b[0] + b[1]): f64 == 4.0);
|
||||
};
|
||||
|
||||
// [v...] repeat-fill init store (#122): 1.5 * 3 == 4.5 (exact in IEEE).
|
||||
@test fn f32_repeat_fill() void = {
|
||||
let c: [3]f32 = [1.5f32...];
|
||||
assert((c[0] + c[1] + c[2]): f64 == 4.5);
|
||||
};
|
||||
45
test/lang/floatlit_test.ww
Normal file
45
test/lang/floatlit_test.ww
Normal file
@@ -0,0 +1,45 @@
|
||||
// floatlit_test — float LITERAL fold correctly-rounded, migrated from
|
||||
// test/wcc/989_floatlit_run.c (#5-C4). The two stages once folded float
|
||||
// literals differently: cstage via strtod (correctly rounded), wwstage via a
|
||||
// pow-10 accumulation that was 1-2 ULP off on decimal fractions, overflowed its
|
||||
// i64 accumulator past 19 mantissa digits, and missed DBL_MIN/DBL_MAX by up to
|
||||
// 2 ULP (989 ratchet #59.10). The fix routes lexnum through strconv.stof64 (the
|
||||
// Hare-ported correctly-rounded decimal engine).
|
||||
//
|
||||
// Each row reads back a literal's IEEE bits via a *u64 reinterpret and asserts
|
||||
// the C-strtod-oracle bit pattern. The C driver ran (a) cstage build+run + (b)
|
||||
// w6c-vs-w6c_ww .s byte-id; here the cstage run is test-lang (T1) and byte-id is
|
||||
// test-lang-byteid (T2), so both dimensions survive. The overflow-reject leg
|
||||
// (1.7976931348623159e308, both stages must reject) is the fold-3 runww carrier
|
||||
// test/wcc/data/floatlit_overflow/case.ww.
|
||||
|
||||
package floatlit_test;
|
||||
|
||||
fn bits(v: f64) u64 = {
|
||||
let x: f64 = v;
|
||||
let p: *u64 = (&x): *u64;
|
||||
return *p;
|
||||
};
|
||||
|
||||
@test fn vectors() void = {
|
||||
assert(bits(1.0000000000000002) == 0x3FF0000000000001u64);
|
||||
assert(bits(9007199254740993.0) == 0x4340000000000000u64);
|
||||
assert(bits(1.2345e67) == 0x4DDD4E421712C0B7u64);
|
||||
assert(bits(0.1) == 0x3FB999999999999Au64);
|
||||
assert(bits(1.1) == 0x3FF199999999999Au64);
|
||||
assert(bits(123456789012345678901234567890.0) == 0x45F8EE90FF6C373Eu64);
|
||||
assert(bits(2.2250738585072014e-308) == 0x0010000000000000u64);
|
||||
assert(bits(0.3) == 0x3FD3333333333333u64);
|
||||
assert(bits(3.141592653589793) == 0x400921FB54442D18u64);
|
||||
assert(bits(1.7976931348623157e308) == 0x7FEFFFFFFFFFFFFFu64);
|
||||
assert(bits(1.7976931348623158e308) == 0x7FEFFFFFFFFFFFFFu64);
|
||||
assert(bits(7.2057594037927933e16) == 0x4370000000000000u64);
|
||||
assert(bits(1000000000000000000000.0) == 0x444B1AE4D6E2EF50u64);
|
||||
assert(bits(1_000.5) == 0x408F440000000000u64);
|
||||
assert(bits(1.00000000000000011102230246251565404236316680908203125) == 0x3FF0000000000000u64);
|
||||
assert(bits(1.00000000000000011102230246251565404236316680908203126) == 0x3FF0000000000001u64);
|
||||
assert(bits(4503599627370497.5) == 0x4330000000000002u64);
|
||||
assert(bits(0.5) == 0x3FE0000000000000u64);
|
||||
assert(bits(1.0e308) == 0x7FE1CCF385EBC8A0u64);
|
||||
assert(bits(2.225073858507202e-308) == 0x0010000000000001u64);
|
||||
};
|
||||
40
test/lang/globfloatstructarg_test.ww
Normal file
40
test/lang/globfloatstructarg_test.ww
Normal file
@@ -0,0 +1,40 @@
|
||||
// globfloatstructarg_test — module-global float-bearing struct passed by value,
|
||||
// migrated from test/wcc/989_globfloatstructarg_run.c (#5-C4, #31, F8 EXCEPTION).
|
||||
// cgcall's per-arg drain decides the SysV eightbyte class via structfloatclass
|
||||
// read from the local slot's tnode ONLY; a module-global struct arg (lc==nil)
|
||||
// kept stfc=0, so the float eightbytes drained as integers (POPQ into DI/SI) and
|
||||
// X0 was never loaded — the callee read its f64 fields from the wrong registers.
|
||||
// cstage classifies off the operand TYPE and ran correct — the cat-A divergence.
|
||||
// The fix keys structfloatclass off the global's declared tnode (align ww UP);
|
||||
// .s is byte-identical. The C driver ran both stages (rule-10 agree+hit); here
|
||||
// T1 cstage run + T2 byte-id. Each row had its own `pair`/`gp`; renamed per-row
|
||||
// for single-file coexistence (the global-vs-local drain class is unchanged).
|
||||
|
||||
package globfloatstructarg_test;
|
||||
|
||||
type pair_ff = struct { f: f64, i: i64 };
|
||||
let gp_ff: pair_ff = pair_ff { f = 1.5, i = 9 };
|
||||
fn take_ff(p: pair_ff) int = { if (p.f == 1.5 && p.i == 9) { return 0; }; return 2; };
|
||||
|
||||
type pair_if = struct { i: i64, f: f64 };
|
||||
let gp_if: pair_if = pair_if { i = 9, f = 2.5 };
|
||||
fn take_if(p: pair_if) int = { if (p.i == 9 && p.f == 2.5) { return 0; }; return 2; };
|
||||
|
||||
type pair_bb = struct { a: f64, b: f64 };
|
||||
let gp_bb: pair_bb = pair_bb { a = 1.5, b = 2.5 };
|
||||
fn take_bb(p: pair_bb) int = { if (p.a == 1.5 && p.b == 2.5) { return 0; }; return 2; };
|
||||
|
||||
// float_first: f->X0 (#31).
|
||||
@test fn float_first() void = {
|
||||
assert(take_ff(gp_ff) == 0);
|
||||
};
|
||||
|
||||
// int_first: int eb->GP, f->X0.
|
||||
@test fn int_first() void = {
|
||||
assert(take_if(gp_if) == 0);
|
||||
};
|
||||
|
||||
// both_float: two SSE eightbytes.
|
||||
@test fn both_float() void = {
|
||||
assert(take_bb(gp_bb) == 0);
|
||||
};
|
||||
230
test/lang/tuprecv_f64_test.ww
Normal file
230
test/lang/tuprecv_f64_test.ww
Normal file
@@ -0,0 +1,230 @@
|
||||
// tuprecv_f64_test — f64 SysV SSE-cursor tuple receive, migrated from
|
||||
// test/wcc/956_tuprecv_f64_run.c (#5-C4, #105 + #164/#107 + #10). A tuple-from-
|
||||
// call receive (single-var 16B/32B, destructure, reassign) must spill each
|
||||
// element CLASS-AWARE: an f64/f32 word `MOVSD/MOVSS X0, slot`, an integer word
|
||||
// `MOVQ <reg>, slot`. #105 (the headline) is RUNTIME-only and byte-id-BLIND —
|
||||
// both stages were symmetric-WRONG on master (both MOVQ AX,slot), diverging only
|
||||
// at runtime — so the T1 cstage run (which executes the asserts) is the live net
|
||||
// here; T2 byte-id guards rule-10. #164/#107 gave the multi-float tuple return a
|
||||
// parallel SSE cursor [X0,X1]; #10 sret's the over-cap (f64,f64,f64). The bug
|
||||
// rows use BRANCHED callees with an f64-param word (an inner issub() CALL
|
||||
// clobbers AX) — single-return callees mask the bug via register coincidence.
|
||||
//
|
||||
// SPLIT DIMENSION: the C driver's chk_stamped sub-dimension (w6c_ww emits NO
|
||||
// `asserttyped:` diagnostic on the float destructure binding — the #121 A-narrow
|
||||
// stamp net) is a wwstage-stderr grep test-lang has no channel for (T1 cstage run
|
||||
// + T2 .s byte-id). It is RETAINED in the slim C pin test/wcc/956_tuprecv_f64_run.c
|
||||
// (drew C4 ruling: slim-in-place, the 954 ctl_destr precedent), NOT dropped.
|
||||
|
||||
package tuprecv_f64_test;
|
||||
|
||||
fn issub_f64(n: f64) bool = { return false; };
|
||||
|
||||
fn issub_i64(n: i64) bool = { return false; };
|
||||
|
||||
fn norm_fi(n: f64) (f64, i64) = {
|
||||
if (issub_f64(n)) { return (n * 2.0, -52); };
|
||||
return (n, 0);
|
||||
};
|
||||
|
||||
fn norm_if(n: f64) (i64, f64) = {
|
||||
if (issub_f64(n)) { return (-52, n * 2.0); };
|
||||
return (0, n);
|
||||
};
|
||||
|
||||
fn clob(x: f64) f64 = { return x + 1.0; };
|
||||
|
||||
fn pair(a: f64, b: f64) (f64, f64) = {
|
||||
if (issub_f64(a)) { return (a * 2.0, b * 2.0); };
|
||||
return (a, b);
|
||||
};
|
||||
|
||||
fn tri(a: i64, b: f64, c: i64) (i64, f64, i64) = {
|
||||
if (issub_i64(a)) { return (a * 2, b * 2.0, c * 2); };
|
||||
return (a, b, c);
|
||||
};
|
||||
|
||||
fn fs(n: f64) (f64, str) = {
|
||||
if (issub_f64(n)) { return (n * 2.0, "x"); };
|
||||
return (n, "hello");
|
||||
};
|
||||
|
||||
fn sf(n: f64) (str, f64) = {
|
||||
if (issub_f64(n)) { return ("x", n * 2.0); };
|
||||
return ("hello", n);
|
||||
};
|
||||
|
||||
fn si(n: i64) (str, i64) = {
|
||||
if (issub_i64(n)) { return ("x", n * 2); };
|
||||
return ("hello", n);
|
||||
};
|
||||
|
||||
fn tri3(a: f64, b: f64, c: f64) (f64, f64, f64) = {
|
||||
return (a, b, c);
|
||||
};
|
||||
|
||||
fn mk_int(n: i64) (i64, i64) = {
|
||||
if (issub_i64(n)) { return (n * 2, -1); };
|
||||
return (n, 7);
|
||||
};
|
||||
|
||||
fn mk_fz() (f64, i64) = { return (2.5, 7); };
|
||||
|
||||
// BUG — minimal repro: branched norm, f64 word is param n not a literal.
|
||||
@test fn f64_i64_br() void = {
|
||||
const r = norm_fi(16.0);
|
||||
const m: f64 = r.0;
|
||||
assert(m == 16.0);
|
||||
};
|
||||
|
||||
// BUG — deferred read: an intervening f64 CALL clobbers X0 AFTER the receive;
|
||||
// r.0 must come from the spilled slot, not a stale X0.
|
||||
@test fn f64_i64_deferred() void = {
|
||||
const r = norm_fi(16.0);
|
||||
const junk: f64 = clob(3.0);
|
||||
const m: f64 = r.0;
|
||||
assert(m == 16.0);
|
||||
assert(junk == 4.0);
|
||||
};
|
||||
|
||||
// BUG — order-swap (i64, f64): f64 is word1.
|
||||
@test fn i64_f64_br() void = {
|
||||
const r = norm_if(16.0);
|
||||
const i: i64 = r.0;
|
||||
const m: f64 = r.1;
|
||||
assert(m == 16.0);
|
||||
assert(i == 0);
|
||||
};
|
||||
|
||||
// BUG — DESTRUCTURE `let (m,i)=norm()` (N_MLET / cgmlet+tupstore).
|
||||
@test fn destr_f64_i64_br() void = {
|
||||
let (m, i) = norm_fi(16.0);
|
||||
assert(m == 16.0);
|
||||
assert(i == 0);
|
||||
};
|
||||
|
||||
// BUG — DESTRUCTURE order-swap (i64,f64): f64 binding is element 1 (cursor DX).
|
||||
@test fn destr_i64_f64_br() void = {
|
||||
let (i, m) = norm_if(16.0);
|
||||
assert(m == 16.0);
|
||||
assert(i == 0);
|
||||
};
|
||||
|
||||
// BUG — REASSIGN `m,i = norm()` (N_MASSIGN / cgmassign+tupstore).
|
||||
@test fn massign_f64_i64_br() void = {
|
||||
let m: f64 = 0.0;
|
||||
let i: i64 = 0;
|
||||
m, i = norm_fi(16.0);
|
||||
assert(m == 16.0);
|
||||
assert(i == 0);
|
||||
};
|
||||
|
||||
// BUG — REASSIGN order-swap (i64,f64): f64 target is element 1 (cursor DX).
|
||||
@test fn massign_i64_f64_br() void = {
|
||||
let i: i64 = 0;
|
||||
let m: f64 = 0.0;
|
||||
i, m = norm_if(16.0);
|
||||
assert(m == 16.0);
|
||||
assert(i == 0);
|
||||
};
|
||||
|
||||
// #164 — multi-float (f64,f64) destructure: on master both elements collide on
|
||||
// X0; post-fix a rides X0, b rides X1.
|
||||
@test fn f64f64_destr_br() void = {
|
||||
let (x, y) = pair(3.0, 5.0);
|
||||
assert(x == 3.0);
|
||||
assert(y == 5.0);
|
||||
};
|
||||
|
||||
// #164 — multi-float (f64,f64) SINGLE-VAR whole-tuple receive (16B rt16 branch).
|
||||
@test fn f64f64_single_br() void = {
|
||||
const r = pair(3.0, 5.0);
|
||||
const x: f64 = r.0;
|
||||
const y: f64 = r.1;
|
||||
assert(x == 3.0);
|
||||
assert(y == 5.0);
|
||||
};
|
||||
|
||||
// #164 — multi-float (f64,f64) REASSIGN.
|
||||
@test fn f64f64_massign_br() void = {
|
||||
let x: f64 = 0.0;
|
||||
let y: f64 = 0.0;
|
||||
x, y = pair(3.0, 5.0);
|
||||
assert(x == 3.0);
|
||||
assert(y == 5.0);
|
||||
};
|
||||
|
||||
// #164 — INTERLEAVED (i64,f64,i64): kills naive position->reg; i64s ride AX,DX,
|
||||
// f64 rides X0 on an independent counter.
|
||||
@test fn i64_f64_i64_destr() void = {
|
||||
let (x, y, z) = tri(3, 2.0, 7);
|
||||
assert(x == 3);
|
||||
assert(y == 2.0);
|
||||
assert(z == 7);
|
||||
};
|
||||
|
||||
// #164 — (f64,str): SSE + wide 24B header coexist; f64->X0, str->AX,DX,CX.
|
||||
@test fn f64_str_destr() void = {
|
||||
let (f, s) = fs(4.0);
|
||||
assert(f == 4.0);
|
||||
assert(s.len == 5);
|
||||
};
|
||||
|
||||
// #164 STR-FIRST (str,f64): wide header in slot 0, f64 consumes no GP slot.
|
||||
@test fn str_f64_destr() void = {
|
||||
let (s, f) = sf(4.0);
|
||||
assert(s.len == 5);
|
||||
assert(f == 4.0);
|
||||
};
|
||||
|
||||
// #164 STR-FIRST (str,i64): pure-integer str-first, str@AX,DX,CX then i64@R8.
|
||||
@test fn str_i64_destr() void = {
|
||||
let (s, k) = si(7);
|
||||
assert(s.len == 5);
|
||||
assert(k == 7);
|
||||
};
|
||||
|
||||
// #164 STR-FIRST SINGLE-VAR annotated `let t: (str,i64)` — the shape the old 32B
|
||||
// single-var branch got wrong (read .ptr from DX while send placed it in AX).
|
||||
@test fn str_i64_single() void = {
|
||||
let t: (str, i64) = si(7);
|
||||
assert(t.1 == 7);
|
||||
assert(t.0.len == 5);
|
||||
};
|
||||
|
||||
// #10 OVER-CAP RECV — three f64 exceeds the SSE return cap (X0,X1); #10 sret's
|
||||
// it (callee stores X0/X1 -> @sretarg) and destructures it out of @sretscr.
|
||||
@test fn f64x3_recv() void = {
|
||||
let (x, y, z) = tri3(1.0, 2.0, 3.0);
|
||||
assert(x == 1.0);
|
||||
assert(y == 2.0);
|
||||
assert(z == 3.0);
|
||||
};
|
||||
|
||||
// CONTROL — all-integer branched 2-tuple (MOVQ path untouched).
|
||||
@test fn ctl_int_br() void = {
|
||||
const r = mk_int(5);
|
||||
const a: i64 = r.0;
|
||||
const b: i64 = r.1;
|
||||
assert((a: i32) + (b: i32) == 12);
|
||||
};
|
||||
|
||||
// CONTROL — destructure with NO f64 (integer element takes the unchanged path).
|
||||
@test fn ctl_destr() void = {
|
||||
let (a, b) = mk_int(5);
|
||||
assert((a: i32) + (b: i32) == 12);
|
||||
};
|
||||
|
||||
// CONTROL — #103 FACE-Z single-return (f64,i64) field read (literal f64 word).
|
||||
@test fn ctl_facez_single() void = {
|
||||
const t = mk_fz();
|
||||
const f: f64 = t.0;
|
||||
const i: i64 = t.1;
|
||||
assert((f: i32) + (i: i32) == 9);
|
||||
};
|
||||
|
||||
// CONTROL — #103 FACE-X bare 0f64 compare (no tuple).
|
||||
@test fn ctl_facex_0f64() void = {
|
||||
const z: f64 = 0.0;
|
||||
assert(z == 0.0);
|
||||
};
|
||||
Reference in New Issue
Block a user