ww: implement package initialization

This commit is contained in:
2026-08-14 19:02:35 +09:00
parent 72e890d5aa
commit c6bec0914d
24 changed files with 5279 additions and 1009 deletions

View File

@@ -1189,6 +1189,7 @@ ffi_collect(Cg *c, Node *file)
if (file == NULL) return;
for (Node *d = file->list; d; d = d->next) {
if (d->kind != N_FNDECL) continue;
if (d->initfn || d->initsynthetic) continue;
for (Node *a = d->attr; a; a = a->next) {
if (a->kind != N_ATTR) continue;
if (strcmp(a->str, "symbol") != 0) continue;
@@ -1487,6 +1488,7 @@ mod_collect(Cg *c, Node *file)
use_map = u;
continue;
}
if (d->initfn || d->initsynthetic) continue;
int isfn = (d->kind == N_FNDECL);
int track = isfn || (d->kind == N_TYPEDECL)
|| (d->kind == N_DEF) || (d->kind == N_LET);
@@ -4196,6 +4198,238 @@ cg_arrlit_fill_bp(Cg *c, Local **locals, Type *lu, Node *arrlit, int off)
}
}
/* Package-variable helpers publish their local only after the complete value
* has been evaluated. Composite literals need a memory-directed path here:
* the ordinary tuple cursor has finite register capacity, nested aggregates
* are not scalar expressions, and an ordinary slice literal points at stack
* storage that dies when the helper returns. This path is gated by the
* checker-owned N_LET.initsynthetic bit and therefore cannot perturb normal
* user locals or any statically emitted initializer. */
static void cg_init_value_bp(Cg*, Local**, Type*, Node*, int);
static void
cg_init_zero_bp(Cg *c, int off, int sz)
{
ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
int k = 0;
for (; k + 8 <= sz; k += 8)
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off + k));
if (k + 4 <= sz) {
ins2(c, A_MOVL, areg(D_AX), amem(D_BP, off + k));
k += 4;
}
if (k + 2 <= sz) {
ins2(c, A_MOVW, areg(D_AX), amem(D_BP, off + k));
k += 2;
}
if (k + 1 <= sz)
ins2(c, A_MOVB, areg(D_AX), amem(D_BP, off + k));
}
static void
cg_init_copy_bp(Cg *c, int src, int dst, int sz)
{
ins2(c, A_LEAQ, amem(D_BP, src), areg(D_SI));
ins2(c, A_LEAQ, amem(D_BP, dst), areg(D_BX));
cg_aggcopy(c, sz);
}
static Node *
cg_init_strip_cast(Node *n)
{
while (n != NULL && n->kind == N_CAST) n = n->lhs;
return n;
}
static Tfield *
cg_init_field(Type *u, const char *name)
{
for (Tfield *f = u ? u->fields : NULL; f; f = f->next)
if (name != NULL && f->name != NULL
&& strcmp(name, f->name) == 0)
return f;
return NULL;
}
static void
cg_init_array_bp(Cg *c, Local **locals, Type *u, Node *lit, int off)
{
Type *et = u->sub;
int esz = et ? (int)type_chase_named(et)->size : 1;
int total = (int)u->alen;
cg_init_zero_bp(c, off, (int)u->size);
int idx = 0, lastoff = 0;
for (Node *e = lit->list; e; e = e->next) {
if (e->kind == N_FIELD && e->str != NULL
&& strcmp(e->str, "...") == 0) {
if (idx == 0)
fatal("package initializer array repeat has no value");
while (idx < total) {
cg_init_copy_bp(c, lastoff, off + idx * esz, esz);
idx++;
}
return;
}
if (idx >= total)
fatal("package initializer array literal exceeds destination");
lastoff = off + idx * esz;
cg_init_value_bp(c, locals, et, e, lastoff);
idx++;
}
}
static void
cg_init_struct_bp(Cg *c, Local **locals, Type *u, Node *lit, int off)
{
cg_init_zero_bp(c, off, (int)u->size);
for (Node *e = lit->list; e; e = e->next) {
Tfield *f = cg_init_field(u, e->str);
if (f == NULL) continue; /* checker owns unknown/ellipsis errors */
cg_init_value_bp(c, locals, f->type, e->lhs,
off + (int)f->offset);
}
}
static void
cg_init_tuple_bp(Cg *c, Local **locals, Type *u, Node *lit, int off)
{
cg_init_zero_bp(c, off, (int)u->size);
Node *e = lit->list;
int eoff = 0;
for (Tparam *p = u->params; p && e; p = p->next, e = e->next) {
cg_init_value_bp(c, locals, p->type, e, off + eoff);
eoff += tuple_eslot(p->type);
}
}
static void
cg_init_slice_bp(Cg *c, Local **locals, Type *u, Node *lit, int off)
{
if (lit->linksym == NULL || lit->linksym[0] == '\0')
fatal("runtime package slice literal has no canonical backing");
Type *at = type_chase_named(lit->type);
if (at == NULL || at->kind != TY_ARRAY || at->sub == NULL)
fatal("runtime package slice literal has no backing type");
int count = (int)at->alen;
int bsz = (int)at->size;
if (bsz != 0) {
int scr = local_alloc(c, locals, "@initbacking", bsz, cg_frame);
cg_init_array_bp(c, locals, at, lit, scr);
ins2(c, A_LEAQ, amem(D_BP, scr), areg(D_SI));
ins2(c, A_LEAQ, asym(lit->linksym), areg(D_BX));
cg_aggcopy(c, bsz);
}
ins2(c, A_LEAQ, asym(lit->linksym), areg(D_AX));
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off));
ins2(c, A_MOVQ, aimm(count), amem(D_BP, off + 8));
ins2(c, A_MOVQ, aimm(count), amem(D_BP, off + 16));
}
static void
cg_init_aggregate_expr_bp(Cg *c, Local **locals, Type *t, Type *u,
Node *expr, int off)
{
int sz = (int)u->size;
if (expr->kind == N_CALL && cg_sret_retsize(t) > 0) {
cg_sret_dest_off = off;
cgexpr(c, expr, *locals);
cg_sret_dest_off = 0;
return;
}
if (u->kind == TY_TUPLE && cg_sret_retsize(t) == 0) {
cgexpr(c, expr, *locals);
int gpcur = 0, ssecur = 0, eoff = 0, f32;
for (Tparam *p = u->params; p; p = p->next) {
int isfloat = fld_isfloat(p->type, &f32);
tuple_store(c, p->type, gpcur, ssecur, off + eoff);
if (isfloat) ssecur++;
else gpcur += tuple_eslot(p->type) / 8;
eoff += tuple_eslot(p->type);
}
return;
}
if (expr->kind == N_CALL && u->kind == TY_STRUCT) {
int cls[2], nb = struct_float_class(u, cls);
if (nb > 0) {
cgexpr(c, expr, *locals);
int gp = 0, sse = 0;
for (int i = 0; i < nb; i++) {
if (cls[i]) {
ins2(c, A_MOVSD, areg(tuple_sse_seq[sse++]),
amem(D_BP, off + i * 8));
} else {
ins2(c, A_MOVQ, areg(tuple_rseq[gp++]),
amem(D_BP, off + i * 8));
}
}
return;
}
}
if (expr->kind == N_CALL && sz <= 24) {
cgexpr(c, expr, *locals);
cg_agg_reg_store(c, locals, D_BP, off, sz, 1);
return;
}
if (aggarg_srcaddr(c, expr, D_SI, *locals)) {
ins2(c, A_LEAQ, amem(D_BP, off), areg(D_BX));
cg_aggcopy(c, sz);
return;
}
fatal("package initializer aggregate expression shape unsupported");
}
static void
cg_init_value_bp(Cg *c, Local **locals, Type *t, Node *expr, int off)
{
Type *u = type_chase_named(t);
Node *r = cg_init_strip_cast(expr);
if (u == NULL || r == NULL)
fatal("package initializer value has no type or expression");
if (u->kind == TY_ARRAY && r->kind == N_ARRLIT) {
cg_init_array_bp(c, locals, u, r, off);
return;
}
if (u->kind == TY_STRUCT && r->kind == N_STRUCTLIT) {
cg_init_struct_bp(c, locals, u, r, off);
return;
}
if (u->kind == TY_TUPLE && r->kind == N_TUPLE) {
cg_init_tuple_bp(c, locals, u, r, off);
return;
}
if (u->kind == TY_SLICE && r->kind == N_ARRLIT) {
cg_init_slice_bp(c, locals, u, r, off);
return;
}
if (u->kind == TY_TAGGED) {
cg_widen_tagged_store(c, locals, u, expr, D_BP, off,
(int)u->size);
return;
}
if (type_isstr(t) || u->kind == TY_SLICE) {
cgexpr(c, expr, *locals);
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off));
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, off + 8));
ins2(c, A_MOVQ, areg(D_CX), amem(D_BP, off + 16));
return;
}
if (u->kind == TY_ARRAY || u->kind == TY_STRUCT
|| u->kind == TY_TUPLE) {
cg_init_aggregate_expr_bp(c, locals, t, u, r, off);
return;
}
cgexpr(c, expr, *locals);
int f32 = 0;
if (fld_isfloat(t, &f32)) {
ins2(c, f32 ? A_MOVSS : A_MOVSD, areg(D_X0),
amem(D_BP, off));
return;
}
int sz = (int)u->size;
if (sz != 1 && sz != 2 && sz != 4) sz = 8;
ins2(c, fldstoreop(t, sz), areg(D_AX), amem(D_BP, off));
}
/* cg_dotfield_combine — single-dot field compound combine. The old
* field value is in BX, the rhs in AX; the result is left in AX.
* PLUSEQ/MINUSEQ preserve the pre-#34 emission (byte-id); the other 8
@@ -10941,7 +11175,10 @@ cgexpr(Cg *c, Node *n, Local *locals)
else
ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
}
if (n->lhs->kind == N_IDENT) {
if (n->lhs->kind == N_IDENT && n->lhs->refdecl != NULL
&& n->lhs->refdecl->linksym != NULL) {
ins1(c, A_CALL, asym(n->lhs->refdecl->linksym));
} else if (n->lhs->kind == N_IDENT) {
/* If the callee names a local variable holding a
* function pointer, load it and call indirect. Without
* this check `CALL fp(SB)` is emitted as if `fp` were
@@ -13506,6 +13743,14 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
int off = letloc->off;
int isf = cg_isfloat(lt);
int isf32 = type_isf32(lt);
Node *initlit = cg_init_strip_cast(n->rhs);
if (n->initsynthetic && initlit != NULL
&& (initlit->kind == N_ARRLIT
|| initlit->kind == N_STRUCTLIT
|| initlit->kind == N_TUPLE)) {
cg_init_value_bp(c, locals, lt, n->rhs, off);
goto letlink;
}
/* alloc([], n) initialiser for a slice local: allocate
* n*esize bytes, build the {ptr, 0, n} header in the slot.
* Element size comes from the declared slice type.
@@ -15843,7 +16088,9 @@ cgfn(Cg *c, FILE *out, Node *fn)
* !sep_isdep — under sep a dep unit's main is imported==0 (path-
* carrying `//ww:module-reset`, #57); only the root/link-entry unit
* (wwiout==NULL, #69) keeps the bare label. */
if (fn->str && strcmp(fn->str, "main") == 0 && !fn->imported
if (fn->linksym != NULL)
text->to = asym(fn->linksym);
else if (fn->str && strcmp(fn->str, "main") == 0 && !fn->imported
&& !c->sep_isdep)
text->to = asym("main");
else
@@ -16098,6 +16345,15 @@ cgfn(Cg *c, FILE *out, Node *fn)
fatal("#38b: >48B tagged param mixed with stack-spilled "
"params unwired");
/* The package coordinator owns one complete dependency-first schedule.
* Only the selected source/generated main invokes it; package tasks never
* recurse through dependencies, so independent-package tie order cannot
* accidentally become linker or DFS order. */
if (c->init_dispatch_symbol != NULL && fn->str != NULL
&& strcmp(fn->str, "main") == 0 && !fn->imported
&& !c->sep_isdep)
ins1(c, A_CALL, asym(c->init_dispatch_symbol));
/* Iterate the fn body's statements directly rather than dispatching
* the outermost N_BLOCK through cgstmt — N_BLOCK now save/restores
* the locals head to scope inner shadows, but the function body is
@@ -17674,6 +17930,28 @@ let_pre_intern(Cg *c, Node *file)
c->cur_source = save_source;
}
static void
emit_init_backings(FILE *out, Node *n)
{
for (; n; n = n->next) {
if (n->kind == N_ARRLIT && n->linksym != NULL
&& n->linksym[0] != '\0') {
Type *u = type_chase_named(n->type);
if (u == NULL || u->kind != TY_ARRAY)
fatal("runtime package slice backing has no array type");
emit_data_row_zero(out, "DATAW", n->linksym,
u->size == 0 ? 1 : (int)u->size);
}
emit_init_backings(out, n->attr);
emit_init_backings(out, n->lhs);
emit_init_backings(out, n->rhs);
emit_init_backings(out, n->cond);
emit_init_backings(out, n->body);
emit_init_backings(out, n->els);
emit_init_backings(out, n->list);
}
}
void
cg_file(Cg *c, FILE *out, Node *file)
{
@@ -17697,5 +17975,6 @@ cg_file(Cg *c, FILE *out, Node *file)
let_pre_intern(c, file);
emit_data(c, out);
emit_defs(c, out, file);
emit_init_backings(out, file->list);
emit_lets(c, out, file);
}