w6c+selfhost: principled sub-word signedness (closes #5/#10)

type_isunsigned recurses TY_ENUM and includes TY_RUNE on both stages.
13 LOAD + 6 STORE ladder sites (cstage) plus 4 more wwstage stragglers
in cgindex/cgforrange collapsed to fldloadop/fldstoreop helpers. N_CAST
narrow gate symmetrised; task #1's literal-kind workaround retired.
bool kept out of type_isunsigned, special-cased in field helpers.

Retroactively fixes a u32 mis-sign-extend in deref-compound (sz=4
hardcoded MOVSXD), pinned by new 660_field_signed row.
This commit is contained in:
2026-05-13 22:23:00 +09:00
parent 461a448d5f
commit c4b3aca5e4
9 changed files with 976 additions and 535 deletions

View File

@@ -403,7 +403,11 @@ fn cgcast(c: *cgen, n: *node) void = {
let sz: i32 = primsize(nm);
let is_unsigned: bool = typenameisunsigned(nm);
let is_bool: bool = streq(nm, "bool");
if (sz > 0) { if (sz < 8) {
// Symmetric narrow on signed vs unsigned (task #5):
// unsigned (incl. rune) clears upper bits; signed
// sign-extends. bool is size 1 but neither — falls
// through to its dedicated ANDQ $255 below.
if (sz > 0) { if (sz < 8) { if (!is_bool) {
if (is_unsigned) {
if (sz == 4) {
emitline("\tMOVL\tAX, AX\n");
@@ -414,16 +418,17 @@ fn cgcast(c: *cgen, n: *node) void = {
emitint(mask);
emitline(", AX\n");
};
} else { if (is_bool) {
emitline("\tANDQ\t$255, AX\n");
} else { if (streq(nm, "i8")) {
emitline("\tMOVSBQ\tAX, AX\n");
} else { if (streq(nm, "i16")) {
emitline("\tMOVSWQ\tAX, AX\n");
} else { if (streq(nm, "i32")) {
emitline("\tMOVSXD\tAX, AX\n");
}; }; }; }; };
}; };
} else {
if (sz == 1) {
emitline("\tMOVSBQ\tAX, AX\n");
} else { if (sz == 2) {
emitline("\tMOVSWQ\tAX, AX\n");
} else { if (sz == 4) {
emitline("\tMOVSXD\tAX, AX\n");
}; }; };
};
}; }; };
if (is_bool) { emitline("\tANDQ\t$255, AX\n"); };
};
return;
};
@@ -600,7 +605,7 @@ fn cgindex(c: *cgen, n: *node) void = {
baselocal = localfindnode(c, bn);
if (baselocal != nil) {
esz = elemsizeofc(c, baselocal.tnode);
signed_elem = elemissigned(baselocal.tnode);
signed_elem = elemissignedc(c, baselocal.tnode);
} else {
let tn: *node = letvartnode(c, bn);
if (tn != nil) {
@@ -608,13 +613,13 @@ fn cgindex(c: *cgen, n: *node) void = {
isglobalarr = true;
globalname = bn;
esz = elemsizeofc(c, tn);
signed_elem = elemissigned(tn);
signed_elem = elemissignedc(c, tn);
};
if (tn.kind == nkind.N_TPTR) {
isglobalptr = true;
globalname = bn;
esz = elemsizeofc(c, tn);
signed_elem = elemissigned(tn);
signed_elem = elemissignedc(c, tn);
};
};
};
@@ -696,16 +701,10 @@ fn cgindex(c: *cgen, n: *node) void = {
emitline("\tMOVQ\tCX, BX\n");
return;
};
if (esz == 1) { emitline("\tMOVZBQ\t(BX), AX\n"); }
else { if (esz == 2) {
if (signed_elem) { emitline("\tMOVSWQ\t(BX), AX\n"); }
else { emitline("\tMOVZWQ\t(BX), AX\n"); };
}
else { if (esz == 4) {
if (signed_elem) { emitline("\tMOVSXD\t(BX), AX\n"); }
else { emitline("\tMOVL\t(BX), AX\n"); };
}
else { emitline("\tMOVQ\t(BX), AX\n"); };};};
let lop1: str = loadopsz(signed_elem, esz);
emitline("\t");
emitline(lop1);
emitline("\t(BX), AX\n");
return;
};
if (baselocal != nil) {
@@ -743,16 +742,10 @@ fn cgindex(c: *cgen, n: *node) void = {
emitline("\tMOVQ\tCX, BX\n");
return;
};
if (esz == 1) { emitline("\tMOVZBQ\t(BX), AX\n"); }
else { if (esz == 2) {
if (signed_elem) { emitline("\tMOVSWQ\t(BX), AX\n"); }
else { emitline("\tMOVZWQ\t(BX), AX\n"); };
}
else { if (esz == 4) {
if (signed_elem) { emitline("\tMOVSXD\t(BX), AX\n"); }
else { emitline("\tMOVL\t(BX), AX\n"); };
}
else { emitline("\tMOVQ\t(BX), AX\n"); };};};
let lop2: str = loadopsz(signed_elem, esz);
emitline("\t");
emitline(lop2);
emitline("\t(BX), AX\n");
return;
};
// Generic fallback when base isn't a plain ident.
@@ -778,16 +771,10 @@ fn cgindex(c: *cgen, n: *node) void = {
emitline("\tMOVQ\t(AX), AX\n");
return;
};
if (esz == 1) { emitline("\tMOVZBQ\t(AX), AX\n"); }
else { if (esz == 2) {
if (signed_elem) { emitline("\tMOVSWQ\t(AX), AX\n"); }
else { emitline("\tMOVZWQ\t(AX), AX\n"); };
}
else { if (esz == 4) {
if (signed_elem) { emitline("\tMOVSXD\t(AX), AX\n"); }
else { emitline("\tMOVL\t(AX), AX\n"); };
}
else { emitline("\tMOVQ\t(AX), AX\n"); };};};
let lop3: str = loadopsz(signed_elem, esz);
emitline("\t");
emitline(lop3);
emitline("\t(AX), AX\n");
return;
};
@@ -1207,7 +1194,7 @@ fn cgdot(c: *cgen, n: *node) void = {
emitdispreg(fi.foff: i64, "BX");
emitline(", X0\n");
} else {
let op: str = fieldloadop(fi);
let op: str = fieldloadop(c, fi);
emitline("\t");
emitline(op);
emitline("\t");
@@ -1249,7 +1236,7 @@ fn cgdot(c: *cgen, n: *node) void = {
emitoff((lc.off + fi.foff): i64);
emitline("(BP), X0\n");
} else {
let op: str = fieldloadop(fi);
let op: str = fieldloadop(c, fi);
emitline("\t");
emitline(op);
emitline("\t");
@@ -1315,9 +1302,7 @@ fn cgdot(c: *cgen, n: *node) void = {
return;
};
let sz: i32 = slotsize(c, tp);
let op: str = "MOVQ";
if (sz == 1) { op = "MOVZBQ"; }
else { if (sz == 4) { op = "MOVL"; }; };
let op: str = tnodeloadop(c, tp, sz);
emitline("\t");
emitline(op);
emitline("\t");
@@ -1452,7 +1437,7 @@ fn cgdot(c: *cgen, n: *node) void = {
emitdispreg(fi.foff: i64, "CX");
emitline(", X0\n");
} else {
let op: str = fieldloadop(fi);
let op: str = fieldloadop(c, fi);
emitline("\t");
emitline(op);
emitline("\t");
@@ -1514,7 +1499,7 @@ fn cgdot(c: *cgen, n: *node) void = {
emitline(", X0\n");
return;
};
let lop: str = fieldloadop(fi);
let lop: str = fieldloadop(c, fi);
emitline("\t");
emitline(lop);
emitline("\t");
@@ -1615,7 +1600,7 @@ fn cgdot(c: *cgen, n: *node) void = {
};
return;
};
let lop: str = fieldloadop(r.leaffi);
let lop: str = fieldloadop(c, r.leaffi);
if (r.isglobal) {
emitline("\tLEAQ\t");
emitsymname(c, r.rootname);
@@ -1687,7 +1672,7 @@ fn cgdot(c: *cgen, n: *node) void = {
emitline(", X0\n");
return;
};
let lop: str = fieldloadop(fi);
let lop: str = fieldloadop(c, fi);
emitline("\t");
emitline(lop);
emitline("\t");
@@ -1785,7 +1770,7 @@ fn cgdot(c: *cgen, n: *node) void = {
};
return;
};
let lop: str = fieldloadop(ffi);
let lop: str = fieldloadop(c, ffi);
if (isptr) {
emitline("\tMOVQ\t");
emitoff(lc.off: i64);
@@ -2197,7 +2182,7 @@ fn cgalloc(c: *cgen, n: *node) void = {
fi = nil;
} else {
emitline("\tMOVQ\t(SP), BX\n");
let sop: str = fieldstoreop(fi);
let sop: str = fieldstoreop(c, fi);
emitline("\t");
emitline(sop);
emitline("\tAX, ");
@@ -2251,8 +2236,14 @@ fn cgappend(c: *cgen, n: *node) void = {
if (snlocal == nil) { return; };
let sn_off: i32 = snlocal.off;
let esz: i32 = elemsizeof(snlocal.tnode);
let store_op: str = "MOVQ";
if (esz == 1) { store_op = "MOVB"; };
let etnode: *node = nil;
if (snlocal.tnode != nil) {
let stk: nkind = snlocal.tnode.kind;
if (stk == nkind.N_TSLICE) { etnode = snlocal.tnode.lhs; };
if (stk == nkind.N_TARRAY) { etnode = snlocal.tnode.lhs; };
if (stk == nkind.N_TPTR) { etnode = snlocal.tnode.lhs; };
};
let store_op: str = tnodestoreop(c, etnode, esz);
let vn: *node = sn.next;
for (vn != nil) {
@@ -2263,8 +2254,7 @@ fn cgappend(c: *cgen, n: *node) void = {
let itlocal: *local = localfindnode(c, it.str);
if (itlocal == nil) { vn = vn.next; continue; };
let it_off: i32 = itlocal.off;
let load_op: str = "MOVQ";
if (esz == 1) { load_op = "MOVZBQ"; };
let load_op: str = tnodeloadop(c, etnode, esz);
emitline("\tSUBQ\t$8, SP\n");
emitline("\tMOVQ\t$0, (SP)\n");
let ll: str = mklabel(c, "spr_l");
@@ -2494,9 +2484,7 @@ fn cgcall(c: *cgen, n: *node) void = {
emitline("(BP)\n");
} else {
cgexpr(c, aa2);
let op: str = "MOVQ";
if (esz == 1) { op = "MOVB"; }
else { if (esz == 4) { op = "MOVL"; }; };
let op: str = tnodestoreop(c, varp.lhs, esz);
emitline("\t");
emitline(op);
emitline("\tAX, ");
@@ -2845,19 +2833,10 @@ fn cgassign(c: *cgen, n: *node) void = {
if (tn.kind == nkind.N_TPTR) {
let pe: *node = tn.lhs;
if (pe != nil) {
if (pe.kind == nkind.N_TNAME) {
let ps: i32 = primsize(pe.str);
if (ps == 1) {
loadop = "MOVZBQ";
storeop = "MOVB";
} else { if (ps == 4) {
if (typenameissigned(pe.str)) {
loadop = "MOVSXD";
} else {
loadop = "MOVL";
};
storeop = "MOVL";
}; };
let ps: i32 = fieldsize(c, pe);
if (ps == 1 || ps == 2 || ps == 4) {
loadop = tnodeloadop(c, pe, ps);
storeop = tnodestoreop(c, pe, ps);
};
};
};
@@ -3061,10 +3040,10 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tMOVQ\tCX, 8(BX)\n");
return;
};
if (esz == 1) { emitline("\tMOVB\tAX, (BX)\n"); }
else { if (esz == 2) { emitline("\tMOVW\tAX, (BX)\n"); }
else { if (esz == 4) { emitline("\tMOVL\tAX, (BX)\n"); }
else { emitline("\tMOVQ\tAX, (BX)\n"); };};};
let isop: str = tnodestoreop(c, elemtn, esz);
emitline("\t");
emitline(isop);
emitline("\tAX, (BX)\n");
return;
};
};
@@ -3104,7 +3083,7 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tMOVQ\t");
emitoff(lc.off: i64);
emitline("(BP), BX\n");
let lop: str = fieldloadop(fi);
let lop: str = fieldloadop(c, fi);
emitline("\t");
emitline(lop);
emitline("\t");
@@ -3160,7 +3139,7 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tMOVQ\t");
emitoff(lc.off: i64);
emitline("(BP), BX\n");
let sop: str = fieldstoreop(fi);
let sop: str = fieldstoreop(c, fi);
emitline("\t");
emitline(sop);
emitline("\tAX, ");
@@ -3208,7 +3187,7 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("(BP)\n");
return;
};
let sop: str = fieldstoreop(fi);
let sop: str = fieldstoreop(c, fi);
emitline("\t");
emitline(sop);
emitline("\tAX, ");
@@ -3333,7 +3312,7 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\n");
return;
};
let sop: str = fieldstoreop(fi);
let sop: str = fieldstoreop(c, fi);
emitline("\tLEAQ\t");
emitsymname(c, bn);
emitline("(SB), BX\n");
@@ -3348,7 +3327,7 @@ fn cgassign(c: *cgen, n: *node) void = {
// → push → eval rhs → combine →
// store. cgexpr clobbers BX, so
// re-LEAQ for the store.
let lop: str = fieldloadop(fi);
let lop: str = fieldloadop(c, fi);
emitline("\tLEAQ\t");
emitsymname(c, bn);
emitline("(SB), BX\n");
@@ -3365,7 +3344,7 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tSUBQ\tAX, BX\n");
emitline("\tMOVQ\tBX, AX\n");
};
let sop: str = fieldstoreop(fi);
let sop: str = fieldstoreop(c, fi);
emitline("\tLEAQ\t");
emitsymname(c, bn);
emitline("(SB), BX\n");
@@ -3456,7 +3435,7 @@ fn cgassign(c: *cgen, n: *node) void = {
cgexpr(c, base);
emitline("\tMOVQ\tAX, BX\n");
emitline("\tPOPQ\tAX\n");
let sop: str = fieldstoreop(fi);
let sop: str = fieldstoreop(c, fi);
emitline("\t");
emitline(sop);
emitline("\tAX, ");
@@ -3546,7 +3525,7 @@ fn cgassign(c: *cgen, n: *node) void = {
};
return;
};
let sop: str = fieldstoreop(r.leaffi);
let sop: str = fieldstoreop(c, r.leaffi);
cgexpr(c, n.rhs);
if (r.isglobal) {
emitline("\tLEAQ\t");
@@ -3659,7 +3638,7 @@ fn cgassign(c: *cgen, n: *node) void = {
};
return;
};
let sop: str = fieldstoreop(ffi);
let sop: str = fieldstoreop(c, ffi);
if (isptr) {
emitline("\tMOVQ\t");
emitoff(lc.off: i64);