w6c_ww: tinfo re-key of the cgdot N_INDEX-base arm (F7/F10)
The wwstage `arr[i].field` read arm was syntactic where cstage is type-table-driven: element typing keyed on tnode KINDs (N_TSLICE/ N_TARRAY/N_TPTR) with an N_TNAME element resolved by structlookup NAME — any base typed via an alias (`type result = []capture`, p11b/F10) missed every gate and died at the interim C2 loud guard (pre-C2: fell silently to the SB fallback). Re-key the arm onto the checker-stamped tinfo (lhs.type_ element / idxbase.type_ base, NAMED peeled), mirroring cstage cgen.c case N_DOT's N_INDEX-lhs arm 1:1 — the #209/#211 name-keyed->tinfo-SSoT cluster. Emission sequence is unchanged; the C2 "C3/task #8" guard retires with the arm wired (task #37's ptr-chain guard is untouched). Global classification mirrors cstage let_islet || def_isarraydef via isletvar / defvartnode-N_TARRAY (the cgplaceaddr C2 pattern). F7/FA5/FA3-ww/FA6 (non-ident idxbase shapes, task #17's ww halves) were already closed by C2's read-resolver recursion; p7_composed, pA5, pA9 run exit-0 byte-id and are pinned as rows here. Task #29's asserttyped bail (strings.frombytes over a slice-expr in the composed context) no longer reproduces at HEAD — dissolved during the C1.25->C2 arc; p7_composed builds clean on w6c_ww, runs 0, byte-id. Behind the retired guard three alias-blind NON-cgdot sites surface (`let l: wlist = []` checker reject / alias-array global emits no DATA / alias-array arrlit-init under-copies 8B per element, cs!=ww runtime): filed as task #38, same name-keyed class, separate sites. test/805: +5 rows — alias-slice field-kind matrix, alias-array + viaptr-element bases, p11b-essence let-bound alias reads (the task-#14 `?` factored out), the composed p7 match/compound hot shape, and the pA9 free()-operand acceptance row (FA6). Task #8; the last cgen gate before regex tranche B.
This commit is contained in:
@@ -22957,152 +22957,168 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
// element), so the leaf load is `(field.offset)(AX)` either way.
|
||||
// Bypasses cgindex deliberately — cgindex's final MOVQ would
|
||||
// truncate a value-struct element to 8 bytes.
|
||||
// C3 (task #8): keyed on the checker-stamped tinfo (lhs.type_ /
|
||||
// idxbase.type_), not tnode KINDs + structlookup-by-name — the
|
||||
// name-key dropped any base typed via an N_TNAME alias (`type
|
||||
// result = []capture`, F10) into the silent fallbacks below.
|
||||
// Mirrors cstage cgen.c case N_DOT N_INDEX-lhs arm 1:1; #209/#211
|
||||
// name-keyed→tinfo-SSoT cluster.
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == nkind.N_INDEX) {
|
||||
let idxbase: *node = lhs.lhs;
|
||||
if (idxbase != nil) { if (idxbase.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, idxbase.str);
|
||||
// #21 (READ twin of #11): a module-GLOBAL base makes
|
||||
// localfindnode return nil, so this field-offset-aware
|
||||
// branch was skipped and `g[i].field` fell through to
|
||||
// the module-qualified fallback below (garbage — no
|
||||
// main.g load at all). Resolve the global's tnode via
|
||||
// letvartnode/defvartnode (the same source cgindex's
|
||||
// global arm uses) and dispatch the base load by shape:
|
||||
// array -> LEAQ name(SB) (the symbol IS the storage),
|
||||
// slice/ptr -> MOVQ name(SB) (the symbol's first word
|
||||
// IS the .ptr). Mirrors cstage cgen.c's #21 arm.
|
||||
let tn: *node = nil;
|
||||
let isglobal: bool = false;
|
||||
let gname: str;
|
||||
gname.ptr = nil; gname.len = 0;
|
||||
if (lc != nil) {
|
||||
tn = lc.tnode;
|
||||
} else {
|
||||
tn = letvartnode(c, idxbase.str);
|
||||
if (tn == nil) { tn = defvartnode(c, idxbase.str); };
|
||||
if (tn != nil) { isglobal = true; gname = idxbase.str; };
|
||||
};
|
||||
if (tn != nil) {
|
||||
let elemt: *node = nil;
|
||||
let baseisarray: bool = false;
|
||||
let tk: nkind = tn.kind;
|
||||
if (tk == nkind.N_TSLICE) { elemt = tn.lhs; };
|
||||
if (tk == nkind.N_TARRAY) { elemt = tn.lhs; baseisarray = true; };
|
||||
if (tk == nkind.N_TPTR) { elemt = tn.lhs; };
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
let viaptr: bool = false;
|
||||
if (elemt != nil) {
|
||||
if (elemt.kind == nkind.N_TPTR) {
|
||||
let inner: *node = elemt.lhs;
|
||||
if (inner != nil) { if (inner.kind == nkind.N_TNAME) {
|
||||
sname = inner.str;
|
||||
viaptr = true;
|
||||
};};
|
||||
} else { if (elemt.kind == nkind.N_TNAME) {
|
||||
sname = elemt.str;
|
||||
let elemt: *tinfo = lhs.type_: *tinfo;
|
||||
let elemu: *tinfo = elemt;
|
||||
for (elemu != nil && elemu.kind == tykind.TY_NAMED) { elemu = elemu.under; };
|
||||
let st: *tinfo = nil;
|
||||
let viaptr: bool = false;
|
||||
if (elemu != nil) {
|
||||
if (elemu.kind == tykind.TY_PTR) {
|
||||
let pin: *tinfo = elemu.sub;
|
||||
for (pin != nil && pin.kind == tykind.TY_NAMED) { pin = pin.under; };
|
||||
if (pin != nil) { if (pin.kind == tykind.TY_STRUCT) {
|
||||
st = pin;
|
||||
viaptr = true;
|
||||
};};
|
||||
} else { if (elemu.kind == tykind.TY_STRUCT) {
|
||||
st = elemu;
|
||||
};};
|
||||
};
|
||||
if (st != nil) {
|
||||
let fnd: *tfield = nil;
|
||||
let fwalk: *tfield = st.fields;
|
||||
for (fwalk != nil) {
|
||||
if (streq(fwalk.name, fld)) { fnd = fwalk; break; };
|
||||
fwalk = fwalk.tnext;
|
||||
};
|
||||
if (sname.len > 0) {
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
if (streq(fi.fname, fld)) {
|
||||
let esz: i32 = elemsizeofc(c, tn);
|
||||
cgexpr(c, lhs.rhs); // idx → AX
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
if (isglobal) {
|
||||
if (baseisarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, gname);
|
||||
emitline("(SB), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymname(c, gname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
} else {
|
||||
if (baseisarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
};
|
||||
emitline("\tADDQ\tAX, BX\n");
|
||||
if (viaptr) {
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
};
|
||||
// #270-1a: an `[N]T`-typed field of an
|
||||
// array element (`a[i].m[j]`) — leave the
|
||||
// field's ADDRESS, a base for the outer
|
||||
// index, NEVER deref. AX holds &a[i]; the
|
||||
// field address is &a[i]+foff. The #135
|
||||
// read-side for `d.m[i]`, applied to an
|
||||
// array-element base. Without this an array
|
||||
// field fell to fieldloadop below and loaded
|
||||
// its first 8 bytes as a value → garbage
|
||||
// base → SEGFAULT in the outer index.
|
||||
if (tinfoisarray(fi.tnode.type_: *tinfo)) {
|
||||
if (fi.foff != 0) {
|
||||
emitline("\tADDQ\t$");
|
||||
emitint(fi.foff: i64);
|
||||
emitline(", AX\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (isstrtype(c, fi.tnode) || isslicetype(c, fi.tnode)) {
|
||||
// str/slice: the 3-word {ptr,len,cap}
|
||||
// slice header (#1). AX holds the
|
||||
// element base, so load .ptr (which
|
||||
// targets AX) LAST. Matches the
|
||||
// caseB *struct slice arm and
|
||||
// cgslicehdr(D_AX).
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((fi.foff + 8): i64, "AX");
|
||||
emitline(", BX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((fi.foff + 16): i64, "AX");
|
||||
emitline(", CX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(fi.foff: i64, "AX");
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\t");
|
||||
emitdispreg(fi.foff: i64, "AX");
|
||||
emitline(", X0\n");
|
||||
return;
|
||||
};
|
||||
let lop: str = fieldloadop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
emitdispreg(fi.foff: i64, "AX");
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
fi = fi.finext;
|
||||
let bu: *tinfo = idxbase.type_: *tinfo;
|
||||
for (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; };
|
||||
let baseisarray: bool = false;
|
||||
let baseok: bool = false;
|
||||
if (bu != nil) {
|
||||
if (bu.kind == tykind.TY_ARRAY) { baseisarray = true; baseok = true; };
|
||||
if (bu.kind == tykind.TY_SLICE) { baseok = true; };
|
||||
if (bu.kind == tykind.TY_PTR) { baseok = true; };
|
||||
};
|
||||
let lc: *local = localfindnode(c, idxbase.str);
|
||||
// #21 (READ twin of #11): a module-GLOBAL base makes
|
||||
// localfindnode return nil, so this field-offset-aware
|
||||
// branch was skipped and `g[i].field` fell through to
|
||||
// the module-qualified fallback below (garbage — no
|
||||
// main.g load at all). Classify via the let registry /
|
||||
// array-typed def registry (cstage let_islet ||
|
||||
// def_isarraydef) and dispatch the base load by shape:
|
||||
// array -> LEAQ name(SB) (the symbol IS the storage),
|
||||
// slice/ptr -> MOVQ name(SB) (the symbol's first word
|
||||
// IS the .ptr). Mirrors cstage cgen.c's #21 arm.
|
||||
let isglobal: bool = false;
|
||||
if (lc == nil) {
|
||||
if (isletvar(c, idxbase.str)) {
|
||||
isglobal = true;
|
||||
} else {
|
||||
let dtn: *node = defvartnode(c, idxbase.str);
|
||||
if (dtn != nil) {
|
||||
if (dtn.kind == nkind.N_TARRAY) { isglobal = true; };
|
||||
};
|
||||
};
|
||||
};
|
||||
if (fnd != nil && baseok && (lc != nil || isglobal)) {
|
||||
let esz: i32 = elemt.size: i32;
|
||||
cgexpr(c, lhs.rhs); // idx → AX
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
if (isglobal) {
|
||||
if (baseisarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, idxbase.str);
|
||||
emitline("(SB), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymname(c, idxbase.str);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
} else {
|
||||
if (baseisarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
};
|
||||
emitline("\tADDQ\tAX, BX\n");
|
||||
if (viaptr) {
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
};
|
||||
let foff: i64 = fnd.offset: i64;
|
||||
let ft: *tinfo = fnd.type_;
|
||||
let fu: *tinfo = ft;
|
||||
for (fu != nil && fu.kind == tykind.TY_NAMED) { fu = fu.under; };
|
||||
// #270-1a: an `[N]T`-typed field of an
|
||||
// array element (`a[i].m[j]`) — leave the
|
||||
// field's ADDRESS, a base for the outer
|
||||
// index, NEVER deref. AX holds &a[i]; the
|
||||
// field address is &a[i]+foff. The #135
|
||||
// read-side for `d.m[i]`, applied to an
|
||||
// array-element base. Without this an array
|
||||
// field fell to the scalar load below and loaded
|
||||
// its first 8 bytes as a value → garbage
|
||||
// base → SEGFAULT in the outer index.
|
||||
if (fu != nil && fu.kind == tykind.TY_ARRAY) {
|
||||
if (foff != 0) {
|
||||
emitline("\tADDQ\t$");
|
||||
emitint(foff);
|
||||
emitline(", AX\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (fu != nil && (fu.kind == tykind.TY_STR
|
||||
|| fu.kind == tykind.TY_SLICE)) {
|
||||
// str/slice: the 3-word {ptr,len,cap}
|
||||
// slice header (#1). AX holds the
|
||||
// element base, so load .ptr (which
|
||||
// targets AX) LAST. Matches the
|
||||
// caseB *struct slice arm and
|
||||
// cgslicehdr(D_AX).
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(foff + 8, "AX");
|
||||
emitline(", BX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(foff + 16, "AX");
|
||||
emitline(", CX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(foff, "AX");
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
if (typeisfloat(ft)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (typeisf32(ft)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\t");
|
||||
emitdispreg(foff, "AX");
|
||||
emitline(", X0\n");
|
||||
return;
|
||||
};
|
||||
let fsz: i32 = 8;
|
||||
if (ft != nil) { fsz = ft.size: i32; };
|
||||
let lop: str = loadopsz(typeissigned(ft), fsz);
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
emitdispreg(foff, "AX");
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
};
|
||||
};};
|
||||
};
|
||||
@@ -23631,24 +23647,6 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// C2/C3 boundary: `xs[i].fld` with an IDENT index base is the
|
||||
// enumerated N_INDEX-base arm's territory (cstage handles it
|
||||
// type-keyed); reaching HERE means wwstage's name-keyed arm above
|
||||
// missed (the F10 alias-tnode class). Routing it through the
|
||||
// read-resolver would emit a different (albeit correct) sequence
|
||||
// than cstage's arm → cs≠ww. Loud until C3 re-keys that arm onto
|
||||
// stamped tinfo (task #8).
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == nkind.N_INDEX) {
|
||||
if (lhs.lhs != nil) {
|
||||
if (lhs.lhs.kind == nkind.N_IDENT) {
|
||||
let mg: str = "cgdot: ident-indexed field read unwired in wwstage (C3/task #8)\n";
|
||||
os.write(2, mg.ptr, mg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// cstage reads ptr-chained fields (`a.p.f`, any root) in its
|
||||
// chained-*struct arm; wwstage's #70 mirror above is gated
|
||||
// root-local — the uncovered remainder (global / indexed roots)
|
||||
|
||||
@@ -2879,152 +2879,168 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
// element), so the leaf load is `(field.offset)(AX)` either way.
|
||||
// Bypasses cgindex deliberately — cgindex's final MOVQ would
|
||||
// truncate a value-struct element to 8 bytes.
|
||||
// C3 (task #8): keyed on the checker-stamped tinfo (lhs.type_ /
|
||||
// idxbase.type_), not tnode KINDs + structlookup-by-name — the
|
||||
// name-key dropped any base typed via an N_TNAME alias (`type
|
||||
// result = []capture`, F10) into the silent fallbacks below.
|
||||
// Mirrors cstage cgen.c case N_DOT N_INDEX-lhs arm 1:1; #209/#211
|
||||
// name-keyed→tinfo-SSoT cluster.
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == nkind.N_INDEX) {
|
||||
let idxbase: *node = lhs.lhs;
|
||||
if (idxbase != nil) { if (idxbase.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, idxbase.str);
|
||||
// #21 (READ twin of #11): a module-GLOBAL base makes
|
||||
// localfindnode return nil, so this field-offset-aware
|
||||
// branch was skipped and `g[i].field` fell through to
|
||||
// the module-qualified fallback below (garbage — no
|
||||
// main.g load at all). Resolve the global's tnode via
|
||||
// letvartnode/defvartnode (the same source cgindex's
|
||||
// global arm uses) and dispatch the base load by shape:
|
||||
// array -> LEAQ name(SB) (the symbol IS the storage),
|
||||
// slice/ptr -> MOVQ name(SB) (the symbol's first word
|
||||
// IS the .ptr). Mirrors cstage cgen.c's #21 arm.
|
||||
let tn: *node = nil;
|
||||
let isglobal: bool = false;
|
||||
let gname: str;
|
||||
gname.ptr = nil; gname.len = 0;
|
||||
if (lc != nil) {
|
||||
tn = lc.tnode;
|
||||
} else {
|
||||
tn = letvartnode(c, idxbase.str);
|
||||
if (tn == nil) { tn = defvartnode(c, idxbase.str); };
|
||||
if (tn != nil) { isglobal = true; gname = idxbase.str; };
|
||||
};
|
||||
if (tn != nil) {
|
||||
let elemt: *node = nil;
|
||||
let baseisarray: bool = false;
|
||||
let tk: nkind = tn.kind;
|
||||
if (tk == nkind.N_TSLICE) { elemt = tn.lhs; };
|
||||
if (tk == nkind.N_TARRAY) { elemt = tn.lhs; baseisarray = true; };
|
||||
if (tk == nkind.N_TPTR) { elemt = tn.lhs; };
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
let viaptr: bool = false;
|
||||
if (elemt != nil) {
|
||||
if (elemt.kind == nkind.N_TPTR) {
|
||||
let inner: *node = elemt.lhs;
|
||||
if (inner != nil) { if (inner.kind == nkind.N_TNAME) {
|
||||
sname = inner.str;
|
||||
viaptr = true;
|
||||
};};
|
||||
} else { if (elemt.kind == nkind.N_TNAME) {
|
||||
sname = elemt.str;
|
||||
let elemt: *tinfo = lhs.type_: *tinfo;
|
||||
let elemu: *tinfo = elemt;
|
||||
for (elemu != nil && elemu.kind == tykind.TY_NAMED) { elemu = elemu.under; };
|
||||
let st: *tinfo = nil;
|
||||
let viaptr: bool = false;
|
||||
if (elemu != nil) {
|
||||
if (elemu.kind == tykind.TY_PTR) {
|
||||
let pin: *tinfo = elemu.sub;
|
||||
for (pin != nil && pin.kind == tykind.TY_NAMED) { pin = pin.under; };
|
||||
if (pin != nil) { if (pin.kind == tykind.TY_STRUCT) {
|
||||
st = pin;
|
||||
viaptr = true;
|
||||
};};
|
||||
} else { if (elemu.kind == tykind.TY_STRUCT) {
|
||||
st = elemu;
|
||||
};};
|
||||
};
|
||||
if (st != nil) {
|
||||
let fnd: *tfield = nil;
|
||||
let fwalk: *tfield = st.fields;
|
||||
for (fwalk != nil) {
|
||||
if (streq(fwalk.name, fld)) { fnd = fwalk; break; };
|
||||
fwalk = fwalk.tnext;
|
||||
};
|
||||
if (sname.len > 0) {
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
if (streq(fi.fname, fld)) {
|
||||
let esz: i32 = elemsizeofc(c, tn);
|
||||
cgexpr(c, lhs.rhs); // idx → AX
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
if (isglobal) {
|
||||
if (baseisarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, gname);
|
||||
emitline("(SB), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymname(c, gname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
} else {
|
||||
if (baseisarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
};
|
||||
emitline("\tADDQ\tAX, BX\n");
|
||||
if (viaptr) {
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
};
|
||||
// #270-1a: an `[N]T`-typed field of an
|
||||
// array element (`a[i].m[j]`) — leave the
|
||||
// field's ADDRESS, a base for the outer
|
||||
// index, NEVER deref. AX holds &a[i]; the
|
||||
// field address is &a[i]+foff. The #135
|
||||
// read-side for `d.m[i]`, applied to an
|
||||
// array-element base. Without this an array
|
||||
// field fell to fieldloadop below and loaded
|
||||
// its first 8 bytes as a value → garbage
|
||||
// base → SEGFAULT in the outer index.
|
||||
if (tinfoisarray(fi.tnode.type_: *tinfo)) {
|
||||
if (fi.foff != 0) {
|
||||
emitline("\tADDQ\t$");
|
||||
emitint(fi.foff: i64);
|
||||
emitline(", AX\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (isstrtype(c, fi.tnode) || isslicetype(c, fi.tnode)) {
|
||||
// str/slice: the 3-word {ptr,len,cap}
|
||||
// slice header (#1). AX holds the
|
||||
// element base, so load .ptr (which
|
||||
// targets AX) LAST. Matches the
|
||||
// caseB *struct slice arm and
|
||||
// cgslicehdr(D_AX).
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((fi.foff + 8): i64, "AX");
|
||||
emitline(", BX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((fi.foff + 16): i64, "AX");
|
||||
emitline(", CX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(fi.foff: i64, "AX");
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\t");
|
||||
emitdispreg(fi.foff: i64, "AX");
|
||||
emitline(", X0\n");
|
||||
return;
|
||||
};
|
||||
let lop: str = fieldloadop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
emitdispreg(fi.foff: i64, "AX");
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
fi = fi.finext;
|
||||
let bu: *tinfo = idxbase.type_: *tinfo;
|
||||
for (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; };
|
||||
let baseisarray: bool = false;
|
||||
let baseok: bool = false;
|
||||
if (bu != nil) {
|
||||
if (bu.kind == tykind.TY_ARRAY) { baseisarray = true; baseok = true; };
|
||||
if (bu.kind == tykind.TY_SLICE) { baseok = true; };
|
||||
if (bu.kind == tykind.TY_PTR) { baseok = true; };
|
||||
};
|
||||
let lc: *local = localfindnode(c, idxbase.str);
|
||||
// #21 (READ twin of #11): a module-GLOBAL base makes
|
||||
// localfindnode return nil, so this field-offset-aware
|
||||
// branch was skipped and `g[i].field` fell through to
|
||||
// the module-qualified fallback below (garbage — no
|
||||
// main.g load at all). Classify via the let registry /
|
||||
// array-typed def registry (cstage let_islet ||
|
||||
// def_isarraydef) and dispatch the base load by shape:
|
||||
// array -> LEAQ name(SB) (the symbol IS the storage),
|
||||
// slice/ptr -> MOVQ name(SB) (the symbol's first word
|
||||
// IS the .ptr). Mirrors cstage cgen.c's #21 arm.
|
||||
let isglobal: bool = false;
|
||||
if (lc == nil) {
|
||||
if (isletvar(c, idxbase.str)) {
|
||||
isglobal = true;
|
||||
} else {
|
||||
let dtn: *node = defvartnode(c, idxbase.str);
|
||||
if (dtn != nil) {
|
||||
if (dtn.kind == nkind.N_TARRAY) { isglobal = true; };
|
||||
};
|
||||
};
|
||||
};
|
||||
if (fnd != nil && baseok && (lc != nil || isglobal)) {
|
||||
let esz: i32 = elemt.size: i32;
|
||||
cgexpr(c, lhs.rhs); // idx → AX
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
if (isglobal) {
|
||||
if (baseisarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, idxbase.str);
|
||||
emitline("(SB), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymname(c, idxbase.str);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
} else {
|
||||
if (baseisarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
};
|
||||
emitline("\tADDQ\tAX, BX\n");
|
||||
if (viaptr) {
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
};
|
||||
let foff: i64 = fnd.offset: i64;
|
||||
let ft: *tinfo = fnd.type_;
|
||||
let fu: *tinfo = ft;
|
||||
for (fu != nil && fu.kind == tykind.TY_NAMED) { fu = fu.under; };
|
||||
// #270-1a: an `[N]T`-typed field of an
|
||||
// array element (`a[i].m[j]`) — leave the
|
||||
// field's ADDRESS, a base for the outer
|
||||
// index, NEVER deref. AX holds &a[i]; the
|
||||
// field address is &a[i]+foff. The #135
|
||||
// read-side for `d.m[i]`, applied to an
|
||||
// array-element base. Without this an array
|
||||
// field fell to the scalar load below and loaded
|
||||
// its first 8 bytes as a value → garbage
|
||||
// base → SEGFAULT in the outer index.
|
||||
if (fu != nil && fu.kind == tykind.TY_ARRAY) {
|
||||
if (foff != 0) {
|
||||
emitline("\tADDQ\t$");
|
||||
emitint(foff);
|
||||
emitline(", AX\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (fu != nil && (fu.kind == tykind.TY_STR
|
||||
|| fu.kind == tykind.TY_SLICE)) {
|
||||
// str/slice: the 3-word {ptr,len,cap}
|
||||
// slice header (#1). AX holds the
|
||||
// element base, so load .ptr (which
|
||||
// targets AX) LAST. Matches the
|
||||
// caseB *struct slice arm and
|
||||
// cgslicehdr(D_AX).
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(foff + 8, "AX");
|
||||
emitline(", BX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(foff + 16, "AX");
|
||||
emitline(", CX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(foff, "AX");
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
if (typeisfloat(ft)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (typeisf32(ft)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\t");
|
||||
emitdispreg(foff, "AX");
|
||||
emitline(", X0\n");
|
||||
return;
|
||||
};
|
||||
let fsz: i32 = 8;
|
||||
if (ft != nil) { fsz = ft.size: i32; };
|
||||
let lop: str = loadopsz(typeissigned(ft), fsz);
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
emitdispreg(foff, "AX");
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
};
|
||||
};};
|
||||
};
|
||||
@@ -3553,24 +3569,6 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// C2/C3 boundary: `xs[i].fld` with an IDENT index base is the
|
||||
// enumerated N_INDEX-base arm's territory (cstage handles it
|
||||
// type-keyed); reaching HERE means wwstage's name-keyed arm above
|
||||
// missed (the F10 alias-tnode class). Routing it through the
|
||||
// read-resolver would emit a different (albeit correct) sequence
|
||||
// than cstage's arm → cs≠ww. Loud until C3 re-keys that arm onto
|
||||
// stamped tinfo (task #8).
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == nkind.N_INDEX) {
|
||||
if (lhs.lhs != nil) {
|
||||
if (lhs.lhs.kind == nkind.N_IDENT) {
|
||||
let mg: str = "cgdot: ident-indexed field read unwired in wwstage (C3/task #8)\n";
|
||||
os.write(2, mg.ptr, mg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// cstage reads ptr-chained fields (`a.p.f`, any root) in its
|
||||
// chained-*struct arm; wwstage's #70 mirror above is gated
|
||||
// root-local — the uncovered remainder (global / indexed roots)
|
||||
|
||||
@@ -22957,152 +22957,168 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
// element), so the leaf load is `(field.offset)(AX)` either way.
|
||||
// Bypasses cgindex deliberately — cgindex's final MOVQ would
|
||||
// truncate a value-struct element to 8 bytes.
|
||||
// C3 (task #8): keyed on the checker-stamped tinfo (lhs.type_ /
|
||||
// idxbase.type_), not tnode KINDs + structlookup-by-name — the
|
||||
// name-key dropped any base typed via an N_TNAME alias (`type
|
||||
// result = []capture`, F10) into the silent fallbacks below.
|
||||
// Mirrors cstage cgen.c case N_DOT N_INDEX-lhs arm 1:1; #209/#211
|
||||
// name-keyed→tinfo-SSoT cluster.
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == nkind.N_INDEX) {
|
||||
let idxbase: *node = lhs.lhs;
|
||||
if (idxbase != nil) { if (idxbase.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, idxbase.str);
|
||||
// #21 (READ twin of #11): a module-GLOBAL base makes
|
||||
// localfindnode return nil, so this field-offset-aware
|
||||
// branch was skipped and `g[i].field` fell through to
|
||||
// the module-qualified fallback below (garbage — no
|
||||
// main.g load at all). Resolve the global's tnode via
|
||||
// letvartnode/defvartnode (the same source cgindex's
|
||||
// global arm uses) and dispatch the base load by shape:
|
||||
// array -> LEAQ name(SB) (the symbol IS the storage),
|
||||
// slice/ptr -> MOVQ name(SB) (the symbol's first word
|
||||
// IS the .ptr). Mirrors cstage cgen.c's #21 arm.
|
||||
let tn: *node = nil;
|
||||
let isglobal: bool = false;
|
||||
let gname: str;
|
||||
gname.ptr = nil; gname.len = 0;
|
||||
if (lc != nil) {
|
||||
tn = lc.tnode;
|
||||
} else {
|
||||
tn = letvartnode(c, idxbase.str);
|
||||
if (tn == nil) { tn = defvartnode(c, idxbase.str); };
|
||||
if (tn != nil) { isglobal = true; gname = idxbase.str; };
|
||||
};
|
||||
if (tn != nil) {
|
||||
let elemt: *node = nil;
|
||||
let baseisarray: bool = false;
|
||||
let tk: nkind = tn.kind;
|
||||
if (tk == nkind.N_TSLICE) { elemt = tn.lhs; };
|
||||
if (tk == nkind.N_TARRAY) { elemt = tn.lhs; baseisarray = true; };
|
||||
if (tk == nkind.N_TPTR) { elemt = tn.lhs; };
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
let viaptr: bool = false;
|
||||
if (elemt != nil) {
|
||||
if (elemt.kind == nkind.N_TPTR) {
|
||||
let inner: *node = elemt.lhs;
|
||||
if (inner != nil) { if (inner.kind == nkind.N_TNAME) {
|
||||
sname = inner.str;
|
||||
viaptr = true;
|
||||
};};
|
||||
} else { if (elemt.kind == nkind.N_TNAME) {
|
||||
sname = elemt.str;
|
||||
let elemt: *tinfo = lhs.type_: *tinfo;
|
||||
let elemu: *tinfo = elemt;
|
||||
for (elemu != nil && elemu.kind == tykind.TY_NAMED) { elemu = elemu.under; };
|
||||
let st: *tinfo = nil;
|
||||
let viaptr: bool = false;
|
||||
if (elemu != nil) {
|
||||
if (elemu.kind == tykind.TY_PTR) {
|
||||
let pin: *tinfo = elemu.sub;
|
||||
for (pin != nil && pin.kind == tykind.TY_NAMED) { pin = pin.under; };
|
||||
if (pin != nil) { if (pin.kind == tykind.TY_STRUCT) {
|
||||
st = pin;
|
||||
viaptr = true;
|
||||
};};
|
||||
} else { if (elemu.kind == tykind.TY_STRUCT) {
|
||||
st = elemu;
|
||||
};};
|
||||
};
|
||||
if (st != nil) {
|
||||
let fnd: *tfield = nil;
|
||||
let fwalk: *tfield = st.fields;
|
||||
for (fwalk != nil) {
|
||||
if (streq(fwalk.name, fld)) { fnd = fwalk; break; };
|
||||
fwalk = fwalk.tnext;
|
||||
};
|
||||
if (sname.len > 0) {
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
if (streq(fi.fname, fld)) {
|
||||
let esz: i32 = elemsizeofc(c, tn);
|
||||
cgexpr(c, lhs.rhs); // idx → AX
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
if (isglobal) {
|
||||
if (baseisarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, gname);
|
||||
emitline("(SB), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymname(c, gname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
} else {
|
||||
if (baseisarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
};
|
||||
emitline("\tADDQ\tAX, BX\n");
|
||||
if (viaptr) {
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
};
|
||||
// #270-1a: an `[N]T`-typed field of an
|
||||
// array element (`a[i].m[j]`) — leave the
|
||||
// field's ADDRESS, a base for the outer
|
||||
// index, NEVER deref. AX holds &a[i]; the
|
||||
// field address is &a[i]+foff. The #135
|
||||
// read-side for `d.m[i]`, applied to an
|
||||
// array-element base. Without this an array
|
||||
// field fell to fieldloadop below and loaded
|
||||
// its first 8 bytes as a value → garbage
|
||||
// base → SEGFAULT in the outer index.
|
||||
if (tinfoisarray(fi.tnode.type_: *tinfo)) {
|
||||
if (fi.foff != 0) {
|
||||
emitline("\tADDQ\t$");
|
||||
emitint(fi.foff: i64);
|
||||
emitline(", AX\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (isstrtype(c, fi.tnode) || isslicetype(c, fi.tnode)) {
|
||||
// str/slice: the 3-word {ptr,len,cap}
|
||||
// slice header (#1). AX holds the
|
||||
// element base, so load .ptr (which
|
||||
// targets AX) LAST. Matches the
|
||||
// caseB *struct slice arm and
|
||||
// cgslicehdr(D_AX).
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((fi.foff + 8): i64, "AX");
|
||||
emitline(", BX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg((fi.foff + 16): i64, "AX");
|
||||
emitline(", CX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(fi.foff: i64, "AX");
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\t");
|
||||
emitdispreg(fi.foff: i64, "AX");
|
||||
emitline(", X0\n");
|
||||
return;
|
||||
};
|
||||
let lop: str = fieldloadop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
emitdispreg(fi.foff: i64, "AX");
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
fi = fi.finext;
|
||||
let bu: *tinfo = idxbase.type_: *tinfo;
|
||||
for (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; };
|
||||
let baseisarray: bool = false;
|
||||
let baseok: bool = false;
|
||||
if (bu != nil) {
|
||||
if (bu.kind == tykind.TY_ARRAY) { baseisarray = true; baseok = true; };
|
||||
if (bu.kind == tykind.TY_SLICE) { baseok = true; };
|
||||
if (bu.kind == tykind.TY_PTR) { baseok = true; };
|
||||
};
|
||||
let lc: *local = localfindnode(c, idxbase.str);
|
||||
// #21 (READ twin of #11): a module-GLOBAL base makes
|
||||
// localfindnode return nil, so this field-offset-aware
|
||||
// branch was skipped and `g[i].field` fell through to
|
||||
// the module-qualified fallback below (garbage — no
|
||||
// main.g load at all). Classify via the let registry /
|
||||
// array-typed def registry (cstage let_islet ||
|
||||
// def_isarraydef) and dispatch the base load by shape:
|
||||
// array -> LEAQ name(SB) (the symbol IS the storage),
|
||||
// slice/ptr -> MOVQ name(SB) (the symbol's first word
|
||||
// IS the .ptr). Mirrors cstage cgen.c's #21 arm.
|
||||
let isglobal: bool = false;
|
||||
if (lc == nil) {
|
||||
if (isletvar(c, idxbase.str)) {
|
||||
isglobal = true;
|
||||
} else {
|
||||
let dtn: *node = defvartnode(c, idxbase.str);
|
||||
if (dtn != nil) {
|
||||
if (dtn.kind == nkind.N_TARRAY) { isglobal = true; };
|
||||
};
|
||||
};
|
||||
};
|
||||
if (fnd != nil && baseok && (lc != nil || isglobal)) {
|
||||
let esz: i32 = elemt.size: i32;
|
||||
cgexpr(c, lhs.rhs); // idx → AX
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
if (isglobal) {
|
||||
if (baseisarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, idxbase.str);
|
||||
emitline("(SB), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymname(c, idxbase.str);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
} else {
|
||||
if (baseisarray) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
};
|
||||
emitline("\tADDQ\tAX, BX\n");
|
||||
if (viaptr) {
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
};
|
||||
let foff: i64 = fnd.offset: i64;
|
||||
let ft: *tinfo = fnd.type_;
|
||||
let fu: *tinfo = ft;
|
||||
for (fu != nil && fu.kind == tykind.TY_NAMED) { fu = fu.under; };
|
||||
// #270-1a: an `[N]T`-typed field of an
|
||||
// array element (`a[i].m[j]`) — leave the
|
||||
// field's ADDRESS, a base for the outer
|
||||
// index, NEVER deref. AX holds &a[i]; the
|
||||
// field address is &a[i]+foff. The #135
|
||||
// read-side for `d.m[i]`, applied to an
|
||||
// array-element base. Without this an array
|
||||
// field fell to the scalar load below and loaded
|
||||
// its first 8 bytes as a value → garbage
|
||||
// base → SEGFAULT in the outer index.
|
||||
if (fu != nil && fu.kind == tykind.TY_ARRAY) {
|
||||
if (foff != 0) {
|
||||
emitline("\tADDQ\t$");
|
||||
emitint(foff);
|
||||
emitline(", AX\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (fu != nil && (fu.kind == tykind.TY_STR
|
||||
|| fu.kind == tykind.TY_SLICE)) {
|
||||
// str/slice: the 3-word {ptr,len,cap}
|
||||
// slice header (#1). AX holds the
|
||||
// element base, so load .ptr (which
|
||||
// targets AX) LAST. Matches the
|
||||
// caseB *struct slice arm and
|
||||
// cgslicehdr(D_AX).
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(foff + 8, "AX");
|
||||
emitline(", BX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(foff + 16, "AX");
|
||||
emitline(", CX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(foff, "AX");
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
if (typeisfloat(ft)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (typeisf32(ft)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\t");
|
||||
emitdispreg(foff, "AX");
|
||||
emitline(", X0\n");
|
||||
return;
|
||||
};
|
||||
let fsz: i32 = 8;
|
||||
if (ft != nil) { fsz = ft.size: i32; };
|
||||
let lop: str = loadopsz(typeissigned(ft), fsz);
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
emitdispreg(foff, "AX");
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
};
|
||||
};};
|
||||
};
|
||||
@@ -23631,24 +23647,6 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// C2/C3 boundary: `xs[i].fld` with an IDENT index base is the
|
||||
// enumerated N_INDEX-base arm's territory (cstage handles it
|
||||
// type-keyed); reaching HERE means wwstage's name-keyed arm above
|
||||
// missed (the F10 alias-tnode class). Routing it through the
|
||||
// read-resolver would emit a different (albeit correct) sequence
|
||||
// than cstage's arm → cs≠ww. Loud until C3 re-keys that arm onto
|
||||
// stamped tinfo (task #8).
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == nkind.N_INDEX) {
|
||||
if (lhs.lhs != nil) {
|
||||
if (lhs.lhs.kind == nkind.N_IDENT) {
|
||||
let mg: str = "cgdot: ident-indexed field read unwired in wwstage (C3/task #8)\n";
|
||||
os.write(2, mg.ptr, mg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// cstage reads ptr-chained fields (`a.p.f`, any root) in its
|
||||
// chained-*struct arm; wwstage's #70 mirror above is gated
|
||||
// root-local — the uncovered remainder (global / indexed roots)
|
||||
|
||||
@@ -35,9 +35,17 @@
|
||||
* offset-0 scalars worked by coincidence). TK_AMP's silent tail is the
|
||||
* same resolver-or-loud (`&threads[0].cap` used to SEGFAULT on deref,
|
||||
* reviewer-A route). The C1.25 raw-byte readbacks graduate to typed
|
||||
* depth-2 reads below. wwstage cgdot/cgun mirror symmetrically; its
|
||||
* name-keyed ident-indexed arm gap stays LOUD (C3/task #8 re-key), as
|
||||
* does the ptr-chained global-root remainder (task #37).
|
||||
* depth-2 reads below. wwstage cgdot/cgun mirror symmetrically; the
|
||||
* ptr-chained global-root remainder stays LOUD (task #37).
|
||||
*
|
||||
* C3 (F7+F10, task #8): wwstage's cgdot N_INDEX-base arm re-keyed from
|
||||
* tnode KINDs + structlookup-by-name onto the checker-stamped tinfo
|
||||
* (#209/#211 name-keyed→tinfo-SSoT cluster), mirroring cstage's arm
|
||||
* 1:1. Pre-C3 a base typed via an N_TNAME alias (`type result =
|
||||
* []capture`) missed the name-keyed arm and died at the interim C2
|
||||
* loud guard (and pre-C2, fell silently into the SB fallback). The
|
||||
* c3_* rows below pin the alias-typed base class plus the composed
|
||||
* regex hot shape and the free()-operand consumption (FA6).
|
||||
*
|
||||
* row | shape | want
|
||||
* --------------------+----------------------------------------+------
|
||||
@@ -86,6 +94,17 @@
|
||||
* reject_assign_callbs| mk()[0].pc = (assign-tail text keeps | BUILD_FAIL
|
||||
* | its carrier post arr_field_idx_store) |
|
||||
* reject_addrof_callbs| &mk()[0].pc (TK_AMP loud tail) | BUILD_FAIL
|
||||
* c3_alias_slice_reads| F10: alias-slice base l[i].f — narrow | 65
|
||||
* | signed/unsigned, i64, f64, str, []T, |
|
||||
* | [N]T-field-then-index |
|
||||
* c3_alias_arr_viaptr | F10: alias [2]S base + alias []*S | 66
|
||||
* | (viaptr element) reads |
|
||||
* c3_letbound_alias | p11b essence sans task-#14 `?`: let | 67
|
||||
* | m: result = f(); m[i].field reads |
|
||||
* c3_composed_match | F7: match(re.insts[(*ts)[i].pc]) + | 68
|
||||
* | (*ts)[i] compound/store (p7 hot shape) |
|
||||
* c3_free_operand | FA6: free((*ts)[i].slicefield) reads | 69
|
||||
* | through the operand (pA9) |
|
||||
*
|
||||
* BUILD_FAIL rows also assert the diagnostic TEXT (stderr substring,
|
||||
* both stages) — a build that fails for any other reason (parse error,
|
||||
@@ -911,6 +930,190 @@ static const struct row rows[] = {
|
||||
"\treturn (*p): i32;\n"
|
||||
"};\n",
|
||||
BUILD_FAIL, "unsupported address-of shape" },
|
||||
|
||||
/* C3 (task #8) F10 class: the index BASE is typed via an N_TNAME
|
||||
* alias — pre-C3 wwstage's name-keyed arm missed it (interim C2
|
||||
* loud guard). Field-kind matrix mirrors the cstage arm's
|
||||
* dispatch: narrow signed/unsigned, 8B, float, str header, slice
|
||||
* header, [N]T field as outer-index base (#270-1a). Construction
|
||||
* goes through DIRECT-typed locals: the alias-typed `= []` /
|
||||
* arrlit-init/global-DATA sites are a separate filed family
|
||||
* (unmasked behind C3, not cgdot). */
|
||||
{ "c3_alias_slice_reads",
|
||||
"package main;\n"
|
||||
"type wide = struct { b: u8, w: i16, d: u32, q: i64, f: f64,\n"
|
||||
"\ts: str, xs: []size, m: [2]u32 };\n"
|
||||
"type wlist = []wide;\n"
|
||||
"fn mkw(k: i64) wide = {\n"
|
||||
"\tlet xs: []size = [];\n"
|
||||
"\tlet x0: size = (10 + k): size;\n"
|
||||
"\tlet x1: size = (20 + k): size;\n"
|
||||
"\tappend(xs, x0);\n"
|
||||
"\tappend(xs, x1);\n"
|
||||
"\tlet m0: u32 = (40 + k): u32;\n"
|
||||
"\tlet m1: u32 = (50 + k): u32;\n"
|
||||
"\treturn wide { b = (k + 1): u8, w = (0 - k): i16,\n"
|
||||
"\t\td = (100 + k): u32, q = k, f = 2.5f64, s = \"ab\",\n"
|
||||
"\t\txs = xs, m = [m0, m1] };\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet l0: []wide = [];\n"
|
||||
"\tlet w1: wide = mkw(1);\n"
|
||||
"\tlet w2: wide = mkw(2);\n"
|
||||
"\tappend(l0, w1);\n"
|
||||
"\tappend(l0, w2);\n"
|
||||
"\tlet l: wlist = l0;\n"
|
||||
"\tif (l[0].b != 2u8) { return 1; };\n"
|
||||
"\tif (l[0].w != -1i16) { return 2; };\n"
|
||||
"\tif (l[1].d != 102u32) { return 3; };\n"
|
||||
"\tif (l[1].q != 2i64) { return 4; };\n"
|
||||
"\tif (l[0].f != 2.5f64) { return 5; };\n"
|
||||
"\tif (l[0].s.len != 2) { return 6; };\n"
|
||||
"\tif (l[1].xs[1] != (22: size)) { return 7; };\n"
|
||||
"\tif (l[0].m[1] != 51u32) { return 8; };\n"
|
||||
"\treturn 65;\n"
|
||||
"};\n",
|
||||
65, NULL },
|
||||
|
||||
/* F10 sibling bases: alias-typed [2]S array (local), and an
|
||||
* alias-typed []*S whose ELEMENT is a pointer (the arm's viaptr
|
||||
* deref hop). */
|
||||
{ "c3_alias_arr_viaptr",
|
||||
"package main;\n"
|
||||
"type wide = struct { b: u8, q: i64, xs: []size };\n"
|
||||
"type warr = [2]wide;\n"
|
||||
"type pw = *wide;\n"
|
||||
"type plist = []pw;\n"
|
||||
"fn mkw(k: i64) wide = {\n"
|
||||
"\tlet xs: []size = [];\n"
|
||||
"\tlet x0: size = (10 + k): size;\n"
|
||||
"\tappend(xs, x0);\n"
|
||||
"\treturn wide { b = (k + 1): u8, q = k, xs = xs };\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet w3: wide = mkw(3);\n"
|
||||
"\tlet w4: wide = mkw(4);\n"
|
||||
"\tlet a0: [2]wide = [w3, w4];\n"
|
||||
"\tlet a: warr = a0;\n"
|
||||
"\tif (a[0].q != 3i64) { return 1; };\n"
|
||||
"\tif (a[1].b != 5u8) { return 2; };\n"
|
||||
"\tlet w5: wide = mkw(5);\n"
|
||||
"\tlet ps0: []pw = [];\n"
|
||||
"\tappend(ps0, &w5);\n"
|
||||
"\tlet ps: plist = ps0;\n"
|
||||
"\tif (ps[0].q != 5i64) { return 3; };\n"
|
||||
"\tif (ps[0].b != 6u8) { return 4; };\n"
|
||||
"\tif (ps[0].xs[0] != (15: size)) { return 5; };\n"
|
||||
"\treturn 66;\n"
|
||||
"};\n",
|
||||
66, NULL },
|
||||
|
||||
/* p11b's F10 essence with the task-#14-gated `?` factored out: a
|
||||
* let bound from a fn returning the alias, indexed-field reads off
|
||||
* it (m's tnode is the N_TNAME alias `result`). */
|
||||
{ "c3_letbound_alias",
|
||||
"package main;\n"
|
||||
"type capture = struct { content: str, start: size, end: size };\n"
|
||||
"type result = []capture;\n"
|
||||
"fn mk() result = {\n"
|
||||
"\tlet caps: []capture = [];\n"
|
||||
"\tappend(caps, capture { content = \"xy\", start = 1, end = 3 });\n"
|
||||
"\tappend(caps, capture { content = \"z\", start = 4, end = 5 });\n"
|
||||
"\treturn caps;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet m: result = mk();\n"
|
||||
"\tif (len(m) != 2) { return 1; };\n"
|
||||
"\tif (m[0].start != 1) { return 2; };\n"
|
||||
"\tif (m[0].content.len != 2) { return 3; };\n"
|
||||
"\tif (m[1].end != 5) { return 4; };\n"
|
||||
"\treturn 67;\n"
|
||||
"};\n",
|
||||
67, NULL },
|
||||
|
||||
/* F7: the composed regex run_thread hot shape — a tagged-slice
|
||||
* field indexed by a deref-index-field chain, match-dispatched,
|
||||
* plus compound/store back through the same spine (p7_composed
|
||||
* core; the strings.frombytes tail lives with task #29). */
|
||||
{ "c3_composed_match",
|
||||
"package main;\n"
|
||||
"type inst_lit = rune;\n"
|
||||
"type inst_any = void;\n"
|
||||
"type inst_match = bool;\n"
|
||||
"type inst = (inst_lit | inst_any | inst_match);\n"
|
||||
"type re_t = struct { insts: []inst, n_reps: size };\n"
|
||||
"type capture = struct { content: str, start: size, end: size };\n"
|
||||
"type thread = struct { pc: size, root_capture: capture, failed: bool };\n"
|
||||
"fn step(re: *re_t, ts: *[]thread, i: size) i32 = {\n"
|
||||
"\tmatch (re.insts[(*ts)[i].pc]) {\n"
|
||||
"\tcase let lt: inst_lit => {\n"
|
||||
"\t\t(*ts)[i].root_capture = capture { content = \"hit\", start = 2, end = 4 };\n"
|
||||
"\t\t(*ts)[i].pc += 1;\n"
|
||||
"\t\treturn 1;\n"
|
||||
"\t};\n"
|
||||
"\tcase inst_any => {\n"
|
||||
"\t\t(*ts)[i].failed = true;\n"
|
||||
"\t\treturn 2;\n"
|
||||
"\t};\n"
|
||||
"\tcase let m: inst_match => {\n"
|
||||
"\t\t(*ts)[i].pc += 2;\n"
|
||||
"\t\treturn 3;\n"
|
||||
"\t};\n"
|
||||
"\t};\n"
|
||||
"\treturn 9;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet insts: []inst = [];\n"
|
||||
"\tappend(insts, ('a': inst_lit));\n"
|
||||
"\tlet av: inst_any;\n"
|
||||
"\tlet v: inst = av;\n"
|
||||
"\tappend(insts, v);\n"
|
||||
"\tappend(insts, (true: inst_match));\n"
|
||||
"\tlet re: re_t = re_t { insts = insts, n_reps = 0 };\n"
|
||||
"\tlet ts: []thread = [];\n"
|
||||
"\tlet z: capture = capture { content = \"\", start = 0, end = 0 };\n"
|
||||
"\tappend(ts, thread { pc = 0, root_capture = z, failed = false });\n"
|
||||
"\tif (step(&re, &ts, 0) != 1) { return 1; };\n"
|
||||
"\tif (ts[0].pc != 1) { return 2; };\n"
|
||||
"\tif (ts[0].root_capture.start != 2) { return 3; };\n"
|
||||
"\tif (step(&re, &ts, 0) != 2) { return 4; };\n"
|
||||
"\tif (!ts[0].failed) { return 5; };\n"
|
||||
"\tlet p: *[]thread = &ts;\n"
|
||||
"\t(*p)[0].pc += 1;\n"
|
||||
"\tif (ts[0].pc != 2) { return 6; };\n"
|
||||
"\tif (step(&re, &ts, 0) != 3) { return 7; };\n"
|
||||
"\tif (ts[0].pc != 4) { return 8; };\n"
|
||||
"\treturn 68;\n"
|
||||
"};\n",
|
||||
68, NULL },
|
||||
|
||||
/* FA6 (pA9): free() consumes its operand via cgexpr — the
|
||||
* deref-index-field reads inside the operand are C3's reads, the
|
||||
* free arm itself is sound. Pure acceptance row. */
|
||||
{ "c3_free_operand",
|
||||
"package main;\n"
|
||||
"type capture = struct { content: str, start: size };\n"
|
||||
"type thread = struct { pc: size, captures: []capture,\n"
|
||||
"\trep_counters: []size };\n"
|
||||
"fn delete_thread(i: size, threads: *[]thread) void = {\n"
|
||||
"\tfree((*threads)[i].captures);\n"
|
||||
"\tfree((*threads)[i].rep_counters);\n"
|
||||
"\tdelete((*threads)[i]);\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet z: []capture;\n"
|
||||
"\tlet zr: []size;\n"
|
||||
"\tlet xs: []thread = [];\n"
|
||||
"\tappend(xs, thread { pc = 1, captures = z, rep_counters = zr });\n"
|
||||
"\tappend(xs, thread { pc = 2, captures = z, rep_counters = zr });\n"
|
||||
"\tappend(xs, thread { pc = 3, captures = z, rep_counters = zr });\n"
|
||||
"\tdelete_thread(1, &xs);\n"
|
||||
"\tif (len(xs) != 2) { return 1; };\n"
|
||||
"\tif (xs[0].pc != 1) { return 2; };\n"
|
||||
"\tif (xs[1].pc != 3) { return 3; };\n"
|
||||
"\treturn 69;\n"
|
||||
"};\n",
|
||||
69, NULL },
|
||||
};
|
||||
|
||||
/* errlog_has — the build-failure stderr must carry the row's expected
|
||||
|
||||
Reference in New Issue
Block a user