w6c+selfhost: cgen chained N_DOT/N_ASSIGN spine walk
Loop-shaped spine walker for value-struct chains (o.i.a) and slice/str pseudo-fields (s.buf.len), read+write, both stages. SB-fallback at the catch-all preserved for unresolved module-qualified idents. Follow-ups filed: tasks #7-#10 (wwstage >6-arg frame over-alloc, chained array-elem field BX loss, & through chained DOT, signed sub-word field loads zero-extend).
This commit is contained in:
@@ -1543,6 +1543,99 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
};
|
||||
// Chained N_DOT spine through value-struct fields (any depth).
|
||||
// Walks the spine to a root ident, summing field offsets, then
|
||||
// emits ONE load at base + total_off. Also handles a slice/str
|
||||
// pseudo-field leaf (`b.buf.len`): the walk lands on the slice/
|
||||
// str header and slicedelta picks ptr/len/cap. Mirror of cstage
|
||||
// cgen.c's chained-DOT read branch. Without this, depth ≥ 3
|
||||
// shapes (`v.a.a.a`) and `b.buf.len` fall through to the non-
|
||||
// ident-base pseudo branch below — which would cgexpr the inner
|
||||
// (loading only .ptr into AX) and shuffle stale BX into AX.
|
||||
// Placed BEFORE the .ptr/.len fast paths so the chain wins.
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == nkind.N_DOT) {
|
||||
let r: dotchain;
|
||||
let pok: bool = dotchainresolve(c, n, &r);
|
||||
if (pok) {
|
||||
if (r.slicedelta >= 0i64) {
|
||||
if (r.isglobal) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, r.rootname);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(r.totaloff + r.slicedelta, "CX");
|
||||
emitline(", AX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(r.rootoff + r.totaloff + r.slicedelta);
|
||||
emitline("(BP), AX\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (isstrtype(c, r.leaffi.tnode)) {
|
||||
if (r.isglobal) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, r.rootname);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(r.totaloff + 0i64, "CX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(r.totaloff + 8i64, "CX");
|
||||
emitline(", BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(r.rootoff + r.totaloff);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(r.rootoff + r.totaloff + 8i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (isfloattype(c, r.leaffi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, r.leaffi.tnode)) { mov = "MOVSS"; };
|
||||
if (r.isglobal) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, r.rootname);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\t");
|
||||
emitdispreg(r.totaloff, "CX");
|
||||
emitline(", X0\n");
|
||||
} else {
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\t");
|
||||
emitoff(r.rootoff + r.totaloff);
|
||||
emitline("(BP), X0\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
let lop: str = fieldloadop(r.leaffi);
|
||||
if (r.isglobal) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, r.rootname);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
emitdispreg(r.totaloff, "CX");
|
||||
emitline(", AX\n");
|
||||
} else {
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
emitoff(r.rootoff + r.totaloff);
|
||||
emitline("(BP), AX\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
// Non-ident base pseudo-field: e.g. `"abc".ptr` / `"abc".len`.
|
||||
// Evaluate the str-producing expression — that leaves
|
||||
// (AX=ptr, BX=len). Then `.ptr` returns AX as is; `.len`
|
||||
@@ -1612,6 +1705,9 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
// field. Mirror of the cgassign branch added for the same shape.
|
||||
// Without this, `L.cur.kind` (cur a by-value struct of *L)
|
||||
// falls into the SB-fallback and emits `MOVQ kind(SB), AX`.
|
||||
// Kept as a fallback below the generalized walker above (placed
|
||||
// earlier in cgdot) to preserve byte-identical output on shapes
|
||||
// it already handles.
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == nkind.N_DOT) {
|
||||
let inner: *node = lhs.lhs;
|
||||
@@ -3377,6 +3473,101 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// Chained N_DOT spine write through value-struct fields (any
|
||||
// depth) — `o.i.a = 10`, `v.a.b.c = …`. Also handles a slice/str
|
||||
// pseudo-field leaf (`b.buf.len = 5`). Mirror of cstage cgen.c's
|
||||
// chained-DOT write branch. Without this, depth ≥ 3 writes and
|
||||
// the slice/str pseudo-field write through a value-struct chain
|
||||
// silently emit no store. Only plain `=` is wired.
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == nkind.N_DOT && lhs.lhs != nil
|
||||
&& lhs.lhs.kind == nkind.N_DOT
|
||||
&& n.op == tkind.TK_ASSIGN) {
|
||||
let r: dotchain;
|
||||
let yok: bool = dotchainresolve(c, lhs, &r);
|
||||
if (yok) {
|
||||
if (r.slicedelta >= 0i64) {
|
||||
cgexpr(c, n.rhs);
|
||||
if (r.isglobal) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, r.rootname);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(r.totaloff + r.slicedelta, "CX");
|
||||
emitline("\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(r.rootoff + r.totaloff + r.slicedelta);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (isstrtype(c, r.leaffi.tnode)) {
|
||||
cgexpr(c, n.rhs);
|
||||
if (r.isglobal) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, r.rootname);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(r.totaloff + 0i64, "CX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitdispreg(r.totaloff + 8i64, "CX");
|
||||
emitline("\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(r.rootoff + r.totaloff);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff(r.rootoff + r.totaloff + 8i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (isfloattype(c, r.leaffi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, r.leaffi.tnode)) { mov = "MOVSS"; };
|
||||
cgexpr(c, n.rhs);
|
||||
if (r.isglobal) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, r.rootname);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitdispreg(r.totaloff, "CX");
|
||||
emitline("\n");
|
||||
} else {
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitoff(r.rootoff + r.totaloff);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
let sop: str = fieldstoreop(r.leaffi);
|
||||
cgexpr(c, n.rhs);
|
||||
if (r.isglobal) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, r.rootname);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
emitdispreg(r.totaloff, "CX");
|
||||
emitline("\n");
|
||||
} else {
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
emitoff(r.rootoff + r.totaloff);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
// Chained `(ident).f1.f2 = v` where f1 is a struct-by-value
|
||||
// field. The earlier chained-DOT branch handles f1: *T (deref
|
||||
// then store). This handles f1: T (in-place sub-struct), which
|
||||
@@ -3384,6 +3575,8 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
// to flatten `cur.kind`/`cur.ival`/... into top-level fields to
|
||||
// work around it. Only plain `=` is wired; compound on a by-
|
||||
// value sub-field hasn't surfaced.
|
||||
// Kept as fallback below the generalized walker for any shape
|
||||
// the walker doesn't recognize.
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == nkind.N_DOT) {
|
||||
let base: *node = lhs.lhs;
|
||||
|
||||
Reference in New Issue
Block a user