w6c+w6c_ww: tuple by-value ARG send — every cursor-filling producer rides #163 (C-t2, #32)

node_tuplearg was N_CALL-scoped and its comment claimed non-call forms
"loud-stop" — they did NOT: a tuple ident/literal/unwrap arg fell to
the scalar single-PUSHQ default, skewing every later arg register so
the callee read garbage word 2 (byte-id both stages, the gate-blind
both-wrong class; packed shapes SIGSEGV'd pre-C-t0). The receive side
(cgfn #163 walk) was already correct.

cgexpr already fills the return-ABI cursor for every supported
producer (#241: ident via slot-to-cursor, literal via lit-to-cursor,
unwrap via payload shift; call via the return ABI) — the send now
admits exactly those into the existing @tupargscr restage + per-class
drain (node_tuplearg widened; wwstage gains nodetuplearg, mirroring it
over the local tnode / inferletcalltype; rettupleof stays N_CALL-scoped
for the destructure receives). Any OTHER tuple-typed source shape
loud-stops at the push site — the false comment's claim, now true
(rule 7). Literal tuple elements are stamped expr types, so the
restage/drain wide test goes type_isstr/type_isslice (TY_UNTYPED_STR-
aware) with the ty_str->size header stride; the wwstage twin walks a
literal's VALUE exprs the way cgtuplelittocursor classifies them.

Ken review demands folded in: (1) a NESTED composite element
(tuple/struct/array/tagged inside the tuple) occupies more than the
one GP word the restage walk counts — the checker accepted it and it
ran WRONG (inner words skewed, wwstage SIGSEGV); both stages' restage
walks now loud-stop the element kind (wiring is the filed follow-up,
task #65). (2) the variadic interaction probed: a tuple arg ahead of
a variadic tail rides the restage correctly (positive row);
variadic-of-tuples stays bounded-loud via the tuple-in-slice read
surface.

941 grows the t2 matrix: packed/16B params with branched callees,
mixed arg orders both ways, literal arg, (f64,i64) param, unwrap arg,
ken's >6-GP-pressure stress (4 leading scalars + tuple + a 7th
stack-class word), variadic-after-tuple, plus rule-7 reject rows
(chain-source arg, nested-element arg, variadic-of-tuples, over-cap
ident arg) and the fold-4 charset substrate pin ([](u32,u32) append
stays LOUD). At the C-t1 parent 18/73 checks fail: every runtime arg
row except the (f64,i64) anchor on BOTH stages (byte-identically — the
gate-blind both-wrong class) and the chain/nested args silently
accepted.
This commit is contained in:
2026-06-04 18:43:32 +09:00
parent 12af54f9f8
commit 6426fac6f2
7 changed files with 635 additions and 101 deletions

View File

@@ -6056,17 +6056,23 @@ fn cgcall(c: *cgen, n: *node) void = {
stackslots += 1;
};
popped += 1;
} else { let tuparg: *node = rettupleof(c, a);
} else { let tuparg: *node = nodetuplearg(c, a);
if (tuparg != nil) {
// #163: drain the tuple's staged words (slot+0 pushed
// first) into the SysV arg cursor by SysV class — a
// float MOVSD/MOVSS off (SP) into the next XMM, else
// POPQ into the next INTEGER arg reg; a slice/str its
// 3 words. Reg overflow loud-stops (rule 7); the
// #163/#32: drain the tuple's staged words (slot+0
// pushed first) into the SysV arg cursor by SysV class
// — a float MOVSD/MOVSS off (SP) into the next XMM,
// else POPQ into the next INTEGER arg reg; a slice/str
// its 3 words. Reg overflow loud-stops (rule 7); the
// partial-spill stitch is out of scope (twin of #164).
// C-t2: nodetuplearg admits ident/literal/unwrap
// sources; a literal's elements are VALUE exprs,
// classified the way the @tupargscr restage classified
// them (kind-discriminated twin walk).
let tuplit: bool = tuparg.kind == nkind.N_TUPLE;
let p: *node = tuparg.list;
for (p != nil) {
let et: *node = p.lhs;
if (tuplit) { et = p; };
if (isfloattype(c, et)) {
if (fpidx >= 8) {
let msg: str = "tuple arg float element overflows SSE arg regs (X0..X7); stitch out of scope, see #163\n";
@@ -6084,7 +6090,9 @@ fn cgcall(c: *cgen, n: *node) void = {
fpidx += 1;
popped += 1;
} else {
let wide: bool = isstrtype(c, et) || isslicetype(c, et);
let wide: bool = false;
if (tuplit) { wide = nodeisstr(c, et) || nodeisslice(c, et); }
else { wide = isstrtype(c, et) || isslicetype(c, et); };
let eb: i32 = tupebytes(wide);
if (intidx + eb > 6) {
let msg: str = "tuple arg element overflows integer arg regs (DI/SI/DX/CX/R8/R9); stitch out of scope, see #163\n";