w6c+selfhost: cgen N_DOT slice-field through *T root in call args (closes #29)

This commit is contained in:
2026-05-14 23:29:35 +09:00
parent 9706513e59
commit 6402d8deb7
7 changed files with 857 additions and 24 deletions

View File

@@ -5983,6 +5983,75 @@ fn nodeisslice(c: *cgen, n: *node) bool = {
};
if (k == nkind.N_SLICE) { return true; };
if (k == nkind.N_CAST) { return isslicetype(c, n.rhs); };
// N_DOT to a slice field: resolve the field through the struct
// (or *struct) the base ident / inner chain lands on, then check
// the field tnode. Mirrors nodeisstr's N_DOT branch so call-arg
// push/pop counts 3 words for `p.sl` and `p.inner.sl` shapes.
// `.ptr` / `.len` / `.cap` are pseudo-fields — they yield ptr
// (*u8) and i32, not a slice — so we exclude them up front.
if (k == nkind.N_DOT) {
let base: *node = n.lhs;
let fld: str = n.str;
if (streq(fld, "ptr")) { return false; };
if (streq(fld, "len")) { return false; };
if (streq(fld, "cap")) { return false; };
if (base != nil) {
let sname: str;
sname.ptr = nil; sname.len = 0;
if (base.kind == nkind.N_IDENT) {
let lc: *local = localfindnode(c, base.str);
if (lc != nil) {
let tn: *node = lc.tnode;
let lkind: nkind = nkind.N_NONE;
if (tn != nil) { lkind = tn.kind; };
if (lkind == nkind.N_TNAME) { sname = tn.str; };
if (lkind == nkind.N_TPTR) {
let inner: *node = tn.lhs;
if (inner != nil) {
if (inner.kind == nkind.N_TNAME) { sname = inner.str; };
};
};
};
};
if (base.kind == nkind.N_DOT) {
let innert: *node = dotinnerstructptr(c, base);
if (innert != nil) {
if (innert.kind == nkind.N_TNAME) { sname = innert.str; };
};
};
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)) {
return isslicetype(c, fi.tnode);
};
fi = fi.finext;
};
};
};
// Chained dot through value-struct hops (`o.inner.sl`,
// `p.inner.sl`): dotinnerstructptr above only walks
// *struct fields, so a value-struct chain falls through.
// dotchainresolve handles arbitrary depth through value
// struct AND `*T` root, returning the leaf fieldinfo.
let rootnm: str = "";
let rootoff: i32 = 0;
let totaloff: i32 = 0;
let lfi: *fieldinfo = nil;
let sdelta: i32 = -1;
let isglobal: bool = false;
let ptrroot: bool = false;
let ok: bool = dotchainresolve(c, n,
&rootnm, &rootoff, &totaloff,
&lfi, &sdelta, &isglobal, &ptrroot);
if (ok && sdelta < 0 && lfi != nil) {
return isslicetype(c, lfi.tnode);
};
};
return false;
};
return false;
};
@@ -6064,12 +6133,10 @@ fn nodeisstr(c: *cgen, n: *node) bool = {
};
};
// Chained dot through value-struct hops (`p.inner.s`):
// dotinnerstructptr above only walks *struct fields, so
// a value-struct chain falls through and the call-arg
// path then pushes only 1 word for the str instead of
// 2 (ptr+len), silently dropping the len half.
// dotchainresolve handles arbitrary depth through value
// struct AND `*T` root, returning the leaf fieldinfo.
// dotinnerstructptr above only walks *struct fields;
// dotchainresolve handles arbitrary depth through
// value struct AND `*T` root. Mirror of the nodeisslice
// fallback so chained str-field args also push 2 words.
let rootnm: str = "";
let rootoff: i32 = 0;
let totaloff: i32 = 0;
@@ -9335,6 +9402,21 @@ fn cgdot(c: *cgen, n: *node) void = {
emitdispreg(fi.foff: i64, "BX");
emitline(", AX\n");
emitline("\tMOVQ\tCX, BX\n");
} else { if (isslicetype(c, fi.tnode)) {
// slice field via *struct: load
// (ptr, len, cap) into (AX, BX, CX).
// BX holds the *struct pointer, so
// load .len LAST so the earlier
// reads still index off the base.
emitline("\tMOVQ\t");
emitdispreg(fi.foff: i64, "BX");
emitline(", AX\n");
emitline("\tMOVQ\t");
emitdispreg((fi.foff + 16): i64, "BX");
emitline(", CX\n");
emitline("\tMOVQ\t");
emitdispreg((fi.foff + 8): i64, "BX");
emitline(", BX\n");
} else { if (isfloattype(c, fi.tnode)) {
// f64/f32 via *struct: route through X0.
// MOVQ into AX leaves the SSE reg stale
@@ -9355,7 +9437,7 @@ fn cgdot(c: *cgen, n: *node) void = {
emitline("\t");
emitdispreg(fi.foff: i64, "BX");
emitline(", AX\n");
}; };
}; }; };
return;
};
fi = fi.finext;
@@ -9381,6 +9463,19 @@ fn cgdot(c: *cgen, n: *node) void = {
emitline("\tMOVQ\t");
emitoff((lc.off + fi.foff + 8): i64);
emitline("(BP), BX\n");
} else { if (isslicetype(c, fi.tnode)) {
// slice field: load (ptr, len, cap)
// into (AX, BX, CX). Base is BP so
// no aliasing — order doesn't matter.
emitline("\tMOVQ\t");
emitoff((lc.off + fi.foff): i64);
emitline("(BP), AX\n");
emitline("\tMOVQ\t");
emitoff((lc.off + fi.foff + 8): i64);
emitline("(BP), BX\n");
emitline("\tMOVQ\t");
emitoff((lc.off + fi.foff + 16): i64);
emitline("(BP), CX\n");
} else { if (isfloattype(c, fi.tnode)) {
// f64/f32 field: route through X0.
let mov: str = "MOVSD";
@@ -9397,7 +9492,7 @@ fn cgdot(c: *cgen, n: *node) void = {
emitline("\t");
emitoff((lc.off + fi.foff): i64);
emitline("(BP), AX\n");
}; };
}; }; };
return;
};
fi = fi.finext;
@@ -9806,6 +9901,45 @@ fn cgdot(c: *cgen, n: *node) void = {
};
return;
};
if (isslicetype(c, leaffi.tnode)) {
// Slice leaf: load all three header words into
// (AX=ptr, BX=len, CX=cap). For the viacx path
// (global or `*T` root) CX is the base; load
// .cap LAST so the base survives the earlier
// reads. For BP-rooted locals the registers
// don't alias so order is free.
if (viacx) {
if (ptrroot) {
emitline("\tMOVQ\t");
emitoff(rootoff: i64);
emitline("(BP), CX\n");
} else {
emitline("\tLEAQ\t");
emitsymname(c, rootname);
emitline("(SB), CX\n");
};
emitline("\tMOVQ\t");
emitdispreg(totaloff: i64, "CX");
emitline(", AX\n");
emitline("\tMOVQ\t");
emitdispreg((totaloff + 8): i64, "CX");
emitline(", BX\n");
emitline("\tMOVQ\t");
emitdispreg((totaloff + 16): i64, "CX");
emitline(", CX\n");
} else {
emitline("\tMOVQ\t");
emitoff((rootoff + totaloff): i64);
emitline("(BP), AX\n");
emitline("\tMOVQ\t");
emitoff((rootoff + totaloff + 8): i64);
emitline("(BP), BX\n");
emitline("\tMOVQ\t");
emitoff((rootoff + totaloff + 16): i64);
emitline("(BP), CX\n");
};
return;
};
if (isfloattype(c, leaffi.tnode)) {
let mov: str = "MOVSD";
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
@@ -9900,6 +10034,22 @@ fn cgdot(c: *cgen, n: *node) void = {
emitline(", AX\n");
return;
};
// slice field: load (ptr, len, cap)
// into (AX, BX, CX). AX is the *struct
// base, so load .ptr (which targets
// AX) LAST.
if (isslicetype(c, fi.tnode)) {
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;
};
// f64/f32 chained field: route through X0.
if (isfloattype(c, fi.tnode)) {
let mov: str = "MOVSD";