w6c_ww: matchscrutt resolves non-ident index bases via stamped type (fix #48)

Pre-#48 wwstage matchscrutt's N_INDEX arm required ibase.kind ==
N_IDENT; an index over any other base (match (h.xs[i]) = N_INDEX over
N_DOT, the regex fold-2a re.insts[i] shape) returned nil, so cgmatch
dispatched with scrutt=nil — every case arm's variant index clamped to
0 (CMPQ $0) and @match_spill fell to the 16B default. SILENT cs≠ww
runtime-wrong (cstage N_MATCH reads the checker-stamped s->type for
every scrutinee shape, cmd/w6c/cgen.c:7510). The non-ident-base arm now
returns the scrutinee node itself behind an istaggedtype gate — the
stamped-carrier pattern of the #67 N_DOT arm and the #45 cgtypetest
fix — so any base shape resolves the element's tagged tinfo for both
variant indices and spill sizing.

Same-class load half, one commit per the #133-expanded precedent:
cgindex's generic-fallback tagged-element load was the only arm missing
the slot>24 R8 word (both ident arms and cstage cgen.c:9106-9117 have
it), so a >24B-slot element via a non-ident base under-read the cursor
and the now-correctly-sized spill stored stale R8.

928_match_nonident_idx_run pins the repro shape (field-base slice
index, all variants both polarities), the regex shape (56B-slot
inst-like union, payload reads within the 32B cursor per #43), and
ident/array/slice ident-base controls — per row cs==ww byte-id +
runtime via both drivers. w6c/wwdump combined.ww regen'd via canonical
make; selfhost corpus hand-cmp'd cs==ww both stages.

Pre-existing siblings surfaced while probing, NOT folded (rule 11),
reported for filing: (a) cgindex element classification skips N_CALL
bases entirely (mk()[0] — wrong esz + not tagged-classified, cs≠ww,
runtime-wrong, also non-match contexts); (b) `as` on a non-ident
carrier still clamps the variant to 0 (cgtagvariantidx's N_TTAGGED node
gate rejects the stamped carrier; byte-identical to master, the #200
spill fix covered only slot sizing).
This commit is contained in:
2026-06-04 05:21:46 +09:00
parent 7bbee3005a
commit d099c29b86
6 changed files with 335 additions and 6 deletions

View File

@@ -17790,7 +17790,18 @@ fn matchscrutt(c: *cgen, scrut: *node) *node = {
if (k == nkind.N_INDEX) {
let ibase: *node = scrut.lhs;
if (ibase == nil) { return nil; };
if (ibase.kind != nkind.N_IDENT) { return nil; };
if (ibase.kind != nkind.N_IDENT) {
// #48: non-ident index base (s.field[i], call()[i],
// nested). Only idents carry a declared tnode to walk,
// so resolve from the checker-stamped element tinfo
// instead — the #45/#67 stamped-carrier pattern; cgmatch
// gates on istaggedtype and reads scrutt.type_ directly.
// cstage N_MATCH reads s->type for every scrutinee shape
// (cmd/w6c/cgen.c:7510). Pre-#48 this returned nil and
// the variant clamped to 0 + @match_spill mis-sized.
if (istaggedtype(c, scrut)) { return scrut; };
return nil;
};
let bl: *local = localfindnode(c, ibase.str);
let btn: *node = nil;
if (bl != nil) { btn = bl.tnode; }
@@ -21468,8 +21479,15 @@ fn cgindex(c: *cgen, n: *node) void = {
};
if (elem_tagged) {
// AX holds the element address. Copy to BX (loading slot+0
// into AX clobbers it), then read slot words.
// into AX clobbers it), then read slot words. #48: the >24
// R8 word was missing ONLY in this fallback arm (both ident
// arms have it) — a >24B-slot element via a non-ident base
// under-read the cursor and the match spill stored stale R8.
// Mirrors cstage cgen.c:9106-9117.
emitline("\tMOVQ\tAX, BX\n");
if (elem_slot_sz > 24) {
emitline("\tMOVQ\t24(BX), R8\n");
};
if (elem_slot_sz > 16) {
emitline("\tMOVQ\t16(BX), CX\n");
};