w6c+selfhost: localloadop helper for sign-aware ident loads (closes #19)
Read-side fix dual to fldloadop: signed-narrow local/global ident loads now MOVSXD/MOVSWQ/MOVSBQ from the slot instead of raw MOVQ. Deref-stores (MOVL/MOVW/MOVB) no longer corrupt downstream i64 widens. Compound RMW restructured to gate direct-mem ADDQ/SUBQ on load_op == MOVQ. Top-level lets use LEAQ+indirect (w6a doesn't expose MOVSXD/MOVSWQ/MOVSBQ for D_EXTERN). dotchainresolve out-params restored to natural *i32 (workaround retired). selfhost/CLAUDE.md graduated.
This commit is contained in:
@@ -491,17 +491,25 @@ fn cgident(c: *cgen, n: *node) void = {
|
||||
emitline("(BP), X0\n");
|
||||
return;
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
// str / slice locals load (ptr[, len[, cap]]) through MOVQ
|
||||
// since the header is always 8B-clean. Scalar locals route
|
||||
// through localloadop so signed-narrow slots sign-extend
|
||||
// after a narrow deref-store.
|
||||
let isstr: bool = isstrtype(c, lc.tnode);
|
||||
let issl: bool = isslicetype(c, lc.tnode);
|
||||
let lop: str = "MOVQ";
|
||||
if (!isstr) { if (!issl) { lop = localloadop(c, lc.tnode); }; };
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP), AX\n");
|
||||
// str local: also load the len half into BX.
|
||||
if (isstrtype(c, lc.tnode)) {
|
||||
if (isstr) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
// slice local: load (ptr, len, cap) into (AX, BX, CX).
|
||||
if (isslicetype(c, lc.tnode)) {
|
||||
if (issl) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP), BX\n");
|
||||
@@ -554,7 +562,10 @@ fn cgident(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
// Float global: same LEAQ-indirect shape, since MOVSS/
|
||||
// MOVSD have no D_EXTERN operand form in w6a.
|
||||
// MOVSD have no D_EXTERN operand form in w6a. Signed-narrow
|
||||
// scalar globals route through the same LEAQ scratch since
|
||||
// MOVSXD/MOVSWQ/MOVSBQ also have no D_EXTERN form.
|
||||
let lvtnode: *node = nil;
|
||||
let lv: *letvar = c.lets;
|
||||
for (lv != nil) {
|
||||
if (streq(lv.name, nm)) {
|
||||
@@ -569,14 +580,25 @@ fn cgident(c: *cgen, n: *node) void = {
|
||||
emitline("\t(CX), X0\n");
|
||||
return;
|
||||
};
|
||||
lvtnode = lv.tnode;
|
||||
lv = nil;
|
||||
} else {
|
||||
lv = lv.lvnext;
|
||||
};
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), AX\n");
|
||||
let glop: str = localloadop(c, lvtnode);
|
||||
if (streq(glop, "MOVQ")) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), AX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\t");
|
||||
emitline(glop);
|
||||
emitline("\t(CX), AX\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
return;
|
||||
@@ -1553,13 +1575,25 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
// Module-qualified value reference: `mod.name` where `mod`
|
||||
// is nkind.N_IDENT bound as skind.SK_USE and the leaf isn't a local.
|
||||
// Treat as a SB symbol — `MOVQ leaf(SB), AX`. Same fallback
|
||||
// the C cgen takes when bt is NULL/tyerr.
|
||||
// Treat as a SB symbol — `MOVQ leaf(SB), AX` for the 8B case;
|
||||
// signed-narrow leaves route through LEAQ + localloadop so a
|
||||
// prior narrow deref-store doesn't leave stale upper bytes. Same
|
||||
// fallback the C cgen takes when bt is NULL/tyerr.
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == nkind.N_IDENT) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymname(c, fld);
|
||||
emitline("(SB), AX\n");
|
||||
let mqop: str = localloadop(c, letvartnode(c, fld));
|
||||
if (streq(mqop, "MOVQ")) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymname(c, fld);
|
||||
emitline("(SB), AX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, fld);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\t");
|
||||
emitline(mqop);
|
||||
emitline("\t(CX), AX\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
};
|
||||
@@ -1576,26 +1610,26 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == nkind.N_DOT) {
|
||||
let rootname: str = "";
|
||||
let rootoff: i64 = 0i64;
|
||||
let totaloff: i64 = 0i64;
|
||||
let rootoff: i32 = 0;
|
||||
let totaloff: i32 = 0;
|
||||
let leaffi: *fieldinfo = nil;
|
||||
let slicedelta: i64 = -1i64;
|
||||
let slicedelta: i32 = -1;
|
||||
let isglobal: bool = false;
|
||||
let pok: bool = dotchainresolve(c, n,
|
||||
&rootname, &rootoff, &totaloff,
|
||||
&leaffi, &slicedelta, &isglobal);
|
||||
if (pok) {
|
||||
if (slicedelta >= 0i64) {
|
||||
if (slicedelta >= 0) {
|
||||
if (isglobal) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(totaloff + slicedelta, "CX");
|
||||
emitdispreg((totaloff + slicedelta): i64, "CX");
|
||||
emitline(", AX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff + totaloff + slicedelta);
|
||||
emitoff((rootoff + totaloff + slicedelta): i64);
|
||||
emitline("(BP), AX\n");
|
||||
};
|
||||
return;
|
||||
@@ -1606,17 +1640,17 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(totaloff, "CX");
|
||||
emitdispreg(totaloff: i64, "CX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(totaloff + 8i64, "CX");
|
||||
emitdispreg((totaloff + 8): i64, "CX");
|
||||
emitline(", BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff + totaloff);
|
||||
emitoff((rootoff + totaloff): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff + totaloff + 8i64);
|
||||
emitoff((rootoff + totaloff + 8): i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
return;
|
||||
@@ -1631,13 +1665,13 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\t");
|
||||
emitdispreg(totaloff, "CX");
|
||||
emitdispreg(totaloff: i64, "CX");
|
||||
emitline(", X0\n");
|
||||
} else {
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\t");
|
||||
emitoff(rootoff + totaloff);
|
||||
emitoff((rootoff + totaloff): i64);
|
||||
emitline("(BP), X0\n");
|
||||
};
|
||||
return;
|
||||
@@ -1650,13 +1684,13 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
emitdispreg(totaloff, "CX");
|
||||
emitdispreg(totaloff: i64, "CX");
|
||||
emitline(", AX\n");
|
||||
} else {
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
emitoff(rootoff + totaloff);
|
||||
emitoff((rootoff + totaloff): i64);
|
||||
emitline("(BP), AX\n");
|
||||
};
|
||||
return;
|
||||
@@ -1933,27 +1967,27 @@ fn cgun(c: *cgen, n: *node) void = {
|
||||
if (opnd.lhs != nil) {
|
||||
if (opnd.lhs.kind == nkind.N_DOT) {
|
||||
let rootname: str = "";
|
||||
let rootoff: i64 = 0i64;
|
||||
let totaloff: i64 = 0i64;
|
||||
let rootoff: i32 = 0;
|
||||
let totaloff: i32 = 0;
|
||||
let leaffi: *fieldinfo = nil;
|
||||
let slicedelta: i64 = -1i64;
|
||||
let slicedelta: i32 = -1;
|
||||
let isglobal: bool = false;
|
||||
let pok: bool = dotchainresolve(c, opnd,
|
||||
&rootname, &rootoff, &totaloff,
|
||||
&leaffi, &slicedelta, &isglobal);
|
||||
if (pok) {
|
||||
let extra: i64 = 0i64;
|
||||
if (slicedelta >= 0i64) { extra = slicedelta; };
|
||||
let extra: i32 = 0;
|
||||
if (slicedelta >= 0) { extra = slicedelta; };
|
||||
if (isglobal) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tLEAQ\t");
|
||||
emitdispreg(totaloff + extra, "CX");
|
||||
emitdispreg((totaloff + extra): i64, "CX");
|
||||
emitline(", AX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(rootoff + totaloff + extra);
|
||||
emitoff((rootoff + totaloff + extra): i64);
|
||||
emitline("(BP), AX\n");
|
||||
};
|
||||
return;
|
||||
@@ -3885,27 +3919,27 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
&& lhs.lhs.kind == nkind.N_DOT
|
||||
&& n.op == tkind.TK_ASSIGN) {
|
||||
let rootname: str = "";
|
||||
let rootoff: i64 = 0i64;
|
||||
let totaloff: i64 = 0i64;
|
||||
let rootoff: i32 = 0;
|
||||
let totaloff: i32 = 0;
|
||||
let leaffi: *fieldinfo = nil;
|
||||
let slicedelta: i64 = -1i64;
|
||||
let slicedelta: i32 = -1;
|
||||
let isglobal: bool = false;
|
||||
let yok: bool = dotchainresolve(c, lhs,
|
||||
&rootname, &rootoff, &totaloff,
|
||||
&leaffi, &slicedelta, &isglobal);
|
||||
if (yok) {
|
||||
if (slicedelta >= 0i64) {
|
||||
if (slicedelta >= 0) {
|
||||
cgexpr(c, n.rhs);
|
||||
if (isglobal) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(totaloff + slicedelta, "CX");
|
||||
emitdispreg((totaloff + slicedelta): i64, "CX");
|
||||
emitline("\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(rootoff + totaloff + slicedelta);
|
||||
emitoff((rootoff + totaloff + slicedelta): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
return;
|
||||
@@ -3917,17 +3951,17 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(totaloff, "CX");
|
||||
emitdispreg(totaloff: i64, "CX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitdispreg(totaloff + 8i64, "CX");
|
||||
emitdispreg((totaloff + 8): i64, "CX");
|
||||
emitline("\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(rootoff + totaloff);
|
||||
emitoff((rootoff + totaloff): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff(rootoff + totaloff + 8i64);
|
||||
emitoff((rootoff + totaloff + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
return;
|
||||
@@ -3943,13 +3977,13 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitdispreg(totaloff, "CX");
|
||||
emitdispreg(totaloff: i64, "CX");
|
||||
emitline("\n");
|
||||
} else {
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitoff(rootoff + totaloff);
|
||||
emitoff((rootoff + totaloff): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
return;
|
||||
@@ -3963,13 +3997,13 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
emitdispreg(totaloff, "CX");
|
||||
emitdispreg(totaloff: i64, "CX");
|
||||
emitline("\n");
|
||||
} else {
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
emitoff(rootoff + totaloff);
|
||||
emitoff((rootoff + totaloff): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
return;
|
||||
@@ -4124,10 +4158,12 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
let lvf: *letvar = c.lets;
|
||||
let isfg: bool = false;
|
||||
let isf32g: bool = false;
|
||||
let lvftn: *node = nil;
|
||||
for (lvf != nil) {
|
||||
if (streq(lvf.name, nm)) {
|
||||
isfg = isfloattype(c, lvf.tnode);
|
||||
isf32g = isf32type(c, lvf.tnode);
|
||||
lvftn = lvf.tnode;
|
||||
lvf = nil;
|
||||
} else {
|
||||
lvf = lvf.lvnext;
|
||||
@@ -4209,9 +4245,23 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("(SB)\n");
|
||||
return;
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), BX\n");
|
||||
// Compound RMW for a top-level let: load through
|
||||
// LEAQ + localloadop when the slot is narrow so
|
||||
// a prior `*(&letname): *iN` deref-store doesn't
|
||||
// leave stale upper bytes feeding the combine.
|
||||
let glop: str = localloadop(c, lvftn);
|
||||
if (streq(glop, "MOVQ")) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), BX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\t");
|
||||
emitline(glop);
|
||||
emitline("\t(CX), BX\n");
|
||||
};
|
||||
let didcompound: bool = true;
|
||||
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tAX, BX\n"); }
|
||||
else { if (n.op == tkind.TK_MINUSEQ) { emitline("\tSUBQ\tAX, BX\n"); }
|
||||
@@ -4331,22 +4381,34 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (n.op == tkind.TK_PLUSEQ) {
|
||||
emitline("\tADDQ\tAX, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
if (n.op == tkind.TK_MINUSEQ) {
|
||||
emitline("\tSUBQ\tAX, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
// Pick the load width for compound RMW. Signed-narrow
|
||||
// locals must sign-extend the slot before the combine
|
||||
// — ADDQ/SUBQ on amem reads 8B raw, which is wrong
|
||||
// after a 4B deref-store leaves the upper bytes stale.
|
||||
let llop: str = "MOVQ";
|
||||
if (lcn != nil) { llop = localloadop(c, lcn.tnode); };
|
||||
if (streq(llop, "MOVQ")) {
|
||||
if (n.op == tkind.TK_PLUSEQ) {
|
||||
emitline("\tADDQ\tAX, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
if (n.op == tkind.TK_MINUSEQ) {
|
||||
emitline("\tSUBQ\tAX, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
};
|
||||
// Generic compound: load → combine in BX → store.
|
||||
emitline("\tMOVQ\t");
|
||||
emitline("\t");
|
||||
emitline(llop);
|
||||
emitline("\t");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tAX, BX\n"); };
|
||||
if (n.op == tkind.TK_MINUSEQ) { emitline("\tSUBQ\tAX, BX\n"); };
|
||||
if (n.op == tkind.TK_STAREQ) { emitline("\tIMULQ\tAX, BX\n"); };
|
||||
if (n.op == tkind.TK_AMPEQ) { emitline("\tANDQ\tAX, BX\n"); };
|
||||
if (n.op == tkind.TK_PIPEEQ) { emitline("\tORQ\tAX, BX\n"); };
|
||||
|
||||
Reference in New Issue
Block a user