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:
7
Makefile
7
Makefile
@@ -217,6 +217,7 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
|
|||||||
$(BIN)/test_e2e $(BIN)/test_ffi $(BIN)/test_dyn $(BIN)/test_stdlib \
|
$(BIN)/test_e2e $(BIN)/test_ffi $(BIN)/test_dyn $(BIN)/test_stdlib \
|
||||||
$(BIN)/test_at_test $(BIN)/test_let_global \
|
$(BIN)/test_at_test $(BIN)/test_let_global \
|
||||||
$(BIN)/test_int_cast_signed $(BIN)/test_dot_chain \
|
$(BIN)/test_int_cast_signed $(BIN)/test_dot_chain \
|
||||||
|
$(BIN)/test_field_signed \
|
||||||
$(BIN)/test_selfhost $(BIN)/test_w6a_ww $(BIN)/test_w6l_ww \
|
$(BIN)/test_selfhost $(BIN)/test_w6a_ww $(BIN)/test_w6l_ww \
|
||||||
$(BIN)/test_w6c_ww $(BIN)/test_ww_ww $(BIN)/test_self_rebuild \
|
$(BIN)/test_w6c_ww $(BIN)/test_ww_ww $(BIN)/test_self_rebuild \
|
||||||
$(BIN)/test_dyn_ww $(BIN)/test_selfcheck $(BIN)/test_at_test_ww
|
$(BIN)/test_dyn_ww $(BIN)/test_selfcheck $(BIN)/test_at_test_ww
|
||||||
@@ -289,6 +290,12 @@ $(BIN)/test_dot_chain: test/wcc/650_dot_chain.c $(BIN)/ww \
|
|||||||
$(LIB)/libwwrt.a | $(BIN)
|
$(LIB)/libwwrt.a | $(BIN)
|
||||||
$(CC) $(CFLAGS) -o $@ $<
|
$(CC) $(CFLAGS) -o $@ $<
|
||||||
|
|
||||||
|
$(BIN)/test_field_signed: test/wcc/660_field_signed.c $(BIN)/ww \
|
||||||
|
$(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||||
|
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||||
|
$(LIB)/libwwrt.a | $(BIN)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $<
|
||||||
|
|
||||||
$(BIN)/test_selfhost: test/wcc/990_selfhost.c $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
$(BIN)/test_selfhost: test/wcc/990_selfhost.c $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||||
$(BIN)/ww $(BIN)/wwdump $(BIN)/wwdump_ww $(LIB)/libwwrt.a | $(BIN)
|
$(BIN)/ww $(BIN)/wwdump $(BIN)/wwdump_ww $(LIB)/libwwrt.a | $(BIN)
|
||||||
$(CC) $(CFLAGS) -o $@ $<
|
$(CC) $(CFLAGS) -o $@ $<
|
||||||
|
|||||||
216
cmd/w6c/cgen.c
216
cmd/w6c/cgen.c
@@ -131,6 +131,43 @@ fld_isfloat(Type *t, int *isf32)
|
|||||||
return 0;
|
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.
|
/* struct ≤16B all-INTEGER: 1 or 2 eightbyte regs.
|
||||||
* Returns 0 if not a struct or too large. */
|
* Returns 0 if not a struct or too large. */
|
||||||
static int
|
static int
|
||||||
@@ -1720,13 +1757,8 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
int fsz = (int)(f->type ? f->type->size : 8);
|
int fsz = (int)(f->type ? f->type->size : 8);
|
||||||
int signed_field = f->type && (
|
int load_op = fldloadop(f->type, fsz);
|
||||||
f->type->kind == TY_I8 ||
|
int store_op = fldstoreop(f->type, fsz);
|
||||||
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 boff = localfind(locals, base->str);
|
int boff = localfind(locals, base->str);
|
||||||
int is_global = (boff == 0 && !via_ptr
|
int is_global = (boff == 0 && !via_ptr
|
||||||
&& let_islet(base->str));
|
&& let_islet(base->str));
|
||||||
@@ -1840,13 +1872,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
Type *fu = (ft && ft->kind == TY_NAMED)
|
Type *fu = (ft && ft->kind == TY_NAMED)
|
||||||
? ft->under : ft;
|
? ft->under : ft;
|
||||||
int fsz = (int)(ft ? ft->size : 8);
|
int fsz = (int)(ft ? ft->size : 8);
|
||||||
int signed_field = ft && (
|
int store_op = fldstoreop(ft, fsz);
|
||||||
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 foff = (int)f->offset;
|
int foff = (int)f->offset;
|
||||||
if (n->op == TK_ASSIGN) {
|
if (n->op == TK_ASSIGN) {
|
||||||
int c_isf32 = 0;
|
int c_isf32 = 0;
|
||||||
@@ -1907,10 +1933,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
ins1(c, A_PUSHQ, areg(D_AX));
|
ins1(c, A_PUSHQ, areg(D_AX));
|
||||||
cgexpr(c, n->lhs->lhs, locals);
|
cgexpr(c, n->lhs->lhs, locals);
|
||||||
ins1(c, A_PUSHQ, areg(D_AX));
|
ins1(c, A_PUSHQ, areg(D_AX));
|
||||||
int load_op = A_MOVQ;
|
int load_op = fldloadop(ft, fsz);
|
||||||
if (fsz == 1) load_op = A_MOVZBQ;
|
|
||||||
else if (fsz == 4)
|
|
||||||
load_op = signed_field ? A_MOVSXD : A_MOVL;
|
|
||||||
ins2(c, load_op, amem(D_AX, foff),
|
ins2(c, load_op, amem(D_AX, foff),
|
||||||
areg(D_AX));
|
areg(D_AX));
|
||||||
ins1(c, A_POPQ, areg(D_BX));
|
ins1(c, A_POPQ, areg(D_BX));
|
||||||
@@ -2037,9 +2060,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
? leaf_type->under : leaf_type;
|
? leaf_type->under : leaf_type;
|
||||||
int fsz = (int)(leaf_type
|
int fsz = (int)(leaf_type
|
||||||
? leaf_type->size : 8);
|
? leaf_type->size : 8);
|
||||||
int store_op = A_MOVQ;
|
int store_op = fldstoreop(leaf_type, fsz);
|
||||||
if (fsz == 1) store_op = A_MOVB;
|
|
||||||
else if (fsz == 4) store_op = A_MOVL;
|
|
||||||
if (fu && fu->kind == TY_STR) {
|
if (fu && fu->kind == TY_STR) {
|
||||||
cgexpr(c, n->rhs, locals);
|
cgexpr(c, n->rhs, locals);
|
||||||
if (is_global) {
|
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));
|
ins2(c, A_MOVQ, areg(D_CX), amem(D_BX, 8));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
int store_op = A_MOVQ;
|
int store_op = fldstoreop(esub, esz);
|
||||||
if (esz == 1) store_op = A_MOVB;
|
|
||||||
else if (esz == 2) store_op = A_MOVW;
|
|
||||||
else if (esz == 4) store_op = A_MOVL;
|
|
||||||
ins2(c, store_op, areg(D_AX), amem(D_BX, 0));
|
ins2(c, store_op, areg(D_AX), amem(D_BX, 0));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2331,12 +2349,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
} else {
|
} else {
|
||||||
ins1(c, A_POPQ, areg(D_AX));
|
ins1(c, A_POPQ, areg(D_AX));
|
||||||
int sz = vt ? (int)vt->size : 8;
|
int sz = vt ? (int)vt->size : 8;
|
||||||
int signed_field = vt && (vt->kind == TY_I8 ||
|
int store_op = fldstoreop(vt, sz);
|
||||||
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;
|
|
||||||
ins2(c, store_op, areg(D_AX), amem(D_BX, 0));
|
ins2(c, store_op, areg(D_AX), amem(D_BX, 0));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -2355,17 +2368,9 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
Type *vt = (pu && pu->kind == TY_PTR) ? pu->sub : NULL;
|
Type *vt = (pu && pu->kind == TY_PTR) ? pu->sub : NULL;
|
||||||
if (vt && vt->kind == TY_NAMED) vt = vt->under;
|
if (vt && vt->kind == TY_NAMED) vt = vt->under;
|
||||||
int sz = vt ? (int)vt->size : 8;
|
int sz = vt ? (int)vt->size : 8;
|
||||||
int load_op = A_MOVQ, store_op = A_MOVQ;
|
int load_op = fldloadop(vt, sz);
|
||||||
int handled = 1;
|
int store_op = fldstoreop(vt, sz);
|
||||||
if (sz == 8) {
|
int handled = (sz == 1 || sz == 2 || sz == 4 || 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;
|
|
||||||
}
|
|
||||||
if (handled) {
|
if (handled) {
|
||||||
cgexpr(c, n->rhs, locals); /* AX = rhs */
|
cgexpr(c, n->rhs, locals); /* AX = rhs */
|
||||||
ins1(c, A_PUSHQ, areg(D_AX));
|
ins1(c, A_PUSHQ, areg(D_AX));
|
||||||
@@ -2725,14 +2730,15 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
Type *st = sn->type;
|
Type *st = sn->type;
|
||||||
Type *su = (st && st->kind == TY_NAMED) ? st->under : st;
|
Type *su = (st && st->kind == TY_NAMED) ? st->under : st;
|
||||||
int esz = (su && su->sub) ? (int)su->sub->size : 1;
|
int esz = (su && su->sub) ? (int)su->sub->size : 1;
|
||||||
|
Type *esub = su ? su->sub : NULL;
|
||||||
int sn_off = (sn->kind == N_IDENT)
|
int sn_off = (sn->kind == N_IDENT)
|
||||||
? localfind(locals, sn->str) : 0;
|
? 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) {
|
for (Node *vn = sn->next; vn; vn = vn->next) {
|
||||||
if (vn->kind == N_SPREAD &&
|
if (vn->kind == N_SPREAD &&
|
||||||
vn->lhs && vn->lhs->kind == N_IDENT) {
|
vn->lhs && vn->lhs->kind == N_IDENT) {
|
||||||
int it_off = localfind(locals, vn->lhs->str);
|
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 */
|
/* push counter (i) on stack */
|
||||||
ins2(c, A_SUBQ, aimm(8), areg(D_SP));
|
ins2(c, A_SUBQ, aimm(8), areg(D_SP));
|
||||||
ins2(c, A_MOVQ, aimm(0), amem(D_SP, 0));
|
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
|
* Signed-narrow targets (i8/i16/i32) sign-extend via
|
||||||
* MOVSBQ/MOVSWQ/MOVSXD reg-reg so the sign bit propagates;
|
* MOVSBQ/MOVSWQ/MOVSXD reg-reg so the sign bit propagates;
|
||||||
* this is what lets `(0xFF80i64): i8` compare equal to
|
* this is what lets `(0xFF80i64): i8` compare equal to
|
||||||
* -128i64 after a widening read-back. Gated on the literal
|
* -128i64 after a widening read-back. Symmetric on signed
|
||||||
* TY_I8/TY_I16/TY_I32 kinds so the wwstage cgen path
|
* vs unsigned: both branches gate on `type_isint(tu) &&
|
||||||
* (cgcast in cgenexpr.ww, which keys off primsize on the
|
* size<8`, then dispatch on type_isunsigned(tu). The
|
||||||
* type-name node) emits the same instructions for the same
|
* recursion through TY_ENUM in type_isunsigned (task #5)
|
||||||
* inputs — that byte-identity gate is what test 993 pins. */
|
* 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) {
|
if (!from_f && !to_f && n->type) {
|
||||||
Type *tt = n->type;
|
Type *tt = n->type;
|
||||||
Type *tu = (tt && tt->kind == TY_NAMED) ? tt->under : tt;
|
Type *tu = (tt && tt->kind == TY_NAMED) ? tt->under : tt;
|
||||||
if (tu && type_isint(tu) && tu->size > 0
|
if (tu && type_isint(tu) && tu->size > 0
|
||||||
&& tu->size < 8 && type_isunsigned(tu)) {
|
&& tu->size < 8) {
|
||||||
if (tu->size == 4) {
|
if (type_isunsigned(tu)) {
|
||||||
ins2(c, A_MOVL, areg(D_AX), areg(D_AX));
|
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 {
|
} else {
|
||||||
u64 mask = ((u64)1 << (tu->size * 8)) - 1;
|
int op = A_MOVSXD;
|
||||||
ins2(c, A_ANDQ, aimm((i64)mask),
|
if (tu->size == 1) op = A_MOVSBQ;
|
||||||
areg(D_AX));
|
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
|
/* TY_BOOL is size 1 too; clamp to a single byte so
|
||||||
* `(u32_val): bool` produces 0 or a low-byte value
|
* `(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) {
|
if (tu && tu->kind == TY_BOOL) {
|
||||||
ins2(c, A_ANDQ, aimm(0xFF), areg(D_AX));
|
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
|
int fsz = (int)(leaf_type
|
||||||
? leaf_type->size : 8);
|
? leaf_type->size : 8);
|
||||||
int signed_field = leaf_type && (
|
int op = fldloadop(leaf_type, fsz);
|
||||||
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;
|
|
||||||
ins2(c, op,
|
ins2(c, op,
|
||||||
amem(base_reg,
|
amem(base_reg,
|
||||||
base_disp + total_off),
|
base_disp + total_off),
|
||||||
@@ -3924,13 +3928,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
int fsz = (int)(tp->type ? tp->type->size : 8);
|
int fsz = (int)(tp->type ? tp->type->size : 8);
|
||||||
Type *fu = (tp->type && tp->type->kind == TY_NAMED)
|
Type *fu = (tp->type && tp->type->kind == TY_NAMED)
|
||||||
? tp->type->under : tp->type;
|
? tp->type->under : tp->type;
|
||||||
int signed_field = tp->type && (
|
int op = fldloadop(tp->type, fsz);
|
||||||
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 off = localfind(locals, n->lhs->str);
|
int off = localfind(locals, n->lhs->str);
|
||||||
/* str element: load (ptr, len) into (AX, BX) so chains
|
/* str element: load (ptr, len) into (AX, BX) so chains
|
||||||
* like `t.1.len` propagate through the str-rhs
|
* like `t.1.len` propagate through the str-rhs
|
||||||
@@ -4009,13 +4007,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
int fsz = (int)(f->type ? f->type->size : 8);
|
int fsz = (int)(f->type ? f->type->size : 8);
|
||||||
int signed_field = f->type && (
|
int op = fldloadop(f->type, fsz);
|
||||||
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;
|
|
||||||
ins2(c, op,
|
ins2(c, op,
|
||||||
amem(base_reg, base_disp + (int)f->offset),
|
amem(base_reg, base_disp + (int)f->offset),
|
||||||
areg(D_AX));
|
areg(D_AX));
|
||||||
@@ -4077,13 +4069,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
int fsz = (int)(f->type ? f->type->size : 8);
|
int fsz = (int)(f->type ? f->type->size : 8);
|
||||||
int signed_field = f->type && (
|
int op = fldloadop(f->type, fsz);
|
||||||
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;
|
|
||||||
ins2(c, op,
|
ins2(c, op,
|
||||||
amem(D_BX, (int)f->offset),
|
amem(D_BX, (int)f->offset),
|
||||||
areg(D_AX));
|
areg(D_AX));
|
||||||
@@ -4131,13 +4117,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
goto dot_done;
|
goto dot_done;
|
||||||
}
|
}
|
||||||
int fsz = (int)(ft ? ft->size : 8);
|
int fsz = (int)(ft ? ft->size : 8);
|
||||||
int signed_field = ft && (
|
int op = fldloadop(ft, fsz);
|
||||||
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;
|
|
||||||
ins2(c, op, amem(D_AX, (int)f->offset),
|
ins2(c, op, amem(D_AX, (int)f->offset),
|
||||||
areg(D_AX));
|
areg(D_AX));
|
||||||
goto dot_done;
|
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));
|
ins2(c, A_MOVQ, amem(D_BX, 0), areg(D_AX));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
int signed_elem = esub && (
|
int load_op = fldloadop(esub, esz);
|
||||||
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;
|
|
||||||
ins2(c, load_op, amem(D_BX, 0), areg(D_AX));
|
ins2(c, load_op, amem(D_BX, 0), areg(D_AX));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -4278,14 +4252,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
int signed_elem = esub && (
|
int load_op = fldloadop(esub, esz);
|
||||||
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;
|
|
||||||
ins2(c, load_op, amem(D_AX, 0), areg(D_AX));
|
ins2(c, load_op, amem(D_AX, 0), areg(D_AX));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -4883,7 +4850,7 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
|||||||
int loff = localoff(c, locals, lname, 8, frame);
|
int loff = localoff(c, locals, lname, 8, frame);
|
||||||
|
|
||||||
/* allocate per-name slots */
|
/* 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;
|
int nbinds = 0;
|
||||||
if (destruct) {
|
if (destruct) {
|
||||||
Tparam *tp = (etu && etu->kind == TY_TUPLE) ?
|
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;
|
int slot_sz = (fsz < 8) ? 8 : fsz;
|
||||||
binds[nbinds].sz = fsz;
|
binds[nbinds].sz = fsz;
|
||||||
binds[nbinds].foff = field_off;
|
binds[nbinds].foff = field_off;
|
||||||
binds[nbinds].signed_field = tp && tp->type && (
|
binds[nbinds].ftype = tp ? tp->type : NULL;
|
||||||
tp->type->kind == TY_I8 ||
|
|
||||||
tp->type->kind == TY_I16 ||
|
|
||||||
tp->type->kind == TY_I32);
|
|
||||||
binds[nbinds].off = localoff(c, locals,
|
binds[nbinds].off = localoff(c, locals,
|
||||||
nm->str, slot_sz, frame);
|
nm->str, slot_sz, frame);
|
||||||
field_off += fsz;
|
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].off = localoff(c, locals, n->str, slot_sz, frame);
|
||||||
binds[0].sz = esz;
|
binds[0].sz = esz;
|
||||||
binds[0].foff = 0;
|
binds[0].foff = 0;
|
||||||
binds[0].signed_field = u && u->sub && (
|
binds[0].ftype = u ? u->sub : NULL;
|
||||||
u->sub->kind == TY_I8 ||
|
|
||||||
u->sub->kind == TY_I16 ||
|
|
||||||
u->sub->kind == TY_I32);
|
|
||||||
nbinds = 1;
|
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));
|
ins2(c, A_ADDQ, areg(D_AX), areg(D_BX));
|
||||||
/* load each binding from BX + foff into its slot */
|
/* load each binding from BX + foff into its slot */
|
||||||
for (int b = 0; b < nbinds; b++) {
|
for (int b = 0; b < nbinds; b++) {
|
||||||
int op = A_MOVQ;
|
int op = fldloadop(binds[b].ftype, binds[b].sz);
|
||||||
if (binds[b].sz == 1) op = A_MOVZBQ;
|
|
||||||
else if (binds[b].sz == 4)
|
|
||||||
op = binds[b].signed_field ? A_MOVSXD : A_MOVL;
|
|
||||||
ins2(c, op, amem(D_BX, binds[b].foff), areg(D_AX));
|
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));
|
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, binds[b].off));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,8 +170,10 @@ type_isunsigned(Type *t)
|
|||||||
switch (t->kind) {
|
switch (t->kind) {
|
||||||
case TY_U8: case TY_U16: case TY_U32: case TY_U64:
|
case TY_U8: case TY_U16: case TY_U32: case TY_U64:
|
||||||
case TY_UINT: case TY_UINTPTR:
|
case TY_UINT: case TY_UINTPTR:
|
||||||
|
case TY_RUNE:
|
||||||
return 1;
|
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;
|
default: return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6072,7 +6072,10 @@ fn nodeisstr(c: *cgen, n: *node) bool = {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// typenameisunsigned — true for u8/u16/u32/u64/uint/uintptr.
|
// typenameisunsigned — true for u8/u16/u32/u64/uint/uintptr/rune.
|
||||||
|
// rune is a Unicode codepoint (0..0x10FFFF); cgen treats it as
|
||||||
|
// unsigned so narrow-cast / sub-word load paths zero-extend (MOVL,
|
||||||
|
// not MOVSXD). Mirrors cstage's type_isunsigned post task #5.
|
||||||
fn typenameisunsigned(nm: str) bool = {
|
fn typenameisunsigned(nm: str) bool = {
|
||||||
if (streq(nm, "u8")) { return true; };
|
if (streq(nm, "u8")) { return true; };
|
||||||
if (streq(nm, "u16")) { return true; };
|
if (streq(nm, "u16")) { return true; };
|
||||||
@@ -6080,10 +6083,39 @@ fn typenameisunsigned(nm: str) bool = {
|
|||||||
if (streq(nm, "u64")) { return true; };
|
if (streq(nm, "u64")) { return true; };
|
||||||
if (streq(nm, "uint")) { return true; };
|
if (streq(nm, "uint")) { return true; };
|
||||||
if (streq(nm, "uintptr")) { return true; };
|
if (streq(nm, "uintptr")) { return true; };
|
||||||
|
if (streq(nm, "rune")) { return true; };
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// typenodeisunsigned — recurse through TNAME / TPTR / TSLICE etc.
|
// typenodeisunsigned — recurse through TNAME aliases / TBANG / TENUM
|
||||||
|
// to the resolved primitive. Mirrors cstage's type_isunsigned which
|
||||||
|
// recurses into TY_NAMED.under and TY_ENUM.sub.
|
||||||
|
fn typenodeisunsignedc(c: *cgen, t: *node) bool = {
|
||||||
|
if (t == nil) { return false; };
|
||||||
|
let k: nkind = t.kind;
|
||||||
|
if (k == nkind.N_TBANG) { return typenodeisunsignedc(c, t.lhs); };
|
||||||
|
if (k == nkind.N_TENUM) { return typenodeisunsignedc(c, t.lhs); };
|
||||||
|
if (k == nkind.N_TNAME) {
|
||||||
|
let nm: str = t.str;
|
||||||
|
if (typenameisunsigned(nm)) { return true; };
|
||||||
|
if (typenameissigned(nm)) { return false; };
|
||||||
|
// Follow aliases / enum storage.
|
||||||
|
let al: *node = aliaslookup(c, nm);
|
||||||
|
if (al != nil) { return typenodeisunsignedc(c, al); };
|
||||||
|
let en: *enumtype = enumlookup(c, nm);
|
||||||
|
if (en != nil) {
|
||||||
|
if (en.storage != nil) {
|
||||||
|
return typenodeisunsignedc(c, en.storage);
|
||||||
|
};
|
||||||
|
return false; // default storage i32 is signed
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// typenodeisunsigned — legacy callers without *cgen context. Only
|
||||||
|
// resolves primitive TNAMEs (no alias/enum recursion); use the
|
||||||
|
// _c variant where the cgen registry is in scope.
|
||||||
fn typenodeisunsigned(t: *node) bool = {
|
fn typenodeisunsigned(t: *node) bool = {
|
||||||
if (t == nil) { return false; };
|
if (t == nil) { return false; };
|
||||||
if (t.kind == nkind.N_TNAME) { return typenameisunsigned(t.str); };
|
if (t.kind == nkind.N_TNAME) { return typenameisunsigned(t.str); };
|
||||||
@@ -6138,9 +6170,21 @@ fn typeis8byteprimitive(c: *cgen, t: *node) bool = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// elemissigned — given an indexable type (`*T`, `[]T`, `[N]T`), is
|
// elemissigned — given an indexable type (`*T`, `[]T`, `[N]T`), is
|
||||||
// its element a signed narrow primitive (i8/i16/i32/rune)? Used by
|
// its element a signed narrow primitive (i8/i16/i32)? Used by
|
||||||
// cgindex to pick MOVSXD vs MOVL at esz=4. Mirrors C cgen's
|
// cgindex to pick MOVSXD vs MOVL at esz=4 (and MOVSBQ/MOVSWQ at
|
||||||
// `signed_elem` check.
|
// esz=1/2). Mirrors cstage's `signed_elem`. Follows alias/enum
|
||||||
|
// chains so `[]Alias` arrays resolve to the underlying signedness.
|
||||||
|
fn elemissignedc(c: *cgen, t: *node) bool = {
|
||||||
|
if (t == nil) { return false; };
|
||||||
|
let elem: *node = nil;
|
||||||
|
let k: nkind = t.kind;
|
||||||
|
if (k == nkind.N_TPTR) { elem = t.lhs; };
|
||||||
|
if (k == nkind.N_TSLICE) { elem = t.lhs; };
|
||||||
|
if (k == nkind.N_TARRAY) { elem = t.lhs; };
|
||||||
|
if (elem == nil) { return false; };
|
||||||
|
return fieldissignedc(c, elem);
|
||||||
|
};
|
||||||
|
|
||||||
fn elemissigned(t: *node) bool = {
|
fn elemissigned(t: *node) bool = {
|
||||||
if (t == nil) { return false; };
|
if (t == nil) { return false; };
|
||||||
let elem: *node = nil;
|
let elem: *node = nil;
|
||||||
@@ -6153,47 +6197,103 @@ fn elemissigned(t: *node) bool = {
|
|||||||
return typenameissigned(elem.str);
|
return typenameissigned(elem.str);
|
||||||
};
|
};
|
||||||
|
|
||||||
// typenameissigned — true for i8/i16/i32/i64/int/rune.
|
// typenameissigned — true for i8/i16/i32/i64/int. rune is excluded
|
||||||
|
// (it's a non-negative Unicode codepoint, treated as unsigned).
|
||||||
fn typenameissigned(nm: str) bool = {
|
fn typenameissigned(nm: str) bool = {
|
||||||
if (streq(nm, "i8")) { return true; };
|
if (streq(nm, "i8")) { return true; };
|
||||||
if (streq(nm, "i16")) { return true; };
|
if (streq(nm, "i16")) { return true; };
|
||||||
if (streq(nm, "i32")) { return true; };
|
if (streq(nm, "i32")) { return true; };
|
||||||
if (streq(nm, "i64")) { return true; };
|
if (streq(nm, "i64")) { return true; };
|
||||||
if (streq(nm, "int")) { return true; };
|
if (streq(nm, "int")) { return true; };
|
||||||
if (streq(nm, "rune")) { return true; };
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// fieldissignedc — does this field/element type need sign-extension
|
||||||
|
// on a sub-word load? Walks TBANG / TENUM / TNAME-aliases to the
|
||||||
|
// resolved primitive. Mirrors cstage's fld_issigned: bool is treated
|
||||||
|
// as unsigned (0/1 ⇒ MOVZBQ); rune is unsigned (codepoint ⇒ MOVL).
|
||||||
|
fn fieldissignedc(c: *cgen, t: *node) bool = {
|
||||||
|
if (t == nil) { return false; };
|
||||||
|
let k: nkind = t.kind;
|
||||||
|
if (k == nkind.N_TBANG) { return fieldissignedc(c, t.lhs); };
|
||||||
|
if (k == nkind.N_TENUM) { return fieldissignedc(c, t.lhs); };
|
||||||
|
if (k == nkind.N_TNAME) {
|
||||||
|
let nm: str = t.str;
|
||||||
|
if (streq(nm, "bool")) { return false; };
|
||||||
|
if (typenameisunsigned(nm)) { return false; };
|
||||||
|
if (typenameissigned(nm)) { return true; };
|
||||||
|
let al: *node = aliaslookup(c, nm);
|
||||||
|
if (al != nil) { return fieldissignedc(c, al); };
|
||||||
|
let en: *enumtype = enumlookup(c, nm);
|
||||||
|
if (en != nil) {
|
||||||
|
if (en.storage != nil) {
|
||||||
|
return fieldissignedc(c, en.storage);
|
||||||
|
};
|
||||||
|
return true; // default i32 storage is signed
|
||||||
|
};
|
||||||
|
};
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// fieldloadop — pick the load instruction for a non-str struct
|
// fieldloadop — pick the load instruction for a non-str struct
|
||||||
// field by its declared size + signedness. Mirrors the C cgen op
|
// field by its declared size + signedness. Mirrors cstage's
|
||||||
// dispatch (MOVZBQ for u8/bool/i8, MOVSXD for i32, MOVL for u32, MOVQ
|
// fldloadop: MOVZBQ/MOVSBQ for 1B, MOVZWQ/MOVSWQ for 2B,
|
||||||
// for 8-byte). f might be nil for fields outside our struct registry.
|
// MOVL/MOVSXD for 4B, MOVQ for 8B. f might be nil for fields
|
||||||
fn fieldloadop(f: *fieldinfo) str = {
|
// outside our struct registry.
|
||||||
|
fn fieldloadop(c: *cgen, f: *fieldinfo) str = {
|
||||||
if (f == nil) { return "MOVQ"; };
|
if (f == nil) { return "MOVQ"; };
|
||||||
let sz: i32 = f.fsz;
|
let sz: i32 = f.fsz;
|
||||||
if (sz == 1) { return "MOVZBQ"; };
|
let sigd: bool = fieldissignedc(c, f.tnode);
|
||||||
if (sz == 4) {
|
if (sz == 1) { if (sigd) { return "MOVSBQ"; }; return "MOVZBQ"; };
|
||||||
let t: *node = f.tnode;
|
if (sz == 2) { if (sigd) { return "MOVSWQ"; }; return "MOVZWQ"; };
|
||||||
if (t != nil) {
|
if (sz == 4) { if (sigd) { return "MOVSXD"; }; return "MOVL"; };
|
||||||
if (t.kind == nkind.N_TNAME) {
|
|
||||||
if (typenameissigned(t.str)) { return "MOVSXD"; };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
return "MOVL";
|
|
||||||
};
|
|
||||||
return "MOVQ";
|
return "MOVQ";
|
||||||
};
|
};
|
||||||
|
|
||||||
// fieldstoreop — pick the store instruction for a non-str struct
|
// fieldstoreop — pick the store instruction for a non-str struct
|
||||||
// field by its declared size. MOVB for 1, MOVL for 4, MOVQ for 8.
|
// field by its declared size. MOVB for 1, MOVW for 2, MOVL for 4,
|
||||||
fn fieldstoreop(f: *fieldinfo) str = {
|
// MOVQ for 8. c kept in the signature for symmetry with fieldloadop.
|
||||||
|
fn fieldstoreop(c: *cgen, f: *fieldinfo) str = {
|
||||||
if (f == nil) { return "MOVQ"; };
|
if (f == nil) { return "MOVQ"; };
|
||||||
let sz: i32 = f.fsz;
|
let sz: i32 = f.fsz;
|
||||||
if (sz == 1) { return "MOVB"; };
|
if (sz == 1) { return "MOVB"; };
|
||||||
|
if (sz == 2) { return "MOVW"; };
|
||||||
if (sz == 4) { return "MOVL"; };
|
if (sz == 4) { return "MOVL"; };
|
||||||
return "MOVQ";
|
return "MOVQ";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// tnodeloadop / tnodestoreop — same dispatch as fieldloadop /
|
||||||
|
// fieldstoreop but keyed on a raw type-AST node (tuple element type,
|
||||||
|
// pointer-target, slice-element, etc.) rather than a struct fieldinfo.
|
||||||
|
// Used at the index / tuple / pointer-deref sites where there's no
|
||||||
|
// fieldinfo entry but the type-node + size are both known.
|
||||||
|
fn tnodeloadop(c: *cgen, t: *node, sz: i32) str = {
|
||||||
|
let sigd: bool = fieldissignedc(c, t);
|
||||||
|
if (sz == 1) { if (sigd) { return "MOVSBQ"; }; return "MOVZBQ"; };
|
||||||
|
if (sz == 2) { if (sigd) { return "MOVSWQ"; }; return "MOVZWQ"; };
|
||||||
|
if (sz == 4) { if (sigd) { return "MOVSXD"; }; return "MOVL"; };
|
||||||
|
return "MOVQ";
|
||||||
|
};
|
||||||
|
|
||||||
|
fn tnodestoreop(c: *cgen, t: *node, sz: i32) str = {
|
||||||
|
if (sz == 1) { return "MOVB"; };
|
||||||
|
if (sz == 2) { return "MOVW"; };
|
||||||
|
if (sz == 4) { return "MOVL"; };
|
||||||
|
return "MOVQ";
|
||||||
|
};
|
||||||
|
|
||||||
|
// loadopsz — load op when the (size, signedness) pair has already
|
||||||
|
// been resolved upstream and the type-node isn't carried through.
|
||||||
|
// cgindex precomputes `signed_elem` via elemissignedc; cgforrange
|
||||||
|
// precomputes `bind_signed[b]` via paramissigned. Same dispatch as
|
||||||
|
// tnodeloadop's tail; only the keying differs.
|
||||||
|
fn loadopsz(sigd: bool, sz: i32) str = {
|
||||||
|
if (sz == 1) { if (sigd) { return "MOVSBQ"; }; return "MOVZBQ"; };
|
||||||
|
if (sz == 2) { if (sigd) { return "MOVSWQ"; }; return "MOVZWQ"; };
|
||||||
|
if (sz == 4) { if (sigd) { return "MOVSXD"; }; return "MOVL"; };
|
||||||
|
return "MOVQ";
|
||||||
|
};
|
||||||
|
|
||||||
// indexbaseesz — element size for `arr[i]` where the base is a
|
// indexbaseesz — element size for `arr[i]` where the base is a
|
||||||
// chained-dot pseudo-field `s.ptr` (s being str/*str/slice/*slice).
|
// chained-dot pseudo-field `s.ptr` (s being str/*str/slice/*slice).
|
||||||
// For str the element is one byte; for `[]T` / `*[]T` we drill into
|
// For str the element is one byte; for `[]T` / `*[]T` we drill into
|
||||||
@@ -7667,7 +7767,7 @@ fn cgwidentaggedstore(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz:
|
|||||||
emitoff((slot_off + 8 + fi.foff + 8): i64);
|
emitoff((slot_off + 8 + fi.foff + 8): i64);
|
||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
} else {
|
} else {
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
@@ -8353,7 +8453,11 @@ fn cgcast(c: *cgen, n: *node) void = {
|
|||||||
let sz: i32 = primsize(nm);
|
let sz: i32 = primsize(nm);
|
||||||
let is_unsigned: bool = typenameisunsigned(nm);
|
let is_unsigned: bool = typenameisunsigned(nm);
|
||||||
let is_bool: bool = streq(nm, "bool");
|
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 (is_unsigned) {
|
||||||
if (sz == 4) {
|
if (sz == 4) {
|
||||||
emitline("\tMOVL\tAX, AX\n");
|
emitline("\tMOVL\tAX, AX\n");
|
||||||
@@ -8364,16 +8468,17 @@ fn cgcast(c: *cgen, n: *node) void = {
|
|||||||
emitint(mask);
|
emitint(mask);
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
};
|
};
|
||||||
} else { if (is_bool) {
|
} else {
|
||||||
emitline("\tANDQ\t$255, AX\n");
|
if (sz == 1) {
|
||||||
} else { if (streq(nm, "i8")) {
|
emitline("\tMOVSBQ\tAX, AX\n");
|
||||||
emitline("\tMOVSBQ\tAX, AX\n");
|
} else { if (sz == 2) {
|
||||||
} else { if (streq(nm, "i16")) {
|
emitline("\tMOVSWQ\tAX, AX\n");
|
||||||
emitline("\tMOVSWQ\tAX, AX\n");
|
} else { if (sz == 4) {
|
||||||
} else { if (streq(nm, "i32")) {
|
emitline("\tMOVSXD\tAX, AX\n");
|
||||||
emitline("\tMOVSXD\tAX, AX\n");
|
}; }; };
|
||||||
}; }; }; }; };
|
};
|
||||||
}; };
|
}; }; };
|
||||||
|
if (is_bool) { emitline("\tANDQ\t$255, AX\n"); };
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -8550,7 +8655,7 @@ fn cgindex(c: *cgen, n: *node) void = {
|
|||||||
baselocal = localfindnode(c, bn);
|
baselocal = localfindnode(c, bn);
|
||||||
if (baselocal != nil) {
|
if (baselocal != nil) {
|
||||||
esz = elemsizeofc(c, baselocal.tnode);
|
esz = elemsizeofc(c, baselocal.tnode);
|
||||||
signed_elem = elemissigned(baselocal.tnode);
|
signed_elem = elemissignedc(c, baselocal.tnode);
|
||||||
} else {
|
} else {
|
||||||
let tn: *node = letvartnode(c, bn);
|
let tn: *node = letvartnode(c, bn);
|
||||||
if (tn != nil) {
|
if (tn != nil) {
|
||||||
@@ -8558,13 +8663,13 @@ fn cgindex(c: *cgen, n: *node) void = {
|
|||||||
isglobalarr = true;
|
isglobalarr = true;
|
||||||
globalname = bn;
|
globalname = bn;
|
||||||
esz = elemsizeofc(c, tn);
|
esz = elemsizeofc(c, tn);
|
||||||
signed_elem = elemissigned(tn);
|
signed_elem = elemissignedc(c, tn);
|
||||||
};
|
};
|
||||||
if (tn.kind == nkind.N_TPTR) {
|
if (tn.kind == nkind.N_TPTR) {
|
||||||
isglobalptr = true;
|
isglobalptr = true;
|
||||||
globalname = bn;
|
globalname = bn;
|
||||||
esz = elemsizeofc(c, tn);
|
esz = elemsizeofc(c, tn);
|
||||||
signed_elem = elemissigned(tn);
|
signed_elem = elemissignedc(c, tn);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -8646,16 +8751,10 @@ fn cgindex(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tMOVQ\tCX, BX\n");
|
emitline("\tMOVQ\tCX, BX\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (esz == 1) { emitline("\tMOVZBQ\t(BX), AX\n"); }
|
let lop1: str = loadopsz(signed_elem, esz);
|
||||||
else { if (esz == 2) {
|
emitline("\t");
|
||||||
if (signed_elem) { emitline("\tMOVSWQ\t(BX), AX\n"); }
|
emitline(lop1);
|
||||||
else { emitline("\tMOVZWQ\t(BX), AX\n"); };
|
emitline("\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"); };};};
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (baselocal != nil) {
|
if (baselocal != nil) {
|
||||||
@@ -8693,16 +8792,10 @@ fn cgindex(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tMOVQ\tCX, BX\n");
|
emitline("\tMOVQ\tCX, BX\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (esz == 1) { emitline("\tMOVZBQ\t(BX), AX\n"); }
|
let lop2: str = loadopsz(signed_elem, esz);
|
||||||
else { if (esz == 2) {
|
emitline("\t");
|
||||||
if (signed_elem) { emitline("\tMOVSWQ\t(BX), AX\n"); }
|
emitline(lop2);
|
||||||
else { emitline("\tMOVZWQ\t(BX), AX\n"); };
|
emitline("\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"); };};};
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
// Generic fallback when base isn't a plain ident.
|
// Generic fallback when base isn't a plain ident.
|
||||||
@@ -8728,16 +8821,10 @@ fn cgindex(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tMOVQ\t(AX), AX\n");
|
emitline("\tMOVQ\t(AX), AX\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (esz == 1) { emitline("\tMOVZBQ\t(AX), AX\n"); }
|
let lop3: str = loadopsz(signed_elem, esz);
|
||||||
else { if (esz == 2) {
|
emitline("\t");
|
||||||
if (signed_elem) { emitline("\tMOVSWQ\t(AX), AX\n"); }
|
emitline(lop3);
|
||||||
else { emitline("\tMOVZWQ\t(AX), AX\n"); };
|
emitline("\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"); };};};
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -9157,7 +9244,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
emitdispreg(fi.foff: i64, "BX");
|
emitdispreg(fi.foff: i64, "BX");
|
||||||
emitline(", X0\n");
|
emitline(", X0\n");
|
||||||
} else {
|
} else {
|
||||||
let op: str = fieldloadop(fi);
|
let op: str = fieldloadop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(op);
|
emitline(op);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -9199,7 +9286,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
emitoff((lc.off + fi.foff): i64);
|
emitoff((lc.off + fi.foff): i64);
|
||||||
emitline("(BP), X0\n");
|
emitline("(BP), X0\n");
|
||||||
} else {
|
} else {
|
||||||
let op: str = fieldloadop(fi);
|
let op: str = fieldloadop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(op);
|
emitline(op);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -9265,9 +9352,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let sz: i32 = slotsize(c, tp);
|
let sz: i32 = slotsize(c, tp);
|
||||||
let op: str = "MOVQ";
|
let op: str = tnodeloadop(c, tp, sz);
|
||||||
if (sz == 1) { op = "MOVZBQ"; }
|
|
||||||
else { if (sz == 4) { op = "MOVL"; }; };
|
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(op);
|
emitline(op);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -9402,7 +9487,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
emitdispreg(fi.foff: i64, "CX");
|
emitdispreg(fi.foff: i64, "CX");
|
||||||
emitline(", X0\n");
|
emitline(", X0\n");
|
||||||
} else {
|
} else {
|
||||||
let op: str = fieldloadop(fi);
|
let op: str = fieldloadop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(op);
|
emitline(op);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -9464,7 +9549,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
emitline(", X0\n");
|
emitline(", X0\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let lop: str = fieldloadop(fi);
|
let lop: str = fieldloadop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(lop);
|
emitline(lop);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -9565,7 +9650,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let lop: str = fieldloadop(r.leaffi);
|
let lop: str = fieldloadop(c, r.leaffi);
|
||||||
if (r.isglobal) {
|
if (r.isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, r.rootname);
|
||||||
@@ -9637,7 +9722,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
emitline(", X0\n");
|
emitline(", X0\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let lop: str = fieldloadop(fi);
|
let lop: str = fieldloadop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(lop);
|
emitline(lop);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -9735,7 +9820,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let lop: str = fieldloadop(ffi);
|
let lop: str = fieldloadop(c, ffi);
|
||||||
if (isptr) {
|
if (isptr) {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff(lc.off: i64);
|
emitoff(lc.off: i64);
|
||||||
@@ -10147,7 +10232,7 @@ fn cgalloc(c: *cgen, n: *node) void = {
|
|||||||
fi = nil;
|
fi = nil;
|
||||||
} else {
|
} else {
|
||||||
emitline("\tMOVQ\t(SP), BX\n");
|
emitline("\tMOVQ\t(SP), BX\n");
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
@@ -10201,8 +10286,14 @@ fn cgappend(c: *cgen, n: *node) void = {
|
|||||||
if (snlocal == nil) { return; };
|
if (snlocal == nil) { return; };
|
||||||
let sn_off: i32 = snlocal.off;
|
let sn_off: i32 = snlocal.off;
|
||||||
let esz: i32 = elemsizeof(snlocal.tnode);
|
let esz: i32 = elemsizeof(snlocal.tnode);
|
||||||
let store_op: str = "MOVQ";
|
let etnode: *node = nil;
|
||||||
if (esz == 1) { store_op = "MOVB"; };
|
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;
|
let vn: *node = sn.next;
|
||||||
for (vn != nil) {
|
for (vn != nil) {
|
||||||
@@ -10213,8 +10304,7 @@ fn cgappend(c: *cgen, n: *node) void = {
|
|||||||
let itlocal: *local = localfindnode(c, it.str);
|
let itlocal: *local = localfindnode(c, it.str);
|
||||||
if (itlocal == nil) { vn = vn.next; continue; };
|
if (itlocal == nil) { vn = vn.next; continue; };
|
||||||
let it_off: i32 = itlocal.off;
|
let it_off: i32 = itlocal.off;
|
||||||
let load_op: str = "MOVQ";
|
let load_op: str = tnodeloadop(c, etnode, esz);
|
||||||
if (esz == 1) { load_op = "MOVZBQ"; };
|
|
||||||
emitline("\tSUBQ\t$8, SP\n");
|
emitline("\tSUBQ\t$8, SP\n");
|
||||||
emitline("\tMOVQ\t$0, (SP)\n");
|
emitline("\tMOVQ\t$0, (SP)\n");
|
||||||
let ll: str = mklabel(c, "spr_l");
|
let ll: str = mklabel(c, "spr_l");
|
||||||
@@ -10444,9 +10534,7 @@ fn cgcall(c: *cgen, n: *node) void = {
|
|||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
} else {
|
} else {
|
||||||
cgexpr(c, aa2);
|
cgexpr(c, aa2);
|
||||||
let op: str = "MOVQ";
|
let op: str = tnodestoreop(c, varp.lhs, esz);
|
||||||
if (esz == 1) { op = "MOVB"; }
|
|
||||||
else { if (esz == 4) { op = "MOVL"; }; };
|
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(op);
|
emitline(op);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
@@ -10795,19 +10883,10 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
if (tn.kind == nkind.N_TPTR) {
|
if (tn.kind == nkind.N_TPTR) {
|
||||||
let pe: *node = tn.lhs;
|
let pe: *node = tn.lhs;
|
||||||
if (pe != nil) {
|
if (pe != nil) {
|
||||||
if (pe.kind == nkind.N_TNAME) {
|
let ps: i32 = fieldsize(c, pe);
|
||||||
let ps: i32 = primsize(pe.str);
|
if (ps == 1 || ps == 2 || ps == 4) {
|
||||||
if (ps == 1) {
|
loadop = tnodeloadop(c, pe, ps);
|
||||||
loadop = "MOVZBQ";
|
storeop = tnodestoreop(c, pe, ps);
|
||||||
storeop = "MOVB";
|
|
||||||
} else { if (ps == 4) {
|
|
||||||
if (typenameissigned(pe.str)) {
|
|
||||||
loadop = "MOVSXD";
|
|
||||||
} else {
|
|
||||||
loadop = "MOVL";
|
|
||||||
};
|
|
||||||
storeop = "MOVL";
|
|
||||||
}; };
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -11011,10 +11090,10 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tMOVQ\tCX, 8(BX)\n");
|
emitline("\tMOVQ\tCX, 8(BX)\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (esz == 1) { emitline("\tMOVB\tAX, (BX)\n"); }
|
let isop: str = tnodestoreop(c, elemtn, esz);
|
||||||
else { if (esz == 2) { emitline("\tMOVW\tAX, (BX)\n"); }
|
emitline("\t");
|
||||||
else { if (esz == 4) { emitline("\tMOVL\tAX, (BX)\n"); }
|
emitline(isop);
|
||||||
else { emitline("\tMOVQ\tAX, (BX)\n"); };};};
|
emitline("\tAX, (BX)\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -11054,7 +11133,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff(lc.off: i64);
|
emitoff(lc.off: i64);
|
||||||
emitline("(BP), BX\n");
|
emitline("(BP), BX\n");
|
||||||
let lop: str = fieldloadop(fi);
|
let lop: str = fieldloadop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(lop);
|
emitline(lop);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -11110,7 +11189,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff(lc.off: i64);
|
emitoff(lc.off: i64);
|
||||||
emitline("(BP), BX\n");
|
emitline("(BP), BX\n");
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
@@ -11158,7 +11237,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
@@ -11283,7 +11362,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("\n");
|
emitline("\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, bn);
|
emitsymname(c, bn);
|
||||||
emitline("(SB), BX\n");
|
emitline("(SB), BX\n");
|
||||||
@@ -11298,7 +11377,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
// → push → eval rhs → combine →
|
// → push → eval rhs → combine →
|
||||||
// store. cgexpr clobbers BX, so
|
// store. cgexpr clobbers BX, so
|
||||||
// re-LEAQ for the store.
|
// re-LEAQ for the store.
|
||||||
let lop: str = fieldloadop(fi);
|
let lop: str = fieldloadop(c, fi);
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, bn);
|
emitsymname(c, bn);
|
||||||
emitline("(SB), BX\n");
|
emitline("(SB), BX\n");
|
||||||
@@ -11315,7 +11394,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tSUBQ\tAX, BX\n");
|
emitline("\tSUBQ\tAX, BX\n");
|
||||||
emitline("\tMOVQ\tBX, AX\n");
|
emitline("\tMOVQ\tBX, AX\n");
|
||||||
};
|
};
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, bn);
|
emitsymname(c, bn);
|
||||||
emitline("(SB), BX\n");
|
emitline("(SB), BX\n");
|
||||||
@@ -11406,7 +11485,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
cgexpr(c, base);
|
cgexpr(c, base);
|
||||||
emitline("\tMOVQ\tAX, BX\n");
|
emitline("\tMOVQ\tAX, BX\n");
|
||||||
emitline("\tPOPQ\tAX\n");
|
emitline("\tPOPQ\tAX\n");
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
@@ -11496,7 +11575,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let sop: str = fieldstoreop(r.leaffi);
|
let sop: str = fieldstoreop(c, r.leaffi);
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (r.isglobal) {
|
if (r.isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
@@ -11609,7 +11688,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let sop: str = fieldstoreop(ffi);
|
let sop: str = fieldstoreop(c, ffi);
|
||||||
if (isptr) {
|
if (isptr) {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff(lc.off: i64);
|
emitoff(lc.off: i64);
|
||||||
@@ -12288,9 +12367,7 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
if (ps > 0) { esz = ps; };
|
if (ps > 0) { esz = ps; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
let mop: str = "MOVQ";
|
let mop: str = tnodestoreop(c, elemn, esz);
|
||||||
if (esz == 1) { mop = "MOVB"; }
|
|
||||||
else { if (esz == 4) { mop = "MOVL"; }; };
|
|
||||||
let idx: i32 = 0;
|
let idx: i32 = 0;
|
||||||
let repeat: bool = false;
|
let repeat: bool = false;
|
||||||
let e: *node = rhs.list;
|
let e: *node = rhs.list;
|
||||||
@@ -12406,7 +12483,7 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
fi = nil;
|
fi = nil;
|
||||||
} else {
|
} else {
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
@@ -12721,16 +12798,11 @@ fn paramfieldsize(t: *node) i32 = {
|
|||||||
return 8;
|
return 8;
|
||||||
};
|
};
|
||||||
|
|
||||||
// paramissigned — does this primitive type need sign-extending on a
|
// paramissigned — does this type need sign-extending on a sub-word
|
||||||
// narrow (4B) load? Mirrors C cgen's `binds[b].signed_field` flag.
|
// (1/2/4B) load? Mirrors cstage's signed_field check via
|
||||||
fn paramissigned(t: *node) bool = {
|
// fieldissignedc (resolves TBANG / TENUM / alias chains).
|
||||||
if (t == nil) { return false; };
|
fn paramissigned(c: *cgen, t: *node) bool = {
|
||||||
if (t.kind != nkind.N_TNAME) { return false; };
|
return fieldissignedc(c, t);
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// cgforrange — lower `for (let x .. slice) body` (and the tuple-
|
// cgforrange — lower `for (let x .. slice) body` (and the tuple-
|
||||||
@@ -12804,7 +12876,7 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
|||||||
let signf: bool = false;
|
let signf: bool = false;
|
||||||
if (tp != nil) {
|
if (tp != nil) {
|
||||||
fsz = paramfieldsize(tp);
|
fsz = paramfieldsize(tp);
|
||||||
signf = paramissigned(tp);
|
signf = paramissigned(c, tp);
|
||||||
};
|
};
|
||||||
let slot_sz: i32 = fsz;
|
let slot_sz: i32 = fsz;
|
||||||
if (slot_sz < 8) { slot_sz = 8; };
|
if (slot_sz < 8) { slot_sz = 8; };
|
||||||
@@ -12832,7 +12904,7 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
|||||||
// reads `u->sub->kind` for the elem type.
|
// reads `u->sub->kind` for the elem type.
|
||||||
bind_signed[0] = false;
|
bind_signed[0] = false;
|
||||||
if (elemt != nil) {
|
if (elemt != nil) {
|
||||||
bind_signed[0] = paramissigned(elemt);
|
bind_signed[0] = paramissigned(c, elemt);
|
||||||
};
|
};
|
||||||
if (n.str.len > 0) {
|
if (n.str.len > 0) {
|
||||||
// Register with elem tnode so x.field on a loop
|
// Register with elem tnode so x.field on a loop
|
||||||
@@ -12930,15 +13002,12 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
emitline("\tADDQ\tAX, BX\n");
|
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;
|
let b: i32 = 0;
|
||||||
for (b < nbinds) {
|
for (b < nbinds) {
|
||||||
let op: str = "MOVQ";
|
let op: str = loadopsz(bind_signed[b], bind_sz[b]);
|
||||||
if (bind_sz[b] == 1) { op = "MOVZBQ"; }
|
|
||||||
else { if (bind_sz[b] == 4) {
|
|
||||||
if (bind_signed[b]) { op = "MOVSXD"; }
|
|
||||||
else { op = "MOVL"; };
|
|
||||||
};};
|
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(op);
|
emitline(op);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
|
|||||||
@@ -403,7 +403,11 @@ fn cgcast(c: *cgen, n: *node) void = {
|
|||||||
let sz: i32 = primsize(nm);
|
let sz: i32 = primsize(nm);
|
||||||
let is_unsigned: bool = typenameisunsigned(nm);
|
let is_unsigned: bool = typenameisunsigned(nm);
|
||||||
let is_bool: bool = streq(nm, "bool");
|
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 (is_unsigned) {
|
||||||
if (sz == 4) {
|
if (sz == 4) {
|
||||||
emitline("\tMOVL\tAX, AX\n");
|
emitline("\tMOVL\tAX, AX\n");
|
||||||
@@ -414,16 +418,17 @@ fn cgcast(c: *cgen, n: *node) void = {
|
|||||||
emitint(mask);
|
emitint(mask);
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
};
|
};
|
||||||
} else { if (is_bool) {
|
} else {
|
||||||
emitline("\tANDQ\t$255, AX\n");
|
if (sz == 1) {
|
||||||
} else { if (streq(nm, "i8")) {
|
emitline("\tMOVSBQ\tAX, AX\n");
|
||||||
emitline("\tMOVSBQ\tAX, AX\n");
|
} else { if (sz == 2) {
|
||||||
} else { if (streq(nm, "i16")) {
|
emitline("\tMOVSWQ\tAX, AX\n");
|
||||||
emitline("\tMOVSWQ\tAX, AX\n");
|
} else { if (sz == 4) {
|
||||||
} else { if (streq(nm, "i32")) {
|
emitline("\tMOVSXD\tAX, AX\n");
|
||||||
emitline("\tMOVSXD\tAX, AX\n");
|
}; }; };
|
||||||
}; }; }; }; };
|
};
|
||||||
}; };
|
}; }; };
|
||||||
|
if (is_bool) { emitline("\tANDQ\t$255, AX\n"); };
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -600,7 +605,7 @@ fn cgindex(c: *cgen, n: *node) void = {
|
|||||||
baselocal = localfindnode(c, bn);
|
baselocal = localfindnode(c, bn);
|
||||||
if (baselocal != nil) {
|
if (baselocal != nil) {
|
||||||
esz = elemsizeofc(c, baselocal.tnode);
|
esz = elemsizeofc(c, baselocal.tnode);
|
||||||
signed_elem = elemissigned(baselocal.tnode);
|
signed_elem = elemissignedc(c, baselocal.tnode);
|
||||||
} else {
|
} else {
|
||||||
let tn: *node = letvartnode(c, bn);
|
let tn: *node = letvartnode(c, bn);
|
||||||
if (tn != nil) {
|
if (tn != nil) {
|
||||||
@@ -608,13 +613,13 @@ fn cgindex(c: *cgen, n: *node) void = {
|
|||||||
isglobalarr = true;
|
isglobalarr = true;
|
||||||
globalname = bn;
|
globalname = bn;
|
||||||
esz = elemsizeofc(c, tn);
|
esz = elemsizeofc(c, tn);
|
||||||
signed_elem = elemissigned(tn);
|
signed_elem = elemissignedc(c, tn);
|
||||||
};
|
};
|
||||||
if (tn.kind == nkind.N_TPTR) {
|
if (tn.kind == nkind.N_TPTR) {
|
||||||
isglobalptr = true;
|
isglobalptr = true;
|
||||||
globalname = bn;
|
globalname = bn;
|
||||||
esz = elemsizeofc(c, tn);
|
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");
|
emitline("\tMOVQ\tCX, BX\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (esz == 1) { emitline("\tMOVZBQ\t(BX), AX\n"); }
|
let lop1: str = loadopsz(signed_elem, esz);
|
||||||
else { if (esz == 2) {
|
emitline("\t");
|
||||||
if (signed_elem) { emitline("\tMOVSWQ\t(BX), AX\n"); }
|
emitline(lop1);
|
||||||
else { emitline("\tMOVZWQ\t(BX), AX\n"); };
|
emitline("\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"); };};};
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (baselocal != nil) {
|
if (baselocal != nil) {
|
||||||
@@ -743,16 +742,10 @@ fn cgindex(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tMOVQ\tCX, BX\n");
|
emitline("\tMOVQ\tCX, BX\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (esz == 1) { emitline("\tMOVZBQ\t(BX), AX\n"); }
|
let lop2: str = loadopsz(signed_elem, esz);
|
||||||
else { if (esz == 2) {
|
emitline("\t");
|
||||||
if (signed_elem) { emitline("\tMOVSWQ\t(BX), AX\n"); }
|
emitline(lop2);
|
||||||
else { emitline("\tMOVZWQ\t(BX), AX\n"); };
|
emitline("\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"); };};};
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
// Generic fallback when base isn't a plain ident.
|
// 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");
|
emitline("\tMOVQ\t(AX), AX\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (esz == 1) { emitline("\tMOVZBQ\t(AX), AX\n"); }
|
let lop3: str = loadopsz(signed_elem, esz);
|
||||||
else { if (esz == 2) {
|
emitline("\t");
|
||||||
if (signed_elem) { emitline("\tMOVSWQ\t(AX), AX\n"); }
|
emitline(lop3);
|
||||||
else { emitline("\tMOVZWQ\t(AX), AX\n"); };
|
emitline("\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"); };};};
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1207,7 +1194,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
emitdispreg(fi.foff: i64, "BX");
|
emitdispreg(fi.foff: i64, "BX");
|
||||||
emitline(", X0\n");
|
emitline(", X0\n");
|
||||||
} else {
|
} else {
|
||||||
let op: str = fieldloadop(fi);
|
let op: str = fieldloadop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(op);
|
emitline(op);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -1249,7 +1236,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
emitoff((lc.off + fi.foff): i64);
|
emitoff((lc.off + fi.foff): i64);
|
||||||
emitline("(BP), X0\n");
|
emitline("(BP), X0\n");
|
||||||
} else {
|
} else {
|
||||||
let op: str = fieldloadop(fi);
|
let op: str = fieldloadop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(op);
|
emitline(op);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -1315,9 +1302,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let sz: i32 = slotsize(c, tp);
|
let sz: i32 = slotsize(c, tp);
|
||||||
let op: str = "MOVQ";
|
let op: str = tnodeloadop(c, tp, sz);
|
||||||
if (sz == 1) { op = "MOVZBQ"; }
|
|
||||||
else { if (sz == 4) { op = "MOVL"; }; };
|
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(op);
|
emitline(op);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -1452,7 +1437,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
emitdispreg(fi.foff: i64, "CX");
|
emitdispreg(fi.foff: i64, "CX");
|
||||||
emitline(", X0\n");
|
emitline(", X0\n");
|
||||||
} else {
|
} else {
|
||||||
let op: str = fieldloadop(fi);
|
let op: str = fieldloadop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(op);
|
emitline(op);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -1514,7 +1499,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
emitline(", X0\n");
|
emitline(", X0\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let lop: str = fieldloadop(fi);
|
let lop: str = fieldloadop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(lop);
|
emitline(lop);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -1615,7 +1600,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let lop: str = fieldloadop(r.leaffi);
|
let lop: str = fieldloadop(c, r.leaffi);
|
||||||
if (r.isglobal) {
|
if (r.isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, r.rootname);
|
||||||
@@ -1687,7 +1672,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
emitline(", X0\n");
|
emitline(", X0\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let lop: str = fieldloadop(fi);
|
let lop: str = fieldloadop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(lop);
|
emitline(lop);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -1785,7 +1770,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let lop: str = fieldloadop(ffi);
|
let lop: str = fieldloadop(c, ffi);
|
||||||
if (isptr) {
|
if (isptr) {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff(lc.off: i64);
|
emitoff(lc.off: i64);
|
||||||
@@ -2197,7 +2182,7 @@ fn cgalloc(c: *cgen, n: *node) void = {
|
|||||||
fi = nil;
|
fi = nil;
|
||||||
} else {
|
} else {
|
||||||
emitline("\tMOVQ\t(SP), BX\n");
|
emitline("\tMOVQ\t(SP), BX\n");
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
@@ -2251,8 +2236,14 @@ fn cgappend(c: *cgen, n: *node) void = {
|
|||||||
if (snlocal == nil) { return; };
|
if (snlocal == nil) { return; };
|
||||||
let sn_off: i32 = snlocal.off;
|
let sn_off: i32 = snlocal.off;
|
||||||
let esz: i32 = elemsizeof(snlocal.tnode);
|
let esz: i32 = elemsizeof(snlocal.tnode);
|
||||||
let store_op: str = "MOVQ";
|
let etnode: *node = nil;
|
||||||
if (esz == 1) { store_op = "MOVB"; };
|
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;
|
let vn: *node = sn.next;
|
||||||
for (vn != nil) {
|
for (vn != nil) {
|
||||||
@@ -2263,8 +2254,7 @@ fn cgappend(c: *cgen, n: *node) void = {
|
|||||||
let itlocal: *local = localfindnode(c, it.str);
|
let itlocal: *local = localfindnode(c, it.str);
|
||||||
if (itlocal == nil) { vn = vn.next; continue; };
|
if (itlocal == nil) { vn = vn.next; continue; };
|
||||||
let it_off: i32 = itlocal.off;
|
let it_off: i32 = itlocal.off;
|
||||||
let load_op: str = "MOVQ";
|
let load_op: str = tnodeloadop(c, etnode, esz);
|
||||||
if (esz == 1) { load_op = "MOVZBQ"; };
|
|
||||||
emitline("\tSUBQ\t$8, SP\n");
|
emitline("\tSUBQ\t$8, SP\n");
|
||||||
emitline("\tMOVQ\t$0, (SP)\n");
|
emitline("\tMOVQ\t$0, (SP)\n");
|
||||||
let ll: str = mklabel(c, "spr_l");
|
let ll: str = mklabel(c, "spr_l");
|
||||||
@@ -2494,9 +2484,7 @@ fn cgcall(c: *cgen, n: *node) void = {
|
|||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
} else {
|
} else {
|
||||||
cgexpr(c, aa2);
|
cgexpr(c, aa2);
|
||||||
let op: str = "MOVQ";
|
let op: str = tnodestoreop(c, varp.lhs, esz);
|
||||||
if (esz == 1) { op = "MOVB"; }
|
|
||||||
else { if (esz == 4) { op = "MOVL"; }; };
|
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(op);
|
emitline(op);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
@@ -2845,19 +2833,10 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
if (tn.kind == nkind.N_TPTR) {
|
if (tn.kind == nkind.N_TPTR) {
|
||||||
let pe: *node = tn.lhs;
|
let pe: *node = tn.lhs;
|
||||||
if (pe != nil) {
|
if (pe != nil) {
|
||||||
if (pe.kind == nkind.N_TNAME) {
|
let ps: i32 = fieldsize(c, pe);
|
||||||
let ps: i32 = primsize(pe.str);
|
if (ps == 1 || ps == 2 || ps == 4) {
|
||||||
if (ps == 1) {
|
loadop = tnodeloadop(c, pe, ps);
|
||||||
loadop = "MOVZBQ";
|
storeop = tnodestoreop(c, pe, ps);
|
||||||
storeop = "MOVB";
|
|
||||||
} else { if (ps == 4) {
|
|
||||||
if (typenameissigned(pe.str)) {
|
|
||||||
loadop = "MOVSXD";
|
|
||||||
} else {
|
|
||||||
loadop = "MOVL";
|
|
||||||
};
|
|
||||||
storeop = "MOVL";
|
|
||||||
}; };
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -3061,10 +3040,10 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tMOVQ\tCX, 8(BX)\n");
|
emitline("\tMOVQ\tCX, 8(BX)\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (esz == 1) { emitline("\tMOVB\tAX, (BX)\n"); }
|
let isop: str = tnodestoreop(c, elemtn, esz);
|
||||||
else { if (esz == 2) { emitline("\tMOVW\tAX, (BX)\n"); }
|
emitline("\t");
|
||||||
else { if (esz == 4) { emitline("\tMOVL\tAX, (BX)\n"); }
|
emitline(isop);
|
||||||
else { emitline("\tMOVQ\tAX, (BX)\n"); };};};
|
emitline("\tAX, (BX)\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -3104,7 +3083,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff(lc.off: i64);
|
emitoff(lc.off: i64);
|
||||||
emitline("(BP), BX\n");
|
emitline("(BP), BX\n");
|
||||||
let lop: str = fieldloadop(fi);
|
let lop: str = fieldloadop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(lop);
|
emitline(lop);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -3160,7 +3139,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff(lc.off: i64);
|
emitoff(lc.off: i64);
|
||||||
emitline("(BP), BX\n");
|
emitline("(BP), BX\n");
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
@@ -3208,7 +3187,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
@@ -3333,7 +3312,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("\n");
|
emitline("\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, bn);
|
emitsymname(c, bn);
|
||||||
emitline("(SB), BX\n");
|
emitline("(SB), BX\n");
|
||||||
@@ -3348,7 +3327,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
// → push → eval rhs → combine →
|
// → push → eval rhs → combine →
|
||||||
// store. cgexpr clobbers BX, so
|
// store. cgexpr clobbers BX, so
|
||||||
// re-LEAQ for the store.
|
// re-LEAQ for the store.
|
||||||
let lop: str = fieldloadop(fi);
|
let lop: str = fieldloadop(c, fi);
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, bn);
|
emitsymname(c, bn);
|
||||||
emitline("(SB), BX\n");
|
emitline("(SB), BX\n");
|
||||||
@@ -3365,7 +3344,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tSUBQ\tAX, BX\n");
|
emitline("\tSUBQ\tAX, BX\n");
|
||||||
emitline("\tMOVQ\tBX, AX\n");
|
emitline("\tMOVQ\tBX, AX\n");
|
||||||
};
|
};
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, bn);
|
emitsymname(c, bn);
|
||||||
emitline("(SB), BX\n");
|
emitline("(SB), BX\n");
|
||||||
@@ -3456,7 +3435,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
cgexpr(c, base);
|
cgexpr(c, base);
|
||||||
emitline("\tMOVQ\tAX, BX\n");
|
emitline("\tMOVQ\tAX, BX\n");
|
||||||
emitline("\tPOPQ\tAX\n");
|
emitline("\tPOPQ\tAX\n");
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
@@ -3546,7 +3525,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let sop: str = fieldstoreop(r.leaffi);
|
let sop: str = fieldstoreop(c, r.leaffi);
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (r.isglobal) {
|
if (r.isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
@@ -3659,7 +3638,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let sop: str = fieldstoreop(ffi);
|
let sop: str = fieldstoreop(c, ffi);
|
||||||
if (isptr) {
|
if (isptr) {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff(lc.off: i64);
|
emitoff(lc.off: i64);
|
||||||
|
|||||||
@@ -373,9 +373,7 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
if (ps > 0) { esz = ps; };
|
if (ps > 0) { esz = ps; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
let mop: str = "MOVQ";
|
let mop: str = tnodestoreop(c, elemn, esz);
|
||||||
if (esz == 1) { mop = "MOVB"; }
|
|
||||||
else { if (esz == 4) { mop = "MOVL"; }; };
|
|
||||||
let idx: i32 = 0;
|
let idx: i32 = 0;
|
||||||
let repeat: bool = false;
|
let repeat: bool = false;
|
||||||
let e: *node = rhs.list;
|
let e: *node = rhs.list;
|
||||||
@@ -491,7 +489,7 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
fi = nil;
|
fi = nil;
|
||||||
} else {
|
} else {
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
@@ -806,16 +804,11 @@ fn paramfieldsize(t: *node) i32 = {
|
|||||||
return 8;
|
return 8;
|
||||||
};
|
};
|
||||||
|
|
||||||
// paramissigned — does this primitive type need sign-extending on a
|
// paramissigned — does this type need sign-extending on a sub-word
|
||||||
// narrow (4B) load? Mirrors C cgen's `binds[b].signed_field` flag.
|
// (1/2/4B) load? Mirrors cstage's signed_field check via
|
||||||
fn paramissigned(t: *node) bool = {
|
// fieldissignedc (resolves TBANG / TENUM / alias chains).
|
||||||
if (t == nil) { return false; };
|
fn paramissigned(c: *cgen, t: *node) bool = {
|
||||||
if (t.kind != nkind.N_TNAME) { return false; };
|
return fieldissignedc(c, t);
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// cgforrange — lower `for (let x .. slice) body` (and the tuple-
|
// 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;
|
let signf: bool = false;
|
||||||
if (tp != nil) {
|
if (tp != nil) {
|
||||||
fsz = paramfieldsize(tp);
|
fsz = paramfieldsize(tp);
|
||||||
signf = paramissigned(tp);
|
signf = paramissigned(c, tp);
|
||||||
};
|
};
|
||||||
let slot_sz: i32 = fsz;
|
let slot_sz: i32 = fsz;
|
||||||
if (slot_sz < 8) { slot_sz = 8; };
|
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.
|
// reads `u->sub->kind` for the elem type.
|
||||||
bind_signed[0] = false;
|
bind_signed[0] = false;
|
||||||
if (elemt != nil) {
|
if (elemt != nil) {
|
||||||
bind_signed[0] = paramissigned(elemt);
|
bind_signed[0] = paramissigned(c, elemt);
|
||||||
};
|
};
|
||||||
if (n.str.len > 0) {
|
if (n.str.len > 0) {
|
||||||
// Register with elem tnode so x.field on a loop
|
// 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");
|
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;
|
let b: i32 = 0;
|
||||||
for (b < nbinds) {
|
for (b < nbinds) {
|
||||||
let op: str = "MOVQ";
|
let op: str = loadopsz(bind_signed[b], bind_sz[b]);
|
||||||
if (bind_sz[b] == 1) { op = "MOVZBQ"; }
|
|
||||||
else { if (bind_sz[b] == 4) {
|
|
||||||
if (bind_signed[b]) { op = "MOVSXD"; }
|
|
||||||
else { op = "MOVL"; };
|
|
||||||
};};
|
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(op);
|
emitline(op);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
|
|||||||
@@ -502,7 +502,10 @@ fn nodeisstr(c: *cgen, n: *node) bool = {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// typenameisunsigned — true for u8/u16/u32/u64/uint/uintptr.
|
// typenameisunsigned — true for u8/u16/u32/u64/uint/uintptr/rune.
|
||||||
|
// rune is a Unicode codepoint (0..0x10FFFF); cgen treats it as
|
||||||
|
// unsigned so narrow-cast / sub-word load paths zero-extend (MOVL,
|
||||||
|
// not MOVSXD). Mirrors cstage's type_isunsigned post task #5.
|
||||||
fn typenameisunsigned(nm: str) bool = {
|
fn typenameisunsigned(nm: str) bool = {
|
||||||
if (streq(nm, "u8")) { return true; };
|
if (streq(nm, "u8")) { return true; };
|
||||||
if (streq(nm, "u16")) { return true; };
|
if (streq(nm, "u16")) { return true; };
|
||||||
@@ -510,10 +513,39 @@ fn typenameisunsigned(nm: str) bool = {
|
|||||||
if (streq(nm, "u64")) { return true; };
|
if (streq(nm, "u64")) { return true; };
|
||||||
if (streq(nm, "uint")) { return true; };
|
if (streq(nm, "uint")) { return true; };
|
||||||
if (streq(nm, "uintptr")) { return true; };
|
if (streq(nm, "uintptr")) { return true; };
|
||||||
|
if (streq(nm, "rune")) { return true; };
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// typenodeisunsigned — recurse through TNAME / TPTR / TSLICE etc.
|
// typenodeisunsigned — recurse through TNAME aliases / TBANG / TENUM
|
||||||
|
// to the resolved primitive. Mirrors cstage's type_isunsigned which
|
||||||
|
// recurses into TY_NAMED.under and TY_ENUM.sub.
|
||||||
|
fn typenodeisunsignedc(c: *cgen, t: *node) bool = {
|
||||||
|
if (t == nil) { return false; };
|
||||||
|
let k: nkind = t.kind;
|
||||||
|
if (k == nkind.N_TBANG) { return typenodeisunsignedc(c, t.lhs); };
|
||||||
|
if (k == nkind.N_TENUM) { return typenodeisunsignedc(c, t.lhs); };
|
||||||
|
if (k == nkind.N_TNAME) {
|
||||||
|
let nm: str = t.str;
|
||||||
|
if (typenameisunsigned(nm)) { return true; };
|
||||||
|
if (typenameissigned(nm)) { return false; };
|
||||||
|
// Follow aliases / enum storage.
|
||||||
|
let al: *node = aliaslookup(c, nm);
|
||||||
|
if (al != nil) { return typenodeisunsignedc(c, al); };
|
||||||
|
let en: *enumtype = enumlookup(c, nm);
|
||||||
|
if (en != nil) {
|
||||||
|
if (en.storage != nil) {
|
||||||
|
return typenodeisunsignedc(c, en.storage);
|
||||||
|
};
|
||||||
|
return false; // default storage i32 is signed
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// typenodeisunsigned — legacy callers without *cgen context. Only
|
||||||
|
// resolves primitive TNAMEs (no alias/enum recursion); use the
|
||||||
|
// _c variant where the cgen registry is in scope.
|
||||||
fn typenodeisunsigned(t: *node) bool = {
|
fn typenodeisunsigned(t: *node) bool = {
|
||||||
if (t == nil) { return false; };
|
if (t == nil) { return false; };
|
||||||
if (t.kind == nkind.N_TNAME) { return typenameisunsigned(t.str); };
|
if (t.kind == nkind.N_TNAME) { return typenameisunsigned(t.str); };
|
||||||
@@ -568,9 +600,21 @@ fn typeis8byteprimitive(c: *cgen, t: *node) bool = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// elemissigned — given an indexable type (`*T`, `[]T`, `[N]T`), is
|
// elemissigned — given an indexable type (`*T`, `[]T`, `[N]T`), is
|
||||||
// its element a signed narrow primitive (i8/i16/i32/rune)? Used by
|
// its element a signed narrow primitive (i8/i16/i32)? Used by
|
||||||
// cgindex to pick MOVSXD vs MOVL at esz=4. Mirrors C cgen's
|
// cgindex to pick MOVSXD vs MOVL at esz=4 (and MOVSBQ/MOVSWQ at
|
||||||
// `signed_elem` check.
|
// esz=1/2). Mirrors cstage's `signed_elem`. Follows alias/enum
|
||||||
|
// chains so `[]Alias` arrays resolve to the underlying signedness.
|
||||||
|
fn elemissignedc(c: *cgen, t: *node) bool = {
|
||||||
|
if (t == nil) { return false; };
|
||||||
|
let elem: *node = nil;
|
||||||
|
let k: nkind = t.kind;
|
||||||
|
if (k == nkind.N_TPTR) { elem = t.lhs; };
|
||||||
|
if (k == nkind.N_TSLICE) { elem = t.lhs; };
|
||||||
|
if (k == nkind.N_TARRAY) { elem = t.lhs; };
|
||||||
|
if (elem == nil) { return false; };
|
||||||
|
return fieldissignedc(c, elem);
|
||||||
|
};
|
||||||
|
|
||||||
fn elemissigned(t: *node) bool = {
|
fn elemissigned(t: *node) bool = {
|
||||||
if (t == nil) { return false; };
|
if (t == nil) { return false; };
|
||||||
let elem: *node = nil;
|
let elem: *node = nil;
|
||||||
@@ -583,47 +627,103 @@ fn elemissigned(t: *node) bool = {
|
|||||||
return typenameissigned(elem.str);
|
return typenameissigned(elem.str);
|
||||||
};
|
};
|
||||||
|
|
||||||
// typenameissigned — true for i8/i16/i32/i64/int/rune.
|
// typenameissigned — true for i8/i16/i32/i64/int. rune is excluded
|
||||||
|
// (it's a non-negative Unicode codepoint, treated as unsigned).
|
||||||
fn typenameissigned(nm: str) bool = {
|
fn typenameissigned(nm: str) bool = {
|
||||||
if (streq(nm, "i8")) { return true; };
|
if (streq(nm, "i8")) { return true; };
|
||||||
if (streq(nm, "i16")) { return true; };
|
if (streq(nm, "i16")) { return true; };
|
||||||
if (streq(nm, "i32")) { return true; };
|
if (streq(nm, "i32")) { return true; };
|
||||||
if (streq(nm, "i64")) { return true; };
|
if (streq(nm, "i64")) { return true; };
|
||||||
if (streq(nm, "int")) { return true; };
|
if (streq(nm, "int")) { return true; };
|
||||||
if (streq(nm, "rune")) { return true; };
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// fieldissignedc — does this field/element type need sign-extension
|
||||||
|
// on a sub-word load? Walks TBANG / TENUM / TNAME-aliases to the
|
||||||
|
// resolved primitive. Mirrors cstage's fld_issigned: bool is treated
|
||||||
|
// as unsigned (0/1 ⇒ MOVZBQ); rune is unsigned (codepoint ⇒ MOVL).
|
||||||
|
fn fieldissignedc(c: *cgen, t: *node) bool = {
|
||||||
|
if (t == nil) { return false; };
|
||||||
|
let k: nkind = t.kind;
|
||||||
|
if (k == nkind.N_TBANG) { return fieldissignedc(c, t.lhs); };
|
||||||
|
if (k == nkind.N_TENUM) { return fieldissignedc(c, t.lhs); };
|
||||||
|
if (k == nkind.N_TNAME) {
|
||||||
|
let nm: str = t.str;
|
||||||
|
if (streq(nm, "bool")) { return false; };
|
||||||
|
if (typenameisunsigned(nm)) { return false; };
|
||||||
|
if (typenameissigned(nm)) { return true; };
|
||||||
|
let al: *node = aliaslookup(c, nm);
|
||||||
|
if (al != nil) { return fieldissignedc(c, al); };
|
||||||
|
let en: *enumtype = enumlookup(c, nm);
|
||||||
|
if (en != nil) {
|
||||||
|
if (en.storage != nil) {
|
||||||
|
return fieldissignedc(c, en.storage);
|
||||||
|
};
|
||||||
|
return true; // default i32 storage is signed
|
||||||
|
};
|
||||||
|
};
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// fieldloadop — pick the load instruction for a non-str struct
|
// fieldloadop — pick the load instruction for a non-str struct
|
||||||
// field by its declared size + signedness. Mirrors the C cgen op
|
// field by its declared size + signedness. Mirrors cstage's
|
||||||
// dispatch (MOVZBQ for u8/bool/i8, MOVSXD for i32, MOVL for u32, MOVQ
|
// fldloadop: MOVZBQ/MOVSBQ for 1B, MOVZWQ/MOVSWQ for 2B,
|
||||||
// for 8-byte). f might be nil for fields outside our struct registry.
|
// MOVL/MOVSXD for 4B, MOVQ for 8B. f might be nil for fields
|
||||||
fn fieldloadop(f: *fieldinfo) str = {
|
// outside our struct registry.
|
||||||
|
fn fieldloadop(c: *cgen, f: *fieldinfo) str = {
|
||||||
if (f == nil) { return "MOVQ"; };
|
if (f == nil) { return "MOVQ"; };
|
||||||
let sz: i32 = f.fsz;
|
let sz: i32 = f.fsz;
|
||||||
if (sz == 1) { return "MOVZBQ"; };
|
let sigd: bool = fieldissignedc(c, f.tnode);
|
||||||
if (sz == 4) {
|
if (sz == 1) { if (sigd) { return "MOVSBQ"; }; return "MOVZBQ"; };
|
||||||
let t: *node = f.tnode;
|
if (sz == 2) { if (sigd) { return "MOVSWQ"; }; return "MOVZWQ"; };
|
||||||
if (t != nil) {
|
if (sz == 4) { if (sigd) { return "MOVSXD"; }; return "MOVL"; };
|
||||||
if (t.kind == nkind.N_TNAME) {
|
|
||||||
if (typenameissigned(t.str)) { return "MOVSXD"; };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
return "MOVL";
|
|
||||||
};
|
|
||||||
return "MOVQ";
|
return "MOVQ";
|
||||||
};
|
};
|
||||||
|
|
||||||
// fieldstoreop — pick the store instruction for a non-str struct
|
// fieldstoreop — pick the store instruction for a non-str struct
|
||||||
// field by its declared size. MOVB for 1, MOVL for 4, MOVQ for 8.
|
// field by its declared size. MOVB for 1, MOVW for 2, MOVL for 4,
|
||||||
fn fieldstoreop(f: *fieldinfo) str = {
|
// MOVQ for 8. c kept in the signature for symmetry with fieldloadop.
|
||||||
|
fn fieldstoreop(c: *cgen, f: *fieldinfo) str = {
|
||||||
if (f == nil) { return "MOVQ"; };
|
if (f == nil) { return "MOVQ"; };
|
||||||
let sz: i32 = f.fsz;
|
let sz: i32 = f.fsz;
|
||||||
if (sz == 1) { return "MOVB"; };
|
if (sz == 1) { return "MOVB"; };
|
||||||
|
if (sz == 2) { return "MOVW"; };
|
||||||
if (sz == 4) { return "MOVL"; };
|
if (sz == 4) { return "MOVL"; };
|
||||||
return "MOVQ";
|
return "MOVQ";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// tnodeloadop / tnodestoreop — same dispatch as fieldloadop /
|
||||||
|
// fieldstoreop but keyed on a raw type-AST node (tuple element type,
|
||||||
|
// pointer-target, slice-element, etc.) rather than a struct fieldinfo.
|
||||||
|
// Used at the index / tuple / pointer-deref sites where there's no
|
||||||
|
// fieldinfo entry but the type-node + size are both known.
|
||||||
|
fn tnodeloadop(c: *cgen, t: *node, sz: i32) str = {
|
||||||
|
let sigd: bool = fieldissignedc(c, t);
|
||||||
|
if (sz == 1) { if (sigd) { return "MOVSBQ"; }; return "MOVZBQ"; };
|
||||||
|
if (sz == 2) { if (sigd) { return "MOVSWQ"; }; return "MOVZWQ"; };
|
||||||
|
if (sz == 4) { if (sigd) { return "MOVSXD"; }; return "MOVL"; };
|
||||||
|
return "MOVQ";
|
||||||
|
};
|
||||||
|
|
||||||
|
fn tnodestoreop(c: *cgen, t: *node, sz: i32) str = {
|
||||||
|
if (sz == 1) { return "MOVB"; };
|
||||||
|
if (sz == 2) { return "MOVW"; };
|
||||||
|
if (sz == 4) { return "MOVL"; };
|
||||||
|
return "MOVQ";
|
||||||
|
};
|
||||||
|
|
||||||
|
// loadopsz — load op when the (size, signedness) pair has already
|
||||||
|
// been resolved upstream and the type-node isn't carried through.
|
||||||
|
// cgindex precomputes `signed_elem` via elemissignedc; cgforrange
|
||||||
|
// precomputes `bind_signed[b]` via paramissigned. Same dispatch as
|
||||||
|
// tnodeloadop's tail; only the keying differs.
|
||||||
|
fn loadopsz(sigd: bool, sz: i32) str = {
|
||||||
|
if (sz == 1) { if (sigd) { return "MOVSBQ"; }; return "MOVZBQ"; };
|
||||||
|
if (sz == 2) { if (sigd) { return "MOVSWQ"; }; return "MOVZWQ"; };
|
||||||
|
if (sz == 4) { if (sigd) { return "MOVSXD"; }; return "MOVL"; };
|
||||||
|
return "MOVQ";
|
||||||
|
};
|
||||||
|
|
||||||
// indexbaseesz — element size for `arr[i]` where the base is a
|
// indexbaseesz — element size for `arr[i]` where the base is a
|
||||||
// chained-dot pseudo-field `s.ptr` (s being str/*str/slice/*slice).
|
// chained-dot pseudo-field `s.ptr` (s being str/*str/slice/*slice).
|
||||||
// For str the element is one byte; for `[]T` / `*[]T` we drill into
|
// For str the element is one byte; for `[]T` / `*[]T` we drill into
|
||||||
@@ -2097,7 +2197,7 @@ fn cgwidentaggedstore(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz:
|
|||||||
emitoff((slot_off + 8 + fi.foff + 8): i64);
|
emitoff((slot_off + 8 + fi.foff + 8): i64);
|
||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
} else {
|
} else {
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
|
|||||||
@@ -6072,7 +6072,10 @@ fn nodeisstr(c: *cgen, n: *node) bool = {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// typenameisunsigned — true for u8/u16/u32/u64/uint/uintptr.
|
// typenameisunsigned — true for u8/u16/u32/u64/uint/uintptr/rune.
|
||||||
|
// rune is a Unicode codepoint (0..0x10FFFF); cgen treats it as
|
||||||
|
// unsigned so narrow-cast / sub-word load paths zero-extend (MOVL,
|
||||||
|
// not MOVSXD). Mirrors cstage's type_isunsigned post task #5.
|
||||||
fn typenameisunsigned(nm: str) bool = {
|
fn typenameisunsigned(nm: str) bool = {
|
||||||
if (streq(nm, "u8")) { return true; };
|
if (streq(nm, "u8")) { return true; };
|
||||||
if (streq(nm, "u16")) { return true; };
|
if (streq(nm, "u16")) { return true; };
|
||||||
@@ -6080,10 +6083,39 @@ fn typenameisunsigned(nm: str) bool = {
|
|||||||
if (streq(nm, "u64")) { return true; };
|
if (streq(nm, "u64")) { return true; };
|
||||||
if (streq(nm, "uint")) { return true; };
|
if (streq(nm, "uint")) { return true; };
|
||||||
if (streq(nm, "uintptr")) { return true; };
|
if (streq(nm, "uintptr")) { return true; };
|
||||||
|
if (streq(nm, "rune")) { return true; };
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// typenodeisunsigned — recurse through TNAME / TPTR / TSLICE etc.
|
// typenodeisunsigned — recurse through TNAME aliases / TBANG / TENUM
|
||||||
|
// to the resolved primitive. Mirrors cstage's type_isunsigned which
|
||||||
|
// recurses into TY_NAMED.under and TY_ENUM.sub.
|
||||||
|
fn typenodeisunsignedc(c: *cgen, t: *node) bool = {
|
||||||
|
if (t == nil) { return false; };
|
||||||
|
let k: nkind = t.kind;
|
||||||
|
if (k == nkind.N_TBANG) { return typenodeisunsignedc(c, t.lhs); };
|
||||||
|
if (k == nkind.N_TENUM) { return typenodeisunsignedc(c, t.lhs); };
|
||||||
|
if (k == nkind.N_TNAME) {
|
||||||
|
let nm: str = t.str;
|
||||||
|
if (typenameisunsigned(nm)) { return true; };
|
||||||
|
if (typenameissigned(nm)) { return false; };
|
||||||
|
// Follow aliases / enum storage.
|
||||||
|
let al: *node = aliaslookup(c, nm);
|
||||||
|
if (al != nil) { return typenodeisunsignedc(c, al); };
|
||||||
|
let en: *enumtype = enumlookup(c, nm);
|
||||||
|
if (en != nil) {
|
||||||
|
if (en.storage != nil) {
|
||||||
|
return typenodeisunsignedc(c, en.storage);
|
||||||
|
};
|
||||||
|
return false; // default storage i32 is signed
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// typenodeisunsigned — legacy callers without *cgen context. Only
|
||||||
|
// resolves primitive TNAMEs (no alias/enum recursion); use the
|
||||||
|
// _c variant where the cgen registry is in scope.
|
||||||
fn typenodeisunsigned(t: *node) bool = {
|
fn typenodeisunsigned(t: *node) bool = {
|
||||||
if (t == nil) { return false; };
|
if (t == nil) { return false; };
|
||||||
if (t.kind == nkind.N_TNAME) { return typenameisunsigned(t.str); };
|
if (t.kind == nkind.N_TNAME) { return typenameisunsigned(t.str); };
|
||||||
@@ -6138,9 +6170,21 @@ fn typeis8byteprimitive(c: *cgen, t: *node) bool = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// elemissigned — given an indexable type (`*T`, `[]T`, `[N]T`), is
|
// elemissigned — given an indexable type (`*T`, `[]T`, `[N]T`), is
|
||||||
// its element a signed narrow primitive (i8/i16/i32/rune)? Used by
|
// its element a signed narrow primitive (i8/i16/i32)? Used by
|
||||||
// cgindex to pick MOVSXD vs MOVL at esz=4. Mirrors C cgen's
|
// cgindex to pick MOVSXD vs MOVL at esz=4 (and MOVSBQ/MOVSWQ at
|
||||||
// `signed_elem` check.
|
// esz=1/2). Mirrors cstage's `signed_elem`. Follows alias/enum
|
||||||
|
// chains so `[]Alias` arrays resolve to the underlying signedness.
|
||||||
|
fn elemissignedc(c: *cgen, t: *node) bool = {
|
||||||
|
if (t == nil) { return false; };
|
||||||
|
let elem: *node = nil;
|
||||||
|
let k: nkind = t.kind;
|
||||||
|
if (k == nkind.N_TPTR) { elem = t.lhs; };
|
||||||
|
if (k == nkind.N_TSLICE) { elem = t.lhs; };
|
||||||
|
if (k == nkind.N_TARRAY) { elem = t.lhs; };
|
||||||
|
if (elem == nil) { return false; };
|
||||||
|
return fieldissignedc(c, elem);
|
||||||
|
};
|
||||||
|
|
||||||
fn elemissigned(t: *node) bool = {
|
fn elemissigned(t: *node) bool = {
|
||||||
if (t == nil) { return false; };
|
if (t == nil) { return false; };
|
||||||
let elem: *node = nil;
|
let elem: *node = nil;
|
||||||
@@ -6153,47 +6197,103 @@ fn elemissigned(t: *node) bool = {
|
|||||||
return typenameissigned(elem.str);
|
return typenameissigned(elem.str);
|
||||||
};
|
};
|
||||||
|
|
||||||
// typenameissigned — true for i8/i16/i32/i64/int/rune.
|
// typenameissigned — true for i8/i16/i32/i64/int. rune is excluded
|
||||||
|
// (it's a non-negative Unicode codepoint, treated as unsigned).
|
||||||
fn typenameissigned(nm: str) bool = {
|
fn typenameissigned(nm: str) bool = {
|
||||||
if (streq(nm, "i8")) { return true; };
|
if (streq(nm, "i8")) { return true; };
|
||||||
if (streq(nm, "i16")) { return true; };
|
if (streq(nm, "i16")) { return true; };
|
||||||
if (streq(nm, "i32")) { return true; };
|
if (streq(nm, "i32")) { return true; };
|
||||||
if (streq(nm, "i64")) { return true; };
|
if (streq(nm, "i64")) { return true; };
|
||||||
if (streq(nm, "int")) { return true; };
|
if (streq(nm, "int")) { return true; };
|
||||||
if (streq(nm, "rune")) { return true; };
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// fieldissignedc — does this field/element type need sign-extension
|
||||||
|
// on a sub-word load? Walks TBANG / TENUM / TNAME-aliases to the
|
||||||
|
// resolved primitive. Mirrors cstage's fld_issigned: bool is treated
|
||||||
|
// as unsigned (0/1 ⇒ MOVZBQ); rune is unsigned (codepoint ⇒ MOVL).
|
||||||
|
fn fieldissignedc(c: *cgen, t: *node) bool = {
|
||||||
|
if (t == nil) { return false; };
|
||||||
|
let k: nkind = t.kind;
|
||||||
|
if (k == nkind.N_TBANG) { return fieldissignedc(c, t.lhs); };
|
||||||
|
if (k == nkind.N_TENUM) { return fieldissignedc(c, t.lhs); };
|
||||||
|
if (k == nkind.N_TNAME) {
|
||||||
|
let nm: str = t.str;
|
||||||
|
if (streq(nm, "bool")) { return false; };
|
||||||
|
if (typenameisunsigned(nm)) { return false; };
|
||||||
|
if (typenameissigned(nm)) { return true; };
|
||||||
|
let al: *node = aliaslookup(c, nm);
|
||||||
|
if (al != nil) { return fieldissignedc(c, al); };
|
||||||
|
let en: *enumtype = enumlookup(c, nm);
|
||||||
|
if (en != nil) {
|
||||||
|
if (en.storage != nil) {
|
||||||
|
return fieldissignedc(c, en.storage);
|
||||||
|
};
|
||||||
|
return true; // default i32 storage is signed
|
||||||
|
};
|
||||||
|
};
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// fieldloadop — pick the load instruction for a non-str struct
|
// fieldloadop — pick the load instruction for a non-str struct
|
||||||
// field by its declared size + signedness. Mirrors the C cgen op
|
// field by its declared size + signedness. Mirrors cstage's
|
||||||
// dispatch (MOVZBQ for u8/bool/i8, MOVSXD for i32, MOVL for u32, MOVQ
|
// fldloadop: MOVZBQ/MOVSBQ for 1B, MOVZWQ/MOVSWQ for 2B,
|
||||||
// for 8-byte). f might be nil for fields outside our struct registry.
|
// MOVL/MOVSXD for 4B, MOVQ for 8B. f might be nil for fields
|
||||||
fn fieldloadop(f: *fieldinfo) str = {
|
// outside our struct registry.
|
||||||
|
fn fieldloadop(c: *cgen, f: *fieldinfo) str = {
|
||||||
if (f == nil) { return "MOVQ"; };
|
if (f == nil) { return "MOVQ"; };
|
||||||
let sz: i32 = f.fsz;
|
let sz: i32 = f.fsz;
|
||||||
if (sz == 1) { return "MOVZBQ"; };
|
let sigd: bool = fieldissignedc(c, f.tnode);
|
||||||
if (sz == 4) {
|
if (sz == 1) { if (sigd) { return "MOVSBQ"; }; return "MOVZBQ"; };
|
||||||
let t: *node = f.tnode;
|
if (sz == 2) { if (sigd) { return "MOVSWQ"; }; return "MOVZWQ"; };
|
||||||
if (t != nil) {
|
if (sz == 4) { if (sigd) { return "MOVSXD"; }; return "MOVL"; };
|
||||||
if (t.kind == nkind.N_TNAME) {
|
|
||||||
if (typenameissigned(t.str)) { return "MOVSXD"; };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
return "MOVL";
|
|
||||||
};
|
|
||||||
return "MOVQ";
|
return "MOVQ";
|
||||||
};
|
};
|
||||||
|
|
||||||
// fieldstoreop — pick the store instruction for a non-str struct
|
// fieldstoreop — pick the store instruction for a non-str struct
|
||||||
// field by its declared size. MOVB for 1, MOVL for 4, MOVQ for 8.
|
// field by its declared size. MOVB for 1, MOVW for 2, MOVL for 4,
|
||||||
fn fieldstoreop(f: *fieldinfo) str = {
|
// MOVQ for 8. c kept in the signature for symmetry with fieldloadop.
|
||||||
|
fn fieldstoreop(c: *cgen, f: *fieldinfo) str = {
|
||||||
if (f == nil) { return "MOVQ"; };
|
if (f == nil) { return "MOVQ"; };
|
||||||
let sz: i32 = f.fsz;
|
let sz: i32 = f.fsz;
|
||||||
if (sz == 1) { return "MOVB"; };
|
if (sz == 1) { return "MOVB"; };
|
||||||
|
if (sz == 2) { return "MOVW"; };
|
||||||
if (sz == 4) { return "MOVL"; };
|
if (sz == 4) { return "MOVL"; };
|
||||||
return "MOVQ";
|
return "MOVQ";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// tnodeloadop / tnodestoreop — same dispatch as fieldloadop /
|
||||||
|
// fieldstoreop but keyed on a raw type-AST node (tuple element type,
|
||||||
|
// pointer-target, slice-element, etc.) rather than a struct fieldinfo.
|
||||||
|
// Used at the index / tuple / pointer-deref sites where there's no
|
||||||
|
// fieldinfo entry but the type-node + size are both known.
|
||||||
|
fn tnodeloadop(c: *cgen, t: *node, sz: i32) str = {
|
||||||
|
let sigd: bool = fieldissignedc(c, t);
|
||||||
|
if (sz == 1) { if (sigd) { return "MOVSBQ"; }; return "MOVZBQ"; };
|
||||||
|
if (sz == 2) { if (sigd) { return "MOVSWQ"; }; return "MOVZWQ"; };
|
||||||
|
if (sz == 4) { if (sigd) { return "MOVSXD"; }; return "MOVL"; };
|
||||||
|
return "MOVQ";
|
||||||
|
};
|
||||||
|
|
||||||
|
fn tnodestoreop(c: *cgen, t: *node, sz: i32) str = {
|
||||||
|
if (sz == 1) { return "MOVB"; };
|
||||||
|
if (sz == 2) { return "MOVW"; };
|
||||||
|
if (sz == 4) { return "MOVL"; };
|
||||||
|
return "MOVQ";
|
||||||
|
};
|
||||||
|
|
||||||
|
// loadopsz — load op when the (size, signedness) pair has already
|
||||||
|
// been resolved upstream and the type-node isn't carried through.
|
||||||
|
// cgindex precomputes `signed_elem` via elemissignedc; cgforrange
|
||||||
|
// precomputes `bind_signed[b]` via paramissigned. Same dispatch as
|
||||||
|
// tnodeloadop's tail; only the keying differs.
|
||||||
|
fn loadopsz(sigd: bool, sz: i32) str = {
|
||||||
|
if (sz == 1) { if (sigd) { return "MOVSBQ"; }; return "MOVZBQ"; };
|
||||||
|
if (sz == 2) { if (sigd) { return "MOVSWQ"; }; return "MOVZWQ"; };
|
||||||
|
if (sz == 4) { if (sigd) { return "MOVSXD"; }; return "MOVL"; };
|
||||||
|
return "MOVQ";
|
||||||
|
};
|
||||||
|
|
||||||
// indexbaseesz — element size for `arr[i]` where the base is a
|
// indexbaseesz — element size for `arr[i]` where the base is a
|
||||||
// chained-dot pseudo-field `s.ptr` (s being str/*str/slice/*slice).
|
// chained-dot pseudo-field `s.ptr` (s being str/*str/slice/*slice).
|
||||||
// For str the element is one byte; for `[]T` / `*[]T` we drill into
|
// For str the element is one byte; for `[]T` / `*[]T` we drill into
|
||||||
@@ -7667,7 +7767,7 @@ fn cgwidentaggedstore(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz:
|
|||||||
emitoff((slot_off + 8 + fi.foff + 8): i64);
|
emitoff((slot_off + 8 + fi.foff + 8): i64);
|
||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
} else {
|
} else {
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
@@ -8353,7 +8453,11 @@ fn cgcast(c: *cgen, n: *node) void = {
|
|||||||
let sz: i32 = primsize(nm);
|
let sz: i32 = primsize(nm);
|
||||||
let is_unsigned: bool = typenameisunsigned(nm);
|
let is_unsigned: bool = typenameisunsigned(nm);
|
||||||
let is_bool: bool = streq(nm, "bool");
|
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 (is_unsigned) {
|
||||||
if (sz == 4) {
|
if (sz == 4) {
|
||||||
emitline("\tMOVL\tAX, AX\n");
|
emitline("\tMOVL\tAX, AX\n");
|
||||||
@@ -8364,16 +8468,17 @@ fn cgcast(c: *cgen, n: *node) void = {
|
|||||||
emitint(mask);
|
emitint(mask);
|
||||||
emitline(", AX\n");
|
emitline(", AX\n");
|
||||||
};
|
};
|
||||||
} else { if (is_bool) {
|
} else {
|
||||||
emitline("\tANDQ\t$255, AX\n");
|
if (sz == 1) {
|
||||||
} else { if (streq(nm, "i8")) {
|
emitline("\tMOVSBQ\tAX, AX\n");
|
||||||
emitline("\tMOVSBQ\tAX, AX\n");
|
} else { if (sz == 2) {
|
||||||
} else { if (streq(nm, "i16")) {
|
emitline("\tMOVSWQ\tAX, AX\n");
|
||||||
emitline("\tMOVSWQ\tAX, AX\n");
|
} else { if (sz == 4) {
|
||||||
} else { if (streq(nm, "i32")) {
|
emitline("\tMOVSXD\tAX, AX\n");
|
||||||
emitline("\tMOVSXD\tAX, AX\n");
|
}; }; };
|
||||||
}; }; }; }; };
|
};
|
||||||
}; };
|
}; }; };
|
||||||
|
if (is_bool) { emitline("\tANDQ\t$255, AX\n"); };
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -8550,7 +8655,7 @@ fn cgindex(c: *cgen, n: *node) void = {
|
|||||||
baselocal = localfindnode(c, bn);
|
baselocal = localfindnode(c, bn);
|
||||||
if (baselocal != nil) {
|
if (baselocal != nil) {
|
||||||
esz = elemsizeofc(c, baselocal.tnode);
|
esz = elemsizeofc(c, baselocal.tnode);
|
||||||
signed_elem = elemissigned(baselocal.tnode);
|
signed_elem = elemissignedc(c, baselocal.tnode);
|
||||||
} else {
|
} else {
|
||||||
let tn: *node = letvartnode(c, bn);
|
let tn: *node = letvartnode(c, bn);
|
||||||
if (tn != nil) {
|
if (tn != nil) {
|
||||||
@@ -8558,13 +8663,13 @@ fn cgindex(c: *cgen, n: *node) void = {
|
|||||||
isglobalarr = true;
|
isglobalarr = true;
|
||||||
globalname = bn;
|
globalname = bn;
|
||||||
esz = elemsizeofc(c, tn);
|
esz = elemsizeofc(c, tn);
|
||||||
signed_elem = elemissigned(tn);
|
signed_elem = elemissignedc(c, tn);
|
||||||
};
|
};
|
||||||
if (tn.kind == nkind.N_TPTR) {
|
if (tn.kind == nkind.N_TPTR) {
|
||||||
isglobalptr = true;
|
isglobalptr = true;
|
||||||
globalname = bn;
|
globalname = bn;
|
||||||
esz = elemsizeofc(c, tn);
|
esz = elemsizeofc(c, tn);
|
||||||
signed_elem = elemissigned(tn);
|
signed_elem = elemissignedc(c, tn);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -8646,16 +8751,10 @@ fn cgindex(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tMOVQ\tCX, BX\n");
|
emitline("\tMOVQ\tCX, BX\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (esz == 1) { emitline("\tMOVZBQ\t(BX), AX\n"); }
|
let lop1: str = loadopsz(signed_elem, esz);
|
||||||
else { if (esz == 2) {
|
emitline("\t");
|
||||||
if (signed_elem) { emitline("\tMOVSWQ\t(BX), AX\n"); }
|
emitline(lop1);
|
||||||
else { emitline("\tMOVZWQ\t(BX), AX\n"); };
|
emitline("\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"); };};};
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (baselocal != nil) {
|
if (baselocal != nil) {
|
||||||
@@ -8693,16 +8792,10 @@ fn cgindex(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tMOVQ\tCX, BX\n");
|
emitline("\tMOVQ\tCX, BX\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (esz == 1) { emitline("\tMOVZBQ\t(BX), AX\n"); }
|
let lop2: str = loadopsz(signed_elem, esz);
|
||||||
else { if (esz == 2) {
|
emitline("\t");
|
||||||
if (signed_elem) { emitline("\tMOVSWQ\t(BX), AX\n"); }
|
emitline(lop2);
|
||||||
else { emitline("\tMOVZWQ\t(BX), AX\n"); };
|
emitline("\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"); };};};
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
// Generic fallback when base isn't a plain ident.
|
// Generic fallback when base isn't a plain ident.
|
||||||
@@ -8728,16 +8821,10 @@ fn cgindex(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tMOVQ\t(AX), AX\n");
|
emitline("\tMOVQ\t(AX), AX\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (esz == 1) { emitline("\tMOVZBQ\t(AX), AX\n"); }
|
let lop3: str = loadopsz(signed_elem, esz);
|
||||||
else { if (esz == 2) {
|
emitline("\t");
|
||||||
if (signed_elem) { emitline("\tMOVSWQ\t(AX), AX\n"); }
|
emitline(lop3);
|
||||||
else { emitline("\tMOVZWQ\t(AX), AX\n"); };
|
emitline("\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"); };};};
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -9157,7 +9244,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
emitdispreg(fi.foff: i64, "BX");
|
emitdispreg(fi.foff: i64, "BX");
|
||||||
emitline(", X0\n");
|
emitline(", X0\n");
|
||||||
} else {
|
} else {
|
||||||
let op: str = fieldloadop(fi);
|
let op: str = fieldloadop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(op);
|
emitline(op);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -9199,7 +9286,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
emitoff((lc.off + fi.foff): i64);
|
emitoff((lc.off + fi.foff): i64);
|
||||||
emitline("(BP), X0\n");
|
emitline("(BP), X0\n");
|
||||||
} else {
|
} else {
|
||||||
let op: str = fieldloadop(fi);
|
let op: str = fieldloadop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(op);
|
emitline(op);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -9265,9 +9352,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let sz: i32 = slotsize(c, tp);
|
let sz: i32 = slotsize(c, tp);
|
||||||
let op: str = "MOVQ";
|
let op: str = tnodeloadop(c, tp, sz);
|
||||||
if (sz == 1) { op = "MOVZBQ"; }
|
|
||||||
else { if (sz == 4) { op = "MOVL"; }; };
|
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(op);
|
emitline(op);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -9402,7 +9487,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
emitdispreg(fi.foff: i64, "CX");
|
emitdispreg(fi.foff: i64, "CX");
|
||||||
emitline(", X0\n");
|
emitline(", X0\n");
|
||||||
} else {
|
} else {
|
||||||
let op: str = fieldloadop(fi);
|
let op: str = fieldloadop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(op);
|
emitline(op);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -9464,7 +9549,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
emitline(", X0\n");
|
emitline(", X0\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let lop: str = fieldloadop(fi);
|
let lop: str = fieldloadop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(lop);
|
emitline(lop);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -9565,7 +9650,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let lop: str = fieldloadop(r.leaffi);
|
let lop: str = fieldloadop(c, r.leaffi);
|
||||||
if (r.isglobal) {
|
if (r.isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, r.rootname);
|
emitsymname(c, r.rootname);
|
||||||
@@ -9637,7 +9722,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
emitline(", X0\n");
|
emitline(", X0\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let lop: str = fieldloadop(fi);
|
let lop: str = fieldloadop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(lop);
|
emitline(lop);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -9735,7 +9820,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let lop: str = fieldloadop(ffi);
|
let lop: str = fieldloadop(c, ffi);
|
||||||
if (isptr) {
|
if (isptr) {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff(lc.off: i64);
|
emitoff(lc.off: i64);
|
||||||
@@ -10147,7 +10232,7 @@ fn cgalloc(c: *cgen, n: *node) void = {
|
|||||||
fi = nil;
|
fi = nil;
|
||||||
} else {
|
} else {
|
||||||
emitline("\tMOVQ\t(SP), BX\n");
|
emitline("\tMOVQ\t(SP), BX\n");
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
@@ -10201,8 +10286,14 @@ fn cgappend(c: *cgen, n: *node) void = {
|
|||||||
if (snlocal == nil) { return; };
|
if (snlocal == nil) { return; };
|
||||||
let sn_off: i32 = snlocal.off;
|
let sn_off: i32 = snlocal.off;
|
||||||
let esz: i32 = elemsizeof(snlocal.tnode);
|
let esz: i32 = elemsizeof(snlocal.tnode);
|
||||||
let store_op: str = "MOVQ";
|
let etnode: *node = nil;
|
||||||
if (esz == 1) { store_op = "MOVB"; };
|
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;
|
let vn: *node = sn.next;
|
||||||
for (vn != nil) {
|
for (vn != nil) {
|
||||||
@@ -10213,8 +10304,7 @@ fn cgappend(c: *cgen, n: *node) void = {
|
|||||||
let itlocal: *local = localfindnode(c, it.str);
|
let itlocal: *local = localfindnode(c, it.str);
|
||||||
if (itlocal == nil) { vn = vn.next; continue; };
|
if (itlocal == nil) { vn = vn.next; continue; };
|
||||||
let it_off: i32 = itlocal.off;
|
let it_off: i32 = itlocal.off;
|
||||||
let load_op: str = "MOVQ";
|
let load_op: str = tnodeloadop(c, etnode, esz);
|
||||||
if (esz == 1) { load_op = "MOVZBQ"; };
|
|
||||||
emitline("\tSUBQ\t$8, SP\n");
|
emitline("\tSUBQ\t$8, SP\n");
|
||||||
emitline("\tMOVQ\t$0, (SP)\n");
|
emitline("\tMOVQ\t$0, (SP)\n");
|
||||||
let ll: str = mklabel(c, "spr_l");
|
let ll: str = mklabel(c, "spr_l");
|
||||||
@@ -10444,9 +10534,7 @@ fn cgcall(c: *cgen, n: *node) void = {
|
|||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
} else {
|
} else {
|
||||||
cgexpr(c, aa2);
|
cgexpr(c, aa2);
|
||||||
let op: str = "MOVQ";
|
let op: str = tnodestoreop(c, varp.lhs, esz);
|
||||||
if (esz == 1) { op = "MOVB"; }
|
|
||||||
else { if (esz == 4) { op = "MOVL"; }; };
|
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(op);
|
emitline(op);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
@@ -10795,19 +10883,10 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
if (tn.kind == nkind.N_TPTR) {
|
if (tn.kind == nkind.N_TPTR) {
|
||||||
let pe: *node = tn.lhs;
|
let pe: *node = tn.lhs;
|
||||||
if (pe != nil) {
|
if (pe != nil) {
|
||||||
if (pe.kind == nkind.N_TNAME) {
|
let ps: i32 = fieldsize(c, pe);
|
||||||
let ps: i32 = primsize(pe.str);
|
if (ps == 1 || ps == 2 || ps == 4) {
|
||||||
if (ps == 1) {
|
loadop = tnodeloadop(c, pe, ps);
|
||||||
loadop = "MOVZBQ";
|
storeop = tnodestoreop(c, pe, ps);
|
||||||
storeop = "MOVB";
|
|
||||||
} else { if (ps == 4) {
|
|
||||||
if (typenameissigned(pe.str)) {
|
|
||||||
loadop = "MOVSXD";
|
|
||||||
} else {
|
|
||||||
loadop = "MOVL";
|
|
||||||
};
|
|
||||||
storeop = "MOVL";
|
|
||||||
}; };
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -11011,10 +11090,10 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tMOVQ\tCX, 8(BX)\n");
|
emitline("\tMOVQ\tCX, 8(BX)\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (esz == 1) { emitline("\tMOVB\tAX, (BX)\n"); }
|
let isop: str = tnodestoreop(c, elemtn, esz);
|
||||||
else { if (esz == 2) { emitline("\tMOVW\tAX, (BX)\n"); }
|
emitline("\t");
|
||||||
else { if (esz == 4) { emitline("\tMOVL\tAX, (BX)\n"); }
|
emitline(isop);
|
||||||
else { emitline("\tMOVQ\tAX, (BX)\n"); };};};
|
emitline("\tAX, (BX)\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -11054,7 +11133,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff(lc.off: i64);
|
emitoff(lc.off: i64);
|
||||||
emitline("(BP), BX\n");
|
emitline("(BP), BX\n");
|
||||||
let lop: str = fieldloadop(fi);
|
let lop: str = fieldloadop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(lop);
|
emitline(lop);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
@@ -11110,7 +11189,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff(lc.off: i64);
|
emitoff(lc.off: i64);
|
||||||
emitline("(BP), BX\n");
|
emitline("(BP), BX\n");
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
@@ -11158,7 +11237,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
@@ -11283,7 +11362,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("\n");
|
emitline("\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, bn);
|
emitsymname(c, bn);
|
||||||
emitline("(SB), BX\n");
|
emitline("(SB), BX\n");
|
||||||
@@ -11298,7 +11377,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
// → push → eval rhs → combine →
|
// → push → eval rhs → combine →
|
||||||
// store. cgexpr clobbers BX, so
|
// store. cgexpr clobbers BX, so
|
||||||
// re-LEAQ for the store.
|
// re-LEAQ for the store.
|
||||||
let lop: str = fieldloadop(fi);
|
let lop: str = fieldloadop(c, fi);
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, bn);
|
emitsymname(c, bn);
|
||||||
emitline("(SB), BX\n");
|
emitline("(SB), BX\n");
|
||||||
@@ -11315,7 +11394,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tSUBQ\tAX, BX\n");
|
emitline("\tSUBQ\tAX, BX\n");
|
||||||
emitline("\tMOVQ\tBX, AX\n");
|
emitline("\tMOVQ\tBX, AX\n");
|
||||||
};
|
};
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
emitsymname(c, bn);
|
emitsymname(c, bn);
|
||||||
emitline("(SB), BX\n");
|
emitline("(SB), BX\n");
|
||||||
@@ -11406,7 +11485,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
cgexpr(c, base);
|
cgexpr(c, base);
|
||||||
emitline("\tMOVQ\tAX, BX\n");
|
emitline("\tMOVQ\tAX, BX\n");
|
||||||
emitline("\tPOPQ\tAX\n");
|
emitline("\tPOPQ\tAX\n");
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
@@ -11496,7 +11575,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let sop: str = fieldstoreop(r.leaffi);
|
let sop: str = fieldstoreop(c, r.leaffi);
|
||||||
cgexpr(c, n.rhs);
|
cgexpr(c, n.rhs);
|
||||||
if (r.isglobal) {
|
if (r.isglobal) {
|
||||||
emitline("\tLEAQ\t");
|
emitline("\tLEAQ\t");
|
||||||
@@ -11609,7 +11688,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let sop: str = fieldstoreop(ffi);
|
let sop: str = fieldstoreop(c, ffi);
|
||||||
if (isptr) {
|
if (isptr) {
|
||||||
emitline("\tMOVQ\t");
|
emitline("\tMOVQ\t");
|
||||||
emitoff(lc.off: i64);
|
emitoff(lc.off: i64);
|
||||||
@@ -12288,9 +12367,7 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
if (ps > 0) { esz = ps; };
|
if (ps > 0) { esz = ps; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
let mop: str = "MOVQ";
|
let mop: str = tnodestoreop(c, elemn, esz);
|
||||||
if (esz == 1) { mop = "MOVB"; }
|
|
||||||
else { if (esz == 4) { mop = "MOVL"; }; };
|
|
||||||
let idx: i32 = 0;
|
let idx: i32 = 0;
|
||||||
let repeat: bool = false;
|
let repeat: bool = false;
|
||||||
let e: *node = rhs.list;
|
let e: *node = rhs.list;
|
||||||
@@ -12406,7 +12483,7 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
fi = nil;
|
fi = nil;
|
||||||
} else {
|
} else {
|
||||||
let sop: str = fieldstoreop(fi);
|
let sop: str = fieldstoreop(c, fi);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(sop);
|
emitline(sop);
|
||||||
emitline("\tAX, ");
|
emitline("\tAX, ");
|
||||||
@@ -12721,16 +12798,11 @@ fn paramfieldsize(t: *node) i32 = {
|
|||||||
return 8;
|
return 8;
|
||||||
};
|
};
|
||||||
|
|
||||||
// paramissigned — does this primitive type need sign-extending on a
|
// paramissigned — does this type need sign-extending on a sub-word
|
||||||
// narrow (4B) load? Mirrors C cgen's `binds[b].signed_field` flag.
|
// (1/2/4B) load? Mirrors cstage's signed_field check via
|
||||||
fn paramissigned(t: *node) bool = {
|
// fieldissignedc (resolves TBANG / TENUM / alias chains).
|
||||||
if (t == nil) { return false; };
|
fn paramissigned(c: *cgen, t: *node) bool = {
|
||||||
if (t.kind != nkind.N_TNAME) { return false; };
|
return fieldissignedc(c, t);
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// cgforrange — lower `for (let x .. slice) body` (and the tuple-
|
// cgforrange — lower `for (let x .. slice) body` (and the tuple-
|
||||||
@@ -12804,7 +12876,7 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
|||||||
let signf: bool = false;
|
let signf: bool = false;
|
||||||
if (tp != nil) {
|
if (tp != nil) {
|
||||||
fsz = paramfieldsize(tp);
|
fsz = paramfieldsize(tp);
|
||||||
signf = paramissigned(tp);
|
signf = paramissigned(c, tp);
|
||||||
};
|
};
|
||||||
let slot_sz: i32 = fsz;
|
let slot_sz: i32 = fsz;
|
||||||
if (slot_sz < 8) { slot_sz = 8; };
|
if (slot_sz < 8) { slot_sz = 8; };
|
||||||
@@ -12832,7 +12904,7 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
|||||||
// reads `u->sub->kind` for the elem type.
|
// reads `u->sub->kind` for the elem type.
|
||||||
bind_signed[0] = false;
|
bind_signed[0] = false;
|
||||||
if (elemt != nil) {
|
if (elemt != nil) {
|
||||||
bind_signed[0] = paramissigned(elemt);
|
bind_signed[0] = paramissigned(c, elemt);
|
||||||
};
|
};
|
||||||
if (n.str.len > 0) {
|
if (n.str.len > 0) {
|
||||||
// Register with elem tnode so x.field on a loop
|
// Register with elem tnode so x.field on a loop
|
||||||
@@ -12930,15 +13002,12 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
emitline("\tADDQ\tAX, BX\n");
|
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;
|
let b: i32 = 0;
|
||||||
for (b < nbinds) {
|
for (b < nbinds) {
|
||||||
let op: str = "MOVQ";
|
let op: str = loadopsz(bind_signed[b], bind_sz[b]);
|
||||||
if (bind_sz[b] == 1) { op = "MOVZBQ"; }
|
|
||||||
else { if (bind_sz[b] == 4) {
|
|
||||||
if (bind_signed[b]) { op = "MOVSXD"; }
|
|
||||||
else { op = "MOVL"; };
|
|
||||||
};};
|
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(op);
|
emitline(op);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
|
|||||||
267
test/wcc/660_field_signed.c
Normal file
267
test/wcc/660_field_signed.c
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
/*
|
||||||
|
* 660_field_signed — sub-word signed struct/tuple/index field LOAD
|
||||||
|
* must sign-extend (MOVSBQ / MOVSWQ / MOVSXD), not zero-extend.
|
||||||
|
* Companion to 640 (N_CAST narrow) and 650 (chained N_DOT spine);
|
||||||
|
* pins task #10's field-load fix and task #5's TY_ENUM recursion
|
||||||
|
* through the predicate. Each fixture round-trips a high-bit-set
|
||||||
|
* value through a field, then widens back to i64 and compares.
|
||||||
|
*
|
||||||
|
* Exercises both stages via `ww` (cstage) and `ww_ww` (wwstage)
|
||||||
|
* when present, so a regression on either side is caught here.
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
static int
|
||||||
|
runwait(const char *cmd)
|
||||||
|
{
|
||||||
|
int rc = system(cmd);
|
||||||
|
if (rc == -1) return -1;
|
||||||
|
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct row { const char *label; const char *src; int want; };
|
||||||
|
|
||||||
|
static const struct row rows[] = {
|
||||||
|
/* i8 field LOAD: write -1 (0xFF), widen back; must read -1, not 255. */
|
||||||
|
{ "i8_field_load",
|
||||||
|
"type box = struct { v: i8, pad: i8 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let b: box; b.v = -1i64: i8; b.pad = 0i64: i8;\n"
|
||||||
|
" let z: i64 = b.v: i64;\n"
|
||||||
|
" if (z == -1i64) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* i16 field LOAD: write -32768 (0x8000), widen back; must read -32768. */
|
||||||
|
{ "i16_field_load",
|
||||||
|
"type box = struct { v: i16, pad: i16 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let b: box; b.v = -32768i64: i16; b.pad = 0i64: i16;\n"
|
||||||
|
" let z: i64 = b.v: i64;\n"
|
||||||
|
" if (z == -32768i64) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* i32 field LOAD: pins MOVSXD on the field-read path. */
|
||||||
|
{ "i32_field_load",
|
||||||
|
"type box = struct { v: i32, pad: i32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let b: box; b.v = -2147483648i64: i32; b.pad = 0i32;\n"
|
||||||
|
" let z: i64 = b.v: i64;\n"
|
||||||
|
" if (z == -2147483648i64) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* u8 field LOAD with high bit: must zero-extend (regression check
|
||||||
|
* — the symmetric helper must NOT sign-extend u8). */
|
||||||
|
{ "u8_field_load",
|
||||||
|
"type box = struct { v: u8, pad: u8 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let b: box; b.v = 0xFFu64: u8; b.pad = 0u8;\n"
|
||||||
|
" let z: u64 = b.v: u64;\n"
|
||||||
|
" if (z == 0xFFu64) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* u16 / u32 field LOAD: zero-extension on the field-read path. */
|
||||||
|
{ "u16_field_load",
|
||||||
|
"type box = struct { v: u16, pad: u16 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let b: box; b.v = 0xFFFFu64: u16; b.pad = 0u16;\n"
|
||||||
|
" let z: u64 = b.v: u64;\n"
|
||||||
|
" if (z == 0xFFFFu64) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
{ "u32_field_load",
|
||||||
|
"type box = struct { v: u32, pad: u32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let b: box; b.v = 0xFFFFFFFFu64: u32; b.pad = 0u32;\n"
|
||||||
|
" let z: u64 = b.v: u64;\n"
|
||||||
|
" if (z == 0xFFFFFFFFu64) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* bool field LOAD: zero-extends (0 or 1). The new helper must not
|
||||||
|
* sign-extend bool just because it's not in typenameisunsigned. */
|
||||||
|
{ "bool_field_load",
|
||||||
|
"type box = struct { v: bool, pad: bool };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let b: box; b.v = true; b.pad = false;\n"
|
||||||
|
" if (b.v) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* Chained dotted load over an i8 leaf: spine walker must pick
|
||||||
|
* MOVSBQ at the terminal. Complements 650's i32/u32 leaf rows. */
|
||||||
|
{ "chained_i8_leaf_load",
|
||||||
|
"type inner = struct { v: i8, pad: i8 };\n"
|
||||||
|
"type outer = struct { i: inner, tag: i32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let o: outer; o.i.v = -1i64: i8; o.i.pad = 0i8; o.tag = 0;\n"
|
||||||
|
" let z: i64 = o.i.v: i64;\n"
|
||||||
|
" if (z == -1i64) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* Chained i16 leaf — pins MOVSWQ through the spine walker. */
|
||||||
|
{ "chained_i16_leaf_load",
|
||||||
|
"type inner = struct { v: i16, pad: i16 };\n"
|
||||||
|
"type outer = struct { i: inner, tag: i32 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let o: outer; o.i.v = -32768i64: i16; o.i.pad = 0i16; o.tag = 0;\n"
|
||||||
|
" let z: i64 = o.i.v: i64;\n"
|
||||||
|
" if (z == -32768i64) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* Enum-aliased signed sub-word: `type myflag = i8` field. The
|
||||||
|
* principled predicate (task #5: TY_ENUM recursion in
|
||||||
|
* type_isunsigned, alias recursion in fieldissignedc) is what
|
||||||
|
* makes this fire MOVSBQ instead of MOVZBQ. */
|
||||||
|
{ "enum_signed_i8_alias_field_load",
|
||||||
|
"type myflag = i8;\n"
|
||||||
|
"type box = struct { v: myflag, pad: i8 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let b: box; b.v = -1i64: myflag; b.pad = 0i8;\n"
|
||||||
|
" let z: i64 = b.v: i64;\n"
|
||||||
|
" if (z == -1i64) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* N_CAST narrow on enum-aliased source. Without TY_ENUM recursion
|
||||||
|
* in type_isunsigned, the symmetric narrow gate misses this case
|
||||||
|
* and the cast is a silent no-op. */
|
||||||
|
{ "enum_alias_cast_narrow",
|
||||||
|
"type myflag = i8;\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let x: i64 = 0xFF80i64;\n"
|
||||||
|
" let y: myflag = x: myflag;\n"
|
||||||
|
" let z: i64 = y: i64;\n"
|
||||||
|
" if (z == -128i64) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* Enum-aliased unsigned narrow: the symmetric helper must
|
||||||
|
* zero-extend an enum aliased to u8. */
|
||||||
|
{ "enum_unsigned_u8_alias_field_load",
|
||||||
|
"type byteflag = u8;\n"
|
||||||
|
"type box = struct { v: byteflag, pad: u8 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let b: box; b.v = 0xFFu64: byteflag; b.pad = 0u8;\n"
|
||||||
|
" let z: u64 = b.v: u64;\n"
|
||||||
|
" if (z == 0xFFu64) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
/* `*p OP= v` with *p:u32 and high bit set — pins the hidden
|
||||||
|
* bug at cgen.c N_ASSIGN deref-compound (was hardcoded MOVSXD
|
||||||
|
* for sz=4, sign-extending u32). Old path: load 0x80000001 →
|
||||||
|
* MOVSXD → 0xFFFFFFFF80000001, SHRQ 1 → 0x7FFFFFFFC0000000,
|
||||||
|
* MOVL store → 0xC0000000. New path: MOVL → 0x80000001, SHRQ 1
|
||||||
|
* → 0x40000000 (the correct u32 logical right-shift result). */
|
||||||
|
{ "deref_compound_u32_high_bit",
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let x: u32 = 0x80000001u32;\n"
|
||||||
|
" let p: *u32 = &x;\n"
|
||||||
|
" *p >>= 1u32;\n"
|
||||||
|
" if (x == 0x40000000u32) { return 42; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
run_driver(const char *driver, const struct row *r, int i)
|
||||||
|
{
|
||||||
|
char src[64], tmpdir[64], cmd[1024];
|
||||||
|
snprintf(src, sizeof src, "/tmp/wwfs_%d_%d.ww", getpid(), i);
|
||||||
|
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwfs_%d_d_%d", getpid(), i);
|
||||||
|
|
||||||
|
FILE *f = fopen(src, "wb");
|
||||||
|
if (!f) return -1;
|
||||||
|
fputs(r->src, f);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
mkdir(tmpdir, 0755);
|
||||||
|
snprintf(cmd, sizeof cmd, "cd %s && %s build %s",
|
||||||
|
tmpdir, driver, src);
|
||||||
|
if (runwait(cmd) != 0) {
|
||||||
|
fprintf(stderr, "row[%s]: build via %s failed\n",
|
||||||
|
r->label, driver);
|
||||||
|
unlink(src); rmdir(tmpdir);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *base = strrchr(src, '/');
|
||||||
|
base = base ? base + 1 : src;
|
||||||
|
char outbin[128];
|
||||||
|
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
|
||||||
|
char *dot = strrchr(outbin, '.');
|
||||||
|
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
|
||||||
|
int got = runwait(outbin);
|
||||||
|
|
||||||
|
unlink(src); unlink(outbin); rmdir(tmpdir);
|
||||||
|
return got;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
const char *bin = getenv("BIN");
|
||||||
|
if (!bin) bin = "out/bin";
|
||||||
|
char absbin[1024];
|
||||||
|
if (bin[0] != '/') {
|
||||||
|
char cwd[1024];
|
||||||
|
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||||
|
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||||
|
bin = absbin;
|
||||||
|
}
|
||||||
|
|
||||||
|
char cdrv[1024];
|
||||||
|
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||||
|
char wdrv[1024];
|
||||||
|
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||||
|
|
||||||
|
struct { const char *name; const char *path; int gated_on_existence; }
|
||||||
|
drivers[] = {
|
||||||
|
{ "cstage", cdrv, 0 },
|
||||||
|
{ "wwstage", wdrv, 1 },
|
||||||
|
{ NULL, NULL, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||||
|
int total = 0, fail = 0;
|
||||||
|
for (int d = 0; drivers[d].name; d++) {
|
||||||
|
if (drivers[d].gated_on_existence
|
||||||
|
&& access(drivers[d].path, X_OK) != 0) {
|
||||||
|
fprintf(stderr, "field_signed: skip %s (no %s)\n",
|
||||||
|
drivers[d].name, drivers[d].path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
int got = run_driver(drivers[d].path, &rows[i], i);
|
||||||
|
total++;
|
||||||
|
if (got != rows[i].want) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"field_signed[%s][%s]: exit=%d want=%d\n",
|
||||||
|
drivers[d].name, rows[i].label,
|
||||||
|
got, rows[i].want);
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fail) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"field_signed: %d/%d fixtures failed\n", fail, total);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("field_signed: %d/%d ok\n", total, total);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user