wcc: array static-init let/def DATA emit via SSoT helper (#129 A.3)
Extract emit_array_data + emit_array_lit_bytes helpers (both stages, mirrored) for module-level let/def with N_ARRLIT initializer or no-rhs zero-init. Two-pass validate-then-emit: validate pass walks elements and fails atomically on any non-foldable element (no partial-byte emit on failure); emit pass writes element bytes after success. Element-kind dispatch: integer via fold_int_literal byte-for-byte preserved from pre-A.3 inline arm (bootstrap NEUTRAL — 6 live consumers in lib/os/bufio/strings/encoding-utf8/strconv-stof_data), float via inline bitcast + sign-XOR byte-loop (A.1 shape, no INT64_MIN — sibling #144), struct via recursion into emit_struct_lit_bytes (A.2 helper). Out-of-scope element kinds (ptr-elem, nested-array) rule-7 fatal. emit_struct_lit_bytes gains TY_ARRAY field arm calling emit_array_lit_ bytes recursively — closes A.2 parked shape-15 (array-in-struct `def D: dt = dt{tag=42, buf=[1u8,2u8,3u8,4u8]};`). LOAD-side widened symmetric to A.2 precedent: cstage cgindex N_INDEX direct-ident isglobal gate widened via new DefArray registry (def_isarraydef populated in let_collect parallel to DefStruct); wwstage cgindex N_INDEX falls through to defvartnode on letvartnode nil (reads defent.dtnode field added in A.2). Both stages materialise array-def via LEAQ name(SB) same as array-let. Mid-impl rule-7 stop: refactor initially routed only rhs==N_ARRLIT through emitarraydata, leaving nil-rhs zero-init arrays (e.g. `let f64tos_buf: [64]u8;` in lib/strconv) silently SKIPPED → undef-ref at link of wwstage-rebuilt selfhost binaries. Caught on first gate run via bootstrap 994/995 RED. Fixed by adding nil-rhs branch to emitarraydata (zero-fills arrt.size bytes) + widening wwstage caller to route both N_ARRLIT and nil through helper. Same-class-lower-stratum pattern (recurring across A.1 N_UN-peel, A.2 sz==8-short-circuit, A.3 nil-rhs-drop); banked as feedback memory. Test 919 (11 rows: int-elem 1B/4B/8B + signed-N_UN-peel + float-elem f64/f32 + def-int / def-float / struct-with-array-field shape-15 + explicit-zero + single-elem-regression) registered. Make test: 182/182 incl. 990-997 byte-id + combined_ww_fresh. Followups filed: - #43 — wwstage emitletdataw str/slice-size arms lack !isarr guards; hypothetical no-rhs [16/24]u8 triple-emits (NOT A.3-introduced; no live consumer; 2-line parity fix)
This commit is contained in:
@@ -1144,6 +1144,21 @@ fn defvarstructinfo(c: *cgen, name: str) *structinfo = {
|
||||
return nil;
|
||||
};
|
||||
|
||||
// defvartnode — sister of letvartnode for top-level `def`s. Returns
|
||||
// the type-spec node (defent.dtnode) for the named def, or nil. #129
|
||||
// A.3 uses it in cgindex's array-base resolution so a `def: [N]T`
|
||||
// resolves through the same N_TARRAY-detect → LEAQ name(SB) shape as
|
||||
// a let array. Parallel to defvarstructinfo (#129 A.2) at the LOAD
|
||||
// side widening.
|
||||
fn defvartnode(c: *cgen, name: str) *node = {
|
||||
let e: *defent = c.defs;
|
||||
for (e != nil) {
|
||||
if (streq(e.dname, name)) { return e.dtnode; };
|
||||
e = e.dnext;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// emitdatawbyte — write one byte of an asm string literal using
|
||||
// the same escape rules as emitdefconstants / emitdatasection.
|
||||
fn emitdatawbyte(b: u8) void = {
|
||||
@@ -1368,6 +1383,30 @@ fn emitstructlitbytes(c: *cgen, structt: *tinfo, rhs: *node,
|
||||
f = f.tnext;
|
||||
continue;
|
||||
};
|
||||
// #129 A.3: array-typed field with N_ARRLIT rhs (the shape
|
||||
// parked in A.2). Recurses through emitarraylitbytes for
|
||||
// element-kind dispatch. Rule-7 stops loudly if rhs shape
|
||||
// doesn't match.
|
||||
if (fu != nil && fu.kind == tykind.TY_ARRAY) {
|
||||
if (vr == nil) {
|
||||
let m: str = "emitstructlitbytes: array field rhs nil (#129 A.3)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
if (vr.kind != nkind.N_ARRLIT) {
|
||||
let m: str = "emitstructlitbytes: array field rhs not N_ARRLIT (#129 A.3)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
if (!emitarraylitbytes(c, f.type_, vr, 1)) {
|
||||
let m: str = "emitstructlitbytes: array field rhs has non-reducible elements (#129 A.3)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
pos = fstart + fsz: u64;
|
||||
f = f.tnext;
|
||||
continue;
|
||||
};
|
||||
if (typeisfloat(f.type_)) {
|
||||
let isf32: bool = (fsz == 4);
|
||||
let neg: bool = false;
|
||||
@@ -1456,6 +1495,260 @@ fn emitstructdata(c: *cgen, directive: str, name: str,
|
||||
return true;
|
||||
};
|
||||
|
||||
// emitarraylitbytes — emit alen * esz bytes for an [N]T top-level let/
|
||||
// def with N_ARRLIT rhs. Mirrors cstage emit_array_lit_bytes. Per-
|
||||
// element dispatch:
|
||||
// - int (covers bool/rune/typed-int/N_UN-int): foldintliteral per
|
||||
// element. Existing pre-#129-A.3 emitletdataw array arm logic
|
||||
// preserved byte-for-byte so bootstrap consumers (lib/os, lib/
|
||||
// bufio, lib/strings, lib/encoding/utf8, lib/strconv/stof_data)
|
||||
// don't shift.
|
||||
// - float (f32/f64): peel N_CAST/N_UN(±), bitcast magnitude via
|
||||
// pointer-cast round-trip (mirror emitfloatlitdata), sign-XOR
|
||||
// top byte of each element inline. No 2^63 immediate.
|
||||
// - struct: per element call emitstructlitbytes (#129 A.2 helper).
|
||||
// - other element kinds (ptr/nested-array): returns false — caller
|
||||
// falls through to zero-init.
|
||||
//
|
||||
// Two-pass validate-then-emit (`emit_phase=0` validate-only, `=1`
|
||||
// actually emit) keeps emit-on-failure from emitting partial bytes
|
||||
// into an open DATA literal.
|
||||
fn emitarraylitbytes(c: *cgen, arrt: *tinfo, rhs: *node,
|
||||
emit_phase: i32) bool = {
|
||||
let au: *tinfo = arrt;
|
||||
for (au != nil && au.kind == tykind.TY_NAMED) { au = au.under; };
|
||||
if (au == nil) { return false; };
|
||||
if (au.kind != tykind.TY_ARRAY) { return false; };
|
||||
let esz: i32 = au.sub.size: i32;
|
||||
let alen: i32 = au.alen: i32;
|
||||
let eu: *tinfo = au.sub;
|
||||
for (eu != nil && eu.kind == tykind.TY_NAMED) { eu = eu.under; };
|
||||
|
||||
if (eu != nil && eu.kind == tykind.TY_STRUCT) {
|
||||
// Validate: every element must be N_STRUCTLIT (after N_CAST).
|
||||
let idx: i32 = 0;
|
||||
let last_ev: *node = nil;
|
||||
let e: *node = rhs.list;
|
||||
for (e != nil && idx < alen) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { break; };
|
||||
};
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
if (ev == nil) { return false; };
|
||||
if (ev.kind != nkind.N_STRUCTLIT) { return false; };
|
||||
last_ev = ev;
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
if (emit_phase == 0) { return true; };
|
||||
idx = 0;
|
||||
let repeat: bool = false;
|
||||
e = rhs.list;
|
||||
for (e != nil && idx < alen) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { repeat = true; break; };
|
||||
};
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
emitstructlitbytes(c, au.sub, ev, 0u64);
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
for (idx < alen) {
|
||||
if (repeat && last_ev != nil) {
|
||||
emitstructlitbytes(c, au.sub, last_ev, 0u64);
|
||||
} else {
|
||||
let bb: i32 = 0;
|
||||
for (bb < esz) { emitdatawbyte(0u8); bb += 1; };
|
||||
};
|
||||
idx += 1;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
if (typeisfloat(au.sub)) {
|
||||
let isf32: bool = typeisf32(au.sub);
|
||||
// Validate.
|
||||
let idx: i32 = 0;
|
||||
let e: *node = rhs.list;
|
||||
for (e != nil && idx < alen) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { break; };
|
||||
};
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
if (ev != nil) { if (ev.kind == nkind.N_UN) {
|
||||
if (ev.op == tkind.TK_MINUS) {
|
||||
ev = ev.lhs;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
} else { if (ev.op == tkind.TK_PLUS) {
|
||||
ev = ev.lhs;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
};};
|
||||
};};
|
||||
if (ev == nil) { return false; };
|
||||
if (ev.kind != nkind.N_FLOATLIT) { return false; };
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
if (emit_phase == 0) { return true; };
|
||||
idx = 0;
|
||||
let last_bits: u64 = 0u64;
|
||||
let last_neg: bool = false;
|
||||
let repeat: bool = false;
|
||||
e = rhs.list;
|
||||
for (e != nil && idx < alen) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { repeat = true; break; };
|
||||
};
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
let neg: bool = false;
|
||||
if (ev != nil) { if (ev.kind == nkind.N_UN) {
|
||||
if (ev.op == tkind.TK_MINUS) {
|
||||
neg = true;
|
||||
ev = ev.lhs;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
} else { if (ev.op == tkind.TK_PLUS) {
|
||||
ev = ev.lhs;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
};};
|
||||
};};
|
||||
let bits: u64 = ev.uval;
|
||||
if (isf32) {
|
||||
let dv: f64 = *((&bits): *f64);
|
||||
let fv: f32 = (dv: f32);
|
||||
let uv: u32 = *((&fv): *u32);
|
||||
bits = uv: u64;
|
||||
};
|
||||
let bb: i32 = 0;
|
||||
let nb: u64 = bits;
|
||||
for (bb < esz) {
|
||||
let byt: u8 = (nb & 255u64): u8;
|
||||
if (neg) {
|
||||
if (bb == esz - 1) { byt = byt ^ 128u8; };
|
||||
};
|
||||
emitdatawbyte(byt);
|
||||
nb = nb >> 8u64;
|
||||
bb += 1;
|
||||
};
|
||||
last_bits = bits;
|
||||
last_neg = neg;
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
for (idx < alen) {
|
||||
if (repeat) {
|
||||
let bb: i32 = 0;
|
||||
let nb: u64 = last_bits;
|
||||
for (bb < esz) {
|
||||
let byt: u8 = (nb & 255u64): u8;
|
||||
if (last_neg) {
|
||||
if (bb == esz - 1) { byt = byt ^ 128u8; };
|
||||
};
|
||||
emitdatawbyte(byt);
|
||||
nb = nb >> 8u64;
|
||||
bb += 1;
|
||||
};
|
||||
} else {
|
||||
let bb: i32 = 0;
|
||||
for (bb < esz) { emitdatawbyte(0u8); bb += 1; };
|
||||
};
|
||||
idx += 1;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
// Int-element path — preserved byte-for-byte from the pre-A.3
|
||||
// emitletdataw in-place arm so bootstrap consumers (u8/i8/u16
|
||||
// arrays) don't shift.
|
||||
let idx: i32 = 0;
|
||||
let e: *node = rhs.list;
|
||||
let last: u64 = 0u64;
|
||||
let repeat: bool = false;
|
||||
// Validate first.
|
||||
for (e != nil && idx < alen) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) { repeat = true; break; };
|
||||
};
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
if (ev == nil) { return false; };
|
||||
if (!foldintliteral(ev, &last)) { return false; };
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
if (emit_phase == 0) { return true; };
|
||||
idx = 0;
|
||||
last = 0u64;
|
||||
repeat = false;
|
||||
e = rhs.list;
|
||||
let inrepeat: bool = false;
|
||||
for (idx < alen) {
|
||||
let v: u64 = last;
|
||||
if (!inrepeat && e != nil) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) {
|
||||
inrepeat = true;
|
||||
} else {
|
||||
e = e.next;
|
||||
};
|
||||
} else {
|
||||
let ev: *node = e;
|
||||
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
|
||||
if (!foldintliteral(ev, &v)) { v = 0u64; };
|
||||
last = v;
|
||||
e = e.next;
|
||||
};
|
||||
};
|
||||
let nb: u64 = v;
|
||||
let bb: i32 = 0;
|
||||
for (bb < esz) {
|
||||
emitdatawbyte((nb & 255u64): u8);
|
||||
nb = nb >> 8u64;
|
||||
bb += 1;
|
||||
};
|
||||
idx += 1;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
// emitarraydata — top-level wrapper. Two-pass validate-then-emit
|
||||
// avoids partial-byte corruption if the rhs shape can't reduce.
|
||||
// nil rhs is the "no-rhs zero-init" shape (e.g. `let buf: [N]u8;`
|
||||
// in lib/strconv/strconv.ww:287, lib/os/os.ww:92, etc.) — emit
|
||||
// alen*esz zero bytes. This was the implicit pre-A.3 emitletdataw
|
||||
// behavior (the old loop emitted zeros when `elems` was nil); the
|
||||
// refactor would have skipped emit entirely without this branch,
|
||||
// causing `undefined reference to strconv.f64tos_buf` at link.
|
||||
fn emitarraydata(c: *cgen, directive: str, name: str,
|
||||
arrt: *tinfo, rhs: *node) bool = {
|
||||
let au: *tinfo = arrt;
|
||||
for (au != nil && au.kind == tykind.TY_NAMED) { au = au.under; };
|
||||
if (au == nil) { return false; };
|
||||
if (au.kind != tykind.TY_ARRAY) { return false; };
|
||||
if (rhs == nil) {
|
||||
let total: u64 = arrt.size;
|
||||
emitline(directive);
|
||||
emitline(" ");
|
||||
emitsymname(c, name);
|
||||
emitline("(SB),\"");
|
||||
let i: u64 = 0u64;
|
||||
for (i < total) { emitdatawbyte(0u8); i = i + 1u64; };
|
||||
emitline("\"\n");
|
||||
return true;
|
||||
};
|
||||
if (!emitarraylitbytes(c, arrt, rhs, 0)) { return false; };
|
||||
emitline(directive);
|
||||
emitline(" ");
|
||||
emitsymname(c, name);
|
||||
emitline("(SB),\"");
|
||||
emitarraylitbytes(c, arrt, rhs, 1);
|
||||
emitline("\"\n");
|
||||
return true;
|
||||
};
|
||||
|
||||
fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
@@ -1642,72 +1935,31 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
emitline("\"\n");
|
||||
};
|
||||
};
|
||||
// Top-level `[N]T = [a, b, ...]` array global.
|
||||
// Emits N*esz bytes with each element's bytes
|
||||
// little-endian for the declared primitive width.
|
||||
// Element fold goes through foldintliteral (same
|
||||
// helper as emitdefconstants / scalar arm above)
|
||||
// so `-1i8` and friends emit their two's-complement
|
||||
// bytes after the leading N_CAST peel — pre-#19
|
||||
// this arm only matched bare N_INTLIT/N_RUNELIT and
|
||||
// silently emitted zero for unfoldable elements.
|
||||
// `...` (N_FIELD with str="...") repeats the last
|
||||
// folded value across the remaining slots.
|
||||
// #129 A.3: array global routes through the
|
||||
// emitarraydata SSoT helper. Int-elem path is
|
||||
// byte-for-byte preserved (bootstrap consumers in
|
||||
// lib/os, lib/bufio, lib/strings, lib/encoding/
|
||||
// utf8, lib/strconv/stof_data don't shift). Float/
|
||||
// struct elements gain emit via element-kind
|
||||
// dispatch. Helper validates pre-emit so partial
|
||||
// fold-failures don't corrupt the DATA literal.
|
||||
// No-rhs arrays (e.g. `let buf: [N]u8;`) go through
|
||||
// the same helper with rhs=nil → zero-fill branch.
|
||||
if (d.lhs != nil) {
|
||||
if (d.lhs.kind == nkind.N_TARRAY) {
|
||||
let elemn: *node = d.lhs.lhs;
|
||||
let esz: i32 = 8;
|
||||
if (elemn != nil) {
|
||||
if (elemn.kind == nkind.N_TNAME) {
|
||||
let ps: i32 = primsize(elemn.str);
|
||||
if (ps > 0) { esz = ps; };
|
||||
let rh: *node = d.rhs;
|
||||
let route: bool = false;
|
||||
if (rh == nil) { route = true; };
|
||||
if (rh != nil) {
|
||||
if (rh.kind == nkind.N_ARRLIT) {
|
||||
route = true;
|
||||
};
|
||||
};
|
||||
let total: i32 = sz;
|
||||
let alen: i32 = total / esz;
|
||||
let elems: *node = nil;
|
||||
if (d.rhs != nil) {
|
||||
if (d.rhs.kind == nkind.N_ARRLIT) {
|
||||
elems = d.rhs.list;
|
||||
};
|
||||
if (route) {
|
||||
let at: *tinfo = d.lhs.type_: *tinfo;
|
||||
emitarraydata(c, "DATAW", nm,
|
||||
at, rh);
|
||||
};
|
||||
emitline("DATAW ");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB),\"");
|
||||
let i: i32 = 0;
|
||||
let e: *node = elems;
|
||||
let last: u64 = 0u64;
|
||||
let inrepeat: bool = false;
|
||||
for (i < alen) {
|
||||
let v: u64 = last;
|
||||
if (!inrepeat && e != nil) {
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) {
|
||||
inrepeat = true;
|
||||
} else {
|
||||
e = e.next;
|
||||
};
|
||||
} else {
|
||||
let ev: *node = e;
|
||||
for (ev != nil) {
|
||||
if (ev.kind != nkind.N_CAST) { break; };
|
||||
ev = ev.lhs;
|
||||
};
|
||||
if (!foldintliteral(ev, &v)) { v = 0u64; };
|
||||
last = v;
|
||||
e = e.next;
|
||||
};
|
||||
};
|
||||
let nb: u64 = v;
|
||||
let b: i32 = 0;
|
||||
for (b < esz) {
|
||||
emitdatawbyte((nb & 255u64): u8);
|
||||
nb = nb >> 8u64;
|
||||
b += 1;
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
emitline("\"\n");
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -1770,6 +2022,21 @@ fn emitdefconstants(c: *cgen, file: *node) void = {
|
||||
};
|
||||
};
|
||||
};};
|
||||
// #129 A.3: array-typed def with N_ARRLIT rhs.
|
||||
// Parallel to emitletdataw array arm; uses DATA.
|
||||
if (r != nil) { if (r.kind == nkind.N_ARRLIT) {
|
||||
let at: *tinfo = d.lhs.type_: *tinfo;
|
||||
let au: *tinfo = at;
|
||||
for (au != nil && au.kind == tykind.TY_NAMED) {
|
||||
au = au.under;
|
||||
};
|
||||
if (au != nil) {
|
||||
if (au.kind == tykind.TY_ARRAY) {
|
||||
emitarraydata(c, "DATA",
|
||||
d.str, at, r);
|
||||
};
|
||||
};
|
||||
};};
|
||||
};
|
||||
};
|
||||
if (ok) {
|
||||
|
||||
Reference in New Issue
Block a user