diff --git a/cmd/w6c/cgen.c b/cmd/w6c/cgen.c index 6e4ea358..e37dd1c4 100644 --- a/cmd/w6c/cgen.c +++ b/cmd/w6c/cgen.c @@ -15010,44 +15010,24 @@ emit_tagged_data(FILE *out, Cg *c, const char *name, const char *module, return 1; } -/* emit_tuple_data — module-level `let g: (T0, T1, ...) = (v0, v1, ...);` - * static init (C-t3, #48). Slot layout (C-t0): a scalar element is one - * 8B LE word, a str element its 24B header slot (8 zero ptr placeholder - * + LE len + 8 zero cap) with a DATAR patching the ptr word at the - * element's slot offset — the per-element twin of the scalar-str-global - * arm, offset like emit_strarray_data's rows. Elements must reduce to - * int (fold_int_literal) or str literals; anything else returns 0 and - * the caller loud-stops (rule 7 — pre-C-t3 the whole definition was - * SILENTLY skipped and reads saw BP-frame garbage). rhs == NULL - * zero-inits the slot. */ +/* tuple_row_foldable — validate that every cast-peeled element of `rhs` + * (an N_TUPLE) reduces to a static row: an int literal (fold_int_literal) + * or a str literal in a str/slice slot. A tagged element slot has no + * static-init shape (tag word + payload widening) — reject so the caller + * loud-stops (#22a, rule 7); pre-guard an int init would have emitted one + * 8B word into the 16B+ box (silent layout skew). The validate twin of + * emit_tuple_row_bytes / emit_tuple_row_relocs: two-pass keeps a partial + * row out of the output (emit_array_data precedent). Factored from + * emit_tuple_data so the slice-of-tuple backing (#117) shares it. */ static int -emit_tuple_data(FILE *out, Cg *c, const char *name, const char *module, - Type *t, Node *rhs) +tuple_row_foldable(Cg *c, Type *u, Node *rhs) { - Type *u = type_unwrap(t); - if (u == NULL || u->kind != TY_TUPLE) return 0; - const char *sym = mod_mangle_value(c, name, module); - if (rhs == NULL) { - fprintf(out, "DATAW %s(SB),\"", sym); - for (int i = 0; i < (int)u->size; i++) - emit_data_byte(out, 0); - fputs("\"\n", out); - return 1; - } - if (rhs->kind != N_TUPLE) return 0; - /* Validate: every cast-peeled element folds (int) or is a strlit - * in a str slot. Two-pass so a partial row never reaches the - * output (emit_array_data precedent). */ + (void)c; Tparam *tp = u->params; for (Node *e = rhs->list; e; e = e->next, tp = tp ? tp->next : NULL) { Node *ev = e; while (ev && ev->kind == N_CAST) ev = ev->lhs; if (ev == NULL) return 0; - /* #22a (rule 7): a tagged element slot has no static-init - * shape (tag word + payload widening) — reject so the - * caller loud-stops; pre-guard an int init would have - * emitted one 8B word into the 16B+ box (silent layout - * skew). */ { Type *eu = type_chase_named(tp ? tp->type : NULL); if (eu && eu->kind == TY_TAGGED) return 0; @@ -15061,8 +15041,19 @@ emit_tuple_data(FILE *out, Cg *c, const char *name, const char *module, u64 v; if (!fold_int_literal(ev, &v)) return 0; } - fprintf(out, "DATAW %s(SB),\"", sym); - tp = u->params; + return 1; +} + +/* emit_tuple_row_bytes — the row's element bytes, concatenated, into the + * currently-open DATAW quoted string (no DATAW wrapper, no sym). Slot + * layout (C-t0): a scalar element is one 8B LE word; a str/slice element + * its 24B header slot (8 zero ptr placeholder + LE len + 8 zero cap). + * Caller has already proven the row foldable (tuple_row_foldable). */ +static void +emit_tuple_row_bytes(Cg *c, FILE *out, Type *u, Node *rhs) +{ + (void)c; + Tparam *tp = u->params; for (Node *e = rhs->list; e; e = e->next, tp = tp ? tp->next : NULL) { Node *ev = e; while (ev && ev->kind == N_CAST) ev = ev->lhs; @@ -15082,9 +15073,19 @@ emit_tuple_data(FILE *out, Cg *c, const char *name, const char *module, for (int i = 0; i < 8; i++) emit_data_byte(out, (u8)((v >> (i * 8)) & 0xff)); } - fputs("\"\n", out); - int foff = 0; - tp = u->params; +} + +/* emit_tuple_row_relocs — the row's DATAR ptr patches, at backing-relative + * ++. A str element patches the ptr word with the + * interned strlit's VA; the slot stride steps by tuple_eslot. row_off lets + * a slice backing place k rows contiguously (#117); emit_tuple_data passes + * 0 (foff matches the absolute element offset — byte-neutral). */ +static void +emit_tuple_row_relocs(Cg *c, FILE *out, const char *holder, int row_off, + Type *u, Node *rhs) +{ + int foff = row_off; + Tparam *tp = u->params; for (Node *e = rhs->list; e; e = e->next, tp = tp ? tp->next : NULL) { Node *ev = e; while (ev && ev->kind == N_CAST) ev = ev->lhs; @@ -15094,10 +15095,38 @@ emit_tuple_data(FILE *out, Cg *c, const char *name, const char *module, const char *lab = intern_strlit(c, ev->str, ev->strlen); fprintf(out, "DATAR %s+%d(SB),%s(SB)\n", - sym, foff, lab); + holder, foff, lab); } foff += tuple_eslot(tp ? tp->type : NULL); } +} + +/* emit_tuple_data — module-level `let g: (T0, T1, ...) = (v0, v1, ...);` + * static init (C-t3, #48). One slot-laid DATAW row (+ DATAR str-element + * ptr patches) via the backing-relative emit_tuple_row helpers; rhs == + * NULL zero-inits. Unsupported element inits return 0 and the caller + * loud-stops (rule 7 — pre-C-t3 the whole definition was SILENTLY skipped + * and reads saw BP-frame garbage). */ +static int +emit_tuple_data(FILE *out, Cg *c, const char *name, const char *module, + Type *t, Node *rhs) +{ + Type *u = type_unwrap(t); + if (u == NULL || u->kind != TY_TUPLE) return 0; + const char *sym = mod_mangle_value(c, name, module); + if (rhs == NULL) { + fprintf(out, "DATAW %s(SB),\"", sym); + for (int i = 0; i < (int)u->size; i++) + emit_data_byte(out, 0); + fputs("\"\n", out); + return 1; + } + if (rhs->kind != N_TUPLE) return 0; + if (!tuple_row_foldable(c, u, rhs)) return 0; + fprintf(out, "DATAW %s(SB),\"", sym); + emit_tuple_row_bytes(c, out, u, rhs); + fputs("\"\n", out); + emit_tuple_row_relocs(c, out, sym, 0, u, rhs); return 1; } diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index 1f08478f..9e2d1596 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -39850,38 +39850,17 @@ fn emittaggeddata(c: *cgen, name: str, module: str, tt: *node, rhs: *node, sz: i return true; }; -// emittupledata — module-level `let g: (T0, T1, ...) = (v0, ...);` -// static init (C-t3, #48). Slot layout (C-t0): a scalar element is one -// 8B LE word, a str element its 24B header slot (8 zero ptr placeholder -// + LE len + 8 zero cap) with a DATAR patching the ptr word at the -// element's slot offset — the per-element twin of the scalar-str-global -// arm, offset like emitstrarraydata's rows. Elements must reduce to int -// (foldintliteral) or str literals; anything else returns false and the -// caller loud-stops (rule 7 — pre-C-t3 the whole definition was -// SILENTLY skipped and reads saw garbage). rhs == nil zero-inits the -// slot. Mirror of cstage emit_tuple_data. -fn emittupledata(c: *cgen, name: str, module: str, tt: *node, rhs: *node) bool = { - if (tt == nil) { return false; }; - if (rhs == nil) { - // #22: slot-sum via the accessor so the zero-fill matches - // the checker size (cstage zero-emits u->size). - let zsz: i32 = 0; - let p0: *node = tt.list; - for (p0 != nil) { - zsz += tupeslotn(p0.lhs); - p0 = p0.next; - }; - emitline("DATAW "); - emitsymnamehint(c, name, module); - emitline("(SB),\""); - let zi: i32 = 0; - for (zi < zsz) { emitdatawbyte(0u8); zi += 1; }; - emitline("\"\n"); - return true; - }; - if (rhs.kind != nkind.N_TUPLE) { return false; }; - // Validate first (two-pass, emitarraydata precedent) so a partial - // row never reaches the output. +// tuplerowfoldable — validate every cast-peeled element of `rhs` (an +// N_TUPLE) reduces to a static row: an int literal (foldintliteral) or a +// str literal in a str/slice slot. A tagged element slot has no +// static-init shape (tag word + payload widening) — reject so the caller +// loud-stops (#22a, rule 7); pre-guard an int init would have emitted one +// 8B word into the 16B+ box (silent layout skew). The validate twin of +// emittuplerowbytes / emittuplerowrelocs; two-pass keeps a partial row +// out of the output (emitarraydata precedent). Factored from emittupledata +// so the slice-of-tuple backing (#117) shares it. Mirror of cstage +// tuple_row_foldable. +fn tuplerowfoldable(c: *cgen, tt: *node, rhs: *node) bool = { let tp: *node = tt.list; let e: *node = rhs.list; for (e != nil) { @@ -39890,11 +39869,6 @@ fn emittupledata(c: *cgen, name: str, module: str, tt: *node, rhs: *node) bool = let ev: *node = e; for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; }; if (ev == nil) { return false; }; - // #22a (rule 7): a tagged element slot has no static-init - // shape (tag word + payload widening) — reject so the caller - // loud-stops; pre-guard an int init would have emitted one - // 8B word into the 16B+ box (silent layout skew). Mirrors - // cstage emit_tuple_data. { let eti: *tinfo = nil; if (et != nil) { eti = et.type_: *tinfo; }; @@ -39913,11 +39887,18 @@ fn emittupledata(c: *cgen, name: str, module: str, tt: *node, rhs: *node) bool = e = e.next; if (tp != nil) { tp = tp.next; }; }; - emitline("DATAW "); - emitsymnamehint(c, name, module); - emitline("(SB),\""); - tp = tt.list; - e = rhs.list; + return true; +}; + +// emittuplerowbytes — the row's element bytes, concatenated, into the +// currently-open DATAW quoted string (no DATAW wrapper, no sym). Slot +// layout (C-t0): a scalar element is one 8B LE word; a str/slice element +// its 24B header slot (8 zero ptr placeholder + LE len + 8 zero cap). +// Caller has already proven the row foldable. Mirror of cstage +// emit_tuple_row_bytes. +fn emittuplerowbytes(c: *cgen, tt: *node, rhs: *node) void = { + let tp: *node = tt.list; + let e: *node = rhs.list; for (e != nil) { let et: *node = nil; if (tp != nil) { et = tp.lhs; }; @@ -39951,10 +39932,19 @@ fn emittupledata(c: *cgen, name: str, module: str, tt: *node, rhs: *node) bool = e = e.next; if (tp != nil) { tp = tp.next; }; }; - emitline("\"\n"); - let foff: i32 = 0; - tp = tt.list; - e = rhs.list; +}; + +// emittuplerowrelocs — the row's DATAR ptr patches, at backing-relative +// ++. A str element patches the ptr word with the +// interned strlit's VA; the slot stride steps by tyslicesize/tupeslotn. +// `backing` writes the ".d" backing label; rowoff lets a slice +// backing place k rows contiguously (#117), emittupledata passes 0 (foff +// matches the absolute element offset — byte-neutral). Mirror of cstage +// emit_tuple_row_relocs. +fn emittuplerowrelocs(c: *cgen, name: str, module: str, backing: bool, rowoff: i32, tt: *node, rhs: *node) void = { + let foff: i32 = rowoff; + let tp: *node = tt.list; + let e: *node = rhs.list; for (e != nil) { let et: *node = nil; if (tp != nil) { et = tp.lhs; }; @@ -39966,6 +39956,7 @@ fn emittupledata(c: *cgen, name: str, module: str, tt: *node, rhs: *node) bool = let lab: str = internstrlit(c, ev.str); emitline("DATAR "); emitsymnamehint(c, name, module); + if (backing) { emitline(".d"); }; emitline("+"); emitint(foff: i64); emitline("(SB),"); @@ -39982,6 +39973,41 @@ fn emittupledata(c: *cgen, name: str, module: str, tt: *node, rhs: *node) bool = e = e.next; if (tp != nil) { tp = tp.next; }; }; +}; + +// emittupledata — module-level `let g: (T0, T1, ...) = (v0, ...);` +// static init (C-t3, #48). One slot-laid DATAW row (+ DATAR str-element +// ptr patches) via the backing-relative emittuplerow helpers; rhs == nil +// zero-inits. Unsupported element inits return false and the caller +// loud-stops (rule 7 — pre-C-t3 the whole definition was SILENTLY skipped +// and reads saw garbage). Mirror of cstage emit_tuple_data. +fn emittupledata(c: *cgen, name: str, module: str, tt: *node, rhs: *node) bool = { + if (tt == nil) { return false; }; + if (rhs == nil) { + // #22: slot-sum via the accessor so the zero-fill matches + // the checker size (cstage zero-emits u->size). + let zsz: i32 = 0; + let p0: *node = tt.list; + for (p0 != nil) { + zsz += tupeslotn(p0.lhs); + p0 = p0.next; + }; + emitline("DATAW "); + emitsymnamehint(c, name, module); + emitline("(SB),\""); + let zi: i32 = 0; + for (zi < zsz) { emitdatawbyte(0u8); zi += 1; }; + emitline("\"\n"); + return true; + }; + if (rhs.kind != nkind.N_TUPLE) { return false; }; + if (!tuplerowfoldable(c, tt, rhs)) { return false; }; + emitline("DATAW "); + emitsymnamehint(c, name, module); + emitline("(SB),\""); + emittuplerowbytes(c, tt, rhs); + emitline("\"\n"); + emittuplerowrelocs(c, name, module, false, 0, tt, rhs); return true; }; diff --git a/selfhost/cmd/wcc/cgen.ww b/selfhost/cmd/wcc/cgen.ww index b83663bf..59918ff3 100644 --- a/selfhost/cmd/wcc/cgen.ww +++ b/selfhost/cmd/wcc/cgen.ww @@ -2296,38 +2296,17 @@ fn emittaggeddata(c: *cgen, name: str, module: str, tt: *node, rhs: *node, sz: i return true; }; -// emittupledata — module-level `let g: (T0, T1, ...) = (v0, ...);` -// static init (C-t3, #48). Slot layout (C-t0): a scalar element is one -// 8B LE word, a str element its 24B header slot (8 zero ptr placeholder -// + LE len + 8 zero cap) with a DATAR patching the ptr word at the -// element's slot offset — the per-element twin of the scalar-str-global -// arm, offset like emitstrarraydata's rows. Elements must reduce to int -// (foldintliteral) or str literals; anything else returns false and the -// caller loud-stops (rule 7 — pre-C-t3 the whole definition was -// SILENTLY skipped and reads saw garbage). rhs == nil zero-inits the -// slot. Mirror of cstage emit_tuple_data. -fn emittupledata(c: *cgen, name: str, module: str, tt: *node, rhs: *node) bool = { - if (tt == nil) { return false; }; - if (rhs == nil) { - // #22: slot-sum via the accessor so the zero-fill matches - // the checker size (cstage zero-emits u->size). - let zsz: i32 = 0; - let p0: *node = tt.list; - for (p0 != nil) { - zsz += tupeslotn(p0.lhs); - p0 = p0.next; - }; - emitline("DATAW "); - emitsymnamehint(c, name, module); - emitline("(SB),\""); - let zi: i32 = 0; - for (zi < zsz) { emitdatawbyte(0u8); zi += 1; }; - emitline("\"\n"); - return true; - }; - if (rhs.kind != nkind.N_TUPLE) { return false; }; - // Validate first (two-pass, emitarraydata precedent) so a partial - // row never reaches the output. +// tuplerowfoldable — validate every cast-peeled element of `rhs` (an +// N_TUPLE) reduces to a static row: an int literal (foldintliteral) or a +// str literal in a str/slice slot. A tagged element slot has no +// static-init shape (tag word + payload widening) — reject so the caller +// loud-stops (#22a, rule 7); pre-guard an int init would have emitted one +// 8B word into the 16B+ box (silent layout skew). The validate twin of +// emittuplerowbytes / emittuplerowrelocs; two-pass keeps a partial row +// out of the output (emitarraydata precedent). Factored from emittupledata +// so the slice-of-tuple backing (#117) shares it. Mirror of cstage +// tuple_row_foldable. +fn tuplerowfoldable(c: *cgen, tt: *node, rhs: *node) bool = { let tp: *node = tt.list; let e: *node = rhs.list; for (e != nil) { @@ -2336,11 +2315,6 @@ fn emittupledata(c: *cgen, name: str, module: str, tt: *node, rhs: *node) bool = let ev: *node = e; for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; }; if (ev == nil) { return false; }; - // #22a (rule 7): a tagged element slot has no static-init - // shape (tag word + payload widening) — reject so the caller - // loud-stops; pre-guard an int init would have emitted one - // 8B word into the 16B+ box (silent layout skew). Mirrors - // cstage emit_tuple_data. { let eti: *tinfo = nil; if (et != nil) { eti = et.type_: *tinfo; }; @@ -2359,11 +2333,18 @@ fn emittupledata(c: *cgen, name: str, module: str, tt: *node, rhs: *node) bool = e = e.next; if (tp != nil) { tp = tp.next; }; }; - emitline("DATAW "); - emitsymnamehint(c, name, module); - emitline("(SB),\""); - tp = tt.list; - e = rhs.list; + return true; +}; + +// emittuplerowbytes — the row's element bytes, concatenated, into the +// currently-open DATAW quoted string (no DATAW wrapper, no sym). Slot +// layout (C-t0): a scalar element is one 8B LE word; a str/slice element +// its 24B header slot (8 zero ptr placeholder + LE len + 8 zero cap). +// Caller has already proven the row foldable. Mirror of cstage +// emit_tuple_row_bytes. +fn emittuplerowbytes(c: *cgen, tt: *node, rhs: *node) void = { + let tp: *node = tt.list; + let e: *node = rhs.list; for (e != nil) { let et: *node = nil; if (tp != nil) { et = tp.lhs; }; @@ -2397,10 +2378,19 @@ fn emittupledata(c: *cgen, name: str, module: str, tt: *node, rhs: *node) bool = e = e.next; if (tp != nil) { tp = tp.next; }; }; - emitline("\"\n"); - let foff: i32 = 0; - tp = tt.list; - e = rhs.list; +}; + +// emittuplerowrelocs — the row's DATAR ptr patches, at backing-relative +// ++. A str element patches the ptr word with the +// interned strlit's VA; the slot stride steps by tyslicesize/tupeslotn. +// `backing` writes the ".d" backing label; rowoff lets a slice +// backing place k rows contiguously (#117), emittupledata passes 0 (foff +// matches the absolute element offset — byte-neutral). Mirror of cstage +// emit_tuple_row_relocs. +fn emittuplerowrelocs(c: *cgen, name: str, module: str, backing: bool, rowoff: i32, tt: *node, rhs: *node) void = { + let foff: i32 = rowoff; + let tp: *node = tt.list; + let e: *node = rhs.list; for (e != nil) { let et: *node = nil; if (tp != nil) { et = tp.lhs; }; @@ -2412,6 +2402,7 @@ fn emittupledata(c: *cgen, name: str, module: str, tt: *node, rhs: *node) bool = let lab: str = internstrlit(c, ev.str); emitline("DATAR "); emitsymnamehint(c, name, module); + if (backing) { emitline(".d"); }; emitline("+"); emitint(foff: i64); emitline("(SB),"); @@ -2428,6 +2419,41 @@ fn emittupledata(c: *cgen, name: str, module: str, tt: *node, rhs: *node) bool = e = e.next; if (tp != nil) { tp = tp.next; }; }; +}; + +// emittupledata — module-level `let g: (T0, T1, ...) = (v0, ...);` +// static init (C-t3, #48). One slot-laid DATAW row (+ DATAR str-element +// ptr patches) via the backing-relative emittuplerow helpers; rhs == nil +// zero-inits. Unsupported element inits return false and the caller +// loud-stops (rule 7 — pre-C-t3 the whole definition was SILENTLY skipped +// and reads saw garbage). Mirror of cstage emit_tuple_data. +fn emittupledata(c: *cgen, name: str, module: str, tt: *node, rhs: *node) bool = { + if (tt == nil) { return false; }; + if (rhs == nil) { + // #22: slot-sum via the accessor so the zero-fill matches + // the checker size (cstage zero-emits u->size). + let zsz: i32 = 0; + let p0: *node = tt.list; + for (p0 != nil) { + zsz += tupeslotn(p0.lhs); + p0 = p0.next; + }; + emitline("DATAW "); + emitsymnamehint(c, name, module); + emitline("(SB),\""); + let zi: i32 = 0; + for (zi < zsz) { emitdatawbyte(0u8); zi += 1; }; + emitline("\"\n"); + return true; + }; + if (rhs.kind != nkind.N_TUPLE) { return false; }; + if (!tuplerowfoldable(c, tt, rhs)) { return false; }; + emitline("DATAW "); + emitsymnamehint(c, name, module); + emitline("(SB),\""); + emittuplerowbytes(c, tt, rhs); + emitline("\"\n"); + emittuplerowrelocs(c, name, module, false, 0, tt, rhs); return true; }; diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index dcffb2b1..4ff6954d 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -39850,38 +39850,17 @@ fn emittaggeddata(c: *cgen, name: str, module: str, tt: *node, rhs: *node, sz: i return true; }; -// emittupledata — module-level `let g: (T0, T1, ...) = (v0, ...);` -// static init (C-t3, #48). Slot layout (C-t0): a scalar element is one -// 8B LE word, a str element its 24B header slot (8 zero ptr placeholder -// + LE len + 8 zero cap) with a DATAR patching the ptr word at the -// element's slot offset — the per-element twin of the scalar-str-global -// arm, offset like emitstrarraydata's rows. Elements must reduce to int -// (foldintliteral) or str literals; anything else returns false and the -// caller loud-stops (rule 7 — pre-C-t3 the whole definition was -// SILENTLY skipped and reads saw garbage). rhs == nil zero-inits the -// slot. Mirror of cstage emit_tuple_data. -fn emittupledata(c: *cgen, name: str, module: str, tt: *node, rhs: *node) bool = { - if (tt == nil) { return false; }; - if (rhs == nil) { - // #22: slot-sum via the accessor so the zero-fill matches - // the checker size (cstage zero-emits u->size). - let zsz: i32 = 0; - let p0: *node = tt.list; - for (p0 != nil) { - zsz += tupeslotn(p0.lhs); - p0 = p0.next; - }; - emitline("DATAW "); - emitsymnamehint(c, name, module); - emitline("(SB),\""); - let zi: i32 = 0; - for (zi < zsz) { emitdatawbyte(0u8); zi += 1; }; - emitline("\"\n"); - return true; - }; - if (rhs.kind != nkind.N_TUPLE) { return false; }; - // Validate first (two-pass, emitarraydata precedent) so a partial - // row never reaches the output. +// tuplerowfoldable — validate every cast-peeled element of `rhs` (an +// N_TUPLE) reduces to a static row: an int literal (foldintliteral) or a +// str literal in a str/slice slot. A tagged element slot has no +// static-init shape (tag word + payload widening) — reject so the caller +// loud-stops (#22a, rule 7); pre-guard an int init would have emitted one +// 8B word into the 16B+ box (silent layout skew). The validate twin of +// emittuplerowbytes / emittuplerowrelocs; two-pass keeps a partial row +// out of the output (emitarraydata precedent). Factored from emittupledata +// so the slice-of-tuple backing (#117) shares it. Mirror of cstage +// tuple_row_foldable. +fn tuplerowfoldable(c: *cgen, tt: *node, rhs: *node) bool = { let tp: *node = tt.list; let e: *node = rhs.list; for (e != nil) { @@ -39890,11 +39869,6 @@ fn emittupledata(c: *cgen, name: str, module: str, tt: *node, rhs: *node) bool = let ev: *node = e; for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; }; if (ev == nil) { return false; }; - // #22a (rule 7): a tagged element slot has no static-init - // shape (tag word + payload widening) — reject so the caller - // loud-stops; pre-guard an int init would have emitted one - // 8B word into the 16B+ box (silent layout skew). Mirrors - // cstage emit_tuple_data. { let eti: *tinfo = nil; if (et != nil) { eti = et.type_: *tinfo; }; @@ -39913,11 +39887,18 @@ fn emittupledata(c: *cgen, name: str, module: str, tt: *node, rhs: *node) bool = e = e.next; if (tp != nil) { tp = tp.next; }; }; - emitline("DATAW "); - emitsymnamehint(c, name, module); - emitline("(SB),\""); - tp = tt.list; - e = rhs.list; + return true; +}; + +// emittuplerowbytes — the row's element bytes, concatenated, into the +// currently-open DATAW quoted string (no DATAW wrapper, no sym). Slot +// layout (C-t0): a scalar element is one 8B LE word; a str/slice element +// its 24B header slot (8 zero ptr placeholder + LE len + 8 zero cap). +// Caller has already proven the row foldable. Mirror of cstage +// emit_tuple_row_bytes. +fn emittuplerowbytes(c: *cgen, tt: *node, rhs: *node) void = { + let tp: *node = tt.list; + let e: *node = rhs.list; for (e != nil) { let et: *node = nil; if (tp != nil) { et = tp.lhs; }; @@ -39951,10 +39932,19 @@ fn emittupledata(c: *cgen, name: str, module: str, tt: *node, rhs: *node) bool = e = e.next; if (tp != nil) { tp = tp.next; }; }; - emitline("\"\n"); - let foff: i32 = 0; - tp = tt.list; - e = rhs.list; +}; + +// emittuplerowrelocs — the row's DATAR ptr patches, at backing-relative +// ++. A str element patches the ptr word with the +// interned strlit's VA; the slot stride steps by tyslicesize/tupeslotn. +// `backing` writes the ".d" backing label; rowoff lets a slice +// backing place k rows contiguously (#117), emittupledata passes 0 (foff +// matches the absolute element offset — byte-neutral). Mirror of cstage +// emit_tuple_row_relocs. +fn emittuplerowrelocs(c: *cgen, name: str, module: str, backing: bool, rowoff: i32, tt: *node, rhs: *node) void = { + let foff: i32 = rowoff; + let tp: *node = tt.list; + let e: *node = rhs.list; for (e != nil) { let et: *node = nil; if (tp != nil) { et = tp.lhs; }; @@ -39966,6 +39956,7 @@ fn emittupledata(c: *cgen, name: str, module: str, tt: *node, rhs: *node) bool = let lab: str = internstrlit(c, ev.str); emitline("DATAR "); emitsymnamehint(c, name, module); + if (backing) { emitline(".d"); }; emitline("+"); emitint(foff: i64); emitline("(SB),"); @@ -39982,6 +39973,41 @@ fn emittupledata(c: *cgen, name: str, module: str, tt: *node, rhs: *node) bool = e = e.next; if (tp != nil) { tp = tp.next; }; }; +}; + +// emittupledata — module-level `let g: (T0, T1, ...) = (v0, ...);` +// static init (C-t3, #48). One slot-laid DATAW row (+ DATAR str-element +// ptr patches) via the backing-relative emittuplerow helpers; rhs == nil +// zero-inits. Unsupported element inits return false and the caller +// loud-stops (rule 7 — pre-C-t3 the whole definition was SILENTLY skipped +// and reads saw garbage). Mirror of cstage emit_tuple_data. +fn emittupledata(c: *cgen, name: str, module: str, tt: *node, rhs: *node) bool = { + if (tt == nil) { return false; }; + if (rhs == nil) { + // #22: slot-sum via the accessor so the zero-fill matches + // the checker size (cstage zero-emits u->size). + let zsz: i32 = 0; + let p0: *node = tt.list; + for (p0 != nil) { + zsz += tupeslotn(p0.lhs); + p0 = p0.next; + }; + emitline("DATAW "); + emitsymnamehint(c, name, module); + emitline("(SB),\""); + let zi: i32 = 0; + for (zi < zsz) { emitdatawbyte(0u8); zi += 1; }; + emitline("\"\n"); + return true; + }; + if (rhs.kind != nkind.N_TUPLE) { return false; }; + if (!tuplerowfoldable(c, tt, rhs)) { return false; }; + emitline("DATAW "); + emitsymnamehint(c, name, module); + emitline("(SB),\""); + emittuplerowbytes(c, tt, rhs); + emitline("\"\n"); + emittuplerowrelocs(c, name, module, false, 0, tt, rhs); return true; };