selfhost+test: size match-spill slot by scrutinee, not 24B (#9)

wwstage cgmatch hardcoded `spillsz = 24` + unconditional CX write
where cstage emits `slot_size = (su->kind == TY_TAGGED) ? su->size
: 16` with `if (slot_size > 16)` gating. For 1-word-payload variants
like `(*u8 | oserror)` the slot is 16B; wwstage over-allocated and
over-wrote past the receiver's read window.

Factor cgmatch's non-ident scrutinee-type resolution + spill sizing
into matchscrutt + matchspillsz in cgenutil.ww. cgmatch gates CX
write on `spillsz > 16`; R8 gate `> 24` already correct. scanlocals
N_MATCH branch uses the same helpers — scan+emit lockstep.

Test 716: 4 rows × {cstage runtime, wwstage runtime, asm-byte-id}.
Aliased (*u8 | oserror) ok/err arms, raw (*u8 | i64) for hypothesis
breadth, (str | i64) 24B regression guard.
This commit is contained in:
2026-05-17 00:00:55 +09:00
parent deaa777eb8
commit f4176b8749
7 changed files with 561 additions and 210 deletions

View File

@@ -168,14 +168,19 @@ fn scanlocals(c: *cgen, n: *node) i32 = {
};
};
};
// `match (non-ident)` needs a 24B `@match_spill` scratch slot for
// cgmatch to land the AX:DX:CX return triple. Mirrors C cgen's
// localoff("@match_spill", ...). N_IDENT scrutinees read the slot
// `match (non-ident)` needs an `@match_spill` scratch slot sized
// to the scrutinee's tagged-union slot (16/24/32 for 1/2/3-word
// payload). Mirrors cstage's `slot_size = su->size` default 16
// in cmd/w6c/cgen.c cgmatch (task #9 align-down to cstage).
// matchspillsz must agree with cgmatch's emit-time computation
// for scan+emit lockstep. N_IDENT scrutinees read the slot
// directly off the local — no spill needed.
if (n.kind == nkind.N_MATCH) {
let sc: *node = n.lhs;
if (sc != nil) {
if (sc.kind != nkind.N_IDENT) { total += 24; };
if (sc.kind != nkind.N_IDENT) {
total += matchspillsz(c, matchscrutt(c, sc));
};
};
};
// Match-arm binding (`case let v: T => ...`) gets a slot too.