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:
@@ -14312,6 +14312,59 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node) i32 = {
|
||||
return rest + 1;
|
||||
};
|
||||
cgexpr(c, arg);
|
||||
// #163: tuple ARG (param twin of #164's return). cgexpr left the
|
||||
// tuple in the return-ABI cursor (AX/DX/CX/R8 + X0/X1); restage it
|
||||
// into @tupargscr by SysV class (tupstore, the #164 helper) and push
|
||||
// the slot words high->low so the pop drains slot+0 first into the
|
||||
// SysV ARG cursor. The frame slot decouples the return-class regs
|
||||
// from the overlapping arg-class regs. rettupleof scopes to an
|
||||
// N_CALL producer (tuple idents/literals as values are a separate
|
||||
// unimplemented gap; the SEND never pushes stale regs, rule 7).
|
||||
let tuparg: *node = rettupleof(c, arg);
|
||||
if (tuparg != nil) {
|
||||
let gptot: i32 = 0;
|
||||
let sstot: i32 = 0;
|
||||
let tsz: i32 = 0;
|
||||
let p: *node = tuparg.list;
|
||||
for (p != nil) {
|
||||
let et: *node = p.lhs;
|
||||
let wide: bool = isstrtype(c, et) || isslicetype(c, et);
|
||||
if (isfloattype(c, et)) { sstot += 1; }
|
||||
else { gptot += tupebytes(wide); };
|
||||
tsz += slotsize(c, et);
|
||||
p = p.next;
|
||||
};
|
||||
// The producing call already satisfied #164's return caps;
|
||||
// guard anyway (tupstore indexes [AX,DX,CX,R8] / [X0,X1]).
|
||||
if (gptot > 4 || sstot > 2) {
|
||||
let msg: str = "tuple arg exceeds return-cursor ABI capacity; see #163/#164\n";
|
||||
os.write(2, msg.ptr, msg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let scr: i32 = localadd(c, "@tupargscr", tsz, nil);
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
let eoff: i32 = 0;
|
||||
p = tuparg.list;
|
||||
for (p != nil) {
|
||||
let et: *node = p.lhs;
|
||||
let wide: bool = isstrtype(c, et) || isslicetype(c, et);
|
||||
tupstore(c, gpcur, ssecur, scr + eoff, wide, et);
|
||||
if (isfloattype(c, et)) { ssecur += 1; }
|
||||
else { gpcur += tupebytes(wide); };
|
||||
eoff += slotsize(c, et);
|
||||
p = p.next;
|
||||
};
|
||||
let w: i32 = tsz - 8;
|
||||
for (w >= 0) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scr + w): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
w -= 8;
|
||||
};
|
||||
return rest + tsz / 8;
|
||||
};
|
||||
if (nodeisslice(c, arg)) {
|
||||
emitline("\tPUSHQ\tCX\n");
|
||||
emitline("\tPUSHQ\tBX\n");
|
||||
@@ -21091,7 +21144,54 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
stackslots += 1;
|
||||
};
|
||||
popped += 1;
|
||||
} else {
|
||||
} else { let tuparg: *node = rettupleof(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
|
||||
// partial-spill stitch is out of scope (twin of #164).
|
||||
let p: *node = tuparg.list;
|
||||
for (p != nil) {
|
||||
let et: *node = p.lhs;
|
||||
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";
|
||||
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(SP), ");
|
||||
emitline(fargregname(fpidx));
|
||||
emitline("\n");
|
||||
emitline("\tADDQ\t$8, SP\n");
|
||||
fpidx += 1;
|
||||
popped += 1;
|
||||
} else {
|
||||
let wide: bool = 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";
|
||||
os.write(2, msg.ptr, msg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let k: i32 = 0;
|
||||
for (k < eb) {
|
||||
emitline("\tPOPQ\t");
|
||||
emitline(argregname(intidx));
|
||||
emitline("\n");
|
||||
intidx += 1;
|
||||
popped += 1;
|
||||
k += 1;
|
||||
};
|
||||
};
|
||||
p = p.next;
|
||||
};
|
||||
} else {
|
||||
let extra: i32 = 0;
|
||||
// str IS []u8: 3-word arg, same as slice (#1/Phase 3).
|
||||
if (nodeisstr(c, a)) { extra = 2; };
|
||||
@@ -21116,6 +21216,7 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
popped += 1;
|
||||
w += 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
a = a.next;
|
||||
};
|
||||
@@ -26180,6 +26281,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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -3932,7 +3932,54 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
stackslots += 1;
|
||||
};
|
||||
popped += 1;
|
||||
} else {
|
||||
} else { let tuparg: *node = rettupleof(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
|
||||
// partial-spill stitch is out of scope (twin of #164).
|
||||
let p: *node = tuparg.list;
|
||||
for (p != nil) {
|
||||
let et: *node = p.lhs;
|
||||
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";
|
||||
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(SP), ");
|
||||
emitline(fargregname(fpidx));
|
||||
emitline("\n");
|
||||
emitline("\tADDQ\t$8, SP\n");
|
||||
fpidx += 1;
|
||||
popped += 1;
|
||||
} else {
|
||||
let wide: bool = 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";
|
||||
os.write(2, msg.ptr, msg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let k: i32 = 0;
|
||||
for (k < eb) {
|
||||
emitline("\tPOPQ\t");
|
||||
emitline(argregname(intidx));
|
||||
emitline("\n");
|
||||
intidx += 1;
|
||||
popped += 1;
|
||||
k += 1;
|
||||
};
|
||||
};
|
||||
p = p.next;
|
||||
};
|
||||
} else {
|
||||
let extra: i32 = 0;
|
||||
// str IS []u8: 3-word arg, same as slice (#1/Phase 3).
|
||||
if (nodeisstr(c, a)) { extra = 2; };
|
||||
@@ -3957,6 +4004,7 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
popped += 1;
|
||||
w += 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
a = a.next;
|
||||
};
|
||||
|
||||
@@ -490,6 +490,59 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node) i32 = {
|
||||
return rest + 1;
|
||||
};
|
||||
cgexpr(c, arg);
|
||||
// #163: tuple ARG (param twin of #164's return). cgexpr left the
|
||||
// tuple in the return-ABI cursor (AX/DX/CX/R8 + X0/X1); restage it
|
||||
// into @tupargscr by SysV class (tupstore, the #164 helper) and push
|
||||
// the slot words high->low so the pop drains slot+0 first into the
|
||||
// SysV ARG cursor. The frame slot decouples the return-class regs
|
||||
// from the overlapping arg-class regs. rettupleof scopes to an
|
||||
// N_CALL producer (tuple idents/literals as values are a separate
|
||||
// unimplemented gap; the SEND never pushes stale regs, rule 7).
|
||||
let tuparg: *node = rettupleof(c, arg);
|
||||
if (tuparg != nil) {
|
||||
let gptot: i32 = 0;
|
||||
let sstot: i32 = 0;
|
||||
let tsz: i32 = 0;
|
||||
let p: *node = tuparg.list;
|
||||
for (p != nil) {
|
||||
let et: *node = p.lhs;
|
||||
let wide: bool = isstrtype(c, et) || isslicetype(c, et);
|
||||
if (isfloattype(c, et)) { sstot += 1; }
|
||||
else { gptot += tupebytes(wide); };
|
||||
tsz += slotsize(c, et);
|
||||
p = p.next;
|
||||
};
|
||||
// The producing call already satisfied #164's return caps;
|
||||
// guard anyway (tupstore indexes [AX,DX,CX,R8] / [X0,X1]).
|
||||
if (gptot > 4 || sstot > 2) {
|
||||
let msg: str = "tuple arg exceeds return-cursor ABI capacity; see #163/#164\n";
|
||||
os.write(2, msg.ptr, msg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let scr: i32 = localadd(c, "@tupargscr", tsz, nil);
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
let eoff: i32 = 0;
|
||||
p = tuparg.list;
|
||||
for (p != nil) {
|
||||
let et: *node = p.lhs;
|
||||
let wide: bool = isstrtype(c, et) || isslicetype(c, et);
|
||||
tupstore(c, gpcur, ssecur, scr + eoff, wide, et);
|
||||
if (isfloattype(c, et)) { ssecur += 1; }
|
||||
else { gpcur += tupebytes(wide); };
|
||||
eoff += slotsize(c, et);
|
||||
p = p.next;
|
||||
};
|
||||
let w: i32 = tsz - 8;
|
||||
for (w >= 0) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scr + w): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
w -= 8;
|
||||
};
|
||||
return rest + tsz / 8;
|
||||
};
|
||||
if (nodeisslice(c, arg)) {
|
||||
emitline("\tPUSHQ\tCX\n");
|
||||
emitline("\tPUSHQ\tBX\n");
|
||||
|
||||
@@ -14312,6 +14312,59 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node) i32 = {
|
||||
return rest + 1;
|
||||
};
|
||||
cgexpr(c, arg);
|
||||
// #163: tuple ARG (param twin of #164's return). cgexpr left the
|
||||
// tuple in the return-ABI cursor (AX/DX/CX/R8 + X0/X1); restage it
|
||||
// into @tupargscr by SysV class (tupstore, the #164 helper) and push
|
||||
// the slot words high->low so the pop drains slot+0 first into the
|
||||
// SysV ARG cursor. The frame slot decouples the return-class regs
|
||||
// from the overlapping arg-class regs. rettupleof scopes to an
|
||||
// N_CALL producer (tuple idents/literals as values are a separate
|
||||
// unimplemented gap; the SEND never pushes stale regs, rule 7).
|
||||
let tuparg: *node = rettupleof(c, arg);
|
||||
if (tuparg != nil) {
|
||||
let gptot: i32 = 0;
|
||||
let sstot: i32 = 0;
|
||||
let tsz: i32 = 0;
|
||||
let p: *node = tuparg.list;
|
||||
for (p != nil) {
|
||||
let et: *node = p.lhs;
|
||||
let wide: bool = isstrtype(c, et) || isslicetype(c, et);
|
||||
if (isfloattype(c, et)) { sstot += 1; }
|
||||
else { gptot += tupebytes(wide); };
|
||||
tsz += slotsize(c, et);
|
||||
p = p.next;
|
||||
};
|
||||
// The producing call already satisfied #164's return caps;
|
||||
// guard anyway (tupstore indexes [AX,DX,CX,R8] / [X0,X1]).
|
||||
if (gptot > 4 || sstot > 2) {
|
||||
let msg: str = "tuple arg exceeds return-cursor ABI capacity; see #163/#164\n";
|
||||
os.write(2, msg.ptr, msg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let scr: i32 = localadd(c, "@tupargscr", tsz, nil);
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
let eoff: i32 = 0;
|
||||
p = tuparg.list;
|
||||
for (p != nil) {
|
||||
let et: *node = p.lhs;
|
||||
let wide: bool = isstrtype(c, et) || isslicetype(c, et);
|
||||
tupstore(c, gpcur, ssecur, scr + eoff, wide, et);
|
||||
if (isfloattype(c, et)) { ssecur += 1; }
|
||||
else { gpcur += tupebytes(wide); };
|
||||
eoff += slotsize(c, et);
|
||||
p = p.next;
|
||||
};
|
||||
let w: i32 = tsz - 8;
|
||||
for (w >= 0) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scr + w): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
w -= 8;
|
||||
};
|
||||
return rest + tsz / 8;
|
||||
};
|
||||
if (nodeisslice(c, arg)) {
|
||||
emitline("\tPUSHQ\tCX\n");
|
||||
emitline("\tPUSHQ\tBX\n");
|
||||
@@ -21091,7 +21144,54 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
stackslots += 1;
|
||||
};
|
||||
popped += 1;
|
||||
} else {
|
||||
} else { let tuparg: *node = rettupleof(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
|
||||
// partial-spill stitch is out of scope (twin of #164).
|
||||
let p: *node = tuparg.list;
|
||||
for (p != nil) {
|
||||
let et: *node = p.lhs;
|
||||
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";
|
||||
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(SP), ");
|
||||
emitline(fargregname(fpidx));
|
||||
emitline("\n");
|
||||
emitline("\tADDQ\t$8, SP\n");
|
||||
fpidx += 1;
|
||||
popped += 1;
|
||||
} else {
|
||||
let wide: bool = 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";
|
||||
os.write(2, msg.ptr, msg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let k: i32 = 0;
|
||||
for (k < eb) {
|
||||
emitline("\tPOPQ\t");
|
||||
emitline(argregname(intidx));
|
||||
emitline("\n");
|
||||
intidx += 1;
|
||||
popped += 1;
|
||||
k += 1;
|
||||
};
|
||||
};
|
||||
p = p.next;
|
||||
};
|
||||
} else {
|
||||
let extra: i32 = 0;
|
||||
// str IS []u8: 3-word arg, same as slice (#1/Phase 3).
|
||||
if (nodeisstr(c, a)) { extra = 2; };
|
||||
@@ -21116,6 +21216,7 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
popped += 1;
|
||||
w += 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
a = a.next;
|
||||
};
|
||||
@@ -26180,6 +26281,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.
|
||||
|
||||
Reference in New Issue
Block a user