ww: implement package initialization
This commit is contained in:
@@ -2173,6 +2173,284 @@ fn cgarrlitfillbp(c: *cgen, arrtn: *syntax.node, rhs: *syntax.node, off: i32) vo
|
||||
};
|
||||
};
|
||||
|
||||
// Compiler-generated package-variable helpers need a memory-directed literal
|
||||
// path. It bypasses the finite tuple cursor, recursively fills nested
|
||||
// aggregates, and moves runtime slice-literal backing into canonical writable
|
||||
// package storage before the helper returns. The hook is gated solely by the
|
||||
// checker-owned N_LET.initsynthetic bit, so ordinary locals are unchanged.
|
||||
fn cginitfatal(msg: str) void = {
|
||||
os.write(2, msg.ptr, msg.len: u64);
|
||||
os.write(2, "\n".ptr, 1u64);
|
||||
os.exit(1);
|
||||
};
|
||||
|
||||
fn cginitzerobp(c: *cgen, off: i32, sz: i32) void = {
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
let k: i32 = 0;
|
||||
for (k + 8 <= sz) {
|
||||
emitline("\tMOVQ\tAX, "); emitoff((off + k): i64);
|
||||
emitline("(BP)\n"); k += 8;
|
||||
};
|
||||
if (k + 4 <= sz) {
|
||||
emitline("\tMOVL\tAX, "); emitoff((off + k): i64);
|
||||
emitline("(BP)\n"); k += 4;
|
||||
};
|
||||
if (k + 2 <= sz) {
|
||||
emitline("\tMOVW\tAX, "); emitoff((off + k): i64);
|
||||
emitline("(BP)\n"); k += 2;
|
||||
};
|
||||
if (k + 1 <= sz) {
|
||||
emitline("\tMOVB\tAX, "); emitoff((off + k): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
};
|
||||
|
||||
fn cginitcopybp(c: *cgen, src: i32, dst: i32, sz: i32) void = {
|
||||
emitline("\tLEAQ\t"); emitoff(src: i64); emitline("(BP), SI\n");
|
||||
emitline("\tLEAQ\t"); emitoff(dst: i64); emitline("(BP), BX\n");
|
||||
aggcopy(c, sz);
|
||||
};
|
||||
|
||||
fn cginitstripcast(n: *syntax.node) *syntax.node = {
|
||||
for (n != nil && n.kind == syntax.nkind.N_CAST) { n = n.lhs; };
|
||||
return n;
|
||||
};
|
||||
|
||||
fn cginitfield(u: *syntax.tinfo, name: str) *syntax.tfield = {
|
||||
let f: *syntax.tfield = nil;
|
||||
if (u != nil) { f = u.fields; };
|
||||
for (f != nil) {
|
||||
if (syntax.streq(name, f.name)) { return f; };
|
||||
f = f.tnext;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
fn cginitarraybp(c: *cgen, u: *syntax.tinfo, lit: *syntax.node,
|
||||
off: i32) void = {
|
||||
let et: *syntax.tinfo = u.sub;
|
||||
let eu: *syntax.tinfo = tichase(et);
|
||||
let esz: i32 = 1;
|
||||
if (eu != nil) { esz = eu.size: i32; };
|
||||
let total: i32 = u.alen: i32;
|
||||
cginitzerobp(c, off, u.size: i32);
|
||||
let idx: i32 = 0;
|
||||
let lastoff: i32 = 0;
|
||||
let e: *syntax.node = lit.list;
|
||||
for (e != nil) {
|
||||
if (e.kind == syntax.nkind.N_FIELD
|
||||
&& syntax.streq(e.str, "...")) {
|
||||
if (idx == 0) {
|
||||
cginitfatal("package initializer array repeat has no value");
|
||||
};
|
||||
for (idx < total) {
|
||||
cginitcopybp(c, lastoff, off + idx * esz, esz);
|
||||
idx += 1;
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (idx >= total) {
|
||||
cginitfatal("package initializer array literal exceeds destination");
|
||||
};
|
||||
lastoff = off + idx * esz;
|
||||
cginitvaluebp(c, et, e, lastoff);
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
};
|
||||
|
||||
fn cginitstructbp(c: *cgen, u: *syntax.tinfo, lit: *syntax.node,
|
||||
off: i32) void = {
|
||||
cginitzerobp(c, off, u.size: i32);
|
||||
let e: *syntax.node = lit.list;
|
||||
for (e != nil) {
|
||||
let f: *syntax.tfield = cginitfield(u, e.str);
|
||||
if (f != nil) {
|
||||
cginitvaluebp(c, f.type_, e.lhs, off + (f.offset: i32));
|
||||
};
|
||||
e = e.next;
|
||||
};
|
||||
};
|
||||
|
||||
fn cginittuplebp(c: *cgen, u: *syntax.tinfo, lit: *syntax.node,
|
||||
off: i32) void = {
|
||||
cginitzerobp(c, off, u.size: i32);
|
||||
let te: *syntax.ttupleelem = u.tupleelems;
|
||||
let e: *syntax.node = lit.list;
|
||||
for (te != nil && e != nil) {
|
||||
cginitvaluebp(c, te.type_, e, off + (te.offset: i32));
|
||||
te = te.tnext;
|
||||
e = e.next;
|
||||
};
|
||||
};
|
||||
|
||||
fn cginitslicebp(c: *cgen, u: *syntax.tinfo, lit: *syntax.node,
|
||||
off: i32) void = {
|
||||
if (lit.linksym.len == 0) {
|
||||
cginitfatal("runtime package slice literal has no canonical backing");
|
||||
};
|
||||
let at: *syntax.tinfo = tichase(lit.type_: *syntax.tinfo);
|
||||
if (at == nil || at.kind != syntax.tykind.TY_ARRAY || at.sub == nil) {
|
||||
cginitfatal("runtime package slice literal has no backing type");
|
||||
};
|
||||
let count: i32 = at.alen: i32;
|
||||
let bsz: i32 = at.size: i32;
|
||||
if (bsz != 0) {
|
||||
let scr: i32 = localalloc(c, "@initbacking", bsz, nil);
|
||||
cginitarraybp(c, at, lit, scr);
|
||||
emitline("\tLEAQ\t"); emitoff(scr: i64); emitline("(BP), SI\n");
|
||||
emitline("\tLEAQ\t"); emitbytes(lit.linksym.ptr,
|
||||
lit.linksym.len: u64); emitline("(SB), BX\n");
|
||||
aggcopy(c, bsz);
|
||||
};
|
||||
emitline("\tLEAQ\t"); emitbytes(lit.linksym.ptr,
|
||||
lit.linksym.len: u64); emitline("(SB), AX\n");
|
||||
emitline("\tMOVQ\tAX, "); emitoff(off: i64); emitline("(BP)\n");
|
||||
emitline("\tMOVQ\t$"); emitint(count: i64); emitline(", ");
|
||||
emitoff((off + 8): i64); emitline("(BP)\n");
|
||||
emitline("\tMOVQ\t$"); emitint(count: i64); emitline(", ");
|
||||
emitoff((off + 16): i64); emitline("(BP)\n");
|
||||
};
|
||||
|
||||
fn cginitcursorstore(c: *cgen, ti: *syntax.tinfo, gpcur: i32,
|
||||
ssecur: i32, off: i32) void = {
|
||||
let eslot: i32 = tupeslot(ti);
|
||||
if (eslot == 0) { return; };
|
||||
if (eslot > 8) {
|
||||
let k: i32 = 0;
|
||||
for (k < eslot / 8) {
|
||||
emitline("\tMOVQ\t"); emitline(tupreg(gpcur + k));
|
||||
emitline(", "); emitoff((off + k * 8): i64);
|
||||
emitline("(BP)\n"); k += 1;
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (syntax.typeisfloat(ti)) {
|
||||
let op: str = "MOVSD";
|
||||
if (syntax.typeisf32(ti)) { op = "MOVSS"; };
|
||||
emitline("\t"); emitline(op); emitline("\t");
|
||||
emitline(tupsse(ssecur)); emitline(", ");
|
||||
emitoff(off: i64); emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
emitline("\tMOVQ\t"); emitline(tupreg(gpcur)); emitline(", ");
|
||||
emitoff(off: i64); emitline("(BP)\n");
|
||||
};
|
||||
|
||||
fn cginitaggregatebp(c: *cgen, ti: *syntax.tinfo, u: *syntax.tinfo,
|
||||
expr: *syntax.node, off: i32) void = {
|
||||
let sz: i32 = u.size: i32;
|
||||
if (expr.kind == syntax.nkind.N_CALL && sretretsizetn(c, ti) > 0) {
|
||||
c.sretdestoff = off;
|
||||
cgexpr(c, expr);
|
||||
c.sretdestoff = 0;
|
||||
return;
|
||||
};
|
||||
if (u.kind == syntax.tykind.TY_TUPLE && sretretsizetn(c, ti) == 0) {
|
||||
cgexpr(c, expr);
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
let te: *syntax.ttupleelem = u.tupleelems;
|
||||
for (te != nil) {
|
||||
cginitcursorstore(c, te.type_, gpcur, ssecur,
|
||||
off + (te.offset: i32));
|
||||
if (syntax.typeisfloat(te.type_)) { ssecur += 1; }
|
||||
else { gpcur += tupeslot(te.type_) / 8; };
|
||||
te = te.tnext;
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (expr.kind == syntax.nkind.N_CALL
|
||||
&& u.kind == syntax.tykind.TY_STRUCT) {
|
||||
let fc: i32 = structfloatclassti(u);
|
||||
if (fc != 0) {
|
||||
cgexpr(c, expr);
|
||||
let nb: i32 = fc & 15;
|
||||
let gp: i32 = 0;
|
||||
let sse: i32 = 0;
|
||||
let i: i32 = 0;
|
||||
for (i < nb) {
|
||||
let issse: bool = false;
|
||||
if (i == 0 && (fc & 16) != 0) { issse = true; };
|
||||
if (i == 1 && (fc & 32) != 0) { issse = true; };
|
||||
if (issse) {
|
||||
emitline("\tMOVSD\t"); emitline(tupsse(sse));
|
||||
emitline(", "); sse += 1;
|
||||
} else {
|
||||
emitline("\tMOVQ\t"); emitline(tupreg(gp));
|
||||
emitline(", "); gp += 1;
|
||||
};
|
||||
emitoff((off + i * 8): i64); emitline("(BP)\n");
|
||||
i += 1;
|
||||
};
|
||||
return;
|
||||
};
|
||||
};
|
||||
if (expr.kind == syntax.nkind.N_CALL && sz <= 24) {
|
||||
cgexpr(c, expr);
|
||||
cgaggregstore(c, "BP", off, sz, true);
|
||||
return;
|
||||
};
|
||||
if (aggargsrcaddr(c, expr, "SI")) {
|
||||
emitline("\tLEAQ\t"); emitoff(off: i64); emitline("(BP), BX\n");
|
||||
aggcopy(c, sz);
|
||||
return;
|
||||
};
|
||||
cginitfatal("package initializer aggregate expression shape unsupported");
|
||||
};
|
||||
|
||||
fn cginitvaluebp(c: *cgen, ti: *syntax.tinfo, expr: *syntax.node,
|
||||
off: i32) void = {
|
||||
let u: *syntax.tinfo = tichase(ti);
|
||||
let r: *syntax.node = cginitstripcast(expr);
|
||||
if (u == nil || r == nil) {
|
||||
cginitfatal("package initializer value has no type or expression");
|
||||
};
|
||||
if (u.kind == syntax.tykind.TY_ARRAY
|
||||
&& r.kind == syntax.nkind.N_ARRLIT) {
|
||||
cginitarraybp(c, u, r, off); return;
|
||||
};
|
||||
if (u.kind == syntax.tykind.TY_STRUCT
|
||||
&& r.kind == syntax.nkind.N_STRUCTLIT) {
|
||||
cginitstructbp(c, u, r, off); return;
|
||||
};
|
||||
if (u.kind == syntax.tykind.TY_TUPLE
|
||||
&& r.kind == syntax.nkind.N_TUPLE) {
|
||||
cginittuplebp(c, u, r, off); return;
|
||||
};
|
||||
if (u.kind == syntax.tykind.TY_SLICE
|
||||
&& r.kind == syntax.nkind.N_ARRLIT) {
|
||||
cginitslicebp(c, u, r, off); return;
|
||||
};
|
||||
if (u.kind == syntax.tykind.TY_TAGGED) {
|
||||
cgwidentaggedstore(c, u, expr, "BP", off, u.size: i32);
|
||||
return;
|
||||
};
|
||||
if (syntax.typeisstr(ti) || u.kind == syntax.tykind.TY_SLICE) {
|
||||
cgexpr(c, expr);
|
||||
emitline("\tMOVQ\tAX, "); emitoff(off: i64); emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tBX, "); emitoff((off + 8): i64); emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, "); emitoff((off + 16): i64); emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
if (u.kind == syntax.tykind.TY_ARRAY
|
||||
|| u.kind == syntax.tykind.TY_STRUCT
|
||||
|| u.kind == syntax.tykind.TY_TUPLE) {
|
||||
cginitaggregatebp(c, ti, u, r, off); return;
|
||||
};
|
||||
cgexpr(c, expr);
|
||||
if (syntax.typeisfloat(ti)) {
|
||||
let op: str = "MOVSD";
|
||||
if (syntax.typeisf32(ti)) { op = "MOVSS"; };
|
||||
emitline("\t"); emitline(op); emitline("\tX0, ");
|
||||
emitoff(off: i64); emitline("(BP)\n"); return;
|
||||
};
|
||||
let sz: i32 = u.size: i32;
|
||||
if (sz != 1 && sz != 2 && sz != 4) { sz = 8; };
|
||||
emitline("\t"); emitline(storeopsz(sz)); emitline("\tAX, ");
|
||||
emitoff(off: i64); emitline("(BP)\n");
|
||||
};
|
||||
|
||||
// #152: reserve the let's frame slot, emit its initializer against the
|
||||
// PRE-binding locals chain, then link the binding. A self-shadowing init
|
||||
// (`let x = f(x)`) resolves x in the OUTER scope because nm is not yet in
|
||||
@@ -2205,6 +2483,20 @@ fn cgletbody(c: *cgen, n: *syntax.node, off: i32) void = {
|
||||
if (tn == nil) { tn = inferletcalltype(c, n.rhs); };
|
||||
if (n.rhs != nil) {
|
||||
let rhs: *syntax.node = n.rhs;
|
||||
let initlit: *syntax.node = cginitstripcast(rhs);
|
||||
if (n.initsynthetic != 0 && initlit != nil
|
||||
&& (initlit.kind == syntax.nkind.N_ARRLIT
|
||||
|| initlit.kind == syntax.nkind.N_STRUCTLIT
|
||||
|| initlit.kind == syntax.nkind.N_TUPLE)) {
|
||||
let iti: *syntax.tinfo = nil;
|
||||
if (tn != nil) { iti = tn.type_: *syntax.tinfo; };
|
||||
if (iti == nil && n.type_ != nil) {
|
||||
iti = n.type_: *syntax.tinfo;
|
||||
};
|
||||
cginitvaluebp(c, iti, rhs, off);
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
// `let s: []T = alloc([], n)!;` / `?` shortcut (#32, #45).
|
||||
// Mirror of cstage cgen.c N_LET arrlit-empty branch: allocate
|
||||
// n*esz bytes via rt_malloc, then build the {ptr, 0, n} slice
|
||||
|
||||
Reference in New Issue
Block a user