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