w6c+selfhost: localloadop helper for sign-aware ident loads (closes #19)
Read-side fix dual to fldloadop: signed-narrow local/global ident loads now MOVSXD/MOVSWQ/MOVSBQ from the slot instead of raw MOVQ. Deref-stores (MOVL/MOVW/MOVB) no longer corrupt downstream i64 widens. Compound RMW restructured to gate direct-mem ADDQ/SUBQ on load_op == MOVQ. Top-level lets use LEAQ+indirect (w6a doesn't expose MOVSXD/MOVSWQ/MOVSBQ for D_EXTERN). dotchainresolve out-params restored to natural *i32 (workaround retired). selfhost/CLAUDE.md graduated.
This commit is contained in:
@@ -724,6 +724,39 @@ fn loadopsz(sigd: bool, sz: i32) str = {
|
||||
return "MOVQ";
|
||||
};
|
||||
|
||||
// localloadop — read instruction for a scalar local/let load. Same
|
||||
// dispatch as fieldloadop, but keyed on the value's own tnode. Lets
|
||||
// the caller emit MOVSXD/MOVSWQ/MOVSBQ on a signed-narrow slot instead
|
||||
// of a raw MOVQ, so a slot that was last written by a narrow deref-
|
||||
// store (`*p: *i32 = v` lowers to MOVL, only 4B) reads back as a
|
||||
// properly-sign-extended i64. The natural N_ASSIGN / N_LET paths
|
||||
// store the rhs as a sign-extended 8B word, so MOVQ accidentally
|
||||
// works; deref-stores are the only path that touches fewer bytes
|
||||
// than MOVQ reads. Mirror of cstage's localloadop in cmd/w6c/cgen.c.
|
||||
// Resolves TBANG / TENUM / TNAME-alias chains so `type err = !i32`
|
||||
// picks up size 4 the same way the cstage checker pre-computes
|
||||
// t->size — without this, aliased narrows fall through to MOVQ.
|
||||
export fn localloadop(c: *cgen, tnode: *node) str = {
|
||||
let t: *node = tnode;
|
||||
for (t != nil) {
|
||||
let k: nkind = t.kind;
|
||||
if (k == nkind.N_TBANG) { t = t.lhs; }
|
||||
else { if (k == nkind.N_TENUM) { t = t.lhs; }
|
||||
else { if (k == nkind.N_TNAME) {
|
||||
let nm: str = t.str;
|
||||
if (primsize(nm) > 0) { break; };
|
||||
let al: *node = aliaslookup(c, nm);
|
||||
if (al == nil) { break; };
|
||||
t = al;
|
||||
}
|
||||
else { break; }; }; };
|
||||
};
|
||||
let sz: i32 = fieldsize(c, t);
|
||||
if (sz != 1) { if (sz != 2) { if (sz != 4) { return "MOVQ"; }; }; };
|
||||
let sigd: bool = fieldissignedc(c, tnode);
|
||||
return loadopsz(sigd, sz);
|
||||
};
|
||||
|
||||
// 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
|
||||
@@ -2325,22 +2358,19 @@ fn cgwidentaggedstore(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz:
|
||||
// (*outisglobal false, base = *outrootoff(BP)) or top-level let
|
||||
// (*outisglobal true, base reached via LEAQ *outrootname(SB), CX).
|
||||
//
|
||||
// Numeric out-params are i64 so the deref-stores stay 8-byte (MOVQ).
|
||||
// `*p: *i32 = v` writes only 4 bytes via MOVL, leaving the caller's
|
||||
// 8-byte slot's upper half stale from its zero-init — and a later MOVQ
|
||||
// read sees the zero-extended low half, so a negative i32 root offset
|
||||
// would come back as a huge positive i64. Tracked as task #19; until
|
||||
// it lands, callers cast to i32 at the assign sites.
|
||||
// Numeric out-params are i32 — offsets fit naturally and the post-#19
|
||||
// localloadop sign-extends i32 deref-stored slots on read, so negative
|
||||
// frame offsets round-trip intact.
|
||||
export fn dotchainresolve(c: *cgen, n: *node,
|
||||
outrootname: *str, outrootoff: *i64, outtotaloff: *i64,
|
||||
outleaffi: **fieldinfo, outslicedelta: *i64,
|
||||
outrootname: *str, outrootoff: *i32, outtotaloff: *i32,
|
||||
outleaffi: **fieldinfo, outslicedelta: *i32,
|
||||
outisglobal: *bool) bool = {
|
||||
*outrootname = "";
|
||||
*outrootoff = 0i64;
|
||||
*outrootoff = 0;
|
||||
*outisglobal = false;
|
||||
*outtotaloff = 0i64;
|
||||
*outtotaloff = 0;
|
||||
*outleaffi = nil;
|
||||
*outslicedelta = -1i64;
|
||||
*outslicedelta = -1;
|
||||
if (n == nil) { return false; };
|
||||
if (n.kind != nkind.N_DOT) { return false; };
|
||||
let stk: [16]*node;
|
||||
@@ -2363,7 +2393,7 @@ export fn dotchainresolve(c: *cgen, n: *node,
|
||||
if (lc.tnode != nil) {
|
||||
if (lc.tnode.kind == nkind.N_TNAME) {
|
||||
rootstruct = lc.tnode.str;
|
||||
*outrootoff = lc.off: i64;
|
||||
*outrootoff = lc.off;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -2390,7 +2420,7 @@ export fn dotchainresolve(c: *cgen, n: *node,
|
||||
};
|
||||
if (found == nil) { return false; };
|
||||
if (i == 0) {
|
||||
*outtotaloff = *outtotaloff + (found.foff: i64);
|
||||
*outtotaloff = *outtotaloff + found.foff;
|
||||
*outleaffi = found;
|
||||
return true;
|
||||
};
|
||||
@@ -2400,27 +2430,27 @@ export fn dotchainresolve(c: *cgen, n: *node,
|
||||
if (streq(ft.str, "str")) {
|
||||
if (i != 1) { return false; };
|
||||
let pseudo: str = stk[0].str;
|
||||
let delta: i64 = -1i64;
|
||||
if (streq(pseudo, "ptr")) { delta = 0i64; }
|
||||
else { if (streq(pseudo, "len")) { delta = 8i64; }; };
|
||||
if (delta < 0i64) { return false; };
|
||||
*outtotaloff = *outtotaloff + (found.foff: i64);
|
||||
let delta: i32 = -1;
|
||||
if (streq(pseudo, "ptr")) { delta = 0; }
|
||||
else { if (streq(pseudo, "len")) { delta = 8; }; };
|
||||
if (delta < 0) { return false; };
|
||||
*outtotaloff = *outtotaloff + found.foff;
|
||||
*outslicedelta = delta;
|
||||
return true;
|
||||
};
|
||||
if (primsize(ft.str) != 0) { return false; };
|
||||
*outtotaloff = *outtotaloff + (found.foff: i64);
|
||||
*outtotaloff = *outtotaloff + found.foff;
|
||||
curstruct = ft.str;
|
||||
i -= 1;
|
||||
} else { if (ft.kind == nkind.N_TSLICE) {
|
||||
if (i != 1) { return false; };
|
||||
let pseudo: str = stk[0].str;
|
||||
let delta: i64 = -1i64;
|
||||
if (streq(pseudo, "ptr")) { delta = 0i64; }
|
||||
else { if (streq(pseudo, "len")) { delta = 8i64; }
|
||||
else { if (streq(pseudo, "cap")) { delta = 16i64; }; }; };
|
||||
if (delta < 0i64) { return false; };
|
||||
*outtotaloff = *outtotaloff + (found.foff: i64);
|
||||
let delta: i32 = -1;
|
||||
if (streq(pseudo, "ptr")) { delta = 0; }
|
||||
else { if (streq(pseudo, "len")) { delta = 8; }
|
||||
else { if (streq(pseudo, "cap")) { delta = 16; }; }; };
|
||||
if (delta < 0) { return false; };
|
||||
*outtotaloff = *outtotaloff + found.foff;
|
||||
*outslicedelta = delta;
|
||||
return true;
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user