selfhost/cmd/wcc: collapse chained-dot offset-emit onto tinfo.fields + delete dotinnerstructptr (#70, #12)
Both chained-*struct-field sites (cgdot read, cgassign store) read the inner- struct layout off the stamped inner-dot tinfo (peel NAMED->under, TY_PTR->.sub ->NAMED->under->TY_STRUCT) and walk tinfo.fields for offset + per-field type, replacing dotinnerstructptr's structinfo re-walk + structlookup. Type dispatch (str/slice/float, load/store op) keys on tfield.type_ via the existing *tinfo predicates; ft.slotsize reproduces the old fieldsize exactly (== size for all kinds reached here except struct/array). cstage parity: cgen.c:1653-1655 (read fl->offset), 2514/2541 (store f->offset). Behavior-preserving: a strict per-level gate (every chain dot must peel through a *struct, root must be N_IDENT && localfindnode != nil) reproduces dotinnerstructptr's EXACT locals-only-with-all-*struct-intermediates firing set -- a by-value intermediate dot bails to the identical pre-existing generic path. asm is byte-identical (990-997 hold; both the strict gate and the looser superset tested 134/134, confirming the corpus has no by-value-intermediate chain). Deliberately NOT widened past the old set: global-root and by-value- intermediate chained dots stay on their old paths -- the global case is a pre-existing SHARED base-eval defect (cstage mis-evals the global base as (BP)->segfault; wwstage no-stores), filed #27 to fix in both stages together. dotinnerstructptr deleted (0 callers). Offset now sources from tinfo.fields (natural layout) vs old structinfo (slot-padded); they coincide for flat structs (all reached here), with nested-by-value-struct divergence pre-existing and left to #57/#6/#7 territory.
This commit is contained in:
@@ -10549,7 +10549,7 @@ fn dynamicgrow(m: *state, need: i32) void = {
|
|||||||
// - pushargsrev: per-call arg pushing
|
// - pushargsrev: per-call arg pushing
|
||||||
// - type predicates: isstr*/isslice*/istagged*/nodeis* families
|
// - type predicates: isstr*/isslice*/istagged*/nodeis* families
|
||||||
// - field ops: fieldloadop, fieldstoreop
|
// - field ops: fieldloadop, fieldstoreop
|
||||||
// - index helpers: indexbaseesz, dotinnerstructptr, elemsizeof
|
// - index helpers: indexbaseesz, elemsizeof
|
||||||
// - slot sizing: structlookup, primsize, slotsize, fieldsize,
|
// - slot sizing: structlookup, primsize, slotsize, fieldsize,
|
||||||
// registerstruct, collectstructs
|
// registerstruct, collectstructs
|
||||||
// - rhs helpers: taggedvariantindex
|
// - rhs helpers: taggedvariantindex
|
||||||
@@ -11493,56 +11493,6 @@ fn indexbaseesz(c: *cgen, base: *node) i32 = {
|
|||||||
return 8;
|
return 8;
|
||||||
};
|
};
|
||||||
|
|
||||||
// dotinnerstructptr — for an nkind.N_DOT whose lhs is a chain of dots
|
|
||||||
// or an nkind.N_IDENT, walk the chain and return the nkind.N_TNAME tnode of the
|
|
||||||
// struct that the chain dereferences to (i.e., for `r.sym` where
|
|
||||||
// .sym is *lsym, return nkind.N_TNAME("lsym")). Returns nil if the chain
|
|
||||||
// doesn't resolve to a *struct.
|
|
||||||
//
|
|
||||||
// Used by the chained-DOT cgen path so `r.sym.val` knows the outer
|
|
||||||
// is a field of `lsym`.
|
|
||||||
fn dotinnerstructptr(c: *cgen, n: *node) *node = {
|
|
||||||
if (n == nil) { return nil; };
|
|
||||||
if (n.kind != nkind.N_DOT) { return nil; };
|
|
||||||
let base: *node = n.lhs;
|
|
||||||
let fld: str = n.str;
|
|
||||||
if (base == nil) { return nil; };
|
|
||||||
|
|
||||||
// Resolve base's struct tnode.
|
|
||||||
let baset: *node = nil;
|
|
||||||
if (base.kind == nkind.N_IDENT) {
|
|
||||||
let lc: *local = localfindnode(c, base.str);
|
|
||||||
if (lc == nil) { return nil; };
|
|
||||||
let tn: *node = lc.tnode;
|
|
||||||
if (tn == nil) { return nil; };
|
|
||||||
// base could be either struct-by-value (nkind.N_TNAME) or *struct (nkind.N_TPTR).
|
|
||||||
if (tn.kind == nkind.N_TNAME) { baset = tn; };
|
|
||||||
if (tn.kind == nkind.N_TPTR) { baset = tn.lhs; };
|
|
||||||
} else { if (base.kind == nkind.N_DOT) {
|
|
||||||
baset = dotinnerstructptr(c, base);
|
|
||||||
};};
|
|
||||||
if (baset == nil) { return nil; };
|
|
||||||
if (baset.kind != nkind.N_TNAME) { return nil; };
|
|
||||||
|
|
||||||
// Look up the struct, find the field, return the field's *struct.
|
|
||||||
let si: *structinfo = structlookup(c, baset.str);
|
|
||||||
if (si == nil) { return nil; };
|
|
||||||
let fi: *fieldinfo = si.fields;
|
|
||||||
for (fi != nil) {
|
|
||||||
if (streq(fi.fname, fld)) {
|
|
||||||
let ft: *node = fi.tnode;
|
|
||||||
if (ft == nil) { return nil; };
|
|
||||||
if (ft.kind != nkind.N_TPTR) { return nil; };
|
|
||||||
let inner: *node = ft.lhs;
|
|
||||||
if (inner == nil) { return nil; };
|
|
||||||
if (inner.kind != nkind.N_TNAME) { return nil; };
|
|
||||||
return inner;
|
|
||||||
};
|
|
||||||
fi = fi.finext;
|
|
||||||
};
|
|
||||||
return nil;
|
|
||||||
};
|
|
||||||
|
|
||||||
// elemsizeof — given the type node of an indexable (`*T`, `[]T`,
|
// elemsizeof — given the type node of an indexable (`*T`, `[]T`,
|
||||||
// `[N]T`, `str`), return the byte size of one element (1 for u8/i8/
|
// `[N]T`, `str`), return the byte size of one element (1 for u8/i8/
|
||||||
// bool/str-byte, 8 otherwise — same shape as C cgen's esz fallback).
|
// bool/str-byte, 8 otherwise — same shape as C cgen's esz fallback).
|
||||||
@@ -15908,22 +15858,54 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
// the field. (Showed up porting w6l/pass.ww.)
|
// the field. (Showed up porting w6l/pass.ww.)
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
if (lhs.kind == nkind.N_DOT) {
|
if (lhs.kind == nkind.N_DOT) {
|
||||||
let innert: *node = dotinnerstructptr(c, lhs);
|
// #70 (#12): inner-struct layout via the stamped lhs.type_
|
||||||
if (innert != nil) {
|
// (peel *→struct) + tinfo.fields, replacing dotinnerstructptr's
|
||||||
let sname: str = innert.str;
|
// structinfo walk. Gate is strict-equal to the deleted helper:
|
||||||
let si: *structinfo = structlookup(c, sname);
|
// fire only when the chain root is a LOCAL ident AND every dot
|
||||||
if (si != nil) {
|
// in the chain resolves through a *struct (dotinnerstructptr
|
||||||
let fi: *fieldinfo = si.fields;
|
// recursed per level on a *struct field and bailed on a by-
|
||||||
for (fi != nil) {
|
// value-struct intermediate). Reproducing that exactly avoids
|
||||||
if (streq(fi.fname, fld)) {
|
// an untested widening past cstage; a deliberate widen, if ever
|
||||||
|
// wanted, is a future task with its own probe. Global-root
|
||||||
|
// chains stay in their pre-existing shared base-eval breakage
|
||||||
|
// (filed #27), untouched here.
|
||||||
|
let croot: *node = lhs;
|
||||||
|
let allptr: bool = true;
|
||||||
|
for (croot != nil && croot.kind == nkind.N_DOT) {
|
||||||
|
let ct: *tinfo = croot.type_: *tinfo;
|
||||||
|
for (ct != nil && ct.kind == tykind.TY_NAMED) { ct = ct.under; };
|
||||||
|
let okp: bool = false;
|
||||||
|
if (ct != nil) { if (ct.kind == tykind.TY_PTR) {
|
||||||
|
let cs: *tinfo = ct.sub;
|
||||||
|
for (cs != nil && cs.kind == tykind.TY_NAMED) { cs = cs.under; };
|
||||||
|
if (cs != nil) { if (cs.kind == tykind.TY_STRUCT) { okp = true; }; };
|
||||||
|
}; };
|
||||||
|
if (!okp) { allptr = false; };
|
||||||
|
croot = croot.lhs;
|
||||||
|
};
|
||||||
|
let it: *tinfo = nil;
|
||||||
|
if (allptr) { if (croot != nil) { if (croot.kind == nkind.N_IDENT) {
|
||||||
|
if (localfindnode(c, croot.str) != nil) {
|
||||||
|
it = lhs.type_: *tinfo;
|
||||||
|
};
|
||||||
|
}; }; };
|
||||||
|
for (it != nil && it.kind == tykind.TY_NAMED) { it = it.under; };
|
||||||
|
if (it != nil) { if (it.kind == tykind.TY_PTR) {
|
||||||
|
let st: *tinfo = it.sub;
|
||||||
|
for (st != nil && st.kind == tykind.TY_NAMED) { st = st.under; };
|
||||||
|
if (st != nil) { if (st.kind == tykind.TY_STRUCT) {
|
||||||
|
let tf: *tfield = st.fields;
|
||||||
|
for (tf != nil) {
|
||||||
|
if (streq(tf.name, fld)) {
|
||||||
|
let ft: *tinfo = tf.type_;
|
||||||
cgexpr(c, lhs); // AX = ptr to inner struct
|
cgexpr(c, lhs); // AX = ptr to inner struct
|
||||||
// str field: load both halves.
|
// str field: load both halves.
|
||||||
if (isstrtype(c, fi.tnode)) {
|
if (typeisstr(ft)) {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg((fi.foff + 8): i64, "AX");
|
emitdispreg((tf.offset + 8u64): i64, "AX");
|
||||||
emitline(", BX\n");
|
emitline(", BX\n");
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg(fi.foff: i64, "AX");
|
emitdispreg(tf.offset: i64, "AX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -15931,41 +15913,41 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
// into (AX, BX, CX). AX is the *struct
|
// into (AX, BX, CX). AX is the *struct
|
||||||
// base, so load .ptr (which targets
|
// base, so load .ptr (which targets
|
||||||
// AX) LAST.
|
// AX) LAST.
|
||||||
if (isslicetype(c, fi.tnode)) {
|
if (typeisslice(ft)) {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg((fi.foff + 8): i64, "AX");
|
emitdispreg((tf.offset + 8u64): i64, "AX");
|
||||||
emitline(", BX\n");
|
emitline(", BX\n");
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg((fi.foff + 16): i64, "AX");
|
emitdispreg((tf.offset + 16u64): i64, "AX");
|
||||||
emitline(", CX\n");
|
emitline(", CX\n");
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg(fi.foff: i64, "AX");
|
emitdispreg(tf.offset: i64, "AX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
// f64/f32 chained field: route through X0.
|
// f64/f32 chained field: route through X0.
|
||||||
if (isfloattype(c, fi.tnode)) {
|
if (typeisfloat(ft)) {
|
||||||
let mov: str = "MOVSD";
|
let mov: str = "MOVSD";
|
||||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
if (typeisf32(ft)) { mov = "MOVSS"; };
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitdispreg(fi.foff: i64, "AX");
|
emitdispreg(tf.offset: i64, "AX");
|
||||||
emitline(", X0\n");
|
emitline(", X0\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let lop: str = fieldloadop(c, fi);
|
let lop: str = loadopsz(typeissigned(ft), ft.slotsize: i32);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(lop);
|
emitline(lop);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitdispreg(fi.foff: i64, "AX");
|
emitdispreg(tf.offset: i64, "AX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
fi = fi.finext;
|
tf = tf.tnext;
|
||||||
};
|
};
|
||||||
};
|
}; };
|
||||||
};
|
}; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
// Chained `(ident).f1.f2` read where f1 is a struct-by-value
|
// Chained `(ident).f1.f2` read where f1 is a struct-by-value
|
||||||
@@ -18737,16 +18719,48 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
let fld: str = lhs.str;
|
let fld: str = lhs.str;
|
||||||
if (base != nil) {
|
if (base != nil) {
|
||||||
if (base.kind == nkind.N_DOT) {
|
if (base.kind == nkind.N_DOT) {
|
||||||
let innert: *node = dotinnerstructptr(c, base);
|
// #70 (#12): inner-struct layout via the stamped
|
||||||
if (innert != nil) {
|
// base.type_ (peel *→struct) + tinfo.fields,
|
||||||
let sname: str = innert.str;
|
// replacing dotinnerstructptr's structinfo walk.
|
||||||
let si: *structinfo = structlookup(c, sname);
|
// Gate is strict-equal to the deleted helper: fire
|
||||||
if (si != nil) {
|
// only when the chain root is a LOCAL ident AND every
|
||||||
let fi: *fieldinfo = si.fields;
|
// dot resolves through a *struct (dotinnerstructptr
|
||||||
for (fi != nil) {
|
// recursed per level on a *struct field, bailing on a
|
||||||
if (streq(fi.fname, fld)) {
|
// by-value-struct intermediate). Reproducing that
|
||||||
|
// exactly avoids an untested widening past cstage.
|
||||||
|
// Global-root chains stay in their pre-existing shared
|
||||||
|
// base-eval breakage (filed #27).
|
||||||
|
let croot: *node = base;
|
||||||
|
let allptr: bool = true;
|
||||||
|
for (croot != nil && croot.kind == nkind.N_DOT) {
|
||||||
|
let ct: *tinfo = croot.type_: *tinfo;
|
||||||
|
for (ct != nil && ct.kind == tykind.TY_NAMED) { ct = ct.under; };
|
||||||
|
let okp: bool = false;
|
||||||
|
if (ct != nil) { if (ct.kind == tykind.TY_PTR) {
|
||||||
|
let cs: *tinfo = ct.sub;
|
||||||
|
for (cs != nil && cs.kind == tykind.TY_NAMED) { cs = cs.under; };
|
||||||
|
if (cs != nil) { if (cs.kind == tykind.TY_STRUCT) { okp = true; }; };
|
||||||
|
}; };
|
||||||
|
if (!okp) { allptr = false; };
|
||||||
|
croot = croot.lhs;
|
||||||
|
};
|
||||||
|
let it: *tinfo = nil;
|
||||||
|
if (allptr) { if (croot != nil) { if (croot.kind == nkind.N_IDENT) {
|
||||||
|
if (localfindnode(c, croot.str) != nil) {
|
||||||
|
it = base.type_: *tinfo;
|
||||||
|
};
|
||||||
|
}; }; };
|
||||||
|
for (it != nil && it.kind == tykind.TY_NAMED) { it = it.under; };
|
||||||
|
if (it != nil) { if (it.kind == tykind.TY_PTR) {
|
||||||
|
let st: *tinfo = it.sub;
|
||||||
|
for (st != nil && st.kind == tykind.TY_NAMED) { st = st.under; };
|
||||||
|
if (st != nil) { if (st.kind == tykind.TY_STRUCT) {
|
||||||
|
let tf: *tfield = st.fields;
|
||||||
|
for (tf != nil) {
|
||||||
|
if (streq(tf.name, fld)) {
|
||||||
|
let ft: *tinfo = tf.type_;
|
||||||
if (n.op == tkind.TK_ASSIGN) {
|
if (n.op == tkind.TK_ASSIGN) {
|
||||||
if (isstrtype(c, fi.tnode)) {
|
if (typeisstr(ft)) {
|
||||||
// str rhs: AX=ptr, BX=len.
|
// str rhs: AX=ptr, BX=len.
|
||||||
// Stash both, then load
|
// Stash both, then load
|
||||||
// the struct ptr into CX
|
// the struct ptr into CX
|
||||||
@@ -18759,19 +18773,19 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tPOPQ\tAX\n");
|
emitline("\tPOPQ\tAX\n");
|
||||||
emitline("\tPOPQ\tBX\n");
|
emitline("\tPOPQ\tBX\n");
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitdispreg(fi.foff: i64, "CX");
|
emitdispreg(tf.offset: i64, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
emitline("\tMOVQ\tBX, ");
|
emitline("\tMOVQ\tBX, ");
|
||||||
emitdispreg((fi.foff + 8): i64, "CX");
|
emitdispreg((tf.offset + 8u64): i64, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
// f64/f32 chained plain `=`: cgexpr rhs left value in
|
// f64/f32 chained plain `=`: cgexpr rhs left value in
|
||||||
// X0. Spill to stack so cgexpr(base) can use AX, then
|
// X0. Spill to stack so cgexpr(base) can use AX, then
|
||||||
// reload and MOVSD/MOVSS into the slot.
|
// reload and MOVSD/MOVSS into the slot.
|
||||||
if (isfloattype(c, fi.tnode)) {
|
if (typeisfloat(ft)) {
|
||||||
let mov: str = "MOVSD";
|
let mov: str = "MOVSD";
|
||||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
if (typeisf32(ft)) { mov = "MOVSS"; };
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
emitline("\tSUBQ\t$8, SP\n");
|
emitline("\tSUBQ\t$8, SP\n");
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -18786,7 +18800,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\tX0, ");
|
emitline("\tX0, ");
|
||||||
emitdispreg(fi.foff: i64, "BX");
|
emitdispreg(tf.offset: i64, "BX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -18795,19 +18809,19 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
cgexpr(c, base);
|
cgexpr(c, base);
|
||||||
emitline("\tMOVQ\tAX, BX\n");
|
emitline("\tMOVQ\tAX, BX\n");
|
||||||
emitline("\tPOPQ\tAX\n");
|
emitline("\tPOPQ\tAX\n");
|
||||||
let sop: str = fieldstoreop(c, fi);
|
let sop: str = tnodestoreop(c, n.rhs, ft.slotsize: i32);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
emitdispreg(fi.foff: i64, "BX");
|
emitdispreg(tf.offset: i64, "BX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
fi = fi.finext;
|
tf = tf.tnext;
|
||||||
};
|
};
|
||||||
};
|
}; };
|
||||||
};
|
}; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2015,22 +2015,54 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
// the field. (Showed up porting w6l/pass.ww.)
|
// the field. (Showed up porting w6l/pass.ww.)
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
if (lhs.kind == nkind.N_DOT) {
|
if (lhs.kind == nkind.N_DOT) {
|
||||||
let innert: *node = dotinnerstructptr(c, lhs);
|
// #70 (#12): inner-struct layout via the stamped lhs.type_
|
||||||
if (innert != nil) {
|
// (peel *→struct) + tinfo.fields, replacing dotinnerstructptr's
|
||||||
let sname: str = innert.str;
|
// structinfo walk. Gate is strict-equal to the deleted helper:
|
||||||
let si: *structinfo = structlookup(c, sname);
|
// fire only when the chain root is a LOCAL ident AND every dot
|
||||||
if (si != nil) {
|
// in the chain resolves through a *struct (dotinnerstructptr
|
||||||
let fi: *fieldinfo = si.fields;
|
// recursed per level on a *struct field and bailed on a by-
|
||||||
for (fi != nil) {
|
// value-struct intermediate). Reproducing that exactly avoids
|
||||||
if (streq(fi.fname, fld)) {
|
// an untested widening past cstage; a deliberate widen, if ever
|
||||||
|
// wanted, is a future task with its own probe. Global-root
|
||||||
|
// chains stay in their pre-existing shared base-eval breakage
|
||||||
|
// (filed #27), untouched here.
|
||||||
|
let croot: *node = lhs;
|
||||||
|
let allptr: bool = true;
|
||||||
|
for (croot != nil && croot.kind == nkind.N_DOT) {
|
||||||
|
let ct: *tinfo = croot.type_: *tinfo;
|
||||||
|
for (ct != nil && ct.kind == tykind.TY_NAMED) { ct = ct.under; };
|
||||||
|
let okp: bool = false;
|
||||||
|
if (ct != nil) { if (ct.kind == tykind.TY_PTR) {
|
||||||
|
let cs: *tinfo = ct.sub;
|
||||||
|
for (cs != nil && cs.kind == tykind.TY_NAMED) { cs = cs.under; };
|
||||||
|
if (cs != nil) { if (cs.kind == tykind.TY_STRUCT) { okp = true; }; };
|
||||||
|
}; };
|
||||||
|
if (!okp) { allptr = false; };
|
||||||
|
croot = croot.lhs;
|
||||||
|
};
|
||||||
|
let it: *tinfo = nil;
|
||||||
|
if (allptr) { if (croot != nil) { if (croot.kind == nkind.N_IDENT) {
|
||||||
|
if (localfindnode(c, croot.str) != nil) {
|
||||||
|
it = lhs.type_: *tinfo;
|
||||||
|
};
|
||||||
|
}; }; };
|
||||||
|
for (it != nil && it.kind == tykind.TY_NAMED) { it = it.under; };
|
||||||
|
if (it != nil) { if (it.kind == tykind.TY_PTR) {
|
||||||
|
let st: *tinfo = it.sub;
|
||||||
|
for (st != nil && st.kind == tykind.TY_NAMED) { st = st.under; };
|
||||||
|
if (st != nil) { if (st.kind == tykind.TY_STRUCT) {
|
||||||
|
let tf: *tfield = st.fields;
|
||||||
|
for (tf != nil) {
|
||||||
|
if (streq(tf.name, fld)) {
|
||||||
|
let ft: *tinfo = tf.type_;
|
||||||
cgexpr(c, lhs); // AX = ptr to inner struct
|
cgexpr(c, lhs); // AX = ptr to inner struct
|
||||||
// str field: load both halves.
|
// str field: load both halves.
|
||||||
if (isstrtype(c, fi.tnode)) {
|
if (typeisstr(ft)) {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg((fi.foff + 8): i64, "AX");
|
emitdispreg((tf.offset + 8u64): i64, "AX");
|
||||||
emitline(", BX\n");
|
emitline(", BX\n");
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg(fi.foff: i64, "AX");
|
emitdispreg(tf.offset: i64, "AX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -2038,41 +2070,41 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
// into (AX, BX, CX). AX is the *struct
|
// into (AX, BX, CX). AX is the *struct
|
||||||
// base, so load .ptr (which targets
|
// base, so load .ptr (which targets
|
||||||
// AX) LAST.
|
// AX) LAST.
|
||||||
if (isslicetype(c, fi.tnode)) {
|
if (typeisslice(ft)) {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg((fi.foff + 8): i64, "AX");
|
emitdispreg((tf.offset + 8u64): i64, "AX");
|
||||||
emitline(", BX\n");
|
emitline(", BX\n");
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg((fi.foff + 16): i64, "AX");
|
emitdispreg((tf.offset + 16u64): i64, "AX");
|
||||||
emitline(", CX\n");
|
emitline(", CX\n");
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg(fi.foff: i64, "AX");
|
emitdispreg(tf.offset: i64, "AX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
// f64/f32 chained field: route through X0.
|
// f64/f32 chained field: route through X0.
|
||||||
if (isfloattype(c, fi.tnode)) {
|
if (typeisfloat(ft)) {
|
||||||
let mov: str = "MOVSD";
|
let mov: str = "MOVSD";
|
||||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
if (typeisf32(ft)) { mov = "MOVSS"; };
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitdispreg(fi.foff: i64, "AX");
|
emitdispreg(tf.offset: i64, "AX");
|
||||||
emitline(", X0\n");
|
emitline(", X0\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let lop: str = fieldloadop(c, fi);
|
let lop: str = loadopsz(typeissigned(ft), ft.slotsize: i32);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(lop);
|
emitline(lop);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitdispreg(fi.foff: i64, "AX");
|
emitdispreg(tf.offset: i64, "AX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
fi = fi.finext;
|
tf = tf.tnext;
|
||||||
};
|
};
|
||||||
};
|
}; };
|
||||||
};
|
}; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
// Chained `(ident).f1.f2` read where f1 is a struct-by-value
|
// Chained `(ident).f1.f2` read where f1 is a struct-by-value
|
||||||
@@ -4844,16 +4876,48 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
let fld: str = lhs.str;
|
let fld: str = lhs.str;
|
||||||
if (base != nil) {
|
if (base != nil) {
|
||||||
if (base.kind == nkind.N_DOT) {
|
if (base.kind == nkind.N_DOT) {
|
||||||
let innert: *node = dotinnerstructptr(c, base);
|
// #70 (#12): inner-struct layout via the stamped
|
||||||
if (innert != nil) {
|
// base.type_ (peel *→struct) + tinfo.fields,
|
||||||
let sname: str = innert.str;
|
// replacing dotinnerstructptr's structinfo walk.
|
||||||
let si: *structinfo = structlookup(c, sname);
|
// Gate is strict-equal to the deleted helper: fire
|
||||||
if (si != nil) {
|
// only when the chain root is a LOCAL ident AND every
|
||||||
let fi: *fieldinfo = si.fields;
|
// dot resolves through a *struct (dotinnerstructptr
|
||||||
for (fi != nil) {
|
// recursed per level on a *struct field, bailing on a
|
||||||
if (streq(fi.fname, fld)) {
|
// by-value-struct intermediate). Reproducing that
|
||||||
|
// exactly avoids an untested widening past cstage.
|
||||||
|
// Global-root chains stay in their pre-existing shared
|
||||||
|
// base-eval breakage (filed #27).
|
||||||
|
let croot: *node = base;
|
||||||
|
let allptr: bool = true;
|
||||||
|
for (croot != nil && croot.kind == nkind.N_DOT) {
|
||||||
|
let ct: *tinfo = croot.type_: *tinfo;
|
||||||
|
for (ct != nil && ct.kind == tykind.TY_NAMED) { ct = ct.under; };
|
||||||
|
let okp: bool = false;
|
||||||
|
if (ct != nil) { if (ct.kind == tykind.TY_PTR) {
|
||||||
|
let cs: *tinfo = ct.sub;
|
||||||
|
for (cs != nil && cs.kind == tykind.TY_NAMED) { cs = cs.under; };
|
||||||
|
if (cs != nil) { if (cs.kind == tykind.TY_STRUCT) { okp = true; }; };
|
||||||
|
}; };
|
||||||
|
if (!okp) { allptr = false; };
|
||||||
|
croot = croot.lhs;
|
||||||
|
};
|
||||||
|
let it: *tinfo = nil;
|
||||||
|
if (allptr) { if (croot != nil) { if (croot.kind == nkind.N_IDENT) {
|
||||||
|
if (localfindnode(c, croot.str) != nil) {
|
||||||
|
it = base.type_: *tinfo;
|
||||||
|
};
|
||||||
|
}; }; };
|
||||||
|
for (it != nil && it.kind == tykind.TY_NAMED) { it = it.under; };
|
||||||
|
if (it != nil) { if (it.kind == tykind.TY_PTR) {
|
||||||
|
let st: *tinfo = it.sub;
|
||||||
|
for (st != nil && st.kind == tykind.TY_NAMED) { st = st.under; };
|
||||||
|
if (st != nil) { if (st.kind == tykind.TY_STRUCT) {
|
||||||
|
let tf: *tfield = st.fields;
|
||||||
|
for (tf != nil) {
|
||||||
|
if (streq(tf.name, fld)) {
|
||||||
|
let ft: *tinfo = tf.type_;
|
||||||
if (n.op == tkind.TK_ASSIGN) {
|
if (n.op == tkind.TK_ASSIGN) {
|
||||||
if (isstrtype(c, fi.tnode)) {
|
if (typeisstr(ft)) {
|
||||||
// str rhs: AX=ptr, BX=len.
|
// str rhs: AX=ptr, BX=len.
|
||||||
// Stash both, then load
|
// Stash both, then load
|
||||||
// the struct ptr into CX
|
// the struct ptr into CX
|
||||||
@@ -4866,19 +4930,19 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tPOPQ\tAX\n");
|
emitline("\tPOPQ\tAX\n");
|
||||||
emitline("\tPOPQ\tBX\n");
|
emitline("\tPOPQ\tBX\n");
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitdispreg(fi.foff: i64, "CX");
|
emitdispreg(tf.offset: i64, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
emitline("\tMOVQ\tBX, ");
|
emitline("\tMOVQ\tBX, ");
|
||||||
emitdispreg((fi.foff + 8): i64, "CX");
|
emitdispreg((tf.offset + 8u64): i64, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
// f64/f32 chained plain `=`: cgexpr rhs left value in
|
// f64/f32 chained plain `=`: cgexpr rhs left value in
|
||||||
// X0. Spill to stack so cgexpr(base) can use AX, then
|
// X0. Spill to stack so cgexpr(base) can use AX, then
|
||||||
// reload and MOVSD/MOVSS into the slot.
|
// reload and MOVSD/MOVSS into the slot.
|
||||||
if (isfloattype(c, fi.tnode)) {
|
if (typeisfloat(ft)) {
|
||||||
let mov: str = "MOVSD";
|
let mov: str = "MOVSD";
|
||||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
if (typeisf32(ft)) { mov = "MOVSS"; };
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
emitline("\tSUBQ\t$8, SP\n");
|
emitline("\tSUBQ\t$8, SP\n");
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -4893,7 +4957,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\tX0, ");
|
emitline("\tX0, ");
|
||||||
emitdispreg(fi.foff: i64, "BX");
|
emitdispreg(tf.offset: i64, "BX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -4902,19 +4966,19 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
cgexpr(c, base);
|
cgexpr(c, base);
|
||||||
emitline("\tMOVQ\tAX, BX\n");
|
emitline("\tMOVQ\tAX, BX\n");
|
||||||
emitline("\tPOPQ\tAX\n");
|
emitline("\tPOPQ\tAX\n");
|
||||||
let sop: str = fieldstoreop(c, fi);
|
let sop: str = tnodestoreop(c, n.rhs, ft.slotsize: i32);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
emitdispreg(fi.foff: i64, "BX");
|
emitdispreg(tf.offset: i64, "BX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
fi = fi.finext;
|
tf = tf.tnext;
|
||||||
};
|
};
|
||||||
};
|
}; };
|
||||||
};
|
}; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
// - pushargsrev: per-call arg pushing
|
// - pushargsrev: per-call arg pushing
|
||||||
// - type predicates: isstr*/isslice*/istagged*/nodeis* families
|
// - type predicates: isstr*/isslice*/istagged*/nodeis* families
|
||||||
// - field ops: fieldloadop, fieldstoreop
|
// - field ops: fieldloadop, fieldstoreop
|
||||||
// - index helpers: indexbaseesz, dotinnerstructptr, elemsizeof
|
// - index helpers: indexbaseesz, elemsizeof
|
||||||
// - slot sizing: structlookup, primsize, slotsize, fieldsize,
|
// - slot sizing: structlookup, primsize, slotsize, fieldsize,
|
||||||
// registerstruct, collectstructs
|
// registerstruct, collectstructs
|
||||||
// - rhs helpers: taggedvariantindex
|
// - rhs helpers: taggedvariantindex
|
||||||
@@ -948,56 +948,6 @@ fn indexbaseesz(c: *cgen, base: *node) i32 = {
|
|||||||
return 8;
|
return 8;
|
||||||
};
|
};
|
||||||
|
|
||||||
// dotinnerstructptr — for an nkind.N_DOT whose lhs is a chain of dots
|
|
||||||
// or an nkind.N_IDENT, walk the chain and return the nkind.N_TNAME tnode of the
|
|
||||||
// struct that the chain dereferences to (i.e., for `r.sym` where
|
|
||||||
// .sym is *lsym, return nkind.N_TNAME("lsym")). Returns nil if the chain
|
|
||||||
// doesn't resolve to a *struct.
|
|
||||||
//
|
|
||||||
// Used by the chained-DOT cgen path so `r.sym.val` knows the outer
|
|
||||||
// is a field of `lsym`.
|
|
||||||
fn dotinnerstructptr(c: *cgen, n: *node) *node = {
|
|
||||||
if (n == nil) { return nil; };
|
|
||||||
if (n.kind != nkind.N_DOT) { return nil; };
|
|
||||||
let base: *node = n.lhs;
|
|
||||||
let fld: str = n.str;
|
|
||||||
if (base == nil) { return nil; };
|
|
||||||
|
|
||||||
// Resolve base's struct tnode.
|
|
||||||
let baset: *node = nil;
|
|
||||||
if (base.kind == nkind.N_IDENT) {
|
|
||||||
let lc: *local = localfindnode(c, base.str);
|
|
||||||
if (lc == nil) { return nil; };
|
|
||||||
let tn: *node = lc.tnode;
|
|
||||||
if (tn == nil) { return nil; };
|
|
||||||
// base could be either struct-by-value (nkind.N_TNAME) or *struct (nkind.N_TPTR).
|
|
||||||
if (tn.kind == nkind.N_TNAME) { baset = tn; };
|
|
||||||
if (tn.kind == nkind.N_TPTR) { baset = tn.lhs; };
|
|
||||||
} else { if (base.kind == nkind.N_DOT) {
|
|
||||||
baset = dotinnerstructptr(c, base);
|
|
||||||
};};
|
|
||||||
if (baset == nil) { return nil; };
|
|
||||||
if (baset.kind != nkind.N_TNAME) { return nil; };
|
|
||||||
|
|
||||||
// Look up the struct, find the field, return the field's *struct.
|
|
||||||
let si: *structinfo = structlookup(c, baset.str);
|
|
||||||
if (si == nil) { return nil; };
|
|
||||||
let fi: *fieldinfo = si.fields;
|
|
||||||
for (fi != nil) {
|
|
||||||
if (streq(fi.fname, fld)) {
|
|
||||||
let ft: *node = fi.tnode;
|
|
||||||
if (ft == nil) { return nil; };
|
|
||||||
if (ft.kind != nkind.N_TPTR) { return nil; };
|
|
||||||
let inner: *node = ft.lhs;
|
|
||||||
if (inner == nil) { return nil; };
|
|
||||||
if (inner.kind != nkind.N_TNAME) { return nil; };
|
|
||||||
return inner;
|
|
||||||
};
|
|
||||||
fi = fi.finext;
|
|
||||||
};
|
|
||||||
return nil;
|
|
||||||
};
|
|
||||||
|
|
||||||
// elemsizeof — given the type node of an indexable (`*T`, `[]T`,
|
// elemsizeof — given the type node of an indexable (`*T`, `[]T`,
|
||||||
// `[N]T`, `str`), return the byte size of one element (1 for u8/i8/
|
// `[N]T`, `str`), return the byte size of one element (1 for u8/i8/
|
||||||
// bool/str-byte, 8 otherwise — same shape as C cgen's esz fallback).
|
// bool/str-byte, 8 otherwise — same shape as C cgen's esz fallback).
|
||||||
|
|||||||
@@ -10549,7 +10549,7 @@ fn dynamicgrow(m: *state, need: i32) void = {
|
|||||||
// - pushargsrev: per-call arg pushing
|
// - pushargsrev: per-call arg pushing
|
||||||
// - type predicates: isstr*/isslice*/istagged*/nodeis* families
|
// - type predicates: isstr*/isslice*/istagged*/nodeis* families
|
||||||
// - field ops: fieldloadop, fieldstoreop
|
// - field ops: fieldloadop, fieldstoreop
|
||||||
// - index helpers: indexbaseesz, dotinnerstructptr, elemsizeof
|
// - index helpers: indexbaseesz, elemsizeof
|
||||||
// - slot sizing: structlookup, primsize, slotsize, fieldsize,
|
// - slot sizing: structlookup, primsize, slotsize, fieldsize,
|
||||||
// registerstruct, collectstructs
|
// registerstruct, collectstructs
|
||||||
// - rhs helpers: taggedvariantindex
|
// - rhs helpers: taggedvariantindex
|
||||||
@@ -11493,56 +11493,6 @@ fn indexbaseesz(c: *cgen, base: *node) i32 = {
|
|||||||
return 8;
|
return 8;
|
||||||
};
|
};
|
||||||
|
|
||||||
// dotinnerstructptr — for an nkind.N_DOT whose lhs is a chain of dots
|
|
||||||
// or an nkind.N_IDENT, walk the chain and return the nkind.N_TNAME tnode of the
|
|
||||||
// struct that the chain dereferences to (i.e., for `r.sym` where
|
|
||||||
// .sym is *lsym, return nkind.N_TNAME("lsym")). Returns nil if the chain
|
|
||||||
// doesn't resolve to a *struct.
|
|
||||||
//
|
|
||||||
// Used by the chained-DOT cgen path so `r.sym.val` knows the outer
|
|
||||||
// is a field of `lsym`.
|
|
||||||
fn dotinnerstructptr(c: *cgen, n: *node) *node = {
|
|
||||||
if (n == nil) { return nil; };
|
|
||||||
if (n.kind != nkind.N_DOT) { return nil; };
|
|
||||||
let base: *node = n.lhs;
|
|
||||||
let fld: str = n.str;
|
|
||||||
if (base == nil) { return nil; };
|
|
||||||
|
|
||||||
// Resolve base's struct tnode.
|
|
||||||
let baset: *node = nil;
|
|
||||||
if (base.kind == nkind.N_IDENT) {
|
|
||||||
let lc: *local = localfindnode(c, base.str);
|
|
||||||
if (lc == nil) { return nil; };
|
|
||||||
let tn: *node = lc.tnode;
|
|
||||||
if (tn == nil) { return nil; };
|
|
||||||
// base could be either struct-by-value (nkind.N_TNAME) or *struct (nkind.N_TPTR).
|
|
||||||
if (tn.kind == nkind.N_TNAME) { baset = tn; };
|
|
||||||
if (tn.kind == nkind.N_TPTR) { baset = tn.lhs; };
|
|
||||||
} else { if (base.kind == nkind.N_DOT) {
|
|
||||||
baset = dotinnerstructptr(c, base);
|
|
||||||
};};
|
|
||||||
if (baset == nil) { return nil; };
|
|
||||||
if (baset.kind != nkind.N_TNAME) { return nil; };
|
|
||||||
|
|
||||||
// Look up the struct, find the field, return the field's *struct.
|
|
||||||
let si: *structinfo = structlookup(c, baset.str);
|
|
||||||
if (si == nil) { return nil; };
|
|
||||||
let fi: *fieldinfo = si.fields;
|
|
||||||
for (fi != nil) {
|
|
||||||
if (streq(fi.fname, fld)) {
|
|
||||||
let ft: *node = fi.tnode;
|
|
||||||
if (ft == nil) { return nil; };
|
|
||||||
if (ft.kind != nkind.N_TPTR) { return nil; };
|
|
||||||
let inner: *node = ft.lhs;
|
|
||||||
if (inner == nil) { return nil; };
|
|
||||||
if (inner.kind != nkind.N_TNAME) { return nil; };
|
|
||||||
return inner;
|
|
||||||
};
|
|
||||||
fi = fi.finext;
|
|
||||||
};
|
|
||||||
return nil;
|
|
||||||
};
|
|
||||||
|
|
||||||
// elemsizeof — given the type node of an indexable (`*T`, `[]T`,
|
// elemsizeof — given the type node of an indexable (`*T`, `[]T`,
|
||||||
// `[N]T`, `str`), return the byte size of one element (1 for u8/i8/
|
// `[N]T`, `str`), return the byte size of one element (1 for u8/i8/
|
||||||
// bool/str-byte, 8 otherwise — same shape as C cgen's esz fallback).
|
// bool/str-byte, 8 otherwise — same shape as C cgen's esz fallback).
|
||||||
@@ -15908,22 +15858,54 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
// the field. (Showed up porting w6l/pass.ww.)
|
// the field. (Showed up porting w6l/pass.ww.)
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
if (lhs.kind == nkind.N_DOT) {
|
if (lhs.kind == nkind.N_DOT) {
|
||||||
let innert: *node = dotinnerstructptr(c, lhs);
|
// #70 (#12): inner-struct layout via the stamped lhs.type_
|
||||||
if (innert != nil) {
|
// (peel *→struct) + tinfo.fields, replacing dotinnerstructptr's
|
||||||
let sname: str = innert.str;
|
// structinfo walk. Gate is strict-equal to the deleted helper:
|
||||||
let si: *structinfo = structlookup(c, sname);
|
// fire only when the chain root is a LOCAL ident AND every dot
|
||||||
if (si != nil) {
|
// in the chain resolves through a *struct (dotinnerstructptr
|
||||||
let fi: *fieldinfo = si.fields;
|
// recursed per level on a *struct field and bailed on a by-
|
||||||
for (fi != nil) {
|
// value-struct intermediate). Reproducing that exactly avoids
|
||||||
if (streq(fi.fname, fld)) {
|
// an untested widening past cstage; a deliberate widen, if ever
|
||||||
|
// wanted, is a future task with its own probe. Global-root
|
||||||
|
// chains stay in their pre-existing shared base-eval breakage
|
||||||
|
// (filed #27), untouched here.
|
||||||
|
let croot: *node = lhs;
|
||||||
|
let allptr: bool = true;
|
||||||
|
for (croot != nil && croot.kind == nkind.N_DOT) {
|
||||||
|
let ct: *tinfo = croot.type_: *tinfo;
|
||||||
|
for (ct != nil && ct.kind == tykind.TY_NAMED) { ct = ct.under; };
|
||||||
|
let okp: bool = false;
|
||||||
|
if (ct != nil) { if (ct.kind == tykind.TY_PTR) {
|
||||||
|
let cs: *tinfo = ct.sub;
|
||||||
|
for (cs != nil && cs.kind == tykind.TY_NAMED) { cs = cs.under; };
|
||||||
|
if (cs != nil) { if (cs.kind == tykind.TY_STRUCT) { okp = true; }; };
|
||||||
|
}; };
|
||||||
|
if (!okp) { allptr = false; };
|
||||||
|
croot = croot.lhs;
|
||||||
|
};
|
||||||
|
let it: *tinfo = nil;
|
||||||
|
if (allptr) { if (croot != nil) { if (croot.kind == nkind.N_IDENT) {
|
||||||
|
if (localfindnode(c, croot.str) != nil) {
|
||||||
|
it = lhs.type_: *tinfo;
|
||||||
|
};
|
||||||
|
}; }; };
|
||||||
|
for (it != nil && it.kind == tykind.TY_NAMED) { it = it.under; };
|
||||||
|
if (it != nil) { if (it.kind == tykind.TY_PTR) {
|
||||||
|
let st: *tinfo = it.sub;
|
||||||
|
for (st != nil && st.kind == tykind.TY_NAMED) { st = st.under; };
|
||||||
|
if (st != nil) { if (st.kind == tykind.TY_STRUCT) {
|
||||||
|
let tf: *tfield = st.fields;
|
||||||
|
for (tf != nil) {
|
||||||
|
if (streq(tf.name, fld)) {
|
||||||
|
let ft: *tinfo = tf.type_;
|
||||||
cgexpr(c, lhs); // AX = ptr to inner struct
|
cgexpr(c, lhs); // AX = ptr to inner struct
|
||||||
// str field: load both halves.
|
// str field: load both halves.
|
||||||
if (isstrtype(c, fi.tnode)) {
|
if (typeisstr(ft)) {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg((fi.foff + 8): i64, "AX");
|
emitdispreg((tf.offset + 8u64): i64, "AX");
|
||||||
emitline(", BX\n");
|
emitline(", BX\n");
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg(fi.foff: i64, "AX");
|
emitdispreg(tf.offset: i64, "AX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -15931,41 +15913,41 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
// into (AX, BX, CX). AX is the *struct
|
// into (AX, BX, CX). AX is the *struct
|
||||||
// base, so load .ptr (which targets
|
// base, so load .ptr (which targets
|
||||||
// AX) LAST.
|
// AX) LAST.
|
||||||
if (isslicetype(c, fi.tnode)) {
|
if (typeisslice(ft)) {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg((fi.foff + 8): i64, "AX");
|
emitdispreg((tf.offset + 8u64): i64, "AX");
|
||||||
emitline(", BX\n");
|
emitline(", BX\n");
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg((fi.foff + 16): i64, "AX");
|
emitdispreg((tf.offset + 16u64): i64, "AX");
|
||||||
emitline(", CX\n");
|
emitline(", CX\n");
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitdispreg(fi.foff: i64, "AX");
|
emitdispreg(tf.offset: i64, "AX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
// f64/f32 chained field: route through X0.
|
// f64/f32 chained field: route through X0.
|
||||||
if (isfloattype(c, fi.tnode)) {
|
if (typeisfloat(ft)) {
|
||||||
let mov: str = "MOVSD";
|
let mov: str = "MOVSD";
|
||||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
if (typeisf32(ft)) { mov = "MOVSS"; };
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitdispreg(fi.foff: i64, "AX");
|
emitdispreg(tf.offset: i64, "AX");
|
||||||
emitline(", X0\n");
|
emitline(", X0\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let lop: str = fieldloadop(c, fi);
|
let lop: str = loadopsz(typeissigned(ft), ft.slotsize: i32);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(lop);
|
emitline(lop);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitdispreg(fi.foff: i64, "AX");
|
emitdispreg(tf.offset: i64, "AX");
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
fi = fi.finext;
|
tf = tf.tnext;
|
||||||
};
|
};
|
||||||
};
|
}; };
|
||||||
};
|
}; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
// Chained `(ident).f1.f2` read where f1 is a struct-by-value
|
// Chained `(ident).f1.f2` read where f1 is a struct-by-value
|
||||||
@@ -18737,16 +18719,48 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
let fld: str = lhs.str;
|
let fld: str = lhs.str;
|
||||||
if (base != nil) {
|
if (base != nil) {
|
||||||
if (base.kind == nkind.N_DOT) {
|
if (base.kind == nkind.N_DOT) {
|
||||||
let innert: *node = dotinnerstructptr(c, base);
|
// #70 (#12): inner-struct layout via the stamped
|
||||||
if (innert != nil) {
|
// base.type_ (peel *→struct) + tinfo.fields,
|
||||||
let sname: str = innert.str;
|
// replacing dotinnerstructptr's structinfo walk.
|
||||||
let si: *structinfo = structlookup(c, sname);
|
// Gate is strict-equal to the deleted helper: fire
|
||||||
if (si != nil) {
|
// only when the chain root is a LOCAL ident AND every
|
||||||
let fi: *fieldinfo = si.fields;
|
// dot resolves through a *struct (dotinnerstructptr
|
||||||
for (fi != nil) {
|
// recursed per level on a *struct field, bailing on a
|
||||||
if (streq(fi.fname, fld)) {
|
// by-value-struct intermediate). Reproducing that
|
||||||
|
// exactly avoids an untested widening past cstage.
|
||||||
|
// Global-root chains stay in their pre-existing shared
|
||||||
|
// base-eval breakage (filed #27).
|
||||||
|
let croot: *node = base;
|
||||||
|
let allptr: bool = true;
|
||||||
|
for (croot != nil && croot.kind == nkind.N_DOT) {
|
||||||
|
let ct: *tinfo = croot.type_: *tinfo;
|
||||||
|
for (ct != nil && ct.kind == tykind.TY_NAMED) { ct = ct.under; };
|
||||||
|
let okp: bool = false;
|
||||||
|
if (ct != nil) { if (ct.kind == tykind.TY_PTR) {
|
||||||
|
let cs: *tinfo = ct.sub;
|
||||||
|
for (cs != nil && cs.kind == tykind.TY_NAMED) { cs = cs.under; };
|
||||||
|
if (cs != nil) { if (cs.kind == tykind.TY_STRUCT) { okp = true; }; };
|
||||||
|
}; };
|
||||||
|
if (!okp) { allptr = false; };
|
||||||
|
croot = croot.lhs;
|
||||||
|
};
|
||||||
|
let it: *tinfo = nil;
|
||||||
|
if (allptr) { if (croot != nil) { if (croot.kind == nkind.N_IDENT) {
|
||||||
|
if (localfindnode(c, croot.str) != nil) {
|
||||||
|
it = base.type_: *tinfo;
|
||||||
|
};
|
||||||
|
}; }; };
|
||||||
|
for (it != nil && it.kind == tykind.TY_NAMED) { it = it.under; };
|
||||||
|
if (it != nil) { if (it.kind == tykind.TY_PTR) {
|
||||||
|
let st: *tinfo = it.sub;
|
||||||
|
for (st != nil && st.kind == tykind.TY_NAMED) { st = st.under; };
|
||||||
|
if (st != nil) { if (st.kind == tykind.TY_STRUCT) {
|
||||||
|
let tf: *tfield = st.fields;
|
||||||
|
for (tf != nil) {
|
||||||
|
if (streq(tf.name, fld)) {
|
||||||
|
let ft: *tinfo = tf.type_;
|
||||||
if (n.op == tkind.TK_ASSIGN) {
|
if (n.op == tkind.TK_ASSIGN) {
|
||||||
if (isstrtype(c, fi.tnode)) {
|
if (typeisstr(ft)) {
|
||||||
// str rhs: AX=ptr, BX=len.
|
// str rhs: AX=ptr, BX=len.
|
||||||
// Stash both, then load
|
// Stash both, then load
|
||||||
// the struct ptr into CX
|
// the struct ptr into CX
|
||||||
@@ -18759,19 +18773,19 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tPOPQ\tAX\n");
|
emitline("\tPOPQ\tAX\n");
|
||||||
emitline("\tPOPQ\tBX\n");
|
emitline("\tPOPQ\tBX\n");
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitdispreg(fi.foff: i64, "CX");
|
emitdispreg(tf.offset: i64, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
emitline("\tMOVQ\tBX, ");
|
emitline("\tMOVQ\tBX, ");
|
||||||
emitdispreg((fi.foff + 8): i64, "CX");
|
emitdispreg((tf.offset + 8u64): i64, "CX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
// f64/f32 chained plain `=`: cgexpr rhs left value in
|
// f64/f32 chained plain `=`: cgexpr rhs left value in
|
||||||
// X0. Spill to stack so cgexpr(base) can use AX, then
|
// X0. Spill to stack so cgexpr(base) can use AX, then
|
||||||
// reload and MOVSD/MOVSS into the slot.
|
// reload and MOVSD/MOVSS into the slot.
|
||||||
if (isfloattype(c, fi.tnode)) {
|
if (typeisfloat(ft)) {
|
||||||
let mov: str = "MOVSD";
|
let mov: str = "MOVSD";
|
||||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
if (typeisf32(ft)) { mov = "MOVSS"; };
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
emitline("\tSUBQ\t$8, SP\n");
|
emitline("\tSUBQ\t$8, SP\n");
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -18786,7 +18800,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(mov);
|
emitline(mov);
|
||||||
emitline("\tX0, ");
|
emitline("\tX0, ");
|
||||||
emitdispreg(fi.foff: i64, "BX");
|
emitdispreg(tf.offset: i64, "BX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -18795,19 +18809,19 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
cgexpr(c, base);
|
cgexpr(c, base);
|
||||||
emitline("\tMOVQ\tAX, BX\n");
|
emitline("\tMOVQ\tAX, BX\n");
|
||||||
emitline("\tPOPQ\tAX\n");
|
emitline("\tPOPQ\tAX\n");
|
||||||
let sop: str = fieldstoreop(c, fi);
|
let sop: str = tnodestoreop(c, n.rhs, ft.slotsize: i32);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
emitdispreg(fi.foff: i64, "BX");
|
emitdispreg(tf.offset: i64, "BX");
|
||||||
emitline("\n");
|
emitline("\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
fi = fi.finext;
|
tf = tf.tnext;
|
||||||
};
|
};
|
||||||
};
|
}; };
|
||||||
};
|
}; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user