// Runtime contract for #163, the tuple-PARAM ABI (the param // twin of #164's tuple RETURN). Migrated from test/wcc/905_tupparam_run.c. // A tuple passed AS AN ARGUMENT was unhandled in both stages: a tuple-typed // call result left its elements in the return-ABI cursor and the SEND fell to // the 1-GP-word else, so all but the first element was dropped. The fix // restages the tuple into a frame slot by SysV class then pushes the slot // words; both stages byte-identical (rule 10). GATE-BLIND TO BYTE-ID ALONE: // pre-fix both stages were symmetric-WRONG, so cs==ww held — the rows diverge // only at RUNTIME; these @test fns pin that runtime contract (T1), the byte-id // gate (T2) keeps cs==ww. The loud-stop arg-reg-overflow row is a reject // carrier (test/wcc/data/tupparam_gp_overflow_loudstop/). package tupparam_test; fn pair_f64f64_arg(a: f64, b: f64) (f64, f64) = { return (a, b); }; fn add_f64f64_arg(t: (f64, f64)) f64 = { return t.0 + t.1; }; fn pair_i64i64_arg(a: i64, b: i64) (i64, i64) = { return (a, b); }; fn add_i64i64_arg(t: (i64, i64)) i64 = { return t.0 + t.1; }; fn mk_f64i64_arg(a: f64, b: i64) (f64, i64) = { return (a, b); }; fn add_f64i64_arg(t: (f64, i64)) i64 = { return (t.0: i64) + t.1; }; fn mk_i64f64_arg(a: i64, b: f64) (i64, f64) = { return (a, b); }; fn add_i64f64_arg(t: (i64, f64)) i64 = { return t.0 + (t.1: i64); }; fn mk_f64str_arg(a: f64) (f64, str) = { return (a, "hello"); }; fn add_f64str_arg(t: (f64, str)) i64 = { return (t.0: i64) + (t.1.len: i64); }; fn pair_two_tuple_args(a: i64, b: i64) (i64, i64) = { return (a, b); }; fn add4_two_tuple_args(s: (i64, i64), t: (i64, i64)) i64 = { return s.0 + s.1 + t.0 + t.1; }; fn pair_f64f64_chain(a: f64, b: f64) (f64, f64) = { return (a, b); }; fn id_f64f64_chain(t: (f64, f64)) f64 = { return t.0 * 10.0 + t.1; }; // HEADLINE — (f64, f64) arg. Pre-fix the SEND pushes only AX (the two floats // stay stranded in X0/X1) and the callee reads one GP word. Post-fix each // float rides the SSE arg cursor (X0, X1). @test fn f64f64_arg() void = { assert(!(add_f64f64_arg(pair_f64f64_arg(3.0, 5.0)) != 8.0)); }; // (i64, i64) arg — proves the broader INTEGER-tuple-param drop is fixed // (master dropped the second i64 too). e0->DI, e1->SI. @test fn i64i64_arg() void = { assert(!(add_i64i64_arg(pair_i64i64_arg(3, 5)) != 8)); }; // (f64, i64) — class independent of position: f64@X0 (SSE cursor), i64@DI // (INTEGER cursor), independent counters. @test fn f64i64_arg() void = { assert(!(add_f64i64_arg(mk_f64i64_arg(3.0, 5)) != 8)); }; // (i64, f64) — order-swap: i64@DI, f64@X0. Confirms the float lands in the // next XMM regardless of its positional slot. @test fn i64f64_arg() void = { assert(!(add_i64f64_arg(mk_i64f64_arg(3, 5.0)) != 8)); }; // (f64, str) — SSE + wide (24B {ptr,len,cap}) coexist. The f64 rides X0 (SSE, // consuming no GP slot); the str rides DI/SI/DX (INTEGER cursor). f=4.0, // s.len=5 -> 4+5 = 9. @test fn f64str_arg() void = { assert(!(add_f64str_arg(mk_f64str_arg(4.0)) != 9)); }; // MULTI-TUPLE-ARG, ONE CALL — f(g(), h()) where both args are tuple-producing // calls. Proves @tupargscr (single-slot-per-fn) is REUSED per arg, not // COLLIDED. 1+2+3+4 = 10. @test fn two_tuple_args() void = { assert(!(add4_two_tuple_args(pair_two_tuple_args(1, 2), pair_two_tuple_args(3, 4)) != 10)); }; // CONTROL — a tuple arg threaded through a chain of two calls, proving the // SEND/RECV round-trips through the slot intact. @test fn f64f64_chain() void = { assert(!(id_f64f64_chain(pair_f64f64_chain(3.0, 5.0)) != 35.0)); };