w6c+wwstage: aggregate return from any addressable source (#272 commit-1)
The N_RETURN aggregate arms gated the return source on N_IDENT || N_STRUCTLIT; every other aggregate rvalue (array literal, o.field N_DOT, a[i] N_INDEX, *p deref) fell through to the scalar-AX default = a silent 8-byte truncation. Both stages emitted byte-IDENTICAL wrong asm, so the byte-id gate could not catch it (#263 class) — the fix converges on the runtime oracle. Mirror the arg-side closure #271 landed: both arms (≤24B @retscr and >24B sret) now funnel N_ARRLIT through the literal element fill and N_DOT/N_INDEX/deref through aggarg_srcaddr + the #265/#268 whole- aggregate copy. Type-agnostic, so struct AND array returns are closed. A close-by-construction loud-stop (rule 7) guards any future unhandled aggregate source from reaching the scalar default. Closes the callee-half of (b)/(c) and the addressable siblings. The g = mk() global-receive caller-half is commit-2. 949_aggret_source_run pins the class: array-literal / N_DOT / N_INDEX / deref / named-ident control / >24B-sret-deref / struct-field / struct- deref, each summing all members (full readback) with per-row byte-id.
This commit is contained in:
@@ -28454,8 +28454,19 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
let okrhs: bool = false;
|
||||
// #272: >24B sret addressable-source closure —
|
||||
// N_DOT/N_INDEX/deref land their address in SI then
|
||||
// memcpy through *(@sretarg), mirroring cstage cgen.c
|
||||
// N_RETURN sret arm. N_ARRLIT >24B has no consumer
|
||||
// (loud-stops in cstage); not wired here.
|
||||
let addrsrc: bool = false;
|
||||
if (rhs.kind == nkind.N_IDENT) { okrhs = true; };
|
||||
if (rhs.kind == nkind.N_STRUCTLIT) { okrhs = true; };
|
||||
if (rhs.kind == nkind.N_DOT) { okrhs = true; addrsrc = true; };
|
||||
if (rhs.kind == nkind.N_INDEX) { okrhs = true; addrsrc = true; };
|
||||
if (rhs.kind == nkind.N_UN) {
|
||||
if (rhs.op == tkind.TK_STAR) { okrhs = true; addrsrc = true; };
|
||||
};
|
||||
if (okrhs) {
|
||||
if (rhs.kind == nkind.N_STRUCTLIT) {
|
||||
let trefn: *node = rhs.lhs;
|
||||
@@ -28477,6 +28488,43 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
1, sretargoff, emptys,
|
||||
0);
|
||||
};
|
||||
} else { if (addrsrc) {
|
||||
if (!aggargsrcaddr(c, rhs, "SI")) {
|
||||
let m4: str = "#272: aggregate return from unsupported source kind\n";
|
||||
os.write(2, m4.ptr, m4.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(sretargoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
let k: i32 = 0;
|
||||
for (k + 8 <= scs) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(k: i64);
|
||||
emitline("(BX)\n");
|
||||
k += 8;
|
||||
};
|
||||
for (k + 4 <= scs) {
|
||||
emitline("\tMOVL\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff(k: i64);
|
||||
emitline("(BX)\n");
|
||||
k += 4;
|
||||
};
|
||||
for (k < scs) {
|
||||
emitline("\tMOVB\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff(k: i64);
|
||||
emitline("(BX)\n");
|
||||
k += 1;
|
||||
};
|
||||
} else {
|
||||
let rl: *local = localfindnode(c, rhs.str);
|
||||
if (rl != nil) {
|
||||
@@ -28512,7 +28560,7 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
k += 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
}; };
|
||||
// sret return: RAX = dest pointer.
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(sretargoff: i64);
|
||||
@@ -28549,12 +28597,21 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
let rsz: i32 = structabisize(rsi);
|
||||
if (rsz <= 24) {
|
||||
let okrhs: bool = false;
|
||||
// #272: struct ≤24B addressable-source closure —
|
||||
// N_DOT/N_INDEX/deref memcpy into @retscr before the
|
||||
// shared structfloatclass tail (mirror cstage cgen.c).
|
||||
let addrsrc: bool = false;
|
||||
if (rhs.kind == nkind.N_IDENT) {
|
||||
okrhs = true;
|
||||
};
|
||||
if (rhs.kind == nkind.N_STRUCTLIT) {
|
||||
okrhs = true;
|
||||
};
|
||||
if (rhs.kind == nkind.N_DOT) { okrhs = true; addrsrc = true; };
|
||||
if (rhs.kind == nkind.N_INDEX) { okrhs = true; addrsrc = true; };
|
||||
if (rhs.kind == nkind.N_UN) {
|
||||
if (rhs.op == tkind.TK_STAR) { okrhs = true; addrsrc = true; };
|
||||
};
|
||||
if (okrhs) {
|
||||
let scroff: i32 = localadd(c,
|
||||
"@retscr", 24, nil);
|
||||
@@ -28577,6 +28634,51 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
// values recurse instead of dropping
|
||||
// trailing bytes.
|
||||
cgstructlitfillbp(c, rsi, rhs, scroff);
|
||||
} else { if (addrsrc) {
|
||||
// N_DOT / N_INDEX / deref: land src addr in SI,
|
||||
// then memcpy rsz bytes into @retscr (#265/#268 shape).
|
||||
if (!aggargsrcaddr(c, rhs, "SI")) {
|
||||
let m5: str = "#272: aggregate return from unsupported source kind\n";
|
||||
os.write(2, m5.ptr, m5.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let k: i32 = 0;
|
||||
for (k + 8 <= rsz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 8;
|
||||
};
|
||||
if (k + 4 <= rsz) {
|
||||
emitline("\tMOVL\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 4;
|
||||
};
|
||||
if (k + 2 <= rsz) {
|
||||
emitline("\tMOVW\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVW\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 2;
|
||||
};
|
||||
if (k + 1 <= rsz) {
|
||||
emitline("\tMOVB\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 1;
|
||||
};
|
||||
} else {
|
||||
// N_IDENT: word-copy from rhs slot
|
||||
// to scratch. Whole 8B words via
|
||||
@@ -28614,7 +28716,7 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
k += 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
}; };
|
||||
// #171a: float-bearing struct RETURN (return
|
||||
// twin of #165's param recv). A qualifying
|
||||
// struct's float eightbytes ride the SSE return
|
||||
@@ -28683,30 +28785,45 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
// (tinfo.size = sub.size*len) mirrors cstage rt->size. No
|
||||
// structfloatclass (pure-int arrays); N_CALL forward at reg-
|
||||
// class falls to the default cgexpr passthrough below.
|
||||
if (c.fnret != nil && c.fnret.kind == nkind.N_TARRAY
|
||||
&& rhs.kind == nkind.N_IDENT) {
|
||||
if (c.fnret != nil && c.fnret.kind == nkind.N_TARRAY) {
|
||||
// #272: array return-by-value source-shape closure.
|
||||
// Beyond the #267 N_IDENT word-copy, route N_ARRLIT
|
||||
// (literal fill), N_DOT/N_INDEX/deref (aggargsrcaddr +
|
||||
// memcpy) into @retscr — the mirror of cstage cgen.c
|
||||
// N_RETURN ≤24B arm. N_CALL stays on the cgexpr tail (the
|
||||
// callee already left AX/DX/CX).
|
||||
let arrok: bool = false;
|
||||
if (rhs.kind == nkind.N_IDENT) { arrok = true; };
|
||||
if (rhs.kind == nkind.N_ARRLIT) { arrok = true; };
|
||||
if (rhs.kind == nkind.N_DOT) { arrok = true; };
|
||||
if (rhs.kind == nkind.N_INDEX) { arrok = true; };
|
||||
if (rhs.kind == nkind.N_UN) {
|
||||
if (rhs.op == tkind.TK_STAR) { arrok = true; };
|
||||
};
|
||||
let ati: *tinfo = c.fnret.type_: *tinfo;
|
||||
for (ati != nil && ati.kind == tykind.TY_NAMED) { ati = ati.under; };
|
||||
if (ati != nil) {
|
||||
if (arrok && ati != nil) {
|
||||
let rsz: i32 = ati.size: i32;
|
||||
if (rsz <= 24) {
|
||||
let rl: *local = localfindnode(c, rhs.str);
|
||||
if (rl != nil) {
|
||||
let scroff: i32 = localadd(c, "@retscr", 24, nil);
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(scroff: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
let scroff: i32 = localadd(c, "@retscr", 24, nil);
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(scroff: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
if (rhs.kind == nkind.N_IDENT) {
|
||||
let rl: *local = localfindnode(c, rhs.str);
|
||||
let roff: i32 = 0;
|
||||
if (rl != nil) { roff = rl.off; };
|
||||
let k: i32 = 0;
|
||||
for (k + 8 <= rsz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((rl.off + k): i64);
|
||||
emitoff((roff + k): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
@@ -28715,7 +28832,7 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
};
|
||||
for (k + 4 <= rsz) {
|
||||
emitline("\tMOVL\t");
|
||||
emitoff((rl.off + k): i64);
|
||||
emitoff((roff + k): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
@@ -28724,31 +28841,172 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
};
|
||||
for (k < rsz) {
|
||||
emitline("\tMOVB\t");
|
||||
emitoff((rl.off + k): i64);
|
||||
emitoff((roff + k): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 1;
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(scroff: i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 8): i64);
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 16): i64);
|
||||
emitline("(BP), CX\n");
|
||||
emitline("\tMOVQ\tBP, SP\n");
|
||||
emitline("\tPOPQ\tBP\n");
|
||||
emitline("\tRET\n");
|
||||
c.lastwasreturn = 1;
|
||||
return;
|
||||
};
|
||||
} else { if (rhs.kind == nkind.N_ARRLIT) {
|
||||
// scalar/float element fill; non-scalar
|
||||
// elements loud-stop (rule 7, no consumer).
|
||||
let esubti: *tinfo = nil;
|
||||
if (ati.sub != nil) { esubti = ati.sub; };
|
||||
for (esubti != nil && esubti.kind == tykind.TY_NAMED) { esubti = esubti.under; };
|
||||
let esz: i32 = 8;
|
||||
if (esubti != nil) { esz = esubti.size: i32; };
|
||||
let badel: bool = false;
|
||||
if (esubti != nil) {
|
||||
if (esubti.kind == tykind.TY_STRUCT) { badel = true; };
|
||||
if (esubti.kind == tykind.TY_ARRAY) { badel = true; };
|
||||
if (esubti.kind == tykind.TY_TUPLE) { badel = true; };
|
||||
if (esubti.kind == tykind.TY_SLICE) { badel = true; };
|
||||
if (esubti.kind == tykind.TY_STR) { badel = true; };
|
||||
};
|
||||
if (badel) {
|
||||
let m2: str = "#272: array-literal return with non-scalar element unsupported (rule 7, no consumer)\n";
|
||||
os.write(2, m2.ptr, m2.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let esub: *node = c.fnret.lhs;
|
||||
let isfl: bool = isfloattype(c, esub);
|
||||
let fmov: str = "MOVSD";
|
||||
if (isf32type(c, esub)) { fmov = "MOVSS"; };
|
||||
let op: str = "MOVQ";
|
||||
if (esz == 1) { op = "MOVB"; } else { if (esz == 2) { op = "MOVW"; } else { if (esz == 4) { op = "MOVL"; }; }; };
|
||||
let idx: i32 = 0;
|
||||
let repeat: bool = false;
|
||||
let e: *node = rhs.list;
|
||||
for (e != nil) {
|
||||
let isellip: bool = false;
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { repeat = true; isellip = true; };
|
||||
};
|
||||
if (isellip) {
|
||||
e = nil;
|
||||
} else {
|
||||
cgexpr(c, e);
|
||||
if (isfl) {
|
||||
emitline("\t");
|
||||
emitline(fmov);
|
||||
emitline("\tX0, ");
|
||||
emitoff((scroff + idx * esz): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\tAX, ");
|
||||
emitoff((scroff + idx * esz): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
};
|
||||
if (repeat) {
|
||||
let total: i32 = rsz / esz;
|
||||
for (idx < total) {
|
||||
if (isfl) {
|
||||
emitline("\t");
|
||||
emitline(fmov);
|
||||
emitline("\tX0, ");
|
||||
emitoff((scroff + idx * esz): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\tAX, ");
|
||||
emitoff((scroff + idx * esz): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
idx += 1;
|
||||
};
|
||||
};
|
||||
} else {
|
||||
// N_DOT / N_INDEX / deref: land src addr in SI,
|
||||
// then memcpy rsz bytes into @retscr (#265/#268
|
||||
// copy shape). Loud-stop unaddressable sources.
|
||||
if (!aggargsrcaddr(c, rhs, "SI")) {
|
||||
let m3: str = "#272: aggregate return from unsupported source kind\n";
|
||||
os.write(2, m3.ptr, m3.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let k: i32 = 0;
|
||||
for (k + 8 <= rsz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 8;
|
||||
};
|
||||
if (k + 4 <= rsz) {
|
||||
emitline("\tMOVL\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 4;
|
||||
};
|
||||
if (k + 2 <= rsz) {
|
||||
emitline("\tMOVW\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVW\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 2;
|
||||
};
|
||||
if (k + 1 <= rsz) {
|
||||
emitline("\tMOVB\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 1;
|
||||
};
|
||||
}; };
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(scroff: i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 8): i64);
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 16): i64);
|
||||
emitline("(BP), CX\n");
|
||||
emitline("\tMOVQ\tBP, SP\n");
|
||||
emitline("\tPOPQ\tBP\n");
|
||||
emitline("\tRET\n");
|
||||
c.lastwasreturn = 1;
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
// #272 close-by-construction: addressable aggregate-return
|
||||
// sources (IDENT/STRUCTLIT/ARRLIT/DOT/INDEX/deref) all break in
|
||||
// the arms above; an aggregate N_CALL passes through cgexpr
|
||||
// (callee left AX/DX/CX). Any OTHER aggregate rvalue reaching
|
||||
// here would truncate to AX silently — loud-stop (rule 7),
|
||||
// mirroring cstage cgen.c N_RETURN.
|
||||
{
|
||||
let aggret: bool = false;
|
||||
if (c.fnret != nil) {
|
||||
if (c.fnret.kind == nkind.N_TARRAY) { aggret = true; };
|
||||
if (c.fnret.kind == nkind.N_TNAME) {
|
||||
if (structlookup(c, c.fnret.str) != nil) { aggret = true; };
|
||||
};
|
||||
};
|
||||
if (aggret && rhs.kind != nkind.N_CALL) {
|
||||
let m6: str = "#272: aggregate return reaches scalar default — unclosed shape\n";
|
||||
os.write(2, m6.ptr, m6.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
};
|
||||
cgexpr(c, rhs);
|
||||
} else {
|
||||
// Bare `return;` from a tagged-union-returning fn is
|
||||
|
||||
@@ -868,8 +868,19 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
let okrhs: bool = false;
|
||||
// #272: >24B sret addressable-source closure —
|
||||
// N_DOT/N_INDEX/deref land their address in SI then
|
||||
// memcpy through *(@sretarg), mirroring cstage cgen.c
|
||||
// N_RETURN sret arm. N_ARRLIT >24B has no consumer
|
||||
// (loud-stops in cstage); not wired here.
|
||||
let addrsrc: bool = false;
|
||||
if (rhs.kind == nkind.N_IDENT) { okrhs = true; };
|
||||
if (rhs.kind == nkind.N_STRUCTLIT) { okrhs = true; };
|
||||
if (rhs.kind == nkind.N_DOT) { okrhs = true; addrsrc = true; };
|
||||
if (rhs.kind == nkind.N_INDEX) { okrhs = true; addrsrc = true; };
|
||||
if (rhs.kind == nkind.N_UN) {
|
||||
if (rhs.op == tkind.TK_STAR) { okrhs = true; addrsrc = true; };
|
||||
};
|
||||
if (okrhs) {
|
||||
if (rhs.kind == nkind.N_STRUCTLIT) {
|
||||
let trefn: *node = rhs.lhs;
|
||||
@@ -891,6 +902,43 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
1, sretargoff, emptys,
|
||||
0);
|
||||
};
|
||||
} else { if (addrsrc) {
|
||||
if (!aggargsrcaddr(c, rhs, "SI")) {
|
||||
let m4: str = "#272: aggregate return from unsupported source kind\n";
|
||||
os.write(2, m4.ptr, m4.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(sretargoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
let k: i32 = 0;
|
||||
for (k + 8 <= scs) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(k: i64);
|
||||
emitline("(BX)\n");
|
||||
k += 8;
|
||||
};
|
||||
for (k + 4 <= scs) {
|
||||
emitline("\tMOVL\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff(k: i64);
|
||||
emitline("(BX)\n");
|
||||
k += 4;
|
||||
};
|
||||
for (k < scs) {
|
||||
emitline("\tMOVB\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff(k: i64);
|
||||
emitline("(BX)\n");
|
||||
k += 1;
|
||||
};
|
||||
} else {
|
||||
let rl: *local = localfindnode(c, rhs.str);
|
||||
if (rl != nil) {
|
||||
@@ -926,7 +974,7 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
k += 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
}; };
|
||||
// sret return: RAX = dest pointer.
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(sretargoff: i64);
|
||||
@@ -963,12 +1011,21 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
let rsz: i32 = structabisize(rsi);
|
||||
if (rsz <= 24) {
|
||||
let okrhs: bool = false;
|
||||
// #272: struct ≤24B addressable-source closure —
|
||||
// N_DOT/N_INDEX/deref memcpy into @retscr before the
|
||||
// shared structfloatclass tail (mirror cstage cgen.c).
|
||||
let addrsrc: bool = false;
|
||||
if (rhs.kind == nkind.N_IDENT) {
|
||||
okrhs = true;
|
||||
};
|
||||
if (rhs.kind == nkind.N_STRUCTLIT) {
|
||||
okrhs = true;
|
||||
};
|
||||
if (rhs.kind == nkind.N_DOT) { okrhs = true; addrsrc = true; };
|
||||
if (rhs.kind == nkind.N_INDEX) { okrhs = true; addrsrc = true; };
|
||||
if (rhs.kind == nkind.N_UN) {
|
||||
if (rhs.op == tkind.TK_STAR) { okrhs = true; addrsrc = true; };
|
||||
};
|
||||
if (okrhs) {
|
||||
let scroff: i32 = localadd(c,
|
||||
"@retscr", 24, nil);
|
||||
@@ -991,6 +1048,51 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
// values recurse instead of dropping
|
||||
// trailing bytes.
|
||||
cgstructlitfillbp(c, rsi, rhs, scroff);
|
||||
} else { if (addrsrc) {
|
||||
// N_DOT / N_INDEX / deref: land src addr in SI,
|
||||
// then memcpy rsz bytes into @retscr (#265/#268 shape).
|
||||
if (!aggargsrcaddr(c, rhs, "SI")) {
|
||||
let m5: str = "#272: aggregate return from unsupported source kind\n";
|
||||
os.write(2, m5.ptr, m5.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let k: i32 = 0;
|
||||
for (k + 8 <= rsz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 8;
|
||||
};
|
||||
if (k + 4 <= rsz) {
|
||||
emitline("\tMOVL\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 4;
|
||||
};
|
||||
if (k + 2 <= rsz) {
|
||||
emitline("\tMOVW\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVW\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 2;
|
||||
};
|
||||
if (k + 1 <= rsz) {
|
||||
emitline("\tMOVB\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 1;
|
||||
};
|
||||
} else {
|
||||
// N_IDENT: word-copy from rhs slot
|
||||
// to scratch. Whole 8B words via
|
||||
@@ -1028,7 +1130,7 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
k += 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
}; };
|
||||
// #171a: float-bearing struct RETURN (return
|
||||
// twin of #165's param recv). A qualifying
|
||||
// struct's float eightbytes ride the SSE return
|
||||
@@ -1097,30 +1199,45 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
// (tinfo.size = sub.size*len) mirrors cstage rt->size. No
|
||||
// structfloatclass (pure-int arrays); N_CALL forward at reg-
|
||||
// class falls to the default cgexpr passthrough below.
|
||||
if (c.fnret != nil && c.fnret.kind == nkind.N_TARRAY
|
||||
&& rhs.kind == nkind.N_IDENT) {
|
||||
if (c.fnret != nil && c.fnret.kind == nkind.N_TARRAY) {
|
||||
// #272: array return-by-value source-shape closure.
|
||||
// Beyond the #267 N_IDENT word-copy, route N_ARRLIT
|
||||
// (literal fill), N_DOT/N_INDEX/deref (aggargsrcaddr +
|
||||
// memcpy) into @retscr — the mirror of cstage cgen.c
|
||||
// N_RETURN ≤24B arm. N_CALL stays on the cgexpr tail (the
|
||||
// callee already left AX/DX/CX).
|
||||
let arrok: bool = false;
|
||||
if (rhs.kind == nkind.N_IDENT) { arrok = true; };
|
||||
if (rhs.kind == nkind.N_ARRLIT) { arrok = true; };
|
||||
if (rhs.kind == nkind.N_DOT) { arrok = true; };
|
||||
if (rhs.kind == nkind.N_INDEX) { arrok = true; };
|
||||
if (rhs.kind == nkind.N_UN) {
|
||||
if (rhs.op == tkind.TK_STAR) { arrok = true; };
|
||||
};
|
||||
let ati: *tinfo = c.fnret.type_: *tinfo;
|
||||
for (ati != nil && ati.kind == tykind.TY_NAMED) { ati = ati.under; };
|
||||
if (ati != nil) {
|
||||
if (arrok && ati != nil) {
|
||||
let rsz: i32 = ati.size: i32;
|
||||
if (rsz <= 24) {
|
||||
let rl: *local = localfindnode(c, rhs.str);
|
||||
if (rl != nil) {
|
||||
let scroff: i32 = localadd(c, "@retscr", 24, nil);
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(scroff: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
let scroff: i32 = localadd(c, "@retscr", 24, nil);
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(scroff: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
if (rhs.kind == nkind.N_IDENT) {
|
||||
let rl: *local = localfindnode(c, rhs.str);
|
||||
let roff: i32 = 0;
|
||||
if (rl != nil) { roff = rl.off; };
|
||||
let k: i32 = 0;
|
||||
for (k + 8 <= rsz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((rl.off + k): i64);
|
||||
emitoff((roff + k): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
@@ -1129,7 +1246,7 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
};
|
||||
for (k + 4 <= rsz) {
|
||||
emitline("\tMOVL\t");
|
||||
emitoff((rl.off + k): i64);
|
||||
emitoff((roff + k): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
@@ -1138,31 +1255,172 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
};
|
||||
for (k < rsz) {
|
||||
emitline("\tMOVB\t");
|
||||
emitoff((rl.off + k): i64);
|
||||
emitoff((roff + k): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 1;
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(scroff: i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 8): i64);
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 16): i64);
|
||||
emitline("(BP), CX\n");
|
||||
emitline("\tMOVQ\tBP, SP\n");
|
||||
emitline("\tPOPQ\tBP\n");
|
||||
emitline("\tRET\n");
|
||||
c.lastwasreturn = 1;
|
||||
return;
|
||||
};
|
||||
} else { if (rhs.kind == nkind.N_ARRLIT) {
|
||||
// scalar/float element fill; non-scalar
|
||||
// elements loud-stop (rule 7, no consumer).
|
||||
let esubti: *tinfo = nil;
|
||||
if (ati.sub != nil) { esubti = ati.sub; };
|
||||
for (esubti != nil && esubti.kind == tykind.TY_NAMED) { esubti = esubti.under; };
|
||||
let esz: i32 = 8;
|
||||
if (esubti != nil) { esz = esubti.size: i32; };
|
||||
let badel: bool = false;
|
||||
if (esubti != nil) {
|
||||
if (esubti.kind == tykind.TY_STRUCT) { badel = true; };
|
||||
if (esubti.kind == tykind.TY_ARRAY) { badel = true; };
|
||||
if (esubti.kind == tykind.TY_TUPLE) { badel = true; };
|
||||
if (esubti.kind == tykind.TY_SLICE) { badel = true; };
|
||||
if (esubti.kind == tykind.TY_STR) { badel = true; };
|
||||
};
|
||||
if (badel) {
|
||||
let m2: str = "#272: array-literal return with non-scalar element unsupported (rule 7, no consumer)\n";
|
||||
os.write(2, m2.ptr, m2.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let esub: *node = c.fnret.lhs;
|
||||
let isfl: bool = isfloattype(c, esub);
|
||||
let fmov: str = "MOVSD";
|
||||
if (isf32type(c, esub)) { fmov = "MOVSS"; };
|
||||
let op: str = "MOVQ";
|
||||
if (esz == 1) { op = "MOVB"; } else { if (esz == 2) { op = "MOVW"; } else { if (esz == 4) { op = "MOVL"; }; }; };
|
||||
let idx: i32 = 0;
|
||||
let repeat: bool = false;
|
||||
let e: *node = rhs.list;
|
||||
for (e != nil) {
|
||||
let isellip: bool = false;
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { repeat = true; isellip = true; };
|
||||
};
|
||||
if (isellip) {
|
||||
e = nil;
|
||||
} else {
|
||||
cgexpr(c, e);
|
||||
if (isfl) {
|
||||
emitline("\t");
|
||||
emitline(fmov);
|
||||
emitline("\tX0, ");
|
||||
emitoff((scroff + idx * esz): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\tAX, ");
|
||||
emitoff((scroff + idx * esz): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
};
|
||||
if (repeat) {
|
||||
let total: i32 = rsz / esz;
|
||||
for (idx < total) {
|
||||
if (isfl) {
|
||||
emitline("\t");
|
||||
emitline(fmov);
|
||||
emitline("\tX0, ");
|
||||
emitoff((scroff + idx * esz): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\tAX, ");
|
||||
emitoff((scroff + idx * esz): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
idx += 1;
|
||||
};
|
||||
};
|
||||
} else {
|
||||
// N_DOT / N_INDEX / deref: land src addr in SI,
|
||||
// then memcpy rsz bytes into @retscr (#265/#268
|
||||
// copy shape). Loud-stop unaddressable sources.
|
||||
if (!aggargsrcaddr(c, rhs, "SI")) {
|
||||
let m3: str = "#272: aggregate return from unsupported source kind\n";
|
||||
os.write(2, m3.ptr, m3.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let k: i32 = 0;
|
||||
for (k + 8 <= rsz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 8;
|
||||
};
|
||||
if (k + 4 <= rsz) {
|
||||
emitline("\tMOVL\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 4;
|
||||
};
|
||||
if (k + 2 <= rsz) {
|
||||
emitline("\tMOVW\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVW\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 2;
|
||||
};
|
||||
if (k + 1 <= rsz) {
|
||||
emitline("\tMOVB\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 1;
|
||||
};
|
||||
}; };
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(scroff: i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 8): i64);
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 16): i64);
|
||||
emitline("(BP), CX\n");
|
||||
emitline("\tMOVQ\tBP, SP\n");
|
||||
emitline("\tPOPQ\tBP\n");
|
||||
emitline("\tRET\n");
|
||||
c.lastwasreturn = 1;
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
// #272 close-by-construction: addressable aggregate-return
|
||||
// sources (IDENT/STRUCTLIT/ARRLIT/DOT/INDEX/deref) all break in
|
||||
// the arms above; an aggregate N_CALL passes through cgexpr
|
||||
// (callee left AX/DX/CX). Any OTHER aggregate rvalue reaching
|
||||
// here would truncate to AX silently — loud-stop (rule 7),
|
||||
// mirroring cstage cgen.c N_RETURN.
|
||||
{
|
||||
let aggret: bool = false;
|
||||
if (c.fnret != nil) {
|
||||
if (c.fnret.kind == nkind.N_TARRAY) { aggret = true; };
|
||||
if (c.fnret.kind == nkind.N_TNAME) {
|
||||
if (structlookup(c, c.fnret.str) != nil) { aggret = true; };
|
||||
};
|
||||
};
|
||||
if (aggret && rhs.kind != nkind.N_CALL) {
|
||||
let m6: str = "#272: aggregate return reaches scalar default — unclosed shape\n";
|
||||
os.write(2, m6.ptr, m6.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
};
|
||||
cgexpr(c, rhs);
|
||||
} else {
|
||||
// Bare `return;` from a tagged-union-returning fn is
|
||||
|
||||
@@ -28454,8 +28454,19 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
let okrhs: bool = false;
|
||||
// #272: >24B sret addressable-source closure —
|
||||
// N_DOT/N_INDEX/deref land their address in SI then
|
||||
// memcpy through *(@sretarg), mirroring cstage cgen.c
|
||||
// N_RETURN sret arm. N_ARRLIT >24B has no consumer
|
||||
// (loud-stops in cstage); not wired here.
|
||||
let addrsrc: bool = false;
|
||||
if (rhs.kind == nkind.N_IDENT) { okrhs = true; };
|
||||
if (rhs.kind == nkind.N_STRUCTLIT) { okrhs = true; };
|
||||
if (rhs.kind == nkind.N_DOT) { okrhs = true; addrsrc = true; };
|
||||
if (rhs.kind == nkind.N_INDEX) { okrhs = true; addrsrc = true; };
|
||||
if (rhs.kind == nkind.N_UN) {
|
||||
if (rhs.op == tkind.TK_STAR) { okrhs = true; addrsrc = true; };
|
||||
};
|
||||
if (okrhs) {
|
||||
if (rhs.kind == nkind.N_STRUCTLIT) {
|
||||
let trefn: *node = rhs.lhs;
|
||||
@@ -28477,6 +28488,43 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
1, sretargoff, emptys,
|
||||
0);
|
||||
};
|
||||
} else { if (addrsrc) {
|
||||
if (!aggargsrcaddr(c, rhs, "SI")) {
|
||||
let m4: str = "#272: aggregate return from unsupported source kind\n";
|
||||
os.write(2, m4.ptr, m4.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(sretargoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
let k: i32 = 0;
|
||||
for (k + 8 <= scs) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(k: i64);
|
||||
emitline("(BX)\n");
|
||||
k += 8;
|
||||
};
|
||||
for (k + 4 <= scs) {
|
||||
emitline("\tMOVL\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff(k: i64);
|
||||
emitline("(BX)\n");
|
||||
k += 4;
|
||||
};
|
||||
for (k < scs) {
|
||||
emitline("\tMOVB\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff(k: i64);
|
||||
emitline("(BX)\n");
|
||||
k += 1;
|
||||
};
|
||||
} else {
|
||||
let rl: *local = localfindnode(c, rhs.str);
|
||||
if (rl != nil) {
|
||||
@@ -28512,7 +28560,7 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
k += 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
}; };
|
||||
// sret return: RAX = dest pointer.
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(sretargoff: i64);
|
||||
@@ -28549,12 +28597,21 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
let rsz: i32 = structabisize(rsi);
|
||||
if (rsz <= 24) {
|
||||
let okrhs: bool = false;
|
||||
// #272: struct ≤24B addressable-source closure —
|
||||
// N_DOT/N_INDEX/deref memcpy into @retscr before the
|
||||
// shared structfloatclass tail (mirror cstage cgen.c).
|
||||
let addrsrc: bool = false;
|
||||
if (rhs.kind == nkind.N_IDENT) {
|
||||
okrhs = true;
|
||||
};
|
||||
if (rhs.kind == nkind.N_STRUCTLIT) {
|
||||
okrhs = true;
|
||||
};
|
||||
if (rhs.kind == nkind.N_DOT) { okrhs = true; addrsrc = true; };
|
||||
if (rhs.kind == nkind.N_INDEX) { okrhs = true; addrsrc = true; };
|
||||
if (rhs.kind == nkind.N_UN) {
|
||||
if (rhs.op == tkind.TK_STAR) { okrhs = true; addrsrc = true; };
|
||||
};
|
||||
if (okrhs) {
|
||||
let scroff: i32 = localadd(c,
|
||||
"@retscr", 24, nil);
|
||||
@@ -28577,6 +28634,51 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
// values recurse instead of dropping
|
||||
// trailing bytes.
|
||||
cgstructlitfillbp(c, rsi, rhs, scroff);
|
||||
} else { if (addrsrc) {
|
||||
// N_DOT / N_INDEX / deref: land src addr in SI,
|
||||
// then memcpy rsz bytes into @retscr (#265/#268 shape).
|
||||
if (!aggargsrcaddr(c, rhs, "SI")) {
|
||||
let m5: str = "#272: aggregate return from unsupported source kind\n";
|
||||
os.write(2, m5.ptr, m5.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let k: i32 = 0;
|
||||
for (k + 8 <= rsz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 8;
|
||||
};
|
||||
if (k + 4 <= rsz) {
|
||||
emitline("\tMOVL\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 4;
|
||||
};
|
||||
if (k + 2 <= rsz) {
|
||||
emitline("\tMOVW\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVW\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 2;
|
||||
};
|
||||
if (k + 1 <= rsz) {
|
||||
emitline("\tMOVB\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 1;
|
||||
};
|
||||
} else {
|
||||
// N_IDENT: word-copy from rhs slot
|
||||
// to scratch. Whole 8B words via
|
||||
@@ -28614,7 +28716,7 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
k += 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
}; };
|
||||
// #171a: float-bearing struct RETURN (return
|
||||
// twin of #165's param recv). A qualifying
|
||||
// struct's float eightbytes ride the SSE return
|
||||
@@ -28683,30 +28785,45 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
// (tinfo.size = sub.size*len) mirrors cstage rt->size. No
|
||||
// structfloatclass (pure-int arrays); N_CALL forward at reg-
|
||||
// class falls to the default cgexpr passthrough below.
|
||||
if (c.fnret != nil && c.fnret.kind == nkind.N_TARRAY
|
||||
&& rhs.kind == nkind.N_IDENT) {
|
||||
if (c.fnret != nil && c.fnret.kind == nkind.N_TARRAY) {
|
||||
// #272: array return-by-value source-shape closure.
|
||||
// Beyond the #267 N_IDENT word-copy, route N_ARRLIT
|
||||
// (literal fill), N_DOT/N_INDEX/deref (aggargsrcaddr +
|
||||
// memcpy) into @retscr — the mirror of cstage cgen.c
|
||||
// N_RETURN ≤24B arm. N_CALL stays on the cgexpr tail (the
|
||||
// callee already left AX/DX/CX).
|
||||
let arrok: bool = false;
|
||||
if (rhs.kind == nkind.N_IDENT) { arrok = true; };
|
||||
if (rhs.kind == nkind.N_ARRLIT) { arrok = true; };
|
||||
if (rhs.kind == nkind.N_DOT) { arrok = true; };
|
||||
if (rhs.kind == nkind.N_INDEX) { arrok = true; };
|
||||
if (rhs.kind == nkind.N_UN) {
|
||||
if (rhs.op == tkind.TK_STAR) { arrok = true; };
|
||||
};
|
||||
let ati: *tinfo = c.fnret.type_: *tinfo;
|
||||
for (ati != nil && ati.kind == tykind.TY_NAMED) { ati = ati.under; };
|
||||
if (ati != nil) {
|
||||
if (arrok && ati != nil) {
|
||||
let rsz: i32 = ati.size: i32;
|
||||
if (rsz <= 24) {
|
||||
let rl: *local = localfindnode(c, rhs.str);
|
||||
if (rl != nil) {
|
||||
let scroff: i32 = localadd(c, "@retscr", 24, nil);
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(scroff: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
let scroff: i32 = localadd(c, "@retscr", 24, nil);
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(scroff: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
if (rhs.kind == nkind.N_IDENT) {
|
||||
let rl: *local = localfindnode(c, rhs.str);
|
||||
let roff: i32 = 0;
|
||||
if (rl != nil) { roff = rl.off; };
|
||||
let k: i32 = 0;
|
||||
for (k + 8 <= rsz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((rl.off + k): i64);
|
||||
emitoff((roff + k): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
@@ -28715,7 +28832,7 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
};
|
||||
for (k + 4 <= rsz) {
|
||||
emitline("\tMOVL\t");
|
||||
emitoff((rl.off + k): i64);
|
||||
emitoff((roff + k): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
@@ -28724,31 +28841,172 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
};
|
||||
for (k < rsz) {
|
||||
emitline("\tMOVB\t");
|
||||
emitoff((rl.off + k): i64);
|
||||
emitoff((roff + k): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 1;
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(scroff: i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 8): i64);
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 16): i64);
|
||||
emitline("(BP), CX\n");
|
||||
emitline("\tMOVQ\tBP, SP\n");
|
||||
emitline("\tPOPQ\tBP\n");
|
||||
emitline("\tRET\n");
|
||||
c.lastwasreturn = 1;
|
||||
return;
|
||||
};
|
||||
} else { if (rhs.kind == nkind.N_ARRLIT) {
|
||||
// scalar/float element fill; non-scalar
|
||||
// elements loud-stop (rule 7, no consumer).
|
||||
let esubti: *tinfo = nil;
|
||||
if (ati.sub != nil) { esubti = ati.sub; };
|
||||
for (esubti != nil && esubti.kind == tykind.TY_NAMED) { esubti = esubti.under; };
|
||||
let esz: i32 = 8;
|
||||
if (esubti != nil) { esz = esubti.size: i32; };
|
||||
let badel: bool = false;
|
||||
if (esubti != nil) {
|
||||
if (esubti.kind == tykind.TY_STRUCT) { badel = true; };
|
||||
if (esubti.kind == tykind.TY_ARRAY) { badel = true; };
|
||||
if (esubti.kind == tykind.TY_TUPLE) { badel = true; };
|
||||
if (esubti.kind == tykind.TY_SLICE) { badel = true; };
|
||||
if (esubti.kind == tykind.TY_STR) { badel = true; };
|
||||
};
|
||||
if (badel) {
|
||||
let m2: str = "#272: array-literal return with non-scalar element unsupported (rule 7, no consumer)\n";
|
||||
os.write(2, m2.ptr, m2.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let esub: *node = c.fnret.lhs;
|
||||
let isfl: bool = isfloattype(c, esub);
|
||||
let fmov: str = "MOVSD";
|
||||
if (isf32type(c, esub)) { fmov = "MOVSS"; };
|
||||
let op: str = "MOVQ";
|
||||
if (esz == 1) { op = "MOVB"; } else { if (esz == 2) { op = "MOVW"; } else { if (esz == 4) { op = "MOVL"; }; }; };
|
||||
let idx: i32 = 0;
|
||||
let repeat: bool = false;
|
||||
let e: *node = rhs.list;
|
||||
for (e != nil) {
|
||||
let isellip: bool = false;
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { repeat = true; isellip = true; };
|
||||
};
|
||||
if (isellip) {
|
||||
e = nil;
|
||||
} else {
|
||||
cgexpr(c, e);
|
||||
if (isfl) {
|
||||
emitline("\t");
|
||||
emitline(fmov);
|
||||
emitline("\tX0, ");
|
||||
emitoff((scroff + idx * esz): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\tAX, ");
|
||||
emitoff((scroff + idx * esz): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
};
|
||||
if (repeat) {
|
||||
let total: i32 = rsz / esz;
|
||||
for (idx < total) {
|
||||
if (isfl) {
|
||||
emitline("\t");
|
||||
emitline(fmov);
|
||||
emitline("\tX0, ");
|
||||
emitoff((scroff + idx * esz): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\tAX, ");
|
||||
emitoff((scroff + idx * esz): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
idx += 1;
|
||||
};
|
||||
};
|
||||
} else {
|
||||
// N_DOT / N_INDEX / deref: land src addr in SI,
|
||||
// then memcpy rsz bytes into @retscr (#265/#268
|
||||
// copy shape). Loud-stop unaddressable sources.
|
||||
if (!aggargsrcaddr(c, rhs, "SI")) {
|
||||
let m3: str = "#272: aggregate return from unsupported source kind\n";
|
||||
os.write(2, m3.ptr, m3.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let k: i32 = 0;
|
||||
for (k + 8 <= rsz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 8;
|
||||
};
|
||||
if (k + 4 <= rsz) {
|
||||
emitline("\tMOVL\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 4;
|
||||
};
|
||||
if (k + 2 <= rsz) {
|
||||
emitline("\tMOVW\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVW\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 2;
|
||||
};
|
||||
if (k + 1 <= rsz) {
|
||||
emitline("\tMOVB\t");
|
||||
emitoff(k: i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff((scroff + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 1;
|
||||
};
|
||||
}; };
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(scroff: i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 8): i64);
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 16): i64);
|
||||
emitline("(BP), CX\n");
|
||||
emitline("\tMOVQ\tBP, SP\n");
|
||||
emitline("\tPOPQ\tBP\n");
|
||||
emitline("\tRET\n");
|
||||
c.lastwasreturn = 1;
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
// #272 close-by-construction: addressable aggregate-return
|
||||
// sources (IDENT/STRUCTLIT/ARRLIT/DOT/INDEX/deref) all break in
|
||||
// the arms above; an aggregate N_CALL passes through cgexpr
|
||||
// (callee left AX/DX/CX). Any OTHER aggregate rvalue reaching
|
||||
// here would truncate to AX silently — loud-stop (rule 7),
|
||||
// mirroring cstage cgen.c N_RETURN.
|
||||
{
|
||||
let aggret: bool = false;
|
||||
if (c.fnret != nil) {
|
||||
if (c.fnret.kind == nkind.N_TARRAY) { aggret = true; };
|
||||
if (c.fnret.kind == nkind.N_TNAME) {
|
||||
if (structlookup(c, c.fnret.str) != nil) { aggret = true; };
|
||||
};
|
||||
};
|
||||
if (aggret && rhs.kind != nkind.N_CALL) {
|
||||
let m6: str = "#272: aggregate return reaches scalar default — unclosed shape\n";
|
||||
os.write(2, m6.ptr, m6.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
};
|
||||
cgexpr(c, rhs);
|
||||
} else {
|
||||
// Bare `return;` from a tagged-union-returning fn is
|
||||
|
||||
Reference in New Issue
Block a user