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:
@@ -9467,7 +9467,8 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
int abase_slice = 0, aroot_off = 0;
|
int abase_slice = 0, aroot_off = 0;
|
||||||
Node *aroot = NULL, *aidx = NULL;
|
Node *aroot = NULL, *aidx = NULL;
|
||||||
if (el_struct && vn->kind != N_STRUCTLIT
|
if (el_struct && vn->kind != N_STRUCTLIT
|
||||||
&& vn->kind != N_IDENT) {
|
&& vn->kind != N_IDENT
|
||||||
|
&& vn->kind != N_CALL) {
|
||||||
Node *ch = vn;
|
Node *ch = vn;
|
||||||
int aok = 1;
|
int aok = 1;
|
||||||
while (aok && ch->kind == N_DOT) {
|
while (aok && ch->kind == N_DOT) {
|
||||||
@@ -9567,6 +9568,80 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (vn->kind == 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. */
|
||||||
|
int st_scr = local_alloc(c,
|
||||||
|
&locals, "@appendstructscr",
|
||||||
|
esz, cg_frame);
|
||||||
|
int sclass[2], snb;
|
||||||
|
if (cg_sret_retsize(esub) > 0) {
|
||||||
|
cg_sret_dest_off = st_scr;
|
||||||
|
cgexpr(c, vn, locals);
|
||||||
|
cg_sret_dest_off = 0;
|
||||||
|
} else if ((snb = struct_float_class(esubu, sclass)) > 0) {
|
||||||
|
cgexpr(c, vn, locals);
|
||||||
|
int gpcur = 0, ssecur = 0;
|
||||||
|
for (int e = 0; e < snb; e++) {
|
||||||
|
if (sclass[e]) {
|
||||||
|
ins2(c, A_MOVSD,
|
||||||
|
areg(tuple_sse_seq[ssecur]),
|
||||||
|
amem(D_BP, st_scr + e * 8));
|
||||||
|
ssecur++;
|
||||||
|
} else {
|
||||||
|
ins2(c, A_MOVQ,
|
||||||
|
areg(tuple_rseq[gpcur]),
|
||||||
|
amem(D_BP, st_scr + e * 8));
|
||||||
|
gpcur++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (esz < 8 && (esz % 8 == 3
|
||||||
|
|| esz % 8 == 5 || esz % 8 == 6
|
||||||
|
|| esz % 8 == 7)) {
|
||||||
|
cgexpr(c, vn, locals);
|
||||||
|
ins2(c, A_MOVQ, areg(D_AX),
|
||||||
|
amem(D_BP, st_scr));
|
||||||
|
} else {
|
||||||
|
cgexpr(c, vn, locals);
|
||||||
|
cg_agg_reg_store(c, &locals,
|
||||||
|
D_BP, st_scr, esz, 1);
|
||||||
|
}
|
||||||
|
cg_append_grow(c, sn_direct, sn_off,
|
||||||
|
sn_scr, esz);
|
||||||
|
cg_append_slot(c, sn_direct, sn_off,
|
||||||
|
sn_scr, esz, D_BX);
|
||||||
|
int ck = 0;
|
||||||
|
for (; ck + 8 <= esz; ck += 8) {
|
||||||
|
ins2(c, A_MOVQ, amem(D_BP, st_scr + ck), areg(D_AX));
|
||||||
|
ins2(c, A_MOVQ, areg(D_AX), amem(D_BX, ck));
|
||||||
|
}
|
||||||
|
if (ck + 4 <= esz) {
|
||||||
|
ins2(c, A_MOVL, amem(D_BP, st_scr + ck), areg(D_AX));
|
||||||
|
ins2(c, A_MOVL, areg(D_AX), amem(D_BX, ck));
|
||||||
|
ck += 4;
|
||||||
|
}
|
||||||
|
if (ck + 2 <= esz) {
|
||||||
|
ins2(c, A_MOVW, amem(D_BP, st_scr + ck), areg(D_AX));
|
||||||
|
ins2(c, A_MOVW, areg(D_AX), amem(D_BX, ck));
|
||||||
|
ck += 2;
|
||||||
|
}
|
||||||
|
if (ck + 1 <= esz) {
|
||||||
|
ins2(c, A_MOVB, amem(D_BP, st_scr + ck), areg(D_AX));
|
||||||
|
ins2(c, A_MOVB, areg(D_AX), amem(D_BX, ck));
|
||||||
|
ck += 1;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (vn->kind == N_STRUCTLIT) {
|
if (vn->kind == N_STRUCTLIT) {
|
||||||
/* #59 (#50's eval-order kin):
|
/* #59 (#50's eval-order kin):
|
||||||
* fill the literal into a fresh
|
* fill the literal into a fresh
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package wwfixture;
|
package wwfixture;
|
||||||
|
|
||||||
def protocolversion: i32 = 1;
|
def protocolversion: i32 = 1;
|
||||||
def corpuscount: i32 = 1631;
|
def corpuscount: i32 = 1634;
|
||||||
def errorcount: i32 = 338;
|
def errorcount: i32 = 336;
|
||||||
def compilecount: i32 = 17;
|
def compilecount: i32 = 17;
|
||||||
def runcount: i32 = 198;
|
def runcount: i32 = 198;
|
||||||
def runexitcount: i32 = 1078;
|
def runexitcount: i32 = 1083;
|
||||||
def nativecount: i32 = 3262;
|
def nativecount: i32 = 3268;
|
||||||
def corpushash: str = "f1186d55d4f4a447de61b1246f8296c9f5996b406de36da0e5eb277fd2352e52";
|
def corpushash: str = "cb7cf09decc2f64b40c1f06b836c5b2aec0a4f1b9b2a78665f228606a0245a1f";
|
||||||
|
|
||||||
type directive = enum i32 {
|
type directive = enum i32 {
|
||||||
ERROR = 0,
|
ERROR = 0,
|
||||||
|
|||||||
@@ -6599,7 +6599,8 @@ fn cgappend(c: *cgen, n: *syntax.node) void = {
|
|||||||
let asoff: i32 = 0;
|
let asoff: i32 = 0;
|
||||||
let aroot: *syntax.node = nil;
|
let aroot: *syntax.node = nil;
|
||||||
let aidx: *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 ch: *syntax.node = vn;
|
||||||
let aok: bool = true;
|
let aok: bool = true;
|
||||||
for (aok && ch.kind == syntax.nkind.N_DOT) {
|
for (aok && ch.kind == syntax.nkind.N_DOT) {
|
||||||
@@ -6722,6 +6723,101 @@ fn cgappend(c: *cgen, n: *syntax.node) void = {
|
|||||||
vn = vn.next;
|
vn = vn.next;
|
||||||
continue;
|
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) {
|
if (vn.kind == syntax.nkind.N_STRUCTLIT) {
|
||||||
// #59 (#50's eval-order kin): resolve the struct
|
// #59 (#50's eval-order kin): resolve the struct
|
||||||
// info, then fill the literal into a fresh
|
// info, then fill the literal into a fresh
|
||||||
|
|||||||
@@ -3454,6 +3454,60 @@ fn structfloatclass(c: *cgen, t: *syntax.node) i32 = {
|
|||||||
return enc;
|
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`,
|
// istaggedtype — alias-aware. Reads stamped tinfo so `T`,
|
||||||
// `type alias = (A|B)`, `type error = !(invalid|overflow)` all
|
// `type alias = (A|B)`, `type error = !(invalid|overflow)` all
|
||||||
// resolve to TY_TAGGED — tinfofornode handles the N_TBANG unwrap
|
// resolve to TY_TAGGED — tinfofornode handles the N_TBANG unwrap
|
||||||
|
|||||||
25
test/wcc/data/r34_append_struct_call/case.ww
Normal file
25
test/wcc/data/r34_append_struct_call/case.ww
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
//ww:run-exit 0
|
||||||
|
// #34 close: append() accepts a struct CALL rvalue element — GP
|
||||||
|
// register-class receive (24B str+i32), odd sub-8 (3B), and sized
|
||||||
|
// tail (4B) shapes, evaluated pre-grow into a per-site scratch.
|
||||||
|
package main;
|
||||||
|
type gp24 = struct { name: str, id: i32 };
|
||||||
|
type odd3 = struct { a: u8, b: u8, c: u8 };
|
||||||
|
type tail4 = struct { v: i32 };
|
||||||
|
fn mkgp(id: i32) gp24 = { let e: gp24; e.name = "x"; e.id = id; return e; };
|
||||||
|
fn mkodd() odd3 = { let e: odd3; e.a = 1u8; e.b = 2u8; e.c = 3u8; return e; };
|
||||||
|
fn mktail() tail4 = { let e: tail4; e.v = 5; return e; };
|
||||||
|
fn main() i32 = {
|
||||||
|
let gs: []gp24 = [];
|
||||||
|
append(gs, mkgp(7));
|
||||||
|
append(gs, mkgp(9));
|
||||||
|
if (gs.len != 2 || gs[0].id != 7 || gs[1].id != 9) { return 1; };
|
||||||
|
if (gs[0].name.len != 1) { return 2; };
|
||||||
|
let os_: []odd3 = [];
|
||||||
|
append(os_, mkodd());
|
||||||
|
if (os_[0].a != 1u8 || os_[0].c != 3u8) { return 3; };
|
||||||
|
let ts: []tail4 = [];
|
||||||
|
append(ts, mktail());
|
||||||
|
if (ts[0].v != 5) { return 4; };
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
18
test/wcc/data/r34_append_struct_call_float/case.ww
Normal file
18
test/wcc/data/r34_append_struct_call_float/case.ww
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
//ww:run-exit 0
|
||||||
|
// #34 close, float leg: a float-bearing <=16B struct CALL element is
|
||||||
|
// received on independent SSE/GP return cursors (the #171a matrix).
|
||||||
|
package main;
|
||||||
|
type flt16 = struct { x: f64, n: i64 };
|
||||||
|
fn mkflt(v: f64, n: i64) flt16 = {
|
||||||
|
let e: flt16; e.x = v; e.n = n; return e;
|
||||||
|
};
|
||||||
|
fn main() i32 = {
|
||||||
|
let fs: []flt16 = [];
|
||||||
|
append(fs, mkflt(2.5, 11i64));
|
||||||
|
append(fs, mkflt(0.25, 13i64));
|
||||||
|
if (fs.len != 2) { return 1; };
|
||||||
|
if (fs[0].n != 11i64 || fs[1].n != 13i64) { return 2; };
|
||||||
|
if (fs[0].x != 2.5) { return 3; };
|
||||||
|
if (fs[1].x != 0.25) { return 4; };
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
20
test/wcc/data/r34_append_struct_call_sret/case.ww
Normal file
20
test/wcc/data/r34_append_struct_call_sret/case.ww
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
//ww:run-exit 0
|
||||||
|
// #34 close, sret leg: a >24B struct CALL element writes through the
|
||||||
|
// hidden RDI into the append scratch before the grow.
|
||||||
|
package main;
|
||||||
|
type big40 = struct { a: i64, b: i64, c: i64, d: i64, e: i64 };
|
||||||
|
fn mkbig(base: i64) big40 = {
|
||||||
|
let e: big40;
|
||||||
|
e.a = base; e.b = base + 1i64; e.c = base + 2i64;
|
||||||
|
e.d = base + 3i64; e.e = base + 4i64;
|
||||||
|
return e;
|
||||||
|
};
|
||||||
|
fn main() i32 = {
|
||||||
|
let bs: []big40 = [];
|
||||||
|
append(bs, mkbig(10i64));
|
||||||
|
append(bs, mkbig(20i64));
|
||||||
|
if (bs.len != 2) { return 1; };
|
||||||
|
if (bs[0].a != 10i64 || bs[0].e != 14i64) { return 2; };
|
||||||
|
if (bs[1].a != 20i64 || bs[1].e != 24i64) { return 3; };
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
//ww:error "#34: append() struct element source shape unsupported (rule-7)"
|
|
||||||
package main;
|
|
||||||
type pt = struct { x: i32, y: i32, z: i64 };
|
|
||||||
fn mk() pt = {
|
|
||||||
return pt { x = 1, y = 2, z = 3 };
|
|
||||||
};
|
|
||||||
export fn main() i32 = {
|
|
||||||
let xs: []pt = [];
|
|
||||||
append(xs, mk());
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
//ww:error "#34: append() struct element source shape unsupported (rule-7)"
|
|
||||||
package main;
|
|
||||||
type box = struct { pc: size, a: i64 };
|
|
||||||
fn mk() box = { return box { pc = 1, a = 2 }; };
|
|
||||||
export fn main() i32 = {
|
|
||||||
let bs: []box = [];
|
|
||||||
append(bs, mk());
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user