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:
216
cmd/w6c/cgen.c
216
cmd/w6c/cgen.c
@@ -131,6 +131,43 @@ fld_isfloat(Type *t, int *isf32)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* fld_issigned — true iff a sub-word field/element load needs sign
|
||||
* extension (i8 → MOVSBQ, i16 → MOVSWQ, i32 → MOVSXD). Follows NAMED
|
||||
* and ENUM aliases via type_isunsigned, then peels off the unsigned
|
||||
* cases (u*, bool, rune) so what remains is the genuinely-signed
|
||||
* narrow integers. The literal-kind ladder this replaces missed
|
||||
* TY_ENUM aliases entirely (`type myflag = i8` silently emitted
|
||||
* MOVZBQ on a field load). */
|
||||
static int
|
||||
fld_issigned(Type *t)
|
||||
{
|
||||
Type *u = (t && t->kind == TY_NAMED) ? t->under : t;
|
||||
if (u == NULL) return 0;
|
||||
if (u->kind == TY_BOOL) return 0;
|
||||
if (type_isunsigned(u)) return 0;
|
||||
return type_isint(u);
|
||||
}
|
||||
|
||||
static int
|
||||
fldloadop(Type *t, int sz)
|
||||
{
|
||||
int sigd = fld_issigned(t);
|
||||
if (sz == 1) return sigd ? A_MOVSBQ : A_MOVZBQ;
|
||||
if (sz == 2) return sigd ? A_MOVSWQ : A_MOVZWQ;
|
||||
if (sz == 4) return sigd ? A_MOVSXD : A_MOVL;
|
||||
return A_MOVQ;
|
||||
}
|
||||
|
||||
static int
|
||||
fldstoreop(Type *t, int sz)
|
||||
{
|
||||
(void)t;
|
||||
if (sz == 1) return A_MOVB;
|
||||
if (sz == 2) return A_MOVW;
|
||||
if (sz == 4) return A_MOVL;
|
||||
return A_MOVQ;
|
||||
}
|
||||
|
||||
/* struct ≤16B all-INTEGER: 1 or 2 eightbyte regs.
|
||||
* Returns 0 if not a struct or too large. */
|
||||
static int
|
||||
@@ -1720,13 +1757,8 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
break;
|
||||
}
|
||||
int fsz = (int)(f->type ? f->type->size : 8);
|
||||
int signed_field = f->type && (
|
||||
f->type->kind == TY_I8 ||
|
||||
f->type->kind == TY_I16 ||
|
||||
f->type->kind == TY_I32);
|
||||
int load_op = A_MOVQ, store_op = A_MOVQ;
|
||||
if (fsz == 1) { load_op = A_MOVZBQ; store_op = A_MOVB; }
|
||||
else if (fsz == 4) { load_op = signed_field ? A_MOVSXD : A_MOVL; store_op = A_MOVL; }
|
||||
int load_op = fldloadop(f->type, fsz);
|
||||
int store_op = fldstoreop(f->type, fsz);
|
||||
int boff = localfind(locals, base->str);
|
||||
int is_global = (boff == 0 && !via_ptr
|
||||
&& let_islet(base->str));
|
||||
@@ -1840,13 +1872,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
Type *fu = (ft && ft->kind == TY_NAMED)
|
||||
? ft->under : ft;
|
||||
int fsz = (int)(ft ? ft->size : 8);
|
||||
int signed_field = ft && (
|
||||
ft->kind == TY_I8 ||
|
||||
ft->kind == TY_I16 ||
|
||||
ft->kind == TY_I32);
|
||||
int store_op = A_MOVQ;
|
||||
if (fsz == 1) store_op = A_MOVB;
|
||||
else if (fsz == 4) store_op = A_MOVL;
|
||||
int store_op = fldstoreop(ft, fsz);
|
||||
int foff = (int)f->offset;
|
||||
if (n->op == TK_ASSIGN) {
|
||||
int c_isf32 = 0;
|
||||
@@ -1907,10 +1933,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
ins1(c, A_PUSHQ, areg(D_AX));
|
||||
cgexpr(c, n->lhs->lhs, locals);
|
||||
ins1(c, A_PUSHQ, areg(D_AX));
|
||||
int load_op = A_MOVQ;
|
||||
if (fsz == 1) load_op = A_MOVZBQ;
|
||||
else if (fsz == 4)
|
||||
load_op = signed_field ? A_MOVSXD : A_MOVL;
|
||||
int load_op = fldloadop(ft, fsz);
|
||||
ins2(c, load_op, amem(D_AX, foff),
|
||||
areg(D_AX));
|
||||
ins1(c, A_POPQ, areg(D_BX));
|
||||
@@ -2037,9 +2060,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
? leaf_type->under : leaf_type;
|
||||
int fsz = (int)(leaf_type
|
||||
? leaf_type->size : 8);
|
||||
int store_op = A_MOVQ;
|
||||
if (fsz == 1) store_op = A_MOVB;
|
||||
else if (fsz == 4) store_op = A_MOVL;
|
||||
int store_op = fldstoreop(leaf_type, fsz);
|
||||
if (fu && fu->kind == TY_STR) {
|
||||
cgexpr(c, n->rhs, locals);
|
||||
if (is_global) {
|
||||
@@ -2268,10 +2289,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
ins2(c, A_MOVQ, areg(D_CX), amem(D_BX, 8));
|
||||
break;
|
||||
}
|
||||
int store_op = A_MOVQ;
|
||||
if (esz == 1) store_op = A_MOVB;
|
||||
else if (esz == 2) store_op = A_MOVW;
|
||||
else if (esz == 4) store_op = A_MOVL;
|
||||
int store_op = fldstoreop(esub, esz);
|
||||
ins2(c, store_op, areg(D_AX), amem(D_BX, 0));
|
||||
break;
|
||||
}
|
||||
@@ -2331,12 +2349,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
} else {
|
||||
ins1(c, A_POPQ, areg(D_AX));
|
||||
int sz = vt ? (int)vt->size : 8;
|
||||
int signed_field = vt && (vt->kind == TY_I8 ||
|
||||
vt->kind == TY_I16 || vt->kind == TY_I32);
|
||||
(void)signed_field;
|
||||
int store_op = A_MOVQ;
|
||||
if (sz == 1) store_op = A_MOVB;
|
||||
else if (sz == 4) store_op = A_MOVL;
|
||||
int store_op = fldstoreop(vt, sz);
|
||||
ins2(c, store_op, areg(D_AX), amem(D_BX, 0));
|
||||
}
|
||||
break;
|
||||
@@ -2355,17 +2368,9 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
Type *vt = (pu && pu->kind == TY_PTR) ? pu->sub : NULL;
|
||||
if (vt && vt->kind == TY_NAMED) vt = vt->under;
|
||||
int sz = vt ? (int)vt->size : 8;
|
||||
int load_op = A_MOVQ, store_op = A_MOVQ;
|
||||
int handled = 1;
|
||||
if (sz == 8) {
|
||||
load_op = A_MOVQ; store_op = A_MOVQ;
|
||||
} else if (sz == 4) {
|
||||
load_op = A_MOVSXD; store_op = A_MOVL;
|
||||
} else if (sz == 1) {
|
||||
load_op = A_MOVZBQ; store_op = A_MOVB;
|
||||
} else {
|
||||
handled = 0;
|
||||
}
|
||||
int load_op = fldloadop(vt, sz);
|
||||
int store_op = fldstoreop(vt, sz);
|
||||
int handled = (sz == 1 || sz == 2 || sz == 4 || sz == 8);
|
||||
if (handled) {
|
||||
cgexpr(c, n->rhs, locals); /* AX = rhs */
|
||||
ins1(c, A_PUSHQ, areg(D_AX));
|
||||
@@ -2725,14 +2730,15 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
Type *st = sn->type;
|
||||
Type *su = (st && st->kind == TY_NAMED) ? st->under : st;
|
||||
int esz = (su && su->sub) ? (int)su->sub->size : 1;
|
||||
Type *esub = su ? su->sub : NULL;
|
||||
int sn_off = (sn->kind == N_IDENT)
|
||||
? localfind(locals, sn->str) : 0;
|
||||
int store_op = (esz == 1) ? A_MOVB : A_MOVQ;
|
||||
int store_op = fldstoreop(esub, esz);
|
||||
for (Node *vn = sn->next; vn; vn = vn->next) {
|
||||
if (vn->kind == N_SPREAD &&
|
||||
vn->lhs && vn->lhs->kind == N_IDENT) {
|
||||
int it_off = localfind(locals, vn->lhs->str);
|
||||
int load_op = (esz == 1) ? A_MOVZBQ : A_MOVQ;
|
||||
int load_op = fldloadop(esub, esz);
|
||||
/* push counter (i) on stack */
|
||||
ins2(c, A_SUBQ, aimm(8), areg(D_SP));
|
||||
ins2(c, A_MOVQ, aimm(0), amem(D_SP, 0));
|
||||
@@ -3641,36 +3647,41 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* Signed-narrow targets (i8/i16/i32) sign-extend via
|
||||
* MOVSBQ/MOVSWQ/MOVSXD reg-reg so the sign bit propagates;
|
||||
* this is what lets `(0xFF80i64): i8` compare equal to
|
||||
* -128i64 after a widening read-back. Gated on the literal
|
||||
* TY_I8/TY_I16/TY_I32 kinds so the wwstage cgen path
|
||||
* (cgcast in cgenexpr.ww, which keys off primsize on the
|
||||
* type-name node) emits the same instructions for the same
|
||||
* inputs — that byte-identity gate is what test 993 pins. */
|
||||
* -128i64 after a widening read-back. Symmetric on signed
|
||||
* vs unsigned: both branches gate on `type_isint(tu) &&
|
||||
* size<8`, then dispatch on type_isunsigned(tu). The
|
||||
* recursion through TY_ENUM in type_isunsigned (task #5)
|
||||
* is what lets an enum-aliased narrow (`type myflag = i8`)
|
||||
* pick up the right MOVS*Q. Wwstage's cgcast keys off the
|
||||
* resolved type-name through the same shape. TY_RUNE is
|
||||
* unsigned (Unicode scalar) and lands on the MOVL path. */
|
||||
if (!from_f && !to_f && n->type) {
|
||||
Type *tt = n->type;
|
||||
Type *tu = (tt && tt->kind == TY_NAMED) ? tt->under : tt;
|
||||
if (tu && type_isint(tu) && tu->size > 0
|
||||
&& tu->size < 8 && type_isunsigned(tu)) {
|
||||
if (tu->size == 4) {
|
||||
ins2(c, A_MOVL, areg(D_AX), areg(D_AX));
|
||||
&& tu->size < 8) {
|
||||
if (type_isunsigned(tu)) {
|
||||
if (tu->size == 4) {
|
||||
ins2(c, A_MOVL,
|
||||
areg(D_AX), areg(D_AX));
|
||||
} else {
|
||||
u64 mask = ((u64)1 << (tu->size * 8)) - 1;
|
||||
ins2(c, A_ANDQ,
|
||||
aimm((i64)mask),
|
||||
areg(D_AX));
|
||||
}
|
||||
} else {
|
||||
u64 mask = ((u64)1 << (tu->size * 8)) - 1;
|
||||
ins2(c, A_ANDQ, aimm((i64)mask),
|
||||
areg(D_AX));
|
||||
int op = A_MOVSXD;
|
||||
if (tu->size == 1) op = A_MOVSBQ;
|
||||
else if (tu->size == 2) op = A_MOVSWQ;
|
||||
ins2(c, op, areg(D_AX), areg(D_AX));
|
||||
}
|
||||
} else if (tu && (tu->kind == TY_I8
|
||||
|| tu->kind == TY_I16 || tu->kind == TY_I32)) {
|
||||
/* Literal-kind gate excludes TY_RUNE on purpose:
|
||||
* runes are unsigned Unicode scalars, owed to the
|
||||
* MOVL path once task #5 lands. */
|
||||
int op = A_MOVSXD;
|
||||
if (tu->kind == TY_I8) op = A_MOVSBQ;
|
||||
else if (tu->kind == TY_I16) op = A_MOVSWQ;
|
||||
ins2(c, op, areg(D_AX), areg(D_AX));
|
||||
}
|
||||
/* TY_BOOL is size 1 too; clamp to a single byte so
|
||||
* `(u32_val): bool` produces 0 or a low-byte value
|
||||
* instead of leaking the upper bits. */
|
||||
* instead of leaking the upper bits. type_isint(bool)
|
||||
* is false, so the symmetric narrow above misses it
|
||||
* — this dedicated branch covers the bool case. */
|
||||
if (tu && tu->kind == TY_BOOL) {
|
||||
ins2(c, A_ANDQ, aimm(0xFF), areg(D_AX));
|
||||
}
|
||||
@@ -3822,14 +3833,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
}
|
||||
int fsz = (int)(leaf_type
|
||||
? leaf_type->size : 8);
|
||||
int signed_field = leaf_type && (
|
||||
leaf_type->kind == TY_I8
|
||||
|| leaf_type->kind == TY_I16
|
||||
|| leaf_type->kind == TY_I32);
|
||||
int op = A_MOVQ;
|
||||
if (fsz == 1) op = A_MOVZBQ;
|
||||
else if (fsz == 4)
|
||||
op = signed_field ? A_MOVSXD : A_MOVL;
|
||||
int op = fldloadop(leaf_type, fsz);
|
||||
ins2(c, op,
|
||||
amem(base_reg,
|
||||
base_disp + total_off),
|
||||
@@ -3924,13 +3928,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
int fsz = (int)(tp->type ? tp->type->size : 8);
|
||||
Type *fu = (tp->type && tp->type->kind == TY_NAMED)
|
||||
? tp->type->under : tp->type;
|
||||
int signed_field = tp->type && (
|
||||
tp->type->kind == TY_I8 ||
|
||||
tp->type->kind == TY_I16 ||
|
||||
tp->type->kind == TY_I32);
|
||||
int op = A_MOVQ;
|
||||
if (fsz == 1) op = A_MOVZBQ;
|
||||
else if (fsz == 4) op = signed_field ? A_MOVSXD : A_MOVL;
|
||||
int op = fldloadop(tp->type, fsz);
|
||||
int off = localfind(locals, n->lhs->str);
|
||||
/* str element: load (ptr, len) into (AX, BX) so chains
|
||||
* like `t.1.len` propagate through the str-rhs
|
||||
@@ -4009,13 +4007,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
break;
|
||||
}
|
||||
int fsz = (int)(f->type ? f->type->size : 8);
|
||||
int signed_field = f->type && (
|
||||
f->type->kind == TY_I8 ||
|
||||
f->type->kind == TY_I16 ||
|
||||
f->type->kind == TY_I32);
|
||||
int op = A_MOVQ;
|
||||
if (fsz == 1) op = A_MOVZBQ;
|
||||
else if (fsz == 4) op = signed_field ? A_MOVSXD : A_MOVL;
|
||||
int op = fldloadop(f->type, fsz);
|
||||
ins2(c, op,
|
||||
amem(base_reg, base_disp + (int)f->offset),
|
||||
areg(D_AX));
|
||||
@@ -4077,13 +4069,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
break;
|
||||
}
|
||||
int fsz = (int)(f->type ? f->type->size : 8);
|
||||
int signed_field = f->type && (
|
||||
f->type->kind == TY_I8 ||
|
||||
f->type->kind == TY_I16 ||
|
||||
f->type->kind == TY_I32);
|
||||
int op = A_MOVQ;
|
||||
if (fsz == 1) op = A_MOVZBQ;
|
||||
else if (fsz == 4) op = signed_field ? A_MOVSXD : A_MOVL;
|
||||
int op = fldloadop(f->type, fsz);
|
||||
ins2(c, op,
|
||||
amem(D_BX, (int)f->offset),
|
||||
areg(D_AX));
|
||||
@@ -4131,13 +4117,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
goto dot_done;
|
||||
}
|
||||
int fsz = (int)(ft ? ft->size : 8);
|
||||
int signed_field = ft && (
|
||||
ft->kind == TY_I8 ||
|
||||
ft->kind == TY_I16 ||
|
||||
ft->kind == TY_I32);
|
||||
int op = A_MOVQ;
|
||||
if (fsz == 1) op = A_MOVZBQ;
|
||||
else if (fsz == 4) op = signed_field ? A_MOVSXD : A_MOVL;
|
||||
int op = fldloadop(ft, fsz);
|
||||
ins2(c, op, amem(D_AX, (int)f->offset),
|
||||
areg(D_AX));
|
||||
goto dot_done;
|
||||
@@ -4230,13 +4210,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
ins2(c, A_MOVQ, amem(D_BX, 0), areg(D_AX));
|
||||
break;
|
||||
}
|
||||
int signed_elem = esub && (
|
||||
esub->kind == TY_I8 || esub->kind == TY_I16 ||
|
||||
esub->kind == TY_I32);
|
||||
int load_op = A_MOVQ;
|
||||
if (esz == 1) load_op = A_MOVZBQ;
|
||||
else if (esz == 2) load_op = signed_elem ? A_MOVSWQ : A_MOVZWQ;
|
||||
else if (esz == 4) load_op = signed_elem ? A_MOVSXD : A_MOVL;
|
||||
int load_op = fldloadop(esub, esz);
|
||||
ins2(c, load_op, amem(D_BX, 0), areg(D_AX));
|
||||
break;
|
||||
}
|
||||
@@ -4278,14 +4252,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
break;
|
||||
}
|
||||
{
|
||||
int signed_elem = esub && (
|
||||
esub->kind == TY_I8 || esub->kind == TY_I16 ||
|
||||
esub->kind == TY_I32);
|
||||
int load_op = A_MOVQ;
|
||||
if (esz == 1) load_op = A_MOVZBQ;
|
||||
else if (esz == 2) load_op = signed_elem ? A_MOVSWQ : A_MOVZWQ;
|
||||
else if (esz == 4)
|
||||
load_op = signed_elem ? A_MOVSXD : A_MOVL;
|
||||
int load_op = fldloadop(esub, esz);
|
||||
ins2(c, load_op, amem(D_AX, 0), areg(D_AX));
|
||||
}
|
||||
break;
|
||||
@@ -4883,7 +4850,7 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
int loff = localoff(c, locals, lname, 8, frame);
|
||||
|
||||
/* allocate per-name slots */
|
||||
struct { int off, sz, foff; int signed_field; } binds[8] = {0};
|
||||
struct { int off, sz, foff; Type *ftype; } binds[8] = {0};
|
||||
int nbinds = 0;
|
||||
if (destruct) {
|
||||
Tparam *tp = (etu && etu->kind == TY_TUPLE) ?
|
||||
@@ -4894,10 +4861,7 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
int slot_sz = (fsz < 8) ? 8 : fsz;
|
||||
binds[nbinds].sz = fsz;
|
||||
binds[nbinds].foff = field_off;
|
||||
binds[nbinds].signed_field = tp && tp->type && (
|
||||
tp->type->kind == TY_I8 ||
|
||||
tp->type->kind == TY_I16 ||
|
||||
tp->type->kind == TY_I32);
|
||||
binds[nbinds].ftype = tp ? tp->type : NULL;
|
||||
binds[nbinds].off = localoff(c, locals,
|
||||
nm->str, slot_sz, frame);
|
||||
field_off += fsz;
|
||||
@@ -4909,10 +4873,7 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
binds[0].off = localoff(c, locals, n->str, slot_sz, frame);
|
||||
binds[0].sz = esz;
|
||||
binds[0].foff = 0;
|
||||
binds[0].signed_field = u && u->sub && (
|
||||
u->sub->kind == TY_I8 ||
|
||||
u->sub->kind == TY_I16 ||
|
||||
u->sub->kind == TY_I32);
|
||||
binds[0].ftype = u ? u->sub : NULL;
|
||||
nbinds = 1;
|
||||
}
|
||||
|
||||
@@ -4959,10 +4920,7 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
ins2(c, A_ADDQ, areg(D_AX), areg(D_BX));
|
||||
/* load each binding from BX + foff into its slot */
|
||||
for (int b = 0; b < nbinds; b++) {
|
||||
int op = A_MOVQ;
|
||||
if (binds[b].sz == 1) op = A_MOVZBQ;
|
||||
else if (binds[b].sz == 4)
|
||||
op = binds[b].signed_field ? A_MOVSXD : A_MOVL;
|
||||
int op = fldloadop(binds[b].ftype, binds[b].sz);
|
||||
ins2(c, op, amem(D_BX, binds[b].foff), areg(D_AX));
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, binds[b].off));
|
||||
}
|
||||
|
||||
@@ -170,8 +170,10 @@ type_isunsigned(Type *t)
|
||||
switch (t->kind) {
|
||||
case TY_U8: case TY_U16: case TY_U32: case TY_U64:
|
||||
case TY_UINT: case TY_UINTPTR:
|
||||
case TY_RUNE:
|
||||
return 1;
|
||||
case TY_NAMED: return type_isunsigned(t->under);
|
||||
case TY_NAMED: return type_isunsigned(t->under);
|
||||
case TY_ENUM: return type_isunsigned(t->sub);
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user