Every section banner dies (103 -> 0) across test/lang, the observer suites, the C carriers, and the five comment-heavy corpus fixtures; banner provenance (#N cites, carrier numbers, repair-cluster labels) folded into headers or adjacent WHY comments. Narration deleted; row provenance, ref cites, divergence pins, and layout contracts kept (fwd-ref decl-order guards and bootstrap-gate corpus rationale restored where the sweep over-cut). Comment-only proven: all 3742 wwbuild workdir .s byte-identical before/after; test-commit and test-byteid (161 lang + 1399 data, 0 pinned-divergent) green.
231 lines
6.4 KiB
Plaintext
231 lines
6.4 KiB
Plaintext
// 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);
|
|
};
|