Files
ww/test/lang/tuprecv_f64_test.ww
Hojun-Cho 60dec4a6bf 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).
2026-06-24 03:25:47 +09:00

231 lines
6.5 KiB
Plaintext

// 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);
};