selfhost/cmd/wcc/cgenutil: collapse localloadop onto tinfo (#47, A.6.3c)
localloadop selected MOVBQSX/MOVBQZX/MOVSWQ/MOVL/MOVQ for scalar local/let loads via an AST walk down TBANG/TENUM/TNAME-alias chains, re-consulting aliaslookup and ending in fieldsize + fieldissignedc. Three precursors retire the walk: #53 (e3237d1, scopelookupprefer fixes flat-alias collision in resolvealias), #51 (1ce0f63, INTLIT tsuffix stamp), #52 (747279c, typenameisunsigned collapse onto typeisunsigned). Plus the A.6.2 type-AST stamp invariant at check.ww L426-436. Body collapses to two nil guards + ti.size + size cascade + typeissigned(ti). cstage cmd/w6c/cgen.c:357-362 is the SSoT; wwstage now reads tinfo.size directly the same way cstage reads t->size, with TBANG / TENUM / TNAME-alias chains pre-folded by tinfofornode (check.ww:1102-1153 TNAME, 1154-1161 TBANG, 1196-1208 TENUM). Defensive nil branches mirror cstage's `(t && t->size > 0) ? sz : 8` fall-through: when type info is missing, sz fails the != 1/2/4 discriminator and the helper returns MOVQ. Both nil paths are reachable: cgenexpr.ww callers (4 sites: 643, 664, 5380, 5471) populate the *node via a search loop that may exit with nil. Signature unchanged — `c: *cgen` is retained unused for callsite stability, mirroring A.6.3a fieldissignedc(c, t) which followed the same precedent. 5 callsites in cgenexpr.ww untouched (body-only collapse). Net -33 LOC across cgenutil.ww + two .combined.ww bundler regens. Byte-identity (994/995) is the behavior gate; full make test green at 133/133 confirms.
This commit is contained in:
@@ -11313,35 +11313,25 @@ fn loadopsz(sigd: bool, sz: i32) str = {
|
||||
};
|
||||
|
||||
// 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
|
||||
// dispatch as fieldloadop, but keyed on the value's own tnode.type_.
|
||||
// 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.
|
||||
// than MOVQ reads. Mirror of cstage's localloadop in cmd/w6c/cgen.c
|
||||
// — tinfo.size carries the same numeric width cstage's `t->size`
|
||||
// reports, with TBANG / TENUM / TNAME-alias chains pre-folded by
|
||||
// tinfofornode (check.ww:1102-1153 TNAME, 1154-1161 TBANG,
|
||||
// 1196-1208 TENUM).
|
||||
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 (tnode == nil) { return "MOVQ"; };
|
||||
let ti: *tinfo = tnode.type_: *tinfo;
|
||||
if (ti == nil) { return "MOVQ"; };
|
||||
let sz: i32 = ti.size: i32;
|
||||
if (sz != 1) { if (sz != 2) { if (sz != 4) { return "MOVQ"; }; }; };
|
||||
let sigd: bool = fieldissignedc(c, tnode);
|
||||
let sigd: bool = typeissigned(ti);
|
||||
return loadopsz(sigd, sz);
|
||||
};
|
||||
|
||||
|
||||
@@ -941,35 +941,25 @@ fn loadopsz(sigd: bool, sz: i32) str = {
|
||||
};
|
||||
|
||||
// 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
|
||||
// dispatch as fieldloadop, but keyed on the value's own tnode.type_.
|
||||
// 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.
|
||||
// than MOVQ reads. Mirror of cstage's localloadop in cmd/w6c/cgen.c
|
||||
// — tinfo.size carries the same numeric width cstage's `t->size`
|
||||
// reports, with TBANG / TENUM / TNAME-alias chains pre-folded by
|
||||
// tinfofornode (check.ww:1102-1153 TNAME, 1154-1161 TBANG,
|
||||
// 1196-1208 TENUM).
|
||||
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 (tnode == nil) { return "MOVQ"; };
|
||||
let ti: *tinfo = tnode.type_: *tinfo;
|
||||
if (ti == nil) { return "MOVQ"; };
|
||||
let sz: i32 = ti.size: i32;
|
||||
if (sz != 1) { if (sz != 2) { if (sz != 4) { return "MOVQ"; }; }; };
|
||||
let sigd: bool = fieldissignedc(c, tnode);
|
||||
let sigd: bool = typeissigned(ti);
|
||||
return loadopsz(sigd, sz);
|
||||
};
|
||||
|
||||
|
||||
@@ -11313,35 +11313,25 @@ fn loadopsz(sigd: bool, sz: i32) str = {
|
||||
};
|
||||
|
||||
// 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
|
||||
// dispatch as fieldloadop, but keyed on the value's own tnode.type_.
|
||||
// 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.
|
||||
// than MOVQ reads. Mirror of cstage's localloadop in cmd/w6c/cgen.c
|
||||
// — tinfo.size carries the same numeric width cstage's `t->size`
|
||||
// reports, with TBANG / TENUM / TNAME-alias chains pre-folded by
|
||||
// tinfofornode (check.ww:1102-1153 TNAME, 1154-1161 TBANG,
|
||||
// 1196-1208 TENUM).
|
||||
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 (tnode == nil) { return "MOVQ"; };
|
||||
let ti: *tinfo = tnode.type_: *tinfo;
|
||||
if (ti == nil) { return "MOVQ"; };
|
||||
let sz: i32 = ti.size: i32;
|
||||
if (sz != 1) { if (sz != 2) { if (sz != 4) { return "MOVQ"; }; }; };
|
||||
let sigd: bool = fieldissignedc(c, tnode);
|
||||
let sigd: bool = typeissigned(ti);
|
||||
return loadopsz(sigd, sz);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user