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

@@ -373,9 +373,7 @@ fn cglet(c: *cgen, n: *node) void = {
if (ps > 0) { esz = ps; };
};
};
let mop: str = "MOVQ";
if (esz == 1) { mop = "MOVB"; }
else { if (esz == 4) { mop = "MOVL"; }; };
let mop: str = tnodestoreop(c, elemn, esz);
let idx: i32 = 0;
let repeat: bool = false;
let e: *node = rhs.list;
@@ -491,7 +489,7 @@ fn cglet(c: *cgen, n: *node) void = {
emitline("(BP)\n");
fi = nil;
} else {
let sop: str = fieldstoreop(fi);
let sop: str = fieldstoreop(c, fi);
emitline("\t");
emitline(sop);
emitline("\tAX, ");
@@ -806,16 +804,11 @@ fn paramfieldsize(t: *node) i32 = {
return 8;
};
// paramissigned — does this primitive type need sign-extending on a
// narrow (4B) load? Mirrors C cgen's `binds[b].signed_field` flag.
fn paramissigned(t: *node) bool = {
if (t == nil) { return false; };
if (t.kind != nkind.N_TNAME) { return false; };
let nm: str = t.str;
if (streq(nm, "i8")) { return true; };
if (streq(nm, "i16")) { return true; };
if (streq(nm, "i32")) { return true; };
return false;
// paramissigned — does this type need sign-extending on a sub-word
// (1/2/4B) load? Mirrors cstage's signed_field check via
// fieldissignedc (resolves TBANG / TENUM / alias chains).
fn paramissigned(c: *cgen, t: *node) bool = {
return fieldissignedc(c, t);
};
// cgforrange — lower `for (let x .. slice) body` (and the tuple-
@@ -889,7 +882,7 @@ fn cgforrange(c: *cgen, n: *node) void = {
let signf: bool = false;
if (tp != nil) {
fsz = paramfieldsize(tp);
signf = paramissigned(tp);
signf = paramissigned(c, tp);
};
let slot_sz: i32 = fsz;
if (slot_sz < 8) { slot_sz = 8; };
@@ -917,7 +910,7 @@ fn cgforrange(c: *cgen, n: *node) void = {
// reads `u->sub->kind` for the elem type.
bind_signed[0] = false;
if (elemt != nil) {
bind_signed[0] = paramissigned(elemt);
bind_signed[0] = paramissigned(c, elemt);
};
if (n.str.len > 0) {
// Register with elem tnode so x.field on a loop
@@ -1015,15 +1008,12 @@ fn cgforrange(c: *cgen, n: *node) void = {
};
emitline("\tADDQ\tAX, BX\n");
// Per-binding load from BX+foff.
// Per-binding load from BX+foff. Signedness comes from bind_signed
// (set via paramissigned → fieldissignedc), so enum-aliased narrows
// pick the right MOVS*Q without a literal-name gate.
let b: i32 = 0;
for (b < nbinds) {
let op: str = "MOVQ";
if (bind_sz[b] == 1) { op = "MOVZBQ"; }
else { if (bind_sz[b] == 4) {
if (bind_signed[b]) { op = "MOVSXD"; }
else { op = "MOVL"; };
};};
let op: str = loadopsz(bind_signed[b], bind_sz[b]);
emitline("\t");
emitline(op);
emitline("\t");