wcc: tuple-param ABI via SSE/GP arg cursors (#163)

Tuples were unhandled as parameters — no tuple arm in arg-push, arg-pop, or
callee-recv in either stage — so a tuple param fell to the 1-GP-word else and
dropped all but its first element (integer tuple params too; floats doubly
lost). Add tuple-param arms (SEND push+pop, callee RECV) across both stages,
reusing #164's per-element SysV classify with the 6-GP (DI,SI,DX,CX,R8,R9) +
8-SSE (X0-X7) arg cursors. A frame slot @tupargscr decouples the producing
call's return cursor from the overlapping arg cursor (capture-before-clobber).
Overflow (>6 GP / >8 SSE) fails loud (rule 7). Scoped to the N_CALL producer;
first-class tuple values (ident/literal) remain a separate unimplemented gap.

Gate-blind (the bootstrap passes no tuple params) — covered by table-driven
probe 905, which proves pre-fix element-drop and the loud-stop.
This commit is contained in:
2026-05-27 22:13:46 +09:00
parent 153c7b3b46
commit 0465c423c1
8 changed files with 986 additions and 3 deletions

View File

@@ -100,6 +100,62 @@ fn cgfnparams(c: *cgen, params: *node) void = {
p = p.next;
continue;
};
if (p.lhs != nil) { if (p.lhs.kind == nkind.N_TTUPLE) {
// #163: tuple PARAM receive (param twin of #164's
// return). Walk the tuple's elements over the SysV
// arg cursor — a float reads its XMM (X0..X7),
// everything else an INTEGER arg reg (DI/SI/..); a
// slice/str its 3-word {ptr,len,cap} — storing each
// into the param slot positionally (eoff steps by
// slotsize, matching the t.0/t.1 field-access walk +
// the SEND). Reg overflow loud-stops (rule 7); the
// partial-spill stitch is out of scope (twin of #164).
let off: i32 = localadd(c, nm, slotsize(c, p.lhs), p.lhs);
let eoff: i32 = 0;
let te: *node = p.lhs.list;
for (te != nil) {
let et: *node = te.lhs;
if (isfloattype(c, et)) {
if (fidx >= 8) {
let msg: str = "tuple param float element overflows SSE arg regs (X0..X7); stitch out of scope, see #163\n";
os.write(2, msg.ptr, msg.len: u64);
os.exit(1);
};
let mov: str = "MOVSD";
if (isf32type(c, et)) { mov = "MOVSS"; };
emitline("\t");
emitline(mov);
emitline("\t");
emitline(fargregname(fidx));
emitline(", ");
emitoff((off + eoff): i64);
emitline("(BP)\n");
fidx += 1;
} else {
let wide: bool = isstrtype(c, et) || isslicetype(c, et);
let eb: i32 = tupebytes(wide);
if (idx + eb > 6) {
let msg: str = "tuple param element overflows integer arg regs (DI/SI/DX/CX/R8/R9); stitch out of scope, see #163\n";
os.write(2, msg.ptr, msg.len: u64);
os.exit(1);
};
let k: i32 = 0;
for (k < eb) {
emitline("\tMOVQ\t");
emitline(argregname(idx));
emitline(", ");
emitoff((off + eoff + k*8): i64);
emitline("(BP)\n");
idx += 1;
k += 1;
};
};
eoff += slotsize(c, et);
te = te.next;
};
p = p.next;
continue;
}; };
if (isfloattype(c, p.lhs)) {
// Float param: SysV uses the XMM stream
// (X0..X7). 8B (f64) or 4B (f32) slot.