w6c: append() accepts struct CALL rvalue elements (#34)

Evaluate the call pre-grow into a per-site scratch, receiving by the
N_LET matrix (sret / float / odd-tail / GP), then grow, slot, sized
ladder. Both stages, byte-identical. The two reject pins graduate to
16B GP accept rows; three new fixtures cover 24B GP, 3B/4B tails,
16B float, and 40B sret.
This commit is contained in:
2026-08-08 14:10:55 +09:00
parent 956246b3f0
commit 50d70e74e4
9 changed files with 295 additions and 27 deletions

View File

@@ -6599,7 +6599,8 @@ fn cgappend(c: *cgen, n: *syntax.node) void = {
let asoff: i32 = 0;
let aroot: *syntax.node = nil;
let aidx: *syntax.node = nil;
if (elstruct && vn.kind != syntax.nkind.N_STRUCTLIT && vn.kind != syntax.nkind.N_IDENT) {
if (elstruct && vn.kind != syntax.nkind.N_STRUCTLIT && vn.kind != syntax.nkind.N_IDENT
&& vn.kind != syntax.nkind.N_CALL) {
let ch: *syntax.node = vn;
let aok: bool = true;
for (aok && ch.kind == syntax.nkind.N_DOT) {
@@ -6722,6 +6723,101 @@ fn cgappend(c: *cgen, n: *syntax.node) void = {
vn = vn.next;
continue;
};
if (vn.kind == syntax.nkind.N_CALL) {
// #34 close: a struct CALL rvalue element.
// Evaluate the call FIRST into a fresh per-site
// scratch (the STRUCTLIT arm's pre-grow
// discipline: arg exprs must see the pre-append
// len, and the value must survive rt_ensure),
// receiving by the N_LET matrix (#23 sret /
// #171a float / #107 odd-tail / #169 GP), then
// grow, slot, sized-ladder scratch->slot.
let stscr: i32 = localalloc(c, "@appendstructscr", esz, nil);
let scs: i32 = callsretsize(c, vn);
if (scs > 0) {
c.sretdestoff = stscr;
cgexpr(c, vn);
c.sretdestoff = 0;
} else {
let sfc: i32 = structfloatclassti(esubti);
if (sfc != 0) {
cgexpr(c, vn);
let nb: i32 = sfc & 15;
let gpcur: i32 = 0;
let ssecur: i32 = 0;
let e: i32 = 0;
for (e < nb) {
let issse: bool = (sfc & (16 << e)) != 0;
if (issse) {
emitline("\tMOVSD\t");
emitline(tupsse(ssecur));
emitline(", ");
emitoff((stscr + e * 8): i64);
emitline("(BP)\n");
ssecur += 1;
} else {
emitline("\tMOVQ\t");
emitline(tupreg(gpcur));
emitline(", ");
emitoff((stscr + e * 8): i64);
emitline("(BP)\n");
gpcur += 1;
};
e += 1;
};
} else { if (esz == 3 || esz == 5
|| esz == 6 || esz == 7) {
cgexpr(c, vn);
emitline("\tMOVQ\tAX, ");
emitoff(stscr: i64);
emitline("(BP)\n");
} else {
cgexpr(c, vn);
cgaggregstore(c, "BP", stscr, esz, true);
}; };
};
cgappendgrow(c, sndirect, sn_off, snscr, esz);
cgappendslot(c, sndirect, sn_off, snscr, esz, "BX");
let ck: i32 = 0;
for (ck + 8 <= esz) {
emitline("\tMOVQ\t");
emitoff((stscr + ck): i64);
emitline("(BP), AX\n");
emitline("\tMOVQ\tAX, ");
emitdispreg(ck: i64, "BX");
emitline("\n");
ck += 8;
};
if (ck + 4 <= esz) {
emitline("\tMOVL\t");
emitoff((stscr + ck): i64);
emitline("(BP), AX\n");
emitline("\tMOVL\tAX, ");
emitdispreg(ck: i64, "BX");
emitline("\n");
ck += 4;
};
if (ck + 2 <= esz) {
emitline("\tMOVW\t");
emitoff((stscr + ck): i64);
emitline("(BP), AX\n");
emitline("\tMOVW\tAX, ");
emitdispreg(ck: i64, "BX");
emitline("\n");
ck += 2;
};
if (ck + 1 <= esz) {
emitline("\tMOVB\t");
emitoff((stscr + ck): i64);
emitline("(BP), AX\n");
emitline("\tMOVB\tAX, ");
emitdispreg(ck: i64, "BX");
emitline("\n");
ck += 1;
};
vn = vn.next;
continue;
};
if (vn.kind == syntax.nkind.N_STRUCTLIT) {
// #59 (#50's eval-order kin): resolve the struct
// info, then fill the literal into a fresh

View File

@@ -3454,6 +3454,60 @@ fn structfloatclass(c: *cgen, t: *syntax.node) i32 = {
return enc;
};
// structfloatclassti — tinfo twin of structfloatclass for sites with
// no declared tnode (the stamped element type behind an append CALL
// source, #34). Same gates and encoding as the tnode form: 0, or
// nb + 16/32 per SSE eightbyte. Cite cstage cgen.c struct_float_class
// (Type-keyed there, so the tinfo walk IS the aligned shape).
fn structfloatclassti(t: *syntax.tinfo) i32 = {
t = tichase(t);
if (t == nil || t.kind != syntax.tykind.TY_STRUCT) { return 0; };
let sz: i32 = t.size: i32;
if (sz <= 0 || sz > 16) { return 0; };
// SysV classifies aggregates in 8-byte eightbytes; 8 is the
// eightbyte stride, not a type footprint.
let nb: i32 = 1;
if (sz > 8) { nb = 2; };
let nflt0: i32 = 0; let nflt1: i32 = 0;
let nint0: i32 = 0; let nint1: i32 = 0;
let fi: *syntax.tfield = t.fields;
for (fi != nil) {
let fu: *syntax.tinfo = tichase(fi.type_);
if (fu == nil) { return 0; };
let foff: i32 = fi.offset: i32;
let fsz: i32 = fu.size: i32;
let e: i32 = foff / 8;
if (e < 0 || e >= nb) { return 0; };
if (fu.kind == syntax.tykind.TY_F32) { return 0; };
if (fu.kind == syntax.tykind.TY_F64) {
if (foff - (foff / 8) * 8 != 0) { return 0; };
if (fsz != 8) { return 0; };
if (e == 0) { nflt0 += 1; } else { nflt1 += 1; };
} else {
if (fu.kind == syntax.tykind.TY_STRUCT) { return 0; };
if (fu.kind == syntax.tykind.TY_ARRAY) { return 0; };
if (fu.kind == syntax.tykind.TY_SLICE) { return 0; };
if (fu.kind == syntax.tykind.TY_STR) { return 0; };
if (fu.kind == syntax.tykind.TY_TAGGED) { return 0; };
if (fu.kind == syntax.tykind.TY_TUPLE) { return 0; };
if (fsz > 8) { return 0; };
if ((foff + fsz - 1) / 8 != e) { return 0; };
if (e == 0) { nint0 += 1; } else { nint1 += 1; };
};
fi = fi.tnext;
};
let enc: i32 = nb;
let hasfloat: bool = false;
if (nflt0 == 1 && nint0 == 0) { enc += 16; hasfloat = true; }
else { if (nflt0 != 0) { return 0; }; };
if (nb == 2) {
if (nflt1 == 1 && nint1 == 0) { enc += 32; hasfloat = true; }
else { if (nflt1 != 0) { return 0; }; };
};
if (!hasfloat) { return 0; };
return enc;
};
// istaggedtype — alias-aware. Reads stamped tinfo so `T`,
// `type alias = (A|B)`, `type error = !(invalid|overflow)` all
// resolve to TY_TAGGED — tinfofornode handles the N_TBANG unwrap