w6c+wwstage: receive over-cap tuple sret returns at the call site (#10 Fold B)
Fold A made the CALLEE emit an over-capacity tuple return (> 4 GP or > 2
SSE eightbytes) via sret, but every receive site stayed loud-stopped, so
such a fn was not yet usefully callable. Fold B wires the call/receive end
by aligning every receive gate UP to the shared cg_sret_retsize() /
callsretsize() > 0 predicate (never a kind), per Rob's (B) ruling:
- single-var-let `let t = f();` cstage gate generalised from
TY_STRUCT&&>24 to cg_sret_retsize(lt)>0; the let's slot IS the
sret dest, the callee writes the whole tuple there, t.0/t.1 read
by offset. wwstage already keyed callsretsize (verified).
- N_ASSIGN-ident `t = f();` same generalisation; global arm
kept TY_STRUCT-only (a tuple-global has no sret-to-symbol path in
either stage). wwstage grows a tuple-local arm (rettupleof gates
it apart from the >24B-struct recv, which keeps its own path).
- destructure `let (a,b) = f();` and `a,b = f();` — the genuinely
new wiring: the callee sret's into the @sretscr discard slot, then
a copy-out loop moves each element to its binding at the SAME
packed offset the SEND wrote (foff += element size), each at its
natural width (#169); a `_` binding skips its store but advances
foff. Both stages, byte-identical.
- return-forward `return f();` cstage forward gate generalised
to the predicate, reusing cg_sret_forward verbatim. wwstage
already keyed sretretsize (verified).
The escape boundary stays loud: arg-pass `g(f())` fatals identically in
both stages (tuple arg exceeds return-cursor ABI capacity).
Test 799 is the runtime net Fold A deferred (byte-id is blind to a
SEND/RECEIVE layout mismatch): the bytes.cut-shaped ([]u8,[]u8) round-trip
over destructure / single-var-let / reassign / return-forward, each both
RUN under cstage and asserted cs==ww byte-identical. Tests 945 (row F)
and 956 (f64x3) flip from asserting the old over-cap loud-stop to
asserting the now-working sret round-trip. combined.ww amalgams (w6c +
wwdump embed the wcc cgen) regenerated. Unblocks #4 bytes.cut/rcut.
This commit is contained in:
@@ -25659,6 +25659,26 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
// #10 Fold B: over-cap tuple reassign `t = f();`. t's
|
||||
// slot (off) IS the caller-prealloc dest; the callee
|
||||
// writes the whole tuple through hidden RDI. Keys on
|
||||
// callsretsize (the shared sret SSoT) for a tuple-
|
||||
// returning call — rettupleof distinguishes it from a
|
||||
// >24B struct, which keeps its own size-aware recv
|
||||
// below. Mirrors the cstage N_ASSIGN-ident over-cap arm.
|
||||
if (n.op == tkind.TK_ASSIGN && n.rhs != nil
|
||||
&& n.rhs.kind == nkind.N_CALL) {
|
||||
let rtup: *node = rettupleof(c, n.rhs);
|
||||
if (rtup != nil) {
|
||||
let rscs: i32 = callsretsize(c, n.rhs);
|
||||
if (rscs > 0) {
|
||||
c.sretdestoff = off;
|
||||
cgexpr(c, n.rhs);
|
||||
c.sretdestoff = 0;
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
// Detect str/slice-typed local — assignment must store
|
||||
// both halves (AX=ptr at +0, BX=len at +8) for str,
|
||||
// plus the cap (CX at +16) for slice.
|
||||
@@ -27668,8 +27688,81 @@ fn cgmassign(c: *cgen, n: *node) void = {
|
||||
// multi-assign idiom — rule-9 carve-out. Over-capacity loud-stops.
|
||||
let rettuple: *node = rettupleof(c, n.rhs);
|
||||
|
||||
// #10 Fold B: over-cap tuple destructure REASSIGN. Same sret copy-out
|
||||
// as cgmlet but the slots already exist (localfind); a `_` / missing
|
||||
// binding (off == 0) SKIPS its store yet still ADVANCES foff so the
|
||||
// next element stays aligned (harec `_`). Byte-identical to the
|
||||
// cstage N_MASSIGN over-cap arm.
|
||||
let sretrecv: i32 = 0;
|
||||
if (n.rhs != nil) {
|
||||
if (n.rhs.kind == nkind.N_CALL) {
|
||||
sretrecv = callsretsize(c, n.rhs);
|
||||
};
|
||||
};
|
||||
|
||||
if (n.rhs != nil) { cgexpr(c, n.rhs); };
|
||||
|
||||
if (sretrecv > 0) {
|
||||
let scr: i32 = localfind(c, "@sretscr");
|
||||
let pt2: *node = nil;
|
||||
if (rettuple != nil) { pt2 = rettuple.list; };
|
||||
let foff: i32 = 0;
|
||||
let lb: *node = n.list;
|
||||
for (lb != nil) {
|
||||
let tn: *node = nil;
|
||||
if (pt2 != nil) { tn = pt2.lhs; };
|
||||
let isflt: bool = isfloattype(c, tn);
|
||||
let wide: bool = isstrtype(c, tn) || isslicetype(c, tn);
|
||||
let esz: i32 = 8;
|
||||
if (pt2 != nil) {
|
||||
let eti: *tinfo = pt2.lhs.type_: *tinfo;
|
||||
if (eti != nil) { esz = eti.size: i32; };
|
||||
};
|
||||
let off: i32 = 0;
|
||||
if (lb.kind == nkind.N_IDENT) { off = localfind(c, lb.str); };
|
||||
if (off != 0) {
|
||||
if (isflt) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, tn)) { mov = "MOVSS"; };
|
||||
emitline("\t"); emitline(mov); emitline("\t");
|
||||
emitoff((scr + foff): i64);
|
||||
emitline("(BP), X0\n");
|
||||
emitline("\t"); emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitoff(off: i64); emitline("(BP)\n");
|
||||
} else {
|
||||
if (wide) {
|
||||
let k: i32 = 0;
|
||||
for (k < esz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scr + foff + k): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((off + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 8;
|
||||
};
|
||||
} else {
|
||||
let lop: str = tnodeloadop(c, tn, esz);
|
||||
let sop: str = tnodestoreop(c, tn, esz);
|
||||
emitline("\t"); emitline(lop);
|
||||
emitline("\t");
|
||||
emitoff((scr + foff): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\t"); emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
emitoff(off: i64); emitline("(BP)\n");
|
||||
};
|
||||
};
|
||||
};
|
||||
foff += esz;
|
||||
lb = lb.next;
|
||||
if (pt2 != nil) { pt2 = pt2.next; };
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
|
||||
let ssecap: i32 = TUPLE_SSECAP; // X0,X1 per SysV
|
||||
let gptotal: i32 = 0;
|
||||
let ssetotal: i32 = 0;
|
||||
@@ -27755,8 +27848,75 @@ fn cgmlet(c: *cgen, n: *node) void = {
|
||||
// scalar rides 1 word into an 8B slot. Over-capacity loud-stops.
|
||||
let rettuple: *node = rettupleof(c, rhs);
|
||||
|
||||
// #10 Fold B: over-cap tuple destructure RECEIVE. The callee sret'd
|
||||
// the whole tuple into the @sretscr discard slot (cgcall sees
|
||||
// callsretsize > 0, no lvalue dest wired). Copy each element out to
|
||||
// its binding slot at the SAME packed offset the SEND wrote (foff +=
|
||||
// element size — the t.0/t.1 layout), each at its NATURAL width
|
||||
// (#169). Byte-identical to the cstage N_MLET over-cap arm.
|
||||
let sretrecv: i32 = 0;
|
||||
if (rhs.kind == nkind.N_CALL) { sretrecv = callsretsize(c, rhs); };
|
||||
|
||||
cgexpr(c, rhs);
|
||||
|
||||
if (sretrecv > 0) {
|
||||
let scr: i32 = localfind(c, "@sretscr");
|
||||
let pt2: *node = nil;
|
||||
if (rettuple != nil) { pt2 = rettuple.list; };
|
||||
let foff: i32 = 0;
|
||||
let lb: *node = n.list;
|
||||
for (lb != nil) {
|
||||
let tn: *node = nil;
|
||||
if (pt2 != nil) { tn = pt2.lhs; };
|
||||
let isflt: bool = isfloattype(c, tn);
|
||||
let wide: bool = isstrtype(c, tn) || isslicetype(c, tn);
|
||||
let esz: i32 = 8;
|
||||
if (pt2 != nil) {
|
||||
let eti: *tinfo = pt2.lhs.type_: *tinfo;
|
||||
if (eti != nil) { esz = eti.size: i32; };
|
||||
};
|
||||
let bsz: i32 = 8;
|
||||
if (wide) { bsz = tyslicesize(): i32; };
|
||||
let off: i32 = localadd(c, lb.str, bsz, tn);
|
||||
if (isflt) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, tn)) { mov = "MOVSS"; };
|
||||
emitline("\t"); emitline(mov); emitline("\t");
|
||||
emitoff((scr + foff): i64);
|
||||
emitline("(BP), X0\n");
|
||||
emitline("\t"); emitline(mov); emitline("\tX0, ");
|
||||
emitoff(off: i64); emitline("(BP)\n");
|
||||
} else {
|
||||
if (wide) {
|
||||
let k: i32 = 0;
|
||||
for (k < esz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scr + foff + k): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((off + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 8;
|
||||
};
|
||||
} else {
|
||||
let lop: str = tnodeloadop(c, tn, esz);
|
||||
let sop: str = tnodestoreop(c, tn, esz);
|
||||
emitline("\t"); emitline(lop); emitline("\t");
|
||||
emitoff((scr + foff): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\t"); emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
emitoff(off: i64); emitline("(BP)\n");
|
||||
};
|
||||
};
|
||||
foff += esz;
|
||||
lb = lb.next;
|
||||
if (pt2 != nil) { pt2 = pt2.next; };
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
|
||||
let ssecap: i32 = TUPLE_SSECAP; // X0,X1 per SysV
|
||||
let gptotal: i32 = 0;
|
||||
let ssetotal: i32 = 0;
|
||||
|
||||
Reference in New Issue
Block a user