wcc/ww: match on a global value-struct tagged field reads g(SB) (#29)
A match whose scrutinee is a tagged field of a GLOBAL value-struct read the tag/payload from the BP region (saved-BP + return-addr) instead of g(SB) and returned garbage. Both stages were identical-wrong, so the byte-id gate could not see it -- a gate-blind regression introduced by M1 (#25): M1's in-place N_DOT match arm uses localfind(base), which returns the 0 not-found sentinel for a global base, so 0+field.offset landed in the frame. Gate the in-place arm on a confirmed-local base -- `localfind(base)==0 && let_islet/isletvar(base)`, verbatim from cstage's own global test at cgen.c:2000 (both stages, same spelling). A global base now falls through to the existing spill path, which cgexprs the scrutinee and resolves g(SB). M1's local-field in-place ($32) path is untouched. Regenerates the w6c and wwdump combined.ww. Table-driven 841 test (global int/reassign/str-payload + a local-field M1 regression row), runtime-discriminating: pre-fix returns garbage, post-fix 42 on both stages; rob's direct-global-field spill caveat confirmed at runtime.
This commit is contained in:
@@ -26307,11 +26307,17 @@ fn cgmatch(c: *cgen, n: *node) void = {
|
||||
// type chases to TY_STRUCT and whose field resolves by name.
|
||||
// A *ptr-field base (`match (h.e)`, h:*struct) chases to
|
||||
// TY_PTR, the by-name scan misses, and it falls to the spill
|
||||
// arm below — exactly as cstage, no separate deref guard. A
|
||||
// global VALUE-struct base mis-resolves here in BOTH stages
|
||||
// (shared latent #29), matched identically — no ww-only
|
||||
// global guard. align-DOWN to cstage (rule 10): both stages
|
||||
// emit TEXT $32 on the local-field case.
|
||||
// arm below — exactly as cstage, no separate deref guard.
|
||||
// #29: the in-place arm is also gated on a CONFIRMED-LOCAL
|
||||
// base (bglobal below). A global VALUE-struct base
|
||||
// (`match (g.field)`) has localfind==0, so the M1 base.off
|
||||
// would land in the saved-BP/return-addr region — both
|
||||
// stages emitted `MOVQ (BP),AX` (gate-blind both-wrong). The
|
||||
// global-base predicate `localfind==0 && isletvar` (cstage
|
||||
// twin: let_islet, cgen.c:2000) routes it through to the
|
||||
// spill `else`, which resolves g(SB). align-DOWN to cstage
|
||||
// (rule 10): both stages emit TEXT $32 on the local-field
|
||||
// case and the same g(SB) spill on the global-field case.
|
||||
let mfld: *tfield = nil;
|
||||
if (scrut.kind == nkind.N_DOT && scrut.lhs != nil
|
||||
&& scrut.lhs.kind == nkind.N_IDENT
|
||||
@@ -26328,7 +26334,12 @@ fn cgmatch(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
let bglobal: bool = false;
|
||||
if (mfld != nil) {
|
||||
bglobal = (localfind(c, scrut.lhs.str) == 0)
|
||||
&& isletvar(c, scrut.lhs.str);
|
||||
};
|
||||
if (mfld != nil && !bglobal) {
|
||||
let foff: i32 = mfld.offset: i32;
|
||||
scrutoff = localfind(c, scrut.lhs.str) + foff;
|
||||
scrutt = matchscrutt(c, scrut);
|
||||
|
||||
@@ -2961,11 +2961,17 @@ fn cgmatch(c: *cgen, n: *node) void = {
|
||||
// type chases to TY_STRUCT and whose field resolves by name.
|
||||
// A *ptr-field base (`match (h.e)`, h:*struct) chases to
|
||||
// TY_PTR, the by-name scan misses, and it falls to the spill
|
||||
// arm below — exactly as cstage, no separate deref guard. A
|
||||
// global VALUE-struct base mis-resolves here in BOTH stages
|
||||
// (shared latent #29), matched identically — no ww-only
|
||||
// global guard. align-DOWN to cstage (rule 10): both stages
|
||||
// emit TEXT $32 on the local-field case.
|
||||
// arm below — exactly as cstage, no separate deref guard.
|
||||
// #29: the in-place arm is also gated on a CONFIRMED-LOCAL
|
||||
// base (bglobal below). A global VALUE-struct base
|
||||
// (`match (g.field)`) has localfind==0, so the M1 base.off
|
||||
// would land in the saved-BP/return-addr region — both
|
||||
// stages emitted `MOVQ (BP),AX` (gate-blind both-wrong). The
|
||||
// global-base predicate `localfind==0 && isletvar` (cstage
|
||||
// twin: let_islet, cgen.c:2000) routes it through to the
|
||||
// spill `else`, which resolves g(SB). align-DOWN to cstage
|
||||
// (rule 10): both stages emit TEXT $32 on the local-field
|
||||
// case and the same g(SB) spill on the global-field case.
|
||||
let mfld: *tfield = nil;
|
||||
if (scrut.kind == nkind.N_DOT && scrut.lhs != nil
|
||||
&& scrut.lhs.kind == nkind.N_IDENT
|
||||
@@ -2982,7 +2988,12 @@ fn cgmatch(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
let bglobal: bool = false;
|
||||
if (mfld != nil) {
|
||||
bglobal = (localfind(c, scrut.lhs.str) == 0)
|
||||
&& isletvar(c, scrut.lhs.str);
|
||||
};
|
||||
if (mfld != nil && !bglobal) {
|
||||
let foff: i32 = mfld.offset: i32;
|
||||
scrutoff = localfind(c, scrut.lhs.str) + foff;
|
||||
scrutt = matchscrutt(c, scrut);
|
||||
|
||||
@@ -26307,11 +26307,17 @@ fn cgmatch(c: *cgen, n: *node) void = {
|
||||
// type chases to TY_STRUCT and whose field resolves by name.
|
||||
// A *ptr-field base (`match (h.e)`, h:*struct) chases to
|
||||
// TY_PTR, the by-name scan misses, and it falls to the spill
|
||||
// arm below — exactly as cstage, no separate deref guard. A
|
||||
// global VALUE-struct base mis-resolves here in BOTH stages
|
||||
// (shared latent #29), matched identically — no ww-only
|
||||
// global guard. align-DOWN to cstage (rule 10): both stages
|
||||
// emit TEXT $32 on the local-field case.
|
||||
// arm below — exactly as cstage, no separate deref guard.
|
||||
// #29: the in-place arm is also gated on a CONFIRMED-LOCAL
|
||||
// base (bglobal below). A global VALUE-struct base
|
||||
// (`match (g.field)`) has localfind==0, so the M1 base.off
|
||||
// would land in the saved-BP/return-addr region — both
|
||||
// stages emitted `MOVQ (BP),AX` (gate-blind both-wrong). The
|
||||
// global-base predicate `localfind==0 && isletvar` (cstage
|
||||
// twin: let_islet, cgen.c:2000) routes it through to the
|
||||
// spill `else`, which resolves g(SB). align-DOWN to cstage
|
||||
// (rule 10): both stages emit TEXT $32 on the local-field
|
||||
// case and the same g(SB) spill on the global-field case.
|
||||
let mfld: *tfield = nil;
|
||||
if (scrut.kind == nkind.N_DOT && scrut.lhs != nil
|
||||
&& scrut.lhs.kind == nkind.N_IDENT
|
||||
@@ -26328,7 +26334,12 @@ fn cgmatch(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
let bglobal: bool = false;
|
||||
if (mfld != nil) {
|
||||
bglobal = (localfind(c, scrut.lhs.str) == 0)
|
||||
&& isletvar(c, scrut.lhs.str);
|
||||
};
|
||||
if (mfld != nil && !bglobal) {
|
||||
let foff: i32 = mfld.offset: i32;
|
||||
scrutoff = localfind(c, scrut.lhs.str) + foff;
|
||||
scrutt = matchscrutt(c, scrut);
|
||||
|
||||
Reference in New Issue
Block a user