wcc: struct-composite let/def DATA emit via SSoT helper (#129 A.2)
Extract emit_struct_data + emit_struct_lit_bytes helpers (both stages, mirrored) for module-level let/def with N_STRUCTLIT initializer. Walks Tfield linked-list in declaration order, zero-fills padding via per- field offset (rule 13, no hardcoded sizes), dispatches per field kind: integer via fold_int_literal, float via inline bitcast + sign-XOR byte- loop (A.1 shape, no INT64_MIN materialised — sibling #144), nested struct via recursion (#145 inner-field-name-leak gates the test row). Out-of-scope field kinds (str/slice/ptr/array) fatal loud per rule 7. LOAD-side widened symmetric to A.1 precedent: cstage cgexpr N_DOT direct-struct-ident + chained-N_DOT widened via new DefStruct registry (def_isstructdef populated in let_collect); wwstage cgdot direct-struct- global falls through to defvarstructinfo on letvarstructinfo nil (defent.dtnode field added, populated in collectdefs). Both stages materialise struct-def via LEAQ name(SB) same as struct-let. Pre-existing cstage scalar 8B short-circuit at emit_lets caused silent fold-fail-continue on 8B struct lits (`struct{i32,i32}`); gate now excludes let_isstruct so 8B struct lits route through emit_struct_data. Wwstage's `!issg` gate was already correct; symmetric ordering restored. Closes (all bootstrap-NEUTRAL pre-impl; γ-cleanup #40 first consumer): - emit_lets `is_struct continue` skip → struct lets emitted no DATA - emit_defs no struct arm → struct defs emitted no DATA - cstage cgexpr N_DOT for struct-def emitted MOVSXD (BP), AX (broken stack-frame read) - cstage emit_lets sz==8 short-circuit silently skipped 8B struct lits Test 918 (7 rows: let_int_struct / def_int_struct / let_float_field / def_float_field / let_empty_struct / let_int_struct_8b / let_norhs_ struct_regression) registered. Make test: 181/181 incl. 990-997 byte-id + combined_ww_fresh. Followups filed: - #145 (task #41) — nested struct-lit inner field-name leaks as extern - #42 — wwstage dotchainresolve missing defvarstructinfo lookup (A.2- scope-clean today; surfaces post-#145 nested-struct shapes) - A.3 (task #39) — array static-init audit (parks shape-4 array-in-struct) - γ-cleanup (task #40) — lib/math const-floatinfo re-fold, blocked-by A.2
This commit is contained in:
@@ -16583,9 +16583,15 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
// load at fi.foff(CX). Mirrors the local "Direct struct local"
|
||||
// branch above, swapping the BP frame slot for the global VA.
|
||||
// Field-width-aware op handles MOVQ / MOVL / MOVZBQ / MOVSXD.
|
||||
// #129 A.2: also handles struct-typed `def`s via defvarstructinfo;
|
||||
// emitstructdata gives them DATA storage at name(SB), and this
|
||||
// LEAQ-and-offset shape mirrors the let path. Pre-A.2 the def
|
||||
// fell through to the integer-let MOVQ catch-all (reading garbage
|
||||
// from the wrong offset).
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == nkind.N_IDENT) {
|
||||
let si: *structinfo = letvarstructinfo(c, lhs.str);
|
||||
if (si == nil) { si = defvarstructinfo(c, lhs.str); };
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
@@ -24903,6 +24909,36 @@ fn letvarstructinfo(c: *cgen, name: str) *structinfo = {
|
||||
return nil;
|
||||
};
|
||||
|
||||
// defvarstructinfo — sister of letvarstructinfo for top-level struct
|
||||
// `def`s. #129 A.2 adds DATA storage for struct-typed defs; the
|
||||
// LOAD-side cgdot direct-struct-global branch needs to resolve the
|
||||
// def's structinfo the same way it resolves a let's, so the field-
|
||||
// offset arithmetic + LEAQ name(SB) routing fires. Walks c.defs and
|
||||
// the type-spec node (defent.dtnode), aliaslookup-chasing TY_NAMED
|
||||
// through to the underlying struct name. Returns nil for non-struct
|
||||
// defs (int/float/str — those use the existing emitsymname-based
|
||||
// paths).
|
||||
fn defvarstructinfo(c: *cgen, name: str) *structinfo = {
|
||||
let e: *defent = c.defs;
|
||||
for (e != nil) {
|
||||
if (streq(e.dname, name)) {
|
||||
let t: *node = e.dtnode;
|
||||
for (t != nil) {
|
||||
if (t.kind != nkind.N_TNAME) { return nil; };
|
||||
let nm: str = t.str;
|
||||
let si: *structinfo = structlookup(c, nm);
|
||||
if (si != nil) { return si; };
|
||||
let nx: *node = aliaslookup(c, nm);
|
||||
if (nx == nil) { return nil; };
|
||||
t = nx;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
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 = {
|
||||
@@ -25061,6 +25097,160 @@ fn emitfloatlitdata(c: *cgen, directive: str, name: str,
|
||||
return true;
|
||||
};
|
||||
|
||||
// emitstructlitbytes — payload of a struct-typed top-level let/def
|
||||
// with N_STRUCTLIT rhs. Walks structt.fields, zero-fills padding via
|
||||
// the per-field offset (rule 13), dispatches per field type:
|
||||
// foldintliteral for int/bool/nil, inline bitcast+sign-XOR for float,
|
||||
// recursive call for nested struct. Other field kinds (str / slice /
|
||||
// ptr-with-address / array) are out of #129 A.2 scope — rule-7 aborts
|
||||
// loud rather than silently emitting wrong bytes. Mirror of cstage
|
||||
// emit_struct_lit_bytes. `base` offsets the field-start computation
|
||||
// so the recursive call walks an inner struct's fields within its
|
||||
// outer parent's byte stream.
|
||||
fn emitstructlitbytes(c: *cgen, structt: *tinfo, rhs: *node,
|
||||
base: u64) bool = {
|
||||
let su: *tinfo = structt;
|
||||
for (su != nil && su.kind == tykind.TY_NAMED) { su = su.under; };
|
||||
if (su == nil) { return false; };
|
||||
if (su.kind != tykind.TY_STRUCT) { return false; };
|
||||
let pos: u64 = base;
|
||||
let f: *tfield = su.fields;
|
||||
for (f != nil) {
|
||||
let fstart: u64 = base + f.offset;
|
||||
for (pos < fstart) {
|
||||
emitdatawbyte(0u8);
|
||||
pos = pos + 1u64;
|
||||
};
|
||||
let v: *node = nil;
|
||||
if (rhs != nil) {
|
||||
let fnod: *node = rhs.list;
|
||||
for (fnod != nil) {
|
||||
if (streq(fnod.str, f.name)) {
|
||||
v = fnod.lhs;
|
||||
break;
|
||||
};
|
||||
fnod = fnod.next;
|
||||
};
|
||||
};
|
||||
let fsz: i32 = f.type_.size: i32;
|
||||
if (v == nil) {
|
||||
let i: i32 = 0;
|
||||
for (i < fsz) {
|
||||
emitdatawbyte(0u8);
|
||||
i = i + 1;
|
||||
};
|
||||
pos = fstart + fsz: u64;
|
||||
f = f.tnext;
|
||||
continue;
|
||||
};
|
||||
let vr: *node = v;
|
||||
for (vr != nil && vr.kind == nkind.N_CAST) { vr = vr.lhs; };
|
||||
let fu: *tinfo = f.type_;
|
||||
for (fu != nil && fu.kind == tykind.TY_NAMED) { fu = fu.under; };
|
||||
if (fu != nil && fu.kind == tykind.TY_STRUCT) {
|
||||
if (vr == nil) {
|
||||
let m: str = "emitstructlitbytes: nested struct field rhs nil (#129 A.2)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
if (vr.kind != nkind.N_STRUCTLIT) {
|
||||
let m: str = "emitstructlitbytes: nested struct rhs not N_STRUCTLIT (#129 A.2)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
emitstructlitbytes(c, f.type_, vr, fstart);
|
||||
pos = fstart + fsz: u64;
|
||||
f = f.tnext;
|
||||
continue;
|
||||
};
|
||||
if (typeisfloat(f.type_)) {
|
||||
let isf32: bool = (fsz == 4);
|
||||
let neg: bool = false;
|
||||
let fr: *node = vr;
|
||||
if (fr != nil) { if (fr.kind == nkind.N_UN) {
|
||||
if (fr.op == tkind.TK_MINUS) {
|
||||
neg = true;
|
||||
fr = fr.lhs;
|
||||
for (fr != nil && fr.kind == nkind.N_CAST) { fr = fr.lhs; };
|
||||
} else { if (fr.op == tkind.TK_PLUS) {
|
||||
fr = fr.lhs;
|
||||
for (fr != nil && fr.kind == nkind.N_CAST) { fr = fr.lhs; };
|
||||
};};
|
||||
};};
|
||||
if (fr == nil) {
|
||||
let m: str = "emitstructlitbytes: float field rhs nil (#129 A.2)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
if (fr.kind != nkind.N_FLOATLIT) {
|
||||
let m: str = "emitstructlitbytes: float field rhs not FLOATLIT (#129 A.2)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let bits: u64 = fr.uval;
|
||||
if (isf32) {
|
||||
let dv: f64 = *((&bits): *f64);
|
||||
let fv: f32 = (dv: f32);
|
||||
let uv: u32 = *((&fv): *u32);
|
||||
bits = uv: u64;
|
||||
};
|
||||
let i: i32 = 0;
|
||||
let nb: u64 = bits;
|
||||
for (i < fsz) {
|
||||
let b: u8 = (nb & 255u64): u8;
|
||||
if (neg) {
|
||||
if (i == fsz - 1) { b = b ^ 128u8; };
|
||||
};
|
||||
emitdatawbyte(b);
|
||||
nb = nb >> 8u64;
|
||||
i = i + 1;
|
||||
};
|
||||
pos = fstart + fsz: u64;
|
||||
f = f.tnext;
|
||||
continue;
|
||||
};
|
||||
let iv: u64 = 0u64;
|
||||
if (!foldintliteral(vr, &iv)) {
|
||||
let m: str = "emitstructlitbytes: field rhs not foldable (str/slice/ptr/array out of #129 A.2 scope)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let i: i32 = 0;
|
||||
let nb: u64 = iv;
|
||||
for (i < fsz) {
|
||||
emitdatawbyte((nb & 255u64): u8);
|
||||
nb = nb >> 8u64;
|
||||
i = i + 1;
|
||||
};
|
||||
pos = fstart + fsz: u64;
|
||||
f = f.tnext;
|
||||
};
|
||||
let endpos: u64 = base + structt.size;
|
||||
for (pos < endpos) {
|
||||
emitdatawbyte(0u8);
|
||||
pos = pos + 1u64;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
// emitstructdata — top-level wrapper. Opens the DATA/DATAW directive
|
||||
// then delegates to emitstructlitbytes. Shared between emitletdataw
|
||||
// struct arm and emitdefconstants struct arm (#129 A.2).
|
||||
fn emitstructdata(c: *cgen, directive: str, name: str,
|
||||
structt: *tinfo, rhs: *node) bool = {
|
||||
let su: *tinfo = structt;
|
||||
for (su != nil && su.kind == tykind.TY_NAMED) { su = su.under; };
|
||||
if (su == nil) { return false; };
|
||||
if (su.kind != tykind.TY_STRUCT) { return false; };
|
||||
emitline(directive);
|
||||
emitline(" ");
|
||||
emitsymname(c, name);
|
||||
emitline("(SB),\"");
|
||||
emitstructlitbytes(c, structt, rhs, 0u64);
|
||||
emitline("\"\n");
|
||||
return true;
|
||||
};
|
||||
|
||||
fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
@@ -25080,6 +25270,21 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
emitfloatlitdata(c, "DATAW", nm, fsz,
|
||||
d.rhs);
|
||||
};
|
||||
// #129 A.2: struct-typed let with N_STRUCTLIT rhs
|
||||
// routes through the emitstructdata SSoT helper.
|
||||
// Pre-A.2 emitletdataw had no struct arm, so the
|
||||
// declaration fell out of the .data section and
|
||||
// the link surfaced an undefined-symbol error.
|
||||
if (issg) {
|
||||
let r: *node = d.rhs;
|
||||
if (r != nil) {
|
||||
if (r.kind == nkind.N_STRUCTLIT) {
|
||||
let st: *tinfo = d.lhs.type_: *tinfo;
|
||||
emitstructdata(c, "DATAW", nm,
|
||||
st, r);
|
||||
};
|
||||
};
|
||||
};
|
||||
// Skip the scalar 8B path when the global is a
|
||||
// fixed-size array that just happens to sum to 8
|
||||
// bytes (e.g. [4]u16, [8]u8) — the array path
|
||||
@@ -25341,6 +25546,25 @@ fn emitdefconstants(c: *cgen, file: *node) void = {
|
||||
if (dfsz > 0) {
|
||||
emitfloatlitdata(c, "DATA", d.str,
|
||||
dfsz, d.rhs);
|
||||
} else {
|
||||
// #129 A.2: struct-typed def with N_STRUCTLIT
|
||||
// rhs. The checker stamps d.lhs.type_ with the
|
||||
// struct's tinfo; helper peels TY_NAMED. Parallel
|
||||
// to emitletdataw struct arm; uses DATA (read-
|
||||
// only) directive.
|
||||
if (r != nil) { if (r.kind == nkind.N_STRUCTLIT) {
|
||||
let st: *tinfo = d.lhs.type_: *tinfo;
|
||||
let su: *tinfo = st;
|
||||
for (su != nil && su.kind == tykind.TY_NAMED) {
|
||||
su = su.under;
|
||||
};
|
||||
if (su != nil) {
|
||||
if (su.kind == tykind.TY_STRUCT) {
|
||||
emitstructdata(c, "DATA",
|
||||
d.str, st, r);
|
||||
};
|
||||
};
|
||||
};};
|
||||
};
|
||||
};
|
||||
if (ok) {
|
||||
@@ -25606,6 +25830,9 @@ type defent = struct {
|
||||
dname: str,
|
||||
dmod: str, // originating module (`// MODULE: foo`), or empty
|
||||
drhs: *node,
|
||||
dtnode: *node, // #129 A.2: type-spec node (d.lhs); needed for
|
||||
// struct-def structinfo lookup at the cgdot
|
||||
// LOAD-side widening site.
|
||||
dnext: *defent,
|
||||
};
|
||||
|
||||
@@ -25614,7 +25841,7 @@ fn collectdefs(c: *cgen, file: *node) void = {
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
if (d.kind == nkind.N_DEF) {
|
||||
let e: *defent = alloc(defent{dname=d.str, dmod=d.nmod, drhs=d.rhs, dnext=c.defs})!;
|
||||
let e: *defent = alloc(defent{dname=d.str, dmod=d.nmod, drhs=d.rhs, dtnode=d.lhs, dnext=c.defs})!;
|
||||
c.defs = e;
|
||||
};
|
||||
d = d.next;
|
||||
|
||||
@@ -1114,6 +1114,36 @@ fn letvarstructinfo(c: *cgen, name: str) *structinfo = {
|
||||
return nil;
|
||||
};
|
||||
|
||||
// defvarstructinfo — sister of letvarstructinfo for top-level struct
|
||||
// `def`s. #129 A.2 adds DATA storage for struct-typed defs; the
|
||||
// LOAD-side cgdot direct-struct-global branch needs to resolve the
|
||||
// def's structinfo the same way it resolves a let's, so the field-
|
||||
// offset arithmetic + LEAQ name(SB) routing fires. Walks c.defs and
|
||||
// the type-spec node (defent.dtnode), aliaslookup-chasing TY_NAMED
|
||||
// through to the underlying struct name. Returns nil for non-struct
|
||||
// defs (int/float/str — those use the existing emitsymname-based
|
||||
// paths).
|
||||
fn defvarstructinfo(c: *cgen, name: str) *structinfo = {
|
||||
let e: *defent = c.defs;
|
||||
for (e != nil) {
|
||||
if (streq(e.dname, name)) {
|
||||
let t: *node = e.dtnode;
|
||||
for (t != nil) {
|
||||
if (t.kind != nkind.N_TNAME) { return nil; };
|
||||
let nm: str = t.str;
|
||||
let si: *structinfo = structlookup(c, nm);
|
||||
if (si != nil) { return si; };
|
||||
let nx: *node = aliaslookup(c, nm);
|
||||
if (nx == nil) { return nil; };
|
||||
t = nx;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
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 = {
|
||||
@@ -1272,6 +1302,160 @@ fn emitfloatlitdata(c: *cgen, directive: str, name: str,
|
||||
return true;
|
||||
};
|
||||
|
||||
// emitstructlitbytes — payload of a struct-typed top-level let/def
|
||||
// with N_STRUCTLIT rhs. Walks structt.fields, zero-fills padding via
|
||||
// the per-field offset (rule 13), dispatches per field type:
|
||||
// foldintliteral for int/bool/nil, inline bitcast+sign-XOR for float,
|
||||
// recursive call for nested struct. Other field kinds (str / slice /
|
||||
// ptr-with-address / array) are out of #129 A.2 scope — rule-7 aborts
|
||||
// loud rather than silently emitting wrong bytes. Mirror of cstage
|
||||
// emit_struct_lit_bytes. `base` offsets the field-start computation
|
||||
// so the recursive call walks an inner struct's fields within its
|
||||
// outer parent's byte stream.
|
||||
fn emitstructlitbytes(c: *cgen, structt: *tinfo, rhs: *node,
|
||||
base: u64) bool = {
|
||||
let su: *tinfo = structt;
|
||||
for (su != nil && su.kind == tykind.TY_NAMED) { su = su.under; };
|
||||
if (su == nil) { return false; };
|
||||
if (su.kind != tykind.TY_STRUCT) { return false; };
|
||||
let pos: u64 = base;
|
||||
let f: *tfield = su.fields;
|
||||
for (f != nil) {
|
||||
let fstart: u64 = base + f.offset;
|
||||
for (pos < fstart) {
|
||||
emitdatawbyte(0u8);
|
||||
pos = pos + 1u64;
|
||||
};
|
||||
let v: *node = nil;
|
||||
if (rhs != nil) {
|
||||
let fnod: *node = rhs.list;
|
||||
for (fnod != nil) {
|
||||
if (streq(fnod.str, f.name)) {
|
||||
v = fnod.lhs;
|
||||
break;
|
||||
};
|
||||
fnod = fnod.next;
|
||||
};
|
||||
};
|
||||
let fsz: i32 = f.type_.size: i32;
|
||||
if (v == nil) {
|
||||
let i: i32 = 0;
|
||||
for (i < fsz) {
|
||||
emitdatawbyte(0u8);
|
||||
i = i + 1;
|
||||
};
|
||||
pos = fstart + fsz: u64;
|
||||
f = f.tnext;
|
||||
continue;
|
||||
};
|
||||
let vr: *node = v;
|
||||
for (vr != nil && vr.kind == nkind.N_CAST) { vr = vr.lhs; };
|
||||
let fu: *tinfo = f.type_;
|
||||
for (fu != nil && fu.kind == tykind.TY_NAMED) { fu = fu.under; };
|
||||
if (fu != nil && fu.kind == tykind.TY_STRUCT) {
|
||||
if (vr == nil) {
|
||||
let m: str = "emitstructlitbytes: nested struct field rhs nil (#129 A.2)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
if (vr.kind != nkind.N_STRUCTLIT) {
|
||||
let m: str = "emitstructlitbytes: nested struct rhs not N_STRUCTLIT (#129 A.2)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
emitstructlitbytes(c, f.type_, vr, fstart);
|
||||
pos = fstart + fsz: u64;
|
||||
f = f.tnext;
|
||||
continue;
|
||||
};
|
||||
if (typeisfloat(f.type_)) {
|
||||
let isf32: bool = (fsz == 4);
|
||||
let neg: bool = false;
|
||||
let fr: *node = vr;
|
||||
if (fr != nil) { if (fr.kind == nkind.N_UN) {
|
||||
if (fr.op == tkind.TK_MINUS) {
|
||||
neg = true;
|
||||
fr = fr.lhs;
|
||||
for (fr != nil && fr.kind == nkind.N_CAST) { fr = fr.lhs; };
|
||||
} else { if (fr.op == tkind.TK_PLUS) {
|
||||
fr = fr.lhs;
|
||||
for (fr != nil && fr.kind == nkind.N_CAST) { fr = fr.lhs; };
|
||||
};};
|
||||
};};
|
||||
if (fr == nil) {
|
||||
let m: str = "emitstructlitbytes: float field rhs nil (#129 A.2)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
if (fr.kind != nkind.N_FLOATLIT) {
|
||||
let m: str = "emitstructlitbytes: float field rhs not FLOATLIT (#129 A.2)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let bits: u64 = fr.uval;
|
||||
if (isf32) {
|
||||
let dv: f64 = *((&bits): *f64);
|
||||
let fv: f32 = (dv: f32);
|
||||
let uv: u32 = *((&fv): *u32);
|
||||
bits = uv: u64;
|
||||
};
|
||||
let i: i32 = 0;
|
||||
let nb: u64 = bits;
|
||||
for (i < fsz) {
|
||||
let b: u8 = (nb & 255u64): u8;
|
||||
if (neg) {
|
||||
if (i == fsz - 1) { b = b ^ 128u8; };
|
||||
};
|
||||
emitdatawbyte(b);
|
||||
nb = nb >> 8u64;
|
||||
i = i + 1;
|
||||
};
|
||||
pos = fstart + fsz: u64;
|
||||
f = f.tnext;
|
||||
continue;
|
||||
};
|
||||
let iv: u64 = 0u64;
|
||||
if (!foldintliteral(vr, &iv)) {
|
||||
let m: str = "emitstructlitbytes: field rhs not foldable (str/slice/ptr/array out of #129 A.2 scope)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let i: i32 = 0;
|
||||
let nb: u64 = iv;
|
||||
for (i < fsz) {
|
||||
emitdatawbyte((nb & 255u64): u8);
|
||||
nb = nb >> 8u64;
|
||||
i = i + 1;
|
||||
};
|
||||
pos = fstart + fsz: u64;
|
||||
f = f.tnext;
|
||||
};
|
||||
let endpos: u64 = base + structt.size;
|
||||
for (pos < endpos) {
|
||||
emitdatawbyte(0u8);
|
||||
pos = pos + 1u64;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
// emitstructdata — top-level wrapper. Opens the DATA/DATAW directive
|
||||
// then delegates to emitstructlitbytes. Shared between emitletdataw
|
||||
// struct arm and emitdefconstants struct arm (#129 A.2).
|
||||
fn emitstructdata(c: *cgen, directive: str, name: str,
|
||||
structt: *tinfo, rhs: *node) bool = {
|
||||
let su: *tinfo = structt;
|
||||
for (su != nil && su.kind == tykind.TY_NAMED) { su = su.under; };
|
||||
if (su == nil) { return false; };
|
||||
if (su.kind != tykind.TY_STRUCT) { return false; };
|
||||
emitline(directive);
|
||||
emitline(" ");
|
||||
emitsymname(c, name);
|
||||
emitline("(SB),\"");
|
||||
emitstructlitbytes(c, structt, rhs, 0u64);
|
||||
emitline("\"\n");
|
||||
return true;
|
||||
};
|
||||
|
||||
fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
@@ -1291,6 +1475,21 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
emitfloatlitdata(c, "DATAW", nm, fsz,
|
||||
d.rhs);
|
||||
};
|
||||
// #129 A.2: struct-typed let with N_STRUCTLIT rhs
|
||||
// routes through the emitstructdata SSoT helper.
|
||||
// Pre-A.2 emitletdataw had no struct arm, so the
|
||||
// declaration fell out of the .data section and
|
||||
// the link surfaced an undefined-symbol error.
|
||||
if (issg) {
|
||||
let r: *node = d.rhs;
|
||||
if (r != nil) {
|
||||
if (r.kind == nkind.N_STRUCTLIT) {
|
||||
let st: *tinfo = d.lhs.type_: *tinfo;
|
||||
emitstructdata(c, "DATAW", nm,
|
||||
st, r);
|
||||
};
|
||||
};
|
||||
};
|
||||
// Skip the scalar 8B path when the global is a
|
||||
// fixed-size array that just happens to sum to 8
|
||||
// bytes (e.g. [4]u16, [8]u8) — the array path
|
||||
@@ -1552,6 +1751,25 @@ fn emitdefconstants(c: *cgen, file: *node) void = {
|
||||
if (dfsz > 0) {
|
||||
emitfloatlitdata(c, "DATA", d.str,
|
||||
dfsz, d.rhs);
|
||||
} else {
|
||||
// #129 A.2: struct-typed def with N_STRUCTLIT
|
||||
// rhs. The checker stamps d.lhs.type_ with the
|
||||
// struct's tinfo; helper peels TY_NAMED. Parallel
|
||||
// to emitletdataw struct arm; uses DATA (read-
|
||||
// only) directive.
|
||||
if (r != nil) { if (r.kind == nkind.N_STRUCTLIT) {
|
||||
let st: *tinfo = d.lhs.type_: *tinfo;
|
||||
let su: *tinfo = st;
|
||||
for (su != nil && su.kind == tykind.TY_NAMED) {
|
||||
su = su.under;
|
||||
};
|
||||
if (su != nil) {
|
||||
if (su.kind == tykind.TY_STRUCT) {
|
||||
emitstructdata(c, "DATA",
|
||||
d.str, st, r);
|
||||
};
|
||||
};
|
||||
};};
|
||||
};
|
||||
};
|
||||
if (ok) {
|
||||
@@ -1817,6 +2035,9 @@ type defent = struct {
|
||||
dname: str,
|
||||
dmod: str, // originating module (`// MODULE: foo`), or empty
|
||||
drhs: *node,
|
||||
dtnode: *node, // #129 A.2: type-spec node (d.lhs); needed for
|
||||
// struct-def structinfo lookup at the cgdot
|
||||
// LOAD-side widening site.
|
||||
dnext: *defent,
|
||||
};
|
||||
|
||||
@@ -1825,7 +2046,7 @@ fn collectdefs(c: *cgen, file: *node) void = {
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
if (d.kind == nkind.N_DEF) {
|
||||
let e: *defent = alloc(defent{dname=d.str, dmod=d.nmod, drhs=d.rhs, dnext=c.defs})!;
|
||||
let e: *defent = alloc(defent{dname=d.str, dmod=d.nmod, drhs=d.rhs, dtnode=d.lhs, dnext=c.defs})!;
|
||||
c.defs = e;
|
||||
};
|
||||
d = d.next;
|
||||
|
||||
@@ -1964,9 +1964,15 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
// load at fi.foff(CX). Mirrors the local "Direct struct local"
|
||||
// branch above, swapping the BP frame slot for the global VA.
|
||||
// Field-width-aware op handles MOVQ / MOVL / MOVZBQ / MOVSXD.
|
||||
// #129 A.2: also handles struct-typed `def`s via defvarstructinfo;
|
||||
// emitstructdata gives them DATA storage at name(SB), and this
|
||||
// LEAQ-and-offset shape mirrors the let path. Pre-A.2 the def
|
||||
// fell through to the integer-let MOVQ catch-all (reading garbage
|
||||
// from the wrong offset).
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == nkind.N_IDENT) {
|
||||
let si: *structinfo = letvarstructinfo(c, lhs.str);
|
||||
if (si == nil) { si = defvarstructinfo(c, lhs.str); };
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
|
||||
@@ -16583,9 +16583,15 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
// load at fi.foff(CX). Mirrors the local "Direct struct local"
|
||||
// branch above, swapping the BP frame slot for the global VA.
|
||||
// Field-width-aware op handles MOVQ / MOVL / MOVZBQ / MOVSXD.
|
||||
// #129 A.2: also handles struct-typed `def`s via defvarstructinfo;
|
||||
// emitstructdata gives them DATA storage at name(SB), and this
|
||||
// LEAQ-and-offset shape mirrors the let path. Pre-A.2 the def
|
||||
// fell through to the integer-let MOVQ catch-all (reading garbage
|
||||
// from the wrong offset).
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == nkind.N_IDENT) {
|
||||
let si: *structinfo = letvarstructinfo(c, lhs.str);
|
||||
if (si == nil) { si = defvarstructinfo(c, lhs.str); };
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
@@ -24903,6 +24909,36 @@ fn letvarstructinfo(c: *cgen, name: str) *structinfo = {
|
||||
return nil;
|
||||
};
|
||||
|
||||
// defvarstructinfo — sister of letvarstructinfo for top-level struct
|
||||
// `def`s. #129 A.2 adds DATA storage for struct-typed defs; the
|
||||
// LOAD-side cgdot direct-struct-global branch needs to resolve the
|
||||
// def's structinfo the same way it resolves a let's, so the field-
|
||||
// offset arithmetic + LEAQ name(SB) routing fires. Walks c.defs and
|
||||
// the type-spec node (defent.dtnode), aliaslookup-chasing TY_NAMED
|
||||
// through to the underlying struct name. Returns nil for non-struct
|
||||
// defs (int/float/str — those use the existing emitsymname-based
|
||||
// paths).
|
||||
fn defvarstructinfo(c: *cgen, name: str) *structinfo = {
|
||||
let e: *defent = c.defs;
|
||||
for (e != nil) {
|
||||
if (streq(e.dname, name)) {
|
||||
let t: *node = e.dtnode;
|
||||
for (t != nil) {
|
||||
if (t.kind != nkind.N_TNAME) { return nil; };
|
||||
let nm: str = t.str;
|
||||
let si: *structinfo = structlookup(c, nm);
|
||||
if (si != nil) { return si; };
|
||||
let nx: *node = aliaslookup(c, nm);
|
||||
if (nx == nil) { return nil; };
|
||||
t = nx;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
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 = {
|
||||
@@ -25061,6 +25097,160 @@ fn emitfloatlitdata(c: *cgen, directive: str, name: str,
|
||||
return true;
|
||||
};
|
||||
|
||||
// emitstructlitbytes — payload of a struct-typed top-level let/def
|
||||
// with N_STRUCTLIT rhs. Walks structt.fields, zero-fills padding via
|
||||
// the per-field offset (rule 13), dispatches per field type:
|
||||
// foldintliteral for int/bool/nil, inline bitcast+sign-XOR for float,
|
||||
// recursive call for nested struct. Other field kinds (str / slice /
|
||||
// ptr-with-address / array) are out of #129 A.2 scope — rule-7 aborts
|
||||
// loud rather than silently emitting wrong bytes. Mirror of cstage
|
||||
// emit_struct_lit_bytes. `base` offsets the field-start computation
|
||||
// so the recursive call walks an inner struct's fields within its
|
||||
// outer parent's byte stream.
|
||||
fn emitstructlitbytes(c: *cgen, structt: *tinfo, rhs: *node,
|
||||
base: u64) bool = {
|
||||
let su: *tinfo = structt;
|
||||
for (su != nil && su.kind == tykind.TY_NAMED) { su = su.under; };
|
||||
if (su == nil) { return false; };
|
||||
if (su.kind != tykind.TY_STRUCT) { return false; };
|
||||
let pos: u64 = base;
|
||||
let f: *tfield = su.fields;
|
||||
for (f != nil) {
|
||||
let fstart: u64 = base + f.offset;
|
||||
for (pos < fstart) {
|
||||
emitdatawbyte(0u8);
|
||||
pos = pos + 1u64;
|
||||
};
|
||||
let v: *node = nil;
|
||||
if (rhs != nil) {
|
||||
let fnod: *node = rhs.list;
|
||||
for (fnod != nil) {
|
||||
if (streq(fnod.str, f.name)) {
|
||||
v = fnod.lhs;
|
||||
break;
|
||||
};
|
||||
fnod = fnod.next;
|
||||
};
|
||||
};
|
||||
let fsz: i32 = f.type_.size: i32;
|
||||
if (v == nil) {
|
||||
let i: i32 = 0;
|
||||
for (i < fsz) {
|
||||
emitdatawbyte(0u8);
|
||||
i = i + 1;
|
||||
};
|
||||
pos = fstart + fsz: u64;
|
||||
f = f.tnext;
|
||||
continue;
|
||||
};
|
||||
let vr: *node = v;
|
||||
for (vr != nil && vr.kind == nkind.N_CAST) { vr = vr.lhs; };
|
||||
let fu: *tinfo = f.type_;
|
||||
for (fu != nil && fu.kind == tykind.TY_NAMED) { fu = fu.under; };
|
||||
if (fu != nil && fu.kind == tykind.TY_STRUCT) {
|
||||
if (vr == nil) {
|
||||
let m: str = "emitstructlitbytes: nested struct field rhs nil (#129 A.2)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
if (vr.kind != nkind.N_STRUCTLIT) {
|
||||
let m: str = "emitstructlitbytes: nested struct rhs not N_STRUCTLIT (#129 A.2)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
emitstructlitbytes(c, f.type_, vr, fstart);
|
||||
pos = fstart + fsz: u64;
|
||||
f = f.tnext;
|
||||
continue;
|
||||
};
|
||||
if (typeisfloat(f.type_)) {
|
||||
let isf32: bool = (fsz == 4);
|
||||
let neg: bool = false;
|
||||
let fr: *node = vr;
|
||||
if (fr != nil) { if (fr.kind == nkind.N_UN) {
|
||||
if (fr.op == tkind.TK_MINUS) {
|
||||
neg = true;
|
||||
fr = fr.lhs;
|
||||
for (fr != nil && fr.kind == nkind.N_CAST) { fr = fr.lhs; };
|
||||
} else { if (fr.op == tkind.TK_PLUS) {
|
||||
fr = fr.lhs;
|
||||
for (fr != nil && fr.kind == nkind.N_CAST) { fr = fr.lhs; };
|
||||
};};
|
||||
};};
|
||||
if (fr == nil) {
|
||||
let m: str = "emitstructlitbytes: float field rhs nil (#129 A.2)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
if (fr.kind != nkind.N_FLOATLIT) {
|
||||
let m: str = "emitstructlitbytes: float field rhs not FLOATLIT (#129 A.2)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let bits: u64 = fr.uval;
|
||||
if (isf32) {
|
||||
let dv: f64 = *((&bits): *f64);
|
||||
let fv: f32 = (dv: f32);
|
||||
let uv: u32 = *((&fv): *u32);
|
||||
bits = uv: u64;
|
||||
};
|
||||
let i: i32 = 0;
|
||||
let nb: u64 = bits;
|
||||
for (i < fsz) {
|
||||
let b: u8 = (nb & 255u64): u8;
|
||||
if (neg) {
|
||||
if (i == fsz - 1) { b = b ^ 128u8; };
|
||||
};
|
||||
emitdatawbyte(b);
|
||||
nb = nb >> 8u64;
|
||||
i = i + 1;
|
||||
};
|
||||
pos = fstart + fsz: u64;
|
||||
f = f.tnext;
|
||||
continue;
|
||||
};
|
||||
let iv: u64 = 0u64;
|
||||
if (!foldintliteral(vr, &iv)) {
|
||||
let m: str = "emitstructlitbytes: field rhs not foldable (str/slice/ptr/array out of #129 A.2 scope)\n";
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let i: i32 = 0;
|
||||
let nb: u64 = iv;
|
||||
for (i < fsz) {
|
||||
emitdatawbyte((nb & 255u64): u8);
|
||||
nb = nb >> 8u64;
|
||||
i = i + 1;
|
||||
};
|
||||
pos = fstart + fsz: u64;
|
||||
f = f.tnext;
|
||||
};
|
||||
let endpos: u64 = base + structt.size;
|
||||
for (pos < endpos) {
|
||||
emitdatawbyte(0u8);
|
||||
pos = pos + 1u64;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
// emitstructdata — top-level wrapper. Opens the DATA/DATAW directive
|
||||
// then delegates to emitstructlitbytes. Shared between emitletdataw
|
||||
// struct arm and emitdefconstants struct arm (#129 A.2).
|
||||
fn emitstructdata(c: *cgen, directive: str, name: str,
|
||||
structt: *tinfo, rhs: *node) bool = {
|
||||
let su: *tinfo = structt;
|
||||
for (su != nil && su.kind == tykind.TY_NAMED) { su = su.under; };
|
||||
if (su == nil) { return false; };
|
||||
if (su.kind != tykind.TY_STRUCT) { return false; };
|
||||
emitline(directive);
|
||||
emitline(" ");
|
||||
emitsymname(c, name);
|
||||
emitline("(SB),\"");
|
||||
emitstructlitbytes(c, structt, rhs, 0u64);
|
||||
emitline("\"\n");
|
||||
return true;
|
||||
};
|
||||
|
||||
fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
@@ -25080,6 +25270,21 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
emitfloatlitdata(c, "DATAW", nm, fsz,
|
||||
d.rhs);
|
||||
};
|
||||
// #129 A.2: struct-typed let with N_STRUCTLIT rhs
|
||||
// routes through the emitstructdata SSoT helper.
|
||||
// Pre-A.2 emitletdataw had no struct arm, so the
|
||||
// declaration fell out of the .data section and
|
||||
// the link surfaced an undefined-symbol error.
|
||||
if (issg) {
|
||||
let r: *node = d.rhs;
|
||||
if (r != nil) {
|
||||
if (r.kind == nkind.N_STRUCTLIT) {
|
||||
let st: *tinfo = d.lhs.type_: *tinfo;
|
||||
emitstructdata(c, "DATAW", nm,
|
||||
st, r);
|
||||
};
|
||||
};
|
||||
};
|
||||
// Skip the scalar 8B path when the global is a
|
||||
// fixed-size array that just happens to sum to 8
|
||||
// bytes (e.g. [4]u16, [8]u8) — the array path
|
||||
@@ -25341,6 +25546,25 @@ fn emitdefconstants(c: *cgen, file: *node) void = {
|
||||
if (dfsz > 0) {
|
||||
emitfloatlitdata(c, "DATA", d.str,
|
||||
dfsz, d.rhs);
|
||||
} else {
|
||||
// #129 A.2: struct-typed def with N_STRUCTLIT
|
||||
// rhs. The checker stamps d.lhs.type_ with the
|
||||
// struct's tinfo; helper peels TY_NAMED. Parallel
|
||||
// to emitletdataw struct arm; uses DATA (read-
|
||||
// only) directive.
|
||||
if (r != nil) { if (r.kind == nkind.N_STRUCTLIT) {
|
||||
let st: *tinfo = d.lhs.type_: *tinfo;
|
||||
let su: *tinfo = st;
|
||||
for (su != nil && su.kind == tykind.TY_NAMED) {
|
||||
su = su.under;
|
||||
};
|
||||
if (su != nil) {
|
||||
if (su.kind == tykind.TY_STRUCT) {
|
||||
emitstructdata(c, "DATA",
|
||||
d.str, st, r);
|
||||
};
|
||||
};
|
||||
};};
|
||||
};
|
||||
};
|
||||
if (ok) {
|
||||
@@ -25606,6 +25830,9 @@ type defent = struct {
|
||||
dname: str,
|
||||
dmod: str, // originating module (`// MODULE: foo`), or empty
|
||||
drhs: *node,
|
||||
dtnode: *node, // #129 A.2: type-spec node (d.lhs); needed for
|
||||
// struct-def structinfo lookup at the cgdot
|
||||
// LOAD-side widening site.
|
||||
dnext: *defent,
|
||||
};
|
||||
|
||||
@@ -25614,7 +25841,7 @@ fn collectdefs(c: *cgen, file: *node) void = {
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
if (d.kind == nkind.N_DEF) {
|
||||
let e: *defent = alloc(defent{dname=d.str, dmod=d.nmod, drhs=d.rhs, dnext=c.defs})!;
|
||||
let e: *defent = alloc(defent{dname=d.str, dmod=d.nmod, drhs=d.rhs, dtnode=d.lhs, dnext=c.defs})!;
|
||||
c.defs = e;
|
||||
};
|
||||
d = d.next;
|
||||
|
||||
Reference in New Issue
Block a user