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:
@@ -6072,7 +6072,10 @@ fn nodeisstr(c: *cgen, n: *node) bool = {
|
||||
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 = {
|
||||
if (streq(nm, "u8")) { 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, "uint")) { return true; };
|
||||
if (streq(nm, "uintptr")) { return true; };
|
||||
if (streq(nm, "rune")) { return true; };
|
||||
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 = {
|
||||
if (t == nil) { return false; };
|
||||
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
|
||||
// its element a signed narrow primitive (i8/i16/i32/rune)? Used by
|
||||
// cgindex to pick MOVSXD vs MOVL at esz=4. Mirrors C cgen's
|
||||
// `signed_elem` check.
|
||||
// its element a signed narrow primitive (i8/i16/i32)? Used by
|
||||
// cgindex to pick MOVSXD vs MOVL at esz=4 (and MOVSBQ/MOVSWQ at
|
||||
// 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 = {
|
||||
if (t == nil) { return false; };
|
||||
let elem: *node = nil;
|
||||
@@ -6153,47 +6197,103 @@ fn elemissigned(t: *node) bool = {
|
||||
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 = {
|
||||
if (streq(nm, "i8")) { return true; };
|
||||
if (streq(nm, "i16")) { return true; };
|
||||
if (streq(nm, "i32")) { return true; };
|
||||
if (streq(nm, "i64")) { 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;
|
||||
};
|
||||
|
||||
// fieldloadop — pick the load instruction for a non-str struct
|
||||
// field by its declared size + signedness. Mirrors the C cgen op
|
||||
// dispatch (MOVZBQ for u8/bool/i8, MOVSXD for i32, MOVL for u32, MOVQ
|
||||
// for 8-byte). f might be nil for fields outside our struct registry.
|
||||
fn fieldloadop(f: *fieldinfo) str = {
|
||||
// field by its declared size + signedness. Mirrors cstage's
|
||||
// fldloadop: MOVZBQ/MOVSBQ for 1B, MOVZWQ/MOVSWQ for 2B,
|
||||
// MOVL/MOVSXD for 4B, MOVQ for 8B. f might be nil for fields
|
||||
// outside our struct registry.
|
||||
fn fieldloadop(c: *cgen, f: *fieldinfo) str = {
|
||||
if (f == nil) { return "MOVQ"; };
|
||||
let sz: i32 = f.fsz;
|
||||
if (sz == 1) { return "MOVZBQ"; };
|
||||
if (sz == 4) {
|
||||
let t: *node = f.tnode;
|
||||
if (t != nil) {
|
||||
if (t.kind == nkind.N_TNAME) {
|
||||
if (typenameissigned(t.str)) { return "MOVSXD"; };
|
||||
};
|
||||
};
|
||||
return "MOVL";
|
||||
};
|
||||
let sigd: bool = fieldissignedc(c, f.tnode);
|
||||
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";
|
||||
};
|
||||
|
||||
// fieldstoreop — pick the store instruction for a non-str struct
|
||||
// field by its declared size. MOVB for 1, MOVL for 4, MOVQ for 8.
|
||||
fn fieldstoreop(f: *fieldinfo) str = {
|
||||
// field by its declared size. MOVB for 1, MOVW for 2, MOVL for 4,
|
||||
// MOVQ for 8. c kept in the signature for symmetry with fieldloadop.
|
||||
fn fieldstoreop(c: *cgen, f: *fieldinfo) str = {
|
||||
if (f == nil) { return "MOVQ"; };
|
||||
let sz: i32 = f.fsz;
|
||||
if (sz == 1) { return "MOVB"; };
|
||||
if (sz == 2) { return "MOVW"; };
|
||||
if (sz == 4) { return "MOVL"; };
|
||||
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
|
||||
// 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
|
||||
@@ -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);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
@@ -8353,7 +8453,11 @@ fn cgcast(c: *cgen, n: *node) void = {
|
||||
let sz: i32 = primsize(nm);
|
||||
let is_unsigned: bool = typenameisunsigned(nm);
|
||||
let is_bool: bool = streq(nm, "bool");
|
||||
if (sz > 0) { if (sz < 8) {
|
||||
// Symmetric narrow on signed vs unsigned (task #5):
|
||||
// unsigned (incl. rune) clears upper bits; signed
|
||||
// sign-extends. bool is size 1 but neither — falls
|
||||
// through to its dedicated ANDQ $255 below.
|
||||
if (sz > 0) { if (sz < 8) { if (!is_bool) {
|
||||
if (is_unsigned) {
|
||||
if (sz == 4) {
|
||||
emitline("\tMOVL\tAX, AX\n");
|
||||
@@ -8364,16 +8468,17 @@ fn cgcast(c: *cgen, n: *node) void = {
|
||||
emitint(mask);
|
||||
emitline(", AX\n");
|
||||
};
|
||||
} else { if (is_bool) {
|
||||
emitline("\tANDQ\t$255, AX\n");
|
||||
} else { if (streq(nm, "i8")) {
|
||||
emitline("\tMOVSBQ\tAX, AX\n");
|
||||
} else { if (streq(nm, "i16")) {
|
||||
emitline("\tMOVSWQ\tAX, AX\n");
|
||||
} else { if (streq(nm, "i32")) {
|
||||
emitline("\tMOVSXD\tAX, AX\n");
|
||||
}; }; }; }; };
|
||||
}; };
|
||||
} else {
|
||||
if (sz == 1) {
|
||||
emitline("\tMOVSBQ\tAX, AX\n");
|
||||
} else { if (sz == 2) {
|
||||
emitline("\tMOVSWQ\tAX, AX\n");
|
||||
} else { if (sz == 4) {
|
||||
emitline("\tMOVSXD\tAX, AX\n");
|
||||
}; }; };
|
||||
};
|
||||
}; }; };
|
||||
if (is_bool) { emitline("\tANDQ\t$255, AX\n"); };
|
||||
};
|
||||
return;
|
||||
};
|
||||
@@ -8550,7 +8655,7 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
baselocal = localfindnode(c, bn);
|
||||
if (baselocal != nil) {
|
||||
esz = elemsizeofc(c, baselocal.tnode);
|
||||
signed_elem = elemissigned(baselocal.tnode);
|
||||
signed_elem = elemissignedc(c, baselocal.tnode);
|
||||
} else {
|
||||
let tn: *node = letvartnode(c, bn);
|
||||
if (tn != nil) {
|
||||
@@ -8558,13 +8663,13 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
isglobalarr = true;
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
signed_elem = elemissigned(tn);
|
||||
signed_elem = elemissignedc(c, tn);
|
||||
};
|
||||
if (tn.kind == nkind.N_TPTR) {
|
||||
isglobalptr = true;
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
signed_elem = elemissigned(tn);
|
||||
signed_elem = elemissignedc(c, tn);
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -8646,16 +8751,10 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tCX, BX\n");
|
||||
return;
|
||||
};
|
||||
if (esz == 1) { emitline("\tMOVZBQ\t(BX), AX\n"); }
|
||||
else { if (esz == 2) {
|
||||
if (signed_elem) { emitline("\tMOVSWQ\t(BX), AX\n"); }
|
||||
else { emitline("\tMOVZWQ\t(BX), AX\n"); };
|
||||
}
|
||||
else { if (esz == 4) {
|
||||
if (signed_elem) { emitline("\tMOVSXD\t(BX), AX\n"); }
|
||||
else { emitline("\tMOVL\t(BX), AX\n"); };
|
||||
}
|
||||
else { emitline("\tMOVQ\t(BX), AX\n"); };};};
|
||||
let lop1: str = loadopsz(signed_elem, esz);
|
||||
emitline("\t");
|
||||
emitline(lop1);
|
||||
emitline("\t(BX), AX\n");
|
||||
return;
|
||||
};
|
||||
if (baselocal != nil) {
|
||||
@@ -8693,16 +8792,10 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tCX, BX\n");
|
||||
return;
|
||||
};
|
||||
if (esz == 1) { emitline("\tMOVZBQ\t(BX), AX\n"); }
|
||||
else { if (esz == 2) {
|
||||
if (signed_elem) { emitline("\tMOVSWQ\t(BX), AX\n"); }
|
||||
else { emitline("\tMOVZWQ\t(BX), AX\n"); };
|
||||
}
|
||||
else { if (esz == 4) {
|
||||
if (signed_elem) { emitline("\tMOVSXD\t(BX), AX\n"); }
|
||||
else { emitline("\tMOVL\t(BX), AX\n"); };
|
||||
}
|
||||
else { emitline("\tMOVQ\t(BX), AX\n"); };};};
|
||||
let lop2: str = loadopsz(signed_elem, esz);
|
||||
emitline("\t");
|
||||
emitline(lop2);
|
||||
emitline("\t(BX), AX\n");
|
||||
return;
|
||||
};
|
||||
// Generic fallback when base isn't a plain ident.
|
||||
@@ -8728,16 +8821,10 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t(AX), AX\n");
|
||||
return;
|
||||
};
|
||||
if (esz == 1) { emitline("\tMOVZBQ\t(AX), AX\n"); }
|
||||
else { if (esz == 2) {
|
||||
if (signed_elem) { emitline("\tMOVSWQ\t(AX), AX\n"); }
|
||||
else { emitline("\tMOVZWQ\t(AX), AX\n"); };
|
||||
}
|
||||
else { if (esz == 4) {
|
||||
if (signed_elem) { emitline("\tMOVSXD\t(AX), AX\n"); }
|
||||
else { emitline("\tMOVL\t(AX), AX\n"); };
|
||||
}
|
||||
else { emitline("\tMOVQ\t(AX), AX\n"); };};};
|
||||
let lop3: str = loadopsz(signed_elem, esz);
|
||||
emitline("\t");
|
||||
emitline(lop3);
|
||||
emitline("\t(AX), AX\n");
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -9157,7 +9244,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitdispreg(fi.foff: i64, "BX");
|
||||
emitline(", X0\n");
|
||||
} else {
|
||||
let op: str = fieldloadop(fi);
|
||||
let op: str = fieldloadop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\t");
|
||||
@@ -9199,7 +9286,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
emitline("(BP), X0\n");
|
||||
} else {
|
||||
let op: str = fieldloadop(fi);
|
||||
let op: str = fieldloadop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\t");
|
||||
@@ -9265,9 +9352,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
let sz: i32 = slotsize(c, tp);
|
||||
let op: str = "MOVQ";
|
||||
if (sz == 1) { op = "MOVZBQ"; }
|
||||
else { if (sz == 4) { op = "MOVL"; }; };
|
||||
let op: str = tnodeloadop(c, tp, sz);
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\t");
|
||||
@@ -9402,7 +9487,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitdispreg(fi.foff: i64, "CX");
|
||||
emitline(", X0\n");
|
||||
} else {
|
||||
let op: str = fieldloadop(fi);
|
||||
let op: str = fieldloadop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\t");
|
||||
@@ -9464,7 +9549,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline(", X0\n");
|
||||
return;
|
||||
};
|
||||
let lop: str = fieldloadop(fi);
|
||||
let lop: str = fieldloadop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
@@ -9565,7 +9650,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
let lop: str = fieldloadop(r.leaffi);
|
||||
let lop: str = fieldloadop(c, r.leaffi);
|
||||
if (r.isglobal) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, r.rootname);
|
||||
@@ -9637,7 +9722,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline(", X0\n");
|
||||
return;
|
||||
};
|
||||
let lop: str = fieldloadop(fi);
|
||||
let lop: str = fieldloadop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
@@ -9735,7 +9820,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
let lop: str = fieldloadop(ffi);
|
||||
let lop: str = fieldloadop(c, ffi);
|
||||
if (isptr) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
@@ -10147,7 +10232,7 @@ fn cgalloc(c: *cgen, n: *node) void = {
|
||||
fi = nil;
|
||||
} else {
|
||||
emitline("\tMOVQ\t(SP), BX\n");
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
@@ -10201,8 +10286,14 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
if (snlocal == nil) { return; };
|
||||
let sn_off: i32 = snlocal.off;
|
||||
let esz: i32 = elemsizeof(snlocal.tnode);
|
||||
let store_op: str = "MOVQ";
|
||||
if (esz == 1) { store_op = "MOVB"; };
|
||||
let etnode: *node = nil;
|
||||
if (snlocal.tnode != nil) {
|
||||
let stk: nkind = snlocal.tnode.kind;
|
||||
if (stk == nkind.N_TSLICE) { etnode = snlocal.tnode.lhs; };
|
||||
if (stk == nkind.N_TARRAY) { etnode = snlocal.tnode.lhs; };
|
||||
if (stk == nkind.N_TPTR) { etnode = snlocal.tnode.lhs; };
|
||||
};
|
||||
let store_op: str = tnodestoreop(c, etnode, esz);
|
||||
|
||||
let vn: *node = sn.next;
|
||||
for (vn != nil) {
|
||||
@@ -10213,8 +10304,7 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
let itlocal: *local = localfindnode(c, it.str);
|
||||
if (itlocal == nil) { vn = vn.next; continue; };
|
||||
let it_off: i32 = itlocal.off;
|
||||
let load_op: str = "MOVQ";
|
||||
if (esz == 1) { load_op = "MOVZBQ"; };
|
||||
let load_op: str = tnodeloadop(c, etnode, esz);
|
||||
emitline("\tSUBQ\t$8, SP\n");
|
||||
emitline("\tMOVQ\t$0, (SP)\n");
|
||||
let ll: str = mklabel(c, "spr_l");
|
||||
@@ -10444,9 +10534,7 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
cgexpr(c, aa2);
|
||||
let op: str = "MOVQ";
|
||||
if (esz == 1) { op = "MOVB"; }
|
||||
else { if (esz == 4) { op = "MOVL"; }; };
|
||||
let op: str = tnodestoreop(c, varp.lhs, esz);
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\tAX, ");
|
||||
@@ -10795,19 +10883,10 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (tn.kind == nkind.N_TPTR) {
|
||||
let pe: *node = tn.lhs;
|
||||
if (pe != nil) {
|
||||
if (pe.kind == nkind.N_TNAME) {
|
||||
let ps: i32 = primsize(pe.str);
|
||||
if (ps == 1) {
|
||||
loadop = "MOVZBQ";
|
||||
storeop = "MOVB";
|
||||
} else { if (ps == 4) {
|
||||
if (typenameissigned(pe.str)) {
|
||||
loadop = "MOVSXD";
|
||||
} else {
|
||||
loadop = "MOVL";
|
||||
};
|
||||
storeop = "MOVL";
|
||||
}; };
|
||||
let ps: i32 = fieldsize(c, pe);
|
||||
if (ps == 1 || ps == 2 || ps == 4) {
|
||||
loadop = tnodeloadop(c, pe, ps);
|
||||
storeop = tnodestoreop(c, pe, ps);
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -11011,10 +11090,10 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tCX, 8(BX)\n");
|
||||
return;
|
||||
};
|
||||
if (esz == 1) { emitline("\tMOVB\tAX, (BX)\n"); }
|
||||
else { if (esz == 2) { emitline("\tMOVW\tAX, (BX)\n"); }
|
||||
else { if (esz == 4) { emitline("\tMOVL\tAX, (BX)\n"); }
|
||||
else { emitline("\tMOVQ\tAX, (BX)\n"); };};};
|
||||
let isop: str = tnodestoreop(c, elemtn, esz);
|
||||
emitline("\t");
|
||||
emitline(isop);
|
||||
emitline("\tAX, (BX)\n");
|
||||
return;
|
||||
};
|
||||
};
|
||||
@@ -11054,7 +11133,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
let lop: str = fieldloadop(fi);
|
||||
let lop: str = fieldloadop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
@@ -11110,7 +11189,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
@@ -11158,7 +11237,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
@@ -11283,7 +11362,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\n");
|
||||
return;
|
||||
};
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, bn);
|
||||
emitline("(SB), BX\n");
|
||||
@@ -11298,7 +11377,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
// → push → eval rhs → combine →
|
||||
// store. cgexpr clobbers BX, so
|
||||
// re-LEAQ for the store.
|
||||
let lop: str = fieldloadop(fi);
|
||||
let lop: str = fieldloadop(c, fi);
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, bn);
|
||||
emitline("(SB), BX\n");
|
||||
@@ -11315,7 +11394,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tSUBQ\tAX, BX\n");
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
};
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, bn);
|
||||
emitline("(SB), BX\n");
|
||||
@@ -11406,7 +11485,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
cgexpr(c, base);
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
@@ -11496,7 +11575,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
let sop: str = fieldstoreop(r.leaffi);
|
||||
let sop: str = fieldstoreop(c, r.leaffi);
|
||||
cgexpr(c, n.rhs);
|
||||
if (r.isglobal) {
|
||||
emitline("\tLEAQ\t");
|
||||
@@ -11609,7 +11688,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
let sop: str = fieldstoreop(ffi);
|
||||
let sop: str = fieldstoreop(c, ffi);
|
||||
if (isptr) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
@@ -12288,9 +12367,7 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
if (ps > 0) { esz = ps; };
|
||||
};
|
||||
};
|
||||
let mop: str = "MOVQ";
|
||||
if (esz == 1) { mop = "MOVB"; }
|
||||
else { if (esz == 4) { mop = "MOVL"; }; };
|
||||
let mop: str = tnodestoreop(c, elemn, esz);
|
||||
let idx: i32 = 0;
|
||||
let repeat: bool = false;
|
||||
let e: *node = rhs.list;
|
||||
@@ -12406,7 +12483,7 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
emitline("(BP)\n");
|
||||
fi = nil;
|
||||
} else {
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
@@ -12721,16 +12798,11 @@ fn paramfieldsize(t: *node) i32 = {
|
||||
return 8;
|
||||
};
|
||||
|
||||
// paramissigned — does this primitive type need sign-extending on a
|
||||
// narrow (4B) load? Mirrors C cgen's `binds[b].signed_field` flag.
|
||||
fn paramissigned(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind != nkind.N_TNAME) { return false; };
|
||||
let nm: str = t.str;
|
||||
if (streq(nm, "i8")) { return true; };
|
||||
if (streq(nm, "i16")) { return true; };
|
||||
if (streq(nm, "i32")) { return true; };
|
||||
return false;
|
||||
// paramissigned — does this type need sign-extending on a sub-word
|
||||
// (1/2/4B) load? Mirrors cstage's signed_field check via
|
||||
// fieldissignedc (resolves TBANG / TENUM / alias chains).
|
||||
fn paramissigned(c: *cgen, t: *node) bool = {
|
||||
return fieldissignedc(c, t);
|
||||
};
|
||||
|
||||
// cgforrange — lower `for (let x .. slice) body` (and the tuple-
|
||||
@@ -12804,7 +12876,7 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
||||
let signf: bool = false;
|
||||
if (tp != nil) {
|
||||
fsz = paramfieldsize(tp);
|
||||
signf = paramissigned(tp);
|
||||
signf = paramissigned(c, tp);
|
||||
};
|
||||
let slot_sz: i32 = fsz;
|
||||
if (slot_sz < 8) { slot_sz = 8; };
|
||||
@@ -12832,7 +12904,7 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
||||
// reads `u->sub->kind` for the elem type.
|
||||
bind_signed[0] = false;
|
||||
if (elemt != nil) {
|
||||
bind_signed[0] = paramissigned(elemt);
|
||||
bind_signed[0] = paramissigned(c, elemt);
|
||||
};
|
||||
if (n.str.len > 0) {
|
||||
// Register with elem tnode so x.field on a loop
|
||||
@@ -12930,15 +13002,12 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
||||
};
|
||||
emitline("\tADDQ\tAX, BX\n");
|
||||
|
||||
// Per-binding load from BX+foff.
|
||||
// Per-binding load from BX+foff. Signedness comes from bind_signed
|
||||
// (set via paramissigned → fieldissignedc), so enum-aliased narrows
|
||||
// pick the right MOVS*Q without a literal-name gate.
|
||||
let b: i32 = 0;
|
||||
for (b < nbinds) {
|
||||
let op: str = "MOVQ";
|
||||
if (bind_sz[b] == 1) { op = "MOVZBQ"; }
|
||||
else { if (bind_sz[b] == 4) {
|
||||
if (bind_signed[b]) { op = "MOVSXD"; }
|
||||
else { op = "MOVL"; };
|
||||
};};
|
||||
let op: str = loadopsz(bind_signed[b], bind_sz[b]);
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\t");
|
||||
|
||||
@@ -403,7 +403,11 @@ fn cgcast(c: *cgen, n: *node) void = {
|
||||
let sz: i32 = primsize(nm);
|
||||
let is_unsigned: bool = typenameisunsigned(nm);
|
||||
let is_bool: bool = streq(nm, "bool");
|
||||
if (sz > 0) { if (sz < 8) {
|
||||
// Symmetric narrow on signed vs unsigned (task #5):
|
||||
// unsigned (incl. rune) clears upper bits; signed
|
||||
// sign-extends. bool is size 1 but neither — falls
|
||||
// through to its dedicated ANDQ $255 below.
|
||||
if (sz > 0) { if (sz < 8) { if (!is_bool) {
|
||||
if (is_unsigned) {
|
||||
if (sz == 4) {
|
||||
emitline("\tMOVL\tAX, AX\n");
|
||||
@@ -414,16 +418,17 @@ fn cgcast(c: *cgen, n: *node) void = {
|
||||
emitint(mask);
|
||||
emitline(", AX\n");
|
||||
};
|
||||
} else { if (is_bool) {
|
||||
emitline("\tANDQ\t$255, AX\n");
|
||||
} else { if (streq(nm, "i8")) {
|
||||
emitline("\tMOVSBQ\tAX, AX\n");
|
||||
} else { if (streq(nm, "i16")) {
|
||||
emitline("\tMOVSWQ\tAX, AX\n");
|
||||
} else { if (streq(nm, "i32")) {
|
||||
emitline("\tMOVSXD\tAX, AX\n");
|
||||
}; }; }; }; };
|
||||
}; };
|
||||
} else {
|
||||
if (sz == 1) {
|
||||
emitline("\tMOVSBQ\tAX, AX\n");
|
||||
} else { if (sz == 2) {
|
||||
emitline("\tMOVSWQ\tAX, AX\n");
|
||||
} else { if (sz == 4) {
|
||||
emitline("\tMOVSXD\tAX, AX\n");
|
||||
}; }; };
|
||||
};
|
||||
}; }; };
|
||||
if (is_bool) { emitline("\tANDQ\t$255, AX\n"); };
|
||||
};
|
||||
return;
|
||||
};
|
||||
@@ -600,7 +605,7 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
baselocal = localfindnode(c, bn);
|
||||
if (baselocal != nil) {
|
||||
esz = elemsizeofc(c, baselocal.tnode);
|
||||
signed_elem = elemissigned(baselocal.tnode);
|
||||
signed_elem = elemissignedc(c, baselocal.tnode);
|
||||
} else {
|
||||
let tn: *node = letvartnode(c, bn);
|
||||
if (tn != nil) {
|
||||
@@ -608,13 +613,13 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
isglobalarr = true;
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
signed_elem = elemissigned(tn);
|
||||
signed_elem = elemissignedc(c, tn);
|
||||
};
|
||||
if (tn.kind == nkind.N_TPTR) {
|
||||
isglobalptr = true;
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
signed_elem = elemissigned(tn);
|
||||
signed_elem = elemissignedc(c, tn);
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -696,16 +701,10 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tCX, BX\n");
|
||||
return;
|
||||
};
|
||||
if (esz == 1) { emitline("\tMOVZBQ\t(BX), AX\n"); }
|
||||
else { if (esz == 2) {
|
||||
if (signed_elem) { emitline("\tMOVSWQ\t(BX), AX\n"); }
|
||||
else { emitline("\tMOVZWQ\t(BX), AX\n"); };
|
||||
}
|
||||
else { if (esz == 4) {
|
||||
if (signed_elem) { emitline("\tMOVSXD\t(BX), AX\n"); }
|
||||
else { emitline("\tMOVL\t(BX), AX\n"); };
|
||||
}
|
||||
else { emitline("\tMOVQ\t(BX), AX\n"); };};};
|
||||
let lop1: str = loadopsz(signed_elem, esz);
|
||||
emitline("\t");
|
||||
emitline(lop1);
|
||||
emitline("\t(BX), AX\n");
|
||||
return;
|
||||
};
|
||||
if (baselocal != nil) {
|
||||
@@ -743,16 +742,10 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tCX, BX\n");
|
||||
return;
|
||||
};
|
||||
if (esz == 1) { emitline("\tMOVZBQ\t(BX), AX\n"); }
|
||||
else { if (esz == 2) {
|
||||
if (signed_elem) { emitline("\tMOVSWQ\t(BX), AX\n"); }
|
||||
else { emitline("\tMOVZWQ\t(BX), AX\n"); };
|
||||
}
|
||||
else { if (esz == 4) {
|
||||
if (signed_elem) { emitline("\tMOVSXD\t(BX), AX\n"); }
|
||||
else { emitline("\tMOVL\t(BX), AX\n"); };
|
||||
}
|
||||
else { emitline("\tMOVQ\t(BX), AX\n"); };};};
|
||||
let lop2: str = loadopsz(signed_elem, esz);
|
||||
emitline("\t");
|
||||
emitline(lop2);
|
||||
emitline("\t(BX), AX\n");
|
||||
return;
|
||||
};
|
||||
// Generic fallback when base isn't a plain ident.
|
||||
@@ -778,16 +771,10 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t(AX), AX\n");
|
||||
return;
|
||||
};
|
||||
if (esz == 1) { emitline("\tMOVZBQ\t(AX), AX\n"); }
|
||||
else { if (esz == 2) {
|
||||
if (signed_elem) { emitline("\tMOVSWQ\t(AX), AX\n"); }
|
||||
else { emitline("\tMOVZWQ\t(AX), AX\n"); };
|
||||
}
|
||||
else { if (esz == 4) {
|
||||
if (signed_elem) { emitline("\tMOVSXD\t(AX), AX\n"); }
|
||||
else { emitline("\tMOVL\t(AX), AX\n"); };
|
||||
}
|
||||
else { emitline("\tMOVQ\t(AX), AX\n"); };};};
|
||||
let lop3: str = loadopsz(signed_elem, esz);
|
||||
emitline("\t");
|
||||
emitline(lop3);
|
||||
emitline("\t(AX), AX\n");
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -1207,7 +1194,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitdispreg(fi.foff: i64, "BX");
|
||||
emitline(", X0\n");
|
||||
} else {
|
||||
let op: str = fieldloadop(fi);
|
||||
let op: str = fieldloadop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\t");
|
||||
@@ -1249,7 +1236,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
emitline("(BP), X0\n");
|
||||
} else {
|
||||
let op: str = fieldloadop(fi);
|
||||
let op: str = fieldloadop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\t");
|
||||
@@ -1315,9 +1302,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
let sz: i32 = slotsize(c, tp);
|
||||
let op: str = "MOVQ";
|
||||
if (sz == 1) { op = "MOVZBQ"; }
|
||||
else { if (sz == 4) { op = "MOVL"; }; };
|
||||
let op: str = tnodeloadop(c, tp, sz);
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\t");
|
||||
@@ -1452,7 +1437,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitdispreg(fi.foff: i64, "CX");
|
||||
emitline(", X0\n");
|
||||
} else {
|
||||
let op: str = fieldloadop(fi);
|
||||
let op: str = fieldloadop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\t");
|
||||
@@ -1514,7 +1499,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline(", X0\n");
|
||||
return;
|
||||
};
|
||||
let lop: str = fieldloadop(fi);
|
||||
let lop: str = fieldloadop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
@@ -1615,7 +1600,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
let lop: str = fieldloadop(r.leaffi);
|
||||
let lop: str = fieldloadop(c, r.leaffi);
|
||||
if (r.isglobal) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, r.rootname);
|
||||
@@ -1687,7 +1672,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline(", X0\n");
|
||||
return;
|
||||
};
|
||||
let lop: str = fieldloadop(fi);
|
||||
let lop: str = fieldloadop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
@@ -1785,7 +1770,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
let lop: str = fieldloadop(ffi);
|
||||
let lop: str = fieldloadop(c, ffi);
|
||||
if (isptr) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
@@ -2197,7 +2182,7 @@ fn cgalloc(c: *cgen, n: *node) void = {
|
||||
fi = nil;
|
||||
} else {
|
||||
emitline("\tMOVQ\t(SP), BX\n");
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
@@ -2251,8 +2236,14 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
if (snlocal == nil) { return; };
|
||||
let sn_off: i32 = snlocal.off;
|
||||
let esz: i32 = elemsizeof(snlocal.tnode);
|
||||
let store_op: str = "MOVQ";
|
||||
if (esz == 1) { store_op = "MOVB"; };
|
||||
let etnode: *node = nil;
|
||||
if (snlocal.tnode != nil) {
|
||||
let stk: nkind = snlocal.tnode.kind;
|
||||
if (stk == nkind.N_TSLICE) { etnode = snlocal.tnode.lhs; };
|
||||
if (stk == nkind.N_TARRAY) { etnode = snlocal.tnode.lhs; };
|
||||
if (stk == nkind.N_TPTR) { etnode = snlocal.tnode.lhs; };
|
||||
};
|
||||
let store_op: str = tnodestoreop(c, etnode, esz);
|
||||
|
||||
let vn: *node = sn.next;
|
||||
for (vn != nil) {
|
||||
@@ -2263,8 +2254,7 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
let itlocal: *local = localfindnode(c, it.str);
|
||||
if (itlocal == nil) { vn = vn.next; continue; };
|
||||
let it_off: i32 = itlocal.off;
|
||||
let load_op: str = "MOVQ";
|
||||
if (esz == 1) { load_op = "MOVZBQ"; };
|
||||
let load_op: str = tnodeloadop(c, etnode, esz);
|
||||
emitline("\tSUBQ\t$8, SP\n");
|
||||
emitline("\tMOVQ\t$0, (SP)\n");
|
||||
let ll: str = mklabel(c, "spr_l");
|
||||
@@ -2494,9 +2484,7 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
cgexpr(c, aa2);
|
||||
let op: str = "MOVQ";
|
||||
if (esz == 1) { op = "MOVB"; }
|
||||
else { if (esz == 4) { op = "MOVL"; }; };
|
||||
let op: str = tnodestoreop(c, varp.lhs, esz);
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\tAX, ");
|
||||
@@ -2845,19 +2833,10 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (tn.kind == nkind.N_TPTR) {
|
||||
let pe: *node = tn.lhs;
|
||||
if (pe != nil) {
|
||||
if (pe.kind == nkind.N_TNAME) {
|
||||
let ps: i32 = primsize(pe.str);
|
||||
if (ps == 1) {
|
||||
loadop = "MOVZBQ";
|
||||
storeop = "MOVB";
|
||||
} else { if (ps == 4) {
|
||||
if (typenameissigned(pe.str)) {
|
||||
loadop = "MOVSXD";
|
||||
} else {
|
||||
loadop = "MOVL";
|
||||
};
|
||||
storeop = "MOVL";
|
||||
}; };
|
||||
let ps: i32 = fieldsize(c, pe);
|
||||
if (ps == 1 || ps == 2 || ps == 4) {
|
||||
loadop = tnodeloadop(c, pe, ps);
|
||||
storeop = tnodestoreop(c, pe, ps);
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -3061,10 +3040,10 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tCX, 8(BX)\n");
|
||||
return;
|
||||
};
|
||||
if (esz == 1) { emitline("\tMOVB\tAX, (BX)\n"); }
|
||||
else { if (esz == 2) { emitline("\tMOVW\tAX, (BX)\n"); }
|
||||
else { if (esz == 4) { emitline("\tMOVL\tAX, (BX)\n"); }
|
||||
else { emitline("\tMOVQ\tAX, (BX)\n"); };};};
|
||||
let isop: str = tnodestoreop(c, elemtn, esz);
|
||||
emitline("\t");
|
||||
emitline(isop);
|
||||
emitline("\tAX, (BX)\n");
|
||||
return;
|
||||
};
|
||||
};
|
||||
@@ -3104,7 +3083,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
let lop: str = fieldloadop(fi);
|
||||
let lop: str = fieldloadop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
@@ -3160,7 +3139,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
@@ -3208,7 +3187,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
@@ -3333,7 +3312,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\n");
|
||||
return;
|
||||
};
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, bn);
|
||||
emitline("(SB), BX\n");
|
||||
@@ -3348,7 +3327,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
// → push → eval rhs → combine →
|
||||
// store. cgexpr clobbers BX, so
|
||||
// re-LEAQ for the store.
|
||||
let lop: str = fieldloadop(fi);
|
||||
let lop: str = fieldloadop(c, fi);
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, bn);
|
||||
emitline("(SB), BX\n");
|
||||
@@ -3365,7 +3344,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tSUBQ\tAX, BX\n");
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
};
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, bn);
|
||||
emitline("(SB), BX\n");
|
||||
@@ -3456,7 +3435,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
cgexpr(c, base);
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
@@ -3546,7 +3525,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
let sop: str = fieldstoreop(r.leaffi);
|
||||
let sop: str = fieldstoreop(c, r.leaffi);
|
||||
cgexpr(c, n.rhs);
|
||||
if (r.isglobal) {
|
||||
emitline("\tLEAQ\t");
|
||||
@@ -3659,7 +3638,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
let sop: str = fieldstoreop(ffi);
|
||||
let sop: str = fieldstoreop(c, ffi);
|
||||
if (isptr) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
|
||||
@@ -373,9 +373,7 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
if (ps > 0) { esz = ps; };
|
||||
};
|
||||
};
|
||||
let mop: str = "MOVQ";
|
||||
if (esz == 1) { mop = "MOVB"; }
|
||||
else { if (esz == 4) { mop = "MOVL"; }; };
|
||||
let mop: str = tnodestoreop(c, elemn, esz);
|
||||
let idx: i32 = 0;
|
||||
let repeat: bool = false;
|
||||
let e: *node = rhs.list;
|
||||
@@ -491,7 +489,7 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
emitline("(BP)\n");
|
||||
fi = nil;
|
||||
} else {
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
@@ -806,16 +804,11 @@ fn paramfieldsize(t: *node) i32 = {
|
||||
return 8;
|
||||
};
|
||||
|
||||
// paramissigned — does this primitive type need sign-extending on a
|
||||
// narrow (4B) load? Mirrors C cgen's `binds[b].signed_field` flag.
|
||||
fn paramissigned(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind != nkind.N_TNAME) { return false; };
|
||||
let nm: str = t.str;
|
||||
if (streq(nm, "i8")) { return true; };
|
||||
if (streq(nm, "i16")) { return true; };
|
||||
if (streq(nm, "i32")) { return true; };
|
||||
return false;
|
||||
// paramissigned — does this type need sign-extending on a sub-word
|
||||
// (1/2/4B) load? Mirrors cstage's signed_field check via
|
||||
// fieldissignedc (resolves TBANG / TENUM / alias chains).
|
||||
fn paramissigned(c: *cgen, t: *node) bool = {
|
||||
return fieldissignedc(c, t);
|
||||
};
|
||||
|
||||
// cgforrange — lower `for (let x .. slice) body` (and the tuple-
|
||||
@@ -889,7 +882,7 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
||||
let signf: bool = false;
|
||||
if (tp != nil) {
|
||||
fsz = paramfieldsize(tp);
|
||||
signf = paramissigned(tp);
|
||||
signf = paramissigned(c, tp);
|
||||
};
|
||||
let slot_sz: i32 = fsz;
|
||||
if (slot_sz < 8) { slot_sz = 8; };
|
||||
@@ -917,7 +910,7 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
||||
// reads `u->sub->kind` for the elem type.
|
||||
bind_signed[0] = false;
|
||||
if (elemt != nil) {
|
||||
bind_signed[0] = paramissigned(elemt);
|
||||
bind_signed[0] = paramissigned(c, elemt);
|
||||
};
|
||||
if (n.str.len > 0) {
|
||||
// Register with elem tnode so x.field on a loop
|
||||
@@ -1015,15 +1008,12 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
||||
};
|
||||
emitline("\tADDQ\tAX, BX\n");
|
||||
|
||||
// Per-binding load from BX+foff.
|
||||
// Per-binding load from BX+foff. Signedness comes from bind_signed
|
||||
// (set via paramissigned → fieldissignedc), so enum-aliased narrows
|
||||
// pick the right MOVS*Q without a literal-name gate.
|
||||
let b: i32 = 0;
|
||||
for (b < nbinds) {
|
||||
let op: str = "MOVQ";
|
||||
if (bind_sz[b] == 1) { op = "MOVZBQ"; }
|
||||
else { if (bind_sz[b] == 4) {
|
||||
if (bind_signed[b]) { op = "MOVSXD"; }
|
||||
else { op = "MOVL"; };
|
||||
};};
|
||||
let op: str = loadopsz(bind_signed[b], bind_sz[b]);
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\t");
|
||||
|
||||
@@ -502,7 +502,10 @@ fn nodeisstr(c: *cgen, n: *node) bool = {
|
||||
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 = {
|
||||
if (streq(nm, "u8")) { 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, "uint")) { return true; };
|
||||
if (streq(nm, "uintptr")) { return true; };
|
||||
if (streq(nm, "rune")) { return true; };
|
||||
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 = {
|
||||
if (t == nil) { return false; };
|
||||
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
|
||||
// its element a signed narrow primitive (i8/i16/i32/rune)? Used by
|
||||
// cgindex to pick MOVSXD vs MOVL at esz=4. Mirrors C cgen's
|
||||
// `signed_elem` check.
|
||||
// its element a signed narrow primitive (i8/i16/i32)? Used by
|
||||
// cgindex to pick MOVSXD vs MOVL at esz=4 (and MOVSBQ/MOVSWQ at
|
||||
// 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 = {
|
||||
if (t == nil) { return false; };
|
||||
let elem: *node = nil;
|
||||
@@ -583,47 +627,103 @@ fn elemissigned(t: *node) bool = {
|
||||
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 = {
|
||||
if (streq(nm, "i8")) { return true; };
|
||||
if (streq(nm, "i16")) { return true; };
|
||||
if (streq(nm, "i32")) { return true; };
|
||||
if (streq(nm, "i64")) { 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;
|
||||
};
|
||||
|
||||
// fieldloadop — pick the load instruction for a non-str struct
|
||||
// field by its declared size + signedness. Mirrors the C cgen op
|
||||
// dispatch (MOVZBQ for u8/bool/i8, MOVSXD for i32, MOVL for u32, MOVQ
|
||||
// for 8-byte). f might be nil for fields outside our struct registry.
|
||||
fn fieldloadop(f: *fieldinfo) str = {
|
||||
// field by its declared size + signedness. Mirrors cstage's
|
||||
// fldloadop: MOVZBQ/MOVSBQ for 1B, MOVZWQ/MOVSWQ for 2B,
|
||||
// MOVL/MOVSXD for 4B, MOVQ for 8B. f might be nil for fields
|
||||
// outside our struct registry.
|
||||
fn fieldloadop(c: *cgen, f: *fieldinfo) str = {
|
||||
if (f == nil) { return "MOVQ"; };
|
||||
let sz: i32 = f.fsz;
|
||||
if (sz == 1) { return "MOVZBQ"; };
|
||||
if (sz == 4) {
|
||||
let t: *node = f.tnode;
|
||||
if (t != nil) {
|
||||
if (t.kind == nkind.N_TNAME) {
|
||||
if (typenameissigned(t.str)) { return "MOVSXD"; };
|
||||
};
|
||||
};
|
||||
return "MOVL";
|
||||
};
|
||||
let sigd: bool = fieldissignedc(c, f.tnode);
|
||||
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";
|
||||
};
|
||||
|
||||
// fieldstoreop — pick the store instruction for a non-str struct
|
||||
// field by its declared size. MOVB for 1, MOVL for 4, MOVQ for 8.
|
||||
fn fieldstoreop(f: *fieldinfo) str = {
|
||||
// field by its declared size. MOVB for 1, MOVW for 2, MOVL for 4,
|
||||
// MOVQ for 8. c kept in the signature for symmetry with fieldloadop.
|
||||
fn fieldstoreop(c: *cgen, f: *fieldinfo) str = {
|
||||
if (f == nil) { return "MOVQ"; };
|
||||
let sz: i32 = f.fsz;
|
||||
if (sz == 1) { return "MOVB"; };
|
||||
if (sz == 2) { return "MOVW"; };
|
||||
if (sz == 4) { return "MOVL"; };
|
||||
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
|
||||
// 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
|
||||
@@ -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);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
|
||||
@@ -6072,7 +6072,10 @@ fn nodeisstr(c: *cgen, n: *node) bool = {
|
||||
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 = {
|
||||
if (streq(nm, "u8")) { 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, "uint")) { return true; };
|
||||
if (streq(nm, "uintptr")) { return true; };
|
||||
if (streq(nm, "rune")) { return true; };
|
||||
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 = {
|
||||
if (t == nil) { return false; };
|
||||
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
|
||||
// its element a signed narrow primitive (i8/i16/i32/rune)? Used by
|
||||
// cgindex to pick MOVSXD vs MOVL at esz=4. Mirrors C cgen's
|
||||
// `signed_elem` check.
|
||||
// its element a signed narrow primitive (i8/i16/i32)? Used by
|
||||
// cgindex to pick MOVSXD vs MOVL at esz=4 (and MOVSBQ/MOVSWQ at
|
||||
// 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 = {
|
||||
if (t == nil) { return false; };
|
||||
let elem: *node = nil;
|
||||
@@ -6153,47 +6197,103 @@ fn elemissigned(t: *node) bool = {
|
||||
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 = {
|
||||
if (streq(nm, "i8")) { return true; };
|
||||
if (streq(nm, "i16")) { return true; };
|
||||
if (streq(nm, "i32")) { return true; };
|
||||
if (streq(nm, "i64")) { 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;
|
||||
};
|
||||
|
||||
// fieldloadop — pick the load instruction for a non-str struct
|
||||
// field by its declared size + signedness. Mirrors the C cgen op
|
||||
// dispatch (MOVZBQ for u8/bool/i8, MOVSXD for i32, MOVL for u32, MOVQ
|
||||
// for 8-byte). f might be nil for fields outside our struct registry.
|
||||
fn fieldloadop(f: *fieldinfo) str = {
|
||||
// field by its declared size + signedness. Mirrors cstage's
|
||||
// fldloadop: MOVZBQ/MOVSBQ for 1B, MOVZWQ/MOVSWQ for 2B,
|
||||
// MOVL/MOVSXD for 4B, MOVQ for 8B. f might be nil for fields
|
||||
// outside our struct registry.
|
||||
fn fieldloadop(c: *cgen, f: *fieldinfo) str = {
|
||||
if (f == nil) { return "MOVQ"; };
|
||||
let sz: i32 = f.fsz;
|
||||
if (sz == 1) { return "MOVZBQ"; };
|
||||
if (sz == 4) {
|
||||
let t: *node = f.tnode;
|
||||
if (t != nil) {
|
||||
if (t.kind == nkind.N_TNAME) {
|
||||
if (typenameissigned(t.str)) { return "MOVSXD"; };
|
||||
};
|
||||
};
|
||||
return "MOVL";
|
||||
};
|
||||
let sigd: bool = fieldissignedc(c, f.tnode);
|
||||
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";
|
||||
};
|
||||
|
||||
// fieldstoreop — pick the store instruction for a non-str struct
|
||||
// field by its declared size. MOVB for 1, MOVL for 4, MOVQ for 8.
|
||||
fn fieldstoreop(f: *fieldinfo) str = {
|
||||
// field by its declared size. MOVB for 1, MOVW for 2, MOVL for 4,
|
||||
// MOVQ for 8. c kept in the signature for symmetry with fieldloadop.
|
||||
fn fieldstoreop(c: *cgen, f: *fieldinfo) str = {
|
||||
if (f == nil) { return "MOVQ"; };
|
||||
let sz: i32 = f.fsz;
|
||||
if (sz == 1) { return "MOVB"; };
|
||||
if (sz == 2) { return "MOVW"; };
|
||||
if (sz == 4) { return "MOVL"; };
|
||||
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
|
||||
// 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
|
||||
@@ -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);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
@@ -8353,7 +8453,11 @@ fn cgcast(c: *cgen, n: *node) void = {
|
||||
let sz: i32 = primsize(nm);
|
||||
let is_unsigned: bool = typenameisunsigned(nm);
|
||||
let is_bool: bool = streq(nm, "bool");
|
||||
if (sz > 0) { if (sz < 8) {
|
||||
// Symmetric narrow on signed vs unsigned (task #5):
|
||||
// unsigned (incl. rune) clears upper bits; signed
|
||||
// sign-extends. bool is size 1 but neither — falls
|
||||
// through to its dedicated ANDQ $255 below.
|
||||
if (sz > 0) { if (sz < 8) { if (!is_bool) {
|
||||
if (is_unsigned) {
|
||||
if (sz == 4) {
|
||||
emitline("\tMOVL\tAX, AX\n");
|
||||
@@ -8364,16 +8468,17 @@ fn cgcast(c: *cgen, n: *node) void = {
|
||||
emitint(mask);
|
||||
emitline(", AX\n");
|
||||
};
|
||||
} else { if (is_bool) {
|
||||
emitline("\tANDQ\t$255, AX\n");
|
||||
} else { if (streq(nm, "i8")) {
|
||||
emitline("\tMOVSBQ\tAX, AX\n");
|
||||
} else { if (streq(nm, "i16")) {
|
||||
emitline("\tMOVSWQ\tAX, AX\n");
|
||||
} else { if (streq(nm, "i32")) {
|
||||
emitline("\tMOVSXD\tAX, AX\n");
|
||||
}; }; }; }; };
|
||||
}; };
|
||||
} else {
|
||||
if (sz == 1) {
|
||||
emitline("\tMOVSBQ\tAX, AX\n");
|
||||
} else { if (sz == 2) {
|
||||
emitline("\tMOVSWQ\tAX, AX\n");
|
||||
} else { if (sz == 4) {
|
||||
emitline("\tMOVSXD\tAX, AX\n");
|
||||
}; }; };
|
||||
};
|
||||
}; }; };
|
||||
if (is_bool) { emitline("\tANDQ\t$255, AX\n"); };
|
||||
};
|
||||
return;
|
||||
};
|
||||
@@ -8550,7 +8655,7 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
baselocal = localfindnode(c, bn);
|
||||
if (baselocal != nil) {
|
||||
esz = elemsizeofc(c, baselocal.tnode);
|
||||
signed_elem = elemissigned(baselocal.tnode);
|
||||
signed_elem = elemissignedc(c, baselocal.tnode);
|
||||
} else {
|
||||
let tn: *node = letvartnode(c, bn);
|
||||
if (tn != nil) {
|
||||
@@ -8558,13 +8663,13 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
isglobalarr = true;
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
signed_elem = elemissigned(tn);
|
||||
signed_elem = elemissignedc(c, tn);
|
||||
};
|
||||
if (tn.kind == nkind.N_TPTR) {
|
||||
isglobalptr = true;
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
signed_elem = elemissigned(tn);
|
||||
signed_elem = elemissignedc(c, tn);
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -8646,16 +8751,10 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tCX, BX\n");
|
||||
return;
|
||||
};
|
||||
if (esz == 1) { emitline("\tMOVZBQ\t(BX), AX\n"); }
|
||||
else { if (esz == 2) {
|
||||
if (signed_elem) { emitline("\tMOVSWQ\t(BX), AX\n"); }
|
||||
else { emitline("\tMOVZWQ\t(BX), AX\n"); };
|
||||
}
|
||||
else { if (esz == 4) {
|
||||
if (signed_elem) { emitline("\tMOVSXD\t(BX), AX\n"); }
|
||||
else { emitline("\tMOVL\t(BX), AX\n"); };
|
||||
}
|
||||
else { emitline("\tMOVQ\t(BX), AX\n"); };};};
|
||||
let lop1: str = loadopsz(signed_elem, esz);
|
||||
emitline("\t");
|
||||
emitline(lop1);
|
||||
emitline("\t(BX), AX\n");
|
||||
return;
|
||||
};
|
||||
if (baselocal != nil) {
|
||||
@@ -8693,16 +8792,10 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tCX, BX\n");
|
||||
return;
|
||||
};
|
||||
if (esz == 1) { emitline("\tMOVZBQ\t(BX), AX\n"); }
|
||||
else { if (esz == 2) {
|
||||
if (signed_elem) { emitline("\tMOVSWQ\t(BX), AX\n"); }
|
||||
else { emitline("\tMOVZWQ\t(BX), AX\n"); };
|
||||
}
|
||||
else { if (esz == 4) {
|
||||
if (signed_elem) { emitline("\tMOVSXD\t(BX), AX\n"); }
|
||||
else { emitline("\tMOVL\t(BX), AX\n"); };
|
||||
}
|
||||
else { emitline("\tMOVQ\t(BX), AX\n"); };};};
|
||||
let lop2: str = loadopsz(signed_elem, esz);
|
||||
emitline("\t");
|
||||
emitline(lop2);
|
||||
emitline("\t(BX), AX\n");
|
||||
return;
|
||||
};
|
||||
// Generic fallback when base isn't a plain ident.
|
||||
@@ -8728,16 +8821,10 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t(AX), AX\n");
|
||||
return;
|
||||
};
|
||||
if (esz == 1) { emitline("\tMOVZBQ\t(AX), AX\n"); }
|
||||
else { if (esz == 2) {
|
||||
if (signed_elem) { emitline("\tMOVSWQ\t(AX), AX\n"); }
|
||||
else { emitline("\tMOVZWQ\t(AX), AX\n"); };
|
||||
}
|
||||
else { if (esz == 4) {
|
||||
if (signed_elem) { emitline("\tMOVSXD\t(AX), AX\n"); }
|
||||
else { emitline("\tMOVL\t(AX), AX\n"); };
|
||||
}
|
||||
else { emitline("\tMOVQ\t(AX), AX\n"); };};};
|
||||
let lop3: str = loadopsz(signed_elem, esz);
|
||||
emitline("\t");
|
||||
emitline(lop3);
|
||||
emitline("\t(AX), AX\n");
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -9157,7 +9244,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitdispreg(fi.foff: i64, "BX");
|
||||
emitline(", X0\n");
|
||||
} else {
|
||||
let op: str = fieldloadop(fi);
|
||||
let op: str = fieldloadop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\t");
|
||||
@@ -9199,7 +9286,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
emitline("(BP), X0\n");
|
||||
} else {
|
||||
let op: str = fieldloadop(fi);
|
||||
let op: str = fieldloadop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\t");
|
||||
@@ -9265,9 +9352,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
let sz: i32 = slotsize(c, tp);
|
||||
let op: str = "MOVQ";
|
||||
if (sz == 1) { op = "MOVZBQ"; }
|
||||
else { if (sz == 4) { op = "MOVL"; }; };
|
||||
let op: str = tnodeloadop(c, tp, sz);
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\t");
|
||||
@@ -9402,7 +9487,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitdispreg(fi.foff: i64, "CX");
|
||||
emitline(", X0\n");
|
||||
} else {
|
||||
let op: str = fieldloadop(fi);
|
||||
let op: str = fieldloadop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\t");
|
||||
@@ -9464,7 +9549,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline(", X0\n");
|
||||
return;
|
||||
};
|
||||
let lop: str = fieldloadop(fi);
|
||||
let lop: str = fieldloadop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
@@ -9565,7 +9650,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
let lop: str = fieldloadop(r.leaffi);
|
||||
let lop: str = fieldloadop(c, r.leaffi);
|
||||
if (r.isglobal) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, r.rootname);
|
||||
@@ -9637,7 +9722,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline(", X0\n");
|
||||
return;
|
||||
};
|
||||
let lop: str = fieldloadop(fi);
|
||||
let lop: str = fieldloadop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
@@ -9735,7 +9820,7 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
let lop: str = fieldloadop(ffi);
|
||||
let lop: str = fieldloadop(c, ffi);
|
||||
if (isptr) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
@@ -10147,7 +10232,7 @@ fn cgalloc(c: *cgen, n: *node) void = {
|
||||
fi = nil;
|
||||
} else {
|
||||
emitline("\tMOVQ\t(SP), BX\n");
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
@@ -10201,8 +10286,14 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
if (snlocal == nil) { return; };
|
||||
let sn_off: i32 = snlocal.off;
|
||||
let esz: i32 = elemsizeof(snlocal.tnode);
|
||||
let store_op: str = "MOVQ";
|
||||
if (esz == 1) { store_op = "MOVB"; };
|
||||
let etnode: *node = nil;
|
||||
if (snlocal.tnode != nil) {
|
||||
let stk: nkind = snlocal.tnode.kind;
|
||||
if (stk == nkind.N_TSLICE) { etnode = snlocal.tnode.lhs; };
|
||||
if (stk == nkind.N_TARRAY) { etnode = snlocal.tnode.lhs; };
|
||||
if (stk == nkind.N_TPTR) { etnode = snlocal.tnode.lhs; };
|
||||
};
|
||||
let store_op: str = tnodestoreop(c, etnode, esz);
|
||||
|
||||
let vn: *node = sn.next;
|
||||
for (vn != nil) {
|
||||
@@ -10213,8 +10304,7 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
let itlocal: *local = localfindnode(c, it.str);
|
||||
if (itlocal == nil) { vn = vn.next; continue; };
|
||||
let it_off: i32 = itlocal.off;
|
||||
let load_op: str = "MOVQ";
|
||||
if (esz == 1) { load_op = "MOVZBQ"; };
|
||||
let load_op: str = tnodeloadop(c, etnode, esz);
|
||||
emitline("\tSUBQ\t$8, SP\n");
|
||||
emitline("\tMOVQ\t$0, (SP)\n");
|
||||
let ll: str = mklabel(c, "spr_l");
|
||||
@@ -10444,9 +10534,7 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
cgexpr(c, aa2);
|
||||
let op: str = "MOVQ";
|
||||
if (esz == 1) { op = "MOVB"; }
|
||||
else { if (esz == 4) { op = "MOVL"; }; };
|
||||
let op: str = tnodestoreop(c, varp.lhs, esz);
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\tAX, ");
|
||||
@@ -10795,19 +10883,10 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (tn.kind == nkind.N_TPTR) {
|
||||
let pe: *node = tn.lhs;
|
||||
if (pe != nil) {
|
||||
if (pe.kind == nkind.N_TNAME) {
|
||||
let ps: i32 = primsize(pe.str);
|
||||
if (ps == 1) {
|
||||
loadop = "MOVZBQ";
|
||||
storeop = "MOVB";
|
||||
} else { if (ps == 4) {
|
||||
if (typenameissigned(pe.str)) {
|
||||
loadop = "MOVSXD";
|
||||
} else {
|
||||
loadop = "MOVL";
|
||||
};
|
||||
storeop = "MOVL";
|
||||
}; };
|
||||
let ps: i32 = fieldsize(c, pe);
|
||||
if (ps == 1 || ps == 2 || ps == 4) {
|
||||
loadop = tnodeloadop(c, pe, ps);
|
||||
storeop = tnodestoreop(c, pe, ps);
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -11011,10 +11090,10 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tCX, 8(BX)\n");
|
||||
return;
|
||||
};
|
||||
if (esz == 1) { emitline("\tMOVB\tAX, (BX)\n"); }
|
||||
else { if (esz == 2) { emitline("\tMOVW\tAX, (BX)\n"); }
|
||||
else { if (esz == 4) { emitline("\tMOVL\tAX, (BX)\n"); }
|
||||
else { emitline("\tMOVQ\tAX, (BX)\n"); };};};
|
||||
let isop: str = tnodestoreop(c, elemtn, esz);
|
||||
emitline("\t");
|
||||
emitline(isop);
|
||||
emitline("\tAX, (BX)\n");
|
||||
return;
|
||||
};
|
||||
};
|
||||
@@ -11054,7 +11133,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
let lop: str = fieldloadop(fi);
|
||||
let lop: str = fieldloadop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
@@ -11110,7 +11189,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
@@ -11158,7 +11237,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
@@ -11283,7 +11362,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\n");
|
||||
return;
|
||||
};
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, bn);
|
||||
emitline("(SB), BX\n");
|
||||
@@ -11298,7 +11377,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
// → push → eval rhs → combine →
|
||||
// store. cgexpr clobbers BX, so
|
||||
// re-LEAQ for the store.
|
||||
let lop: str = fieldloadop(fi);
|
||||
let lop: str = fieldloadop(c, fi);
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, bn);
|
||||
emitline("(SB), BX\n");
|
||||
@@ -11315,7 +11394,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tSUBQ\tAX, BX\n");
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
};
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, bn);
|
||||
emitline("(SB), BX\n");
|
||||
@@ -11406,7 +11485,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
cgexpr(c, base);
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
@@ -11496,7 +11575,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
let sop: str = fieldstoreop(r.leaffi);
|
||||
let sop: str = fieldstoreop(c, r.leaffi);
|
||||
cgexpr(c, n.rhs);
|
||||
if (r.isglobal) {
|
||||
emitline("\tLEAQ\t");
|
||||
@@ -11609,7 +11688,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
return;
|
||||
};
|
||||
let sop: str = fieldstoreop(ffi);
|
||||
let sop: str = fieldstoreop(c, ffi);
|
||||
if (isptr) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
@@ -12288,9 +12367,7 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
if (ps > 0) { esz = ps; };
|
||||
};
|
||||
};
|
||||
let mop: str = "MOVQ";
|
||||
if (esz == 1) { mop = "MOVB"; }
|
||||
else { if (esz == 4) { mop = "MOVL"; }; };
|
||||
let mop: str = tnodestoreop(c, elemn, esz);
|
||||
let idx: i32 = 0;
|
||||
let repeat: bool = false;
|
||||
let e: *node = rhs.list;
|
||||
@@ -12406,7 +12483,7 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
emitline("(BP)\n");
|
||||
fi = nil;
|
||||
} else {
|
||||
let sop: str = fieldstoreop(fi);
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
@@ -12721,16 +12798,11 @@ fn paramfieldsize(t: *node) i32 = {
|
||||
return 8;
|
||||
};
|
||||
|
||||
// paramissigned — does this primitive type need sign-extending on a
|
||||
// narrow (4B) load? Mirrors C cgen's `binds[b].signed_field` flag.
|
||||
fn paramissigned(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind != nkind.N_TNAME) { return false; };
|
||||
let nm: str = t.str;
|
||||
if (streq(nm, "i8")) { return true; };
|
||||
if (streq(nm, "i16")) { return true; };
|
||||
if (streq(nm, "i32")) { return true; };
|
||||
return false;
|
||||
// paramissigned — does this type need sign-extending on a sub-word
|
||||
// (1/2/4B) load? Mirrors cstage's signed_field check via
|
||||
// fieldissignedc (resolves TBANG / TENUM / alias chains).
|
||||
fn paramissigned(c: *cgen, t: *node) bool = {
|
||||
return fieldissignedc(c, t);
|
||||
};
|
||||
|
||||
// cgforrange — lower `for (let x .. slice) body` (and the tuple-
|
||||
@@ -12804,7 +12876,7 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
||||
let signf: bool = false;
|
||||
if (tp != nil) {
|
||||
fsz = paramfieldsize(tp);
|
||||
signf = paramissigned(tp);
|
||||
signf = paramissigned(c, tp);
|
||||
};
|
||||
let slot_sz: i32 = fsz;
|
||||
if (slot_sz < 8) { slot_sz = 8; };
|
||||
@@ -12832,7 +12904,7 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
||||
// reads `u->sub->kind` for the elem type.
|
||||
bind_signed[0] = false;
|
||||
if (elemt != nil) {
|
||||
bind_signed[0] = paramissigned(elemt);
|
||||
bind_signed[0] = paramissigned(c, elemt);
|
||||
};
|
||||
if (n.str.len > 0) {
|
||||
// Register with elem tnode so x.field on a loop
|
||||
@@ -12930,15 +13002,12 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
||||
};
|
||||
emitline("\tADDQ\tAX, BX\n");
|
||||
|
||||
// Per-binding load from BX+foff.
|
||||
// Per-binding load from BX+foff. Signedness comes from bind_signed
|
||||
// (set via paramissigned → fieldissignedc), so enum-aliased narrows
|
||||
// pick the right MOVS*Q without a literal-name gate.
|
||||
let b: i32 = 0;
|
||||
for (b < nbinds) {
|
||||
let op: str = "MOVQ";
|
||||
if (bind_sz[b] == 1) { op = "MOVZBQ"; }
|
||||
else { if (bind_sz[b] == 4) {
|
||||
if (bind_signed[b]) { op = "MOVSXD"; }
|
||||
else { op = "MOVL"; };
|
||||
};};
|
||||
let op: str = loadopsz(bind_signed[b], bind_sz[b]);
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\t");
|
||||
|
||||
Reference in New Issue
Block a user