wcc/ww: match on a tagged struct-field scrutinee reads it in place (#25)
wwstage cgmatch unconditionally spilled any non-ident match scrutinee -- including an addressable BP-relative N_DOT struct field -- into @match_spill and dispatched off the copy (frame $48); cstage reads such a field in place at its BP offset ($32). Both stages were already runtime-correct (latent rule-10 leanness, not a miscompile); this aligns wwstage down to cstage so the asm is byte-identical. The new in-place arm mirrors cstage cgen.c:10241-10296 verbatim: an N_DOT scrutinee with a bare N_IDENT base whose type chases to a value TY_STRUCT and whose field is found by name reads tag/payload at localfind(base)+field.offset. The *ptr-field and call-result cases stay on the spill path by construction (their base does not chase to TY_STRUCT) -- no extra guard. A global value-struct base mis-resolves identically in both stages (localfind returns 0); left untouched as a shared latent (#29), since a ww-only guard would break byte-id. Regenerates the w6c and wwdump combined.ww. Table-driven 831 test: 6 rows (local-field, *ptr-field, plain-ident, call-result, payload remap, str payload) x runtime-both-stages + cs-vs-ww byte-id.
This commit is contained in:
@@ -26298,6 +26298,41 @@ fn cgmatch(c: *cgen, n: *node) void = {
|
||||
};
|
||||
}; };
|
||||
} else {
|
||||
// M1 (#25): match on a tagged field of a BARE-ident VALUE
|
||||
// struct reads the box IN PLACE at base.off + field.offset
|
||||
// — the box (tag@+0, word0@+8, word1@+16) is contiguous in
|
||||
// the parent frame, so no @match_spill copy. Verbatim mirror
|
||||
// of cstage N_MATCH's in-place arm (cmd/w6c/cgen.c:10241-
|
||||
// 10296): gate on N_DOT with a bare-IDENT base whose stamped
|
||||
// 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.
|
||||
let mfld: *tfield = nil;
|
||||
if (scrut.kind == nkind.N_DOT && scrut.lhs != nil
|
||||
&& scrut.lhs.kind == nkind.N_IDENT
|
||||
&& scrut.lhs.type_ != nil) {
|
||||
let bu: *tinfo = tichase(scrut.lhs.type_: *tinfo);
|
||||
if (bu != nil && bu.kind == tykind.TY_STRUCT) {
|
||||
let fl: *tfield = bu.fields;
|
||||
for (fl != nil) {
|
||||
if (streq(fl.name, scrut.str)) {
|
||||
mfld = fl;
|
||||
break;
|
||||
};
|
||||
fl = fl.tnext;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (mfld != nil) {
|
||||
let foff: i32 = mfld.offset: i32;
|
||||
scrutoff = localfind(c, scrut.lhs.str) + foff;
|
||||
scrutt = matchscrutt(c, scrut);
|
||||
} else {
|
||||
// Non-ident scrutinee (call result, arr[i], p.field,
|
||||
// ?, etc.). Spill into an `@match_spill` scratch slot
|
||||
// and dispatch off it. Tagged returns (N_CALL) follow
|
||||
@@ -26398,6 +26433,7 @@ fn cgmatch(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
}; };
|
||||
};
|
||||
};
|
||||
};
|
||||
let endl: str = mklabel(c, "match_end");
|
||||
|
||||
Reference in New Issue
Block a user