selfhost: retire dotchain bundle workaround (closes #17)

dotchain struct + bundle pre-declares retired. dotchainresolve now
takes 6 direct out-params; per-iter locals declared inline. Bootstrap
ww2==ww3==ww4 byte-identical — end-to-end validation of session's
N_DOT / N_INDEX / TK_AMP / fldloadop fixes.

i64 widths retained on out-params via task #19 workaround (i32 deref-
stores leave caller slot upper 4B stale, MOVQ reads zero-extend to
garbage). Documented in selfhost/CLAUDE.md active-workarounds.
This commit is contained in:
2026-05-14 01:11:07 +09:00
parent f26c86f5b7
commit 5f87c60e6c
6 changed files with 383 additions and 417 deletions

View File

@@ -1575,81 +1575,88 @@ fn cgdot(c: *cgen, n: *node) void = {
// 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);
let rootname: str = "";
let rootoff: i64 = 0i64;
let totaloff: i64 = 0i64;
let leaffi: *fieldinfo = nil;
let slicedelta: i64 = -1i64;
let isglobal: bool = false;
let pok: bool = dotchainresolve(c, n,
&rootname, &rootoff, &totaloff,
&leaffi, &slicedelta, &isglobal);
if (pok) {
if (r.slicedelta >= 0i64) {
if (r.isglobal) {
if (slicedelta >= 0i64) {
if (isglobal) {
emitline("\tLEAQ\t");
emitsymname(c, r.rootname);
emitsymname(c, rootname);
emitline("(SB), CX\n");
emitline("\tMOVQ\t");
emitdispreg(r.totaloff + r.slicedelta, "CX");
emitdispreg(totaloff + slicedelta, "CX");
emitline(", AX\n");
} else {
emitline("\tMOVQ\t");
emitoff(r.rootoff + r.totaloff + r.slicedelta);
emitoff(rootoff + totaloff + slicedelta);
emitline("(BP), AX\n");
};
return;
};
if (isstrtype(c, r.leaffi.tnode)) {
if (r.isglobal) {
if (isstrtype(c, leaffi.tnode)) {
if (isglobal) {
emitline("\tLEAQ\t");
emitsymname(c, r.rootname);
emitsymname(c, rootname);
emitline("(SB), CX\n");
emitline("\tMOVQ\t");
emitdispreg(r.totaloff + 0i64, "CX");
emitdispreg(totaloff, "CX");
emitline(", AX\n");
emitline("\tMOVQ\t");
emitdispreg(r.totaloff + 8i64, "CX");
emitdispreg(totaloff + 8i64, "CX");
emitline(", BX\n");
} else {
emitline("\tMOVQ\t");
emitoff(r.rootoff + r.totaloff);
emitoff(rootoff + totaloff);
emitline("(BP), AX\n");
emitline("\tMOVQ\t");
emitoff(r.rootoff + r.totaloff + 8i64);
emitoff(rootoff + totaloff + 8i64);
emitline("(BP), BX\n");
};
return;
};
if (isfloattype(c, r.leaffi.tnode)) {
if (isfloattype(c, leaffi.tnode)) {
let mov: str = "MOVSD";
if (isf32type(c, r.leaffi.tnode)) { mov = "MOVSS"; };
if (r.isglobal) {
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
if (isglobal) {
emitline("\tLEAQ\t");
emitsymname(c, r.rootname);
emitsymname(c, rootname);
emitline("(SB), CX\n");
emitline("\t");
emitline(mov);
emitline("\t");
emitdispreg(r.totaloff, "CX");
emitdispreg(totaloff, "CX");
emitline(", X0\n");
} else {
emitline("\t");
emitline(mov);
emitline("\t");
emitoff(r.rootoff + r.totaloff);
emitoff(rootoff + totaloff);
emitline("(BP), X0\n");
};
return;
};
let lop: str = fieldloadop(c, r.leaffi);
if (r.isglobal) {
let lop: str = fieldloadop(c, leaffi);
if (isglobal) {
emitline("\tLEAQ\t");
emitsymname(c, r.rootname);
emitsymname(c, rootname);
emitline("(SB), CX\n");
emitline("\t");
emitline(lop);
emitline("\t");
emitdispreg(r.totaloff, "CX");
emitdispreg(totaloff, "CX");
emitline(", AX\n");
} else {
emitline("\t");
emitline(lop);
emitline("\t");
emitoff(r.rootoff + r.totaloff);
emitoff(rootoff + totaloff);
emitline("(BP), AX\n");
};
return;
@@ -1925,21 +1932,28 @@ fn cgun(c: *cgen, n: *node) void = {
// nsteps ≥ 2 (matches the read path's gate).
if (opnd.lhs != nil) {
if (opnd.lhs.kind == nkind.N_DOT) {
let r: dotchain;
let pok: bool = dotchainresolve(c, opnd, &r);
let rootname: str = "";
let rootoff: i64 = 0i64;
let totaloff: i64 = 0i64;
let leaffi: *fieldinfo = nil;
let slicedelta: i64 = -1i64;
let isglobal: bool = false;
let pok: bool = dotchainresolve(c, opnd,
&rootname, &rootoff, &totaloff,
&leaffi, &slicedelta, &isglobal);
if (pok) {
let extra: i64 = 0i64;
if (r.slicedelta >= 0i64) { extra = r.slicedelta; };
if (r.isglobal) {
if (slicedelta >= 0i64) { extra = slicedelta; };
if (isglobal) {
emitline("\tLEAQ\t");
emitsymname(c, r.rootname);
emitsymname(c, rootname);
emitline("(SB), CX\n");
emitline("\tLEAQ\t");
emitdispreg(r.totaloff + extra, "CX");
emitdispreg(totaloff + extra, "CX");
emitline(", AX\n");
} else {
emitline("\tLEAQ\t");
emitoff(r.rootoff + r.totaloff + extra);
emitoff(rootoff + totaloff + extra);
emitline("(BP), AX\n");
};
return;
@@ -3870,85 +3884,92 @@ fn cgassign(c: *cgen, n: *node) void = {
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);
let rootname: str = "";
let rootoff: i64 = 0i64;
let totaloff: i64 = 0i64;
let leaffi: *fieldinfo = nil;
let slicedelta: i64 = -1i64;
let isglobal: bool = false;
let yok: bool = dotchainresolve(c, lhs,
&rootname, &rootoff, &totaloff,
&leaffi, &slicedelta, &isglobal);
if (yok) {
if (r.slicedelta >= 0i64) {
if (slicedelta >= 0i64) {
cgexpr(c, n.rhs);
if (r.isglobal) {
if (isglobal) {
emitline("\tLEAQ\t");
emitsymname(c, r.rootname);
emitsymname(c, rootname);
emitline("(SB), CX\n");
emitline("\tMOVQ\tAX, ");
emitdispreg(r.totaloff + r.slicedelta, "CX");
emitdispreg(totaloff + slicedelta, "CX");
emitline("\n");
} else {
emitline("\tMOVQ\tAX, ");
emitoff(r.rootoff + r.totaloff + r.slicedelta);
emitoff(rootoff + totaloff + slicedelta);
emitline("(BP)\n");
};
return;
};
if (isstrtype(c, r.leaffi.tnode)) {
if (isstrtype(c, leaffi.tnode)) {
cgexpr(c, n.rhs);
if (r.isglobal) {
if (isglobal) {
emitline("\tLEAQ\t");
emitsymname(c, r.rootname);
emitsymname(c, rootname);
emitline("(SB), CX\n");
emitline("\tMOVQ\tAX, ");
emitdispreg(r.totaloff + 0i64, "CX");
emitdispreg(totaloff, "CX");
emitline("\n");
emitline("\tMOVQ\tBX, ");
emitdispreg(r.totaloff + 8i64, "CX");
emitdispreg(totaloff + 8i64, "CX");
emitline("\n");
} else {
emitline("\tMOVQ\tAX, ");
emitoff(r.rootoff + r.totaloff);
emitoff(rootoff + totaloff);
emitline("(BP)\n");
emitline("\tMOVQ\tBX, ");
emitoff(r.rootoff + r.totaloff + 8i64);
emitoff(rootoff + totaloff + 8i64);
emitline("(BP)\n");
};
return;
};
if (isfloattype(c, r.leaffi.tnode)) {
if (isfloattype(c, leaffi.tnode)) {
let mov: str = "MOVSD";
if (isf32type(c, r.leaffi.tnode)) { mov = "MOVSS"; };
if (isf32type(c, leaffi.tnode)) { mov = "MOVSS"; };
cgexpr(c, n.rhs);
if (r.isglobal) {
if (isglobal) {
emitline("\tLEAQ\t");
emitsymname(c, r.rootname);
emitsymname(c, rootname);
emitline("(SB), CX\n");
emitline("\t");
emitline(mov);
emitline("\tX0, ");
emitdispreg(r.totaloff, "CX");
emitdispreg(totaloff, "CX");
emitline("\n");
} else {
emitline("\t");
emitline(mov);
emitline("\tX0, ");
emitoff(r.rootoff + r.totaloff);
emitoff(rootoff + totaloff);
emitline("(BP)\n");
};
return;
};
let sop: str = fieldstoreop(c, r.leaffi);
let sop: str = fieldstoreop(c, leaffi);
cgexpr(c, n.rhs);
if (r.isglobal) {
if (isglobal) {
emitline("\tLEAQ\t");
emitsymname(c, r.rootname);
emitsymname(c, rootname);
emitline("(SB), CX\n");
emitline("\t");
emitline(sop);
emitline("\tAX, ");
emitdispreg(r.totaloff, "CX");
emitdispreg(totaloff, "CX");
emitline("\n");
} else {
emitline("\t");
emitline(sop);
emitline("\tAX, ");
emitoff(r.rootoff + r.totaloff);
emitoff(rootoff + totaloff);
emitline("(BP)\n");
};
return;