wcc/cgen: #117 prep — factor emit_tuple_row backing-relative (byte-neutral)

This commit is contained in:
2026-06-06 20:02:32 +09:00
parent f8be2ae8dd
commit df1928182e
4 changed files with 282 additions and 175 deletions

View File

@@ -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
// <holder>+<rowoff>+<slot>. A str element patches the ptr word with the
// interned strlit's VA; the slot stride steps by tyslicesize/tupeslotn.
// `backing` writes the "<sym>.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;
};

View File

@@ -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
// <holder>+<rowoff>+<slot>. A str element patches the ptr word with the
// interned strlit's VA; the slot stride steps by tyslicesize/tupeslotn.
// `backing` writes the "<sym>.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;
};

View File

@@ -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
// <holder>+<rowoff>+<slot>. A str element patches the ptr word with the
// interned strlit's VA; the slot stride steps by tyslicesize/tupeslotn.
// `backing` writes the "<sym>.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;
};