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:
@@ -1899,6 +1899,68 @@ export fn resolvetagged(c: *cgen, t: *node) *node = {
|
||||
return nil;
|
||||
};
|
||||
|
||||
// matchscrutt — resolve a non-ident match scrutinee node to its tagged
|
||||
// type (or nil if unresolvable). Mirrors cgmatch's inline scrutinee
|
||||
// type resolution; factored so cgmatch (emit) and scanlocals (count)
|
||||
// agree on the spill slot's size per the scan+emit lockstep invariant.
|
||||
// IDENT scrutinees use a different lookup path (read off the local
|
||||
// directly, no spill) so this returns nil for them too.
|
||||
fn matchscrutt(c: *cgen, scrut: *node) *node = {
|
||||
if (scrut == nil) { return nil; };
|
||||
let k: nkind = scrut.kind;
|
||||
if (k == nkind.N_IDENT) { return nil; };
|
||||
if (k == nkind.N_CALL) {
|
||||
let callee: *node = scrut.lhs;
|
||||
if (callee != nil) {
|
||||
let cnm: str;
|
||||
cnm.ptr = nil; cnm.len = 0;
|
||||
if (callee.kind == nkind.N_IDENT) { cnm = callee.str; };
|
||||
if (callee.kind == nkind.N_DOT) { cnm = callee.str; };
|
||||
if (cnm.len > 0) {
|
||||
let rt: *node = fnretlookup(c, cnm);
|
||||
if (rt != nil) { return resolvetagged(c, rt); };
|
||||
};
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
if (k == nkind.N_INDEX) {
|
||||
let ibase: *node = scrut.lhs;
|
||||
if (ibase == nil) { return nil; };
|
||||
if (ibase.kind != nkind.N_IDENT) { return nil; };
|
||||
let bl: *local = localfindnode(c, ibase.str);
|
||||
let btn: *node = nil;
|
||||
if (bl != nil) { btn = bl.tnode; }
|
||||
else { btn = letvartnode(c, ibase.str); };
|
||||
if (btn == nil) { return nil; };
|
||||
let bk: nkind = btn.kind;
|
||||
let etn: *node = nil;
|
||||
if (bk == nkind.N_TARRAY) { etn = btn.lhs; };
|
||||
if (bk == nkind.N_TSLICE) { etn = btn.lhs; };
|
||||
if (bk == nkind.N_TPTR) { etn = btn.lhs; };
|
||||
if (etn == nil) { return nil; };
|
||||
return resolvetagged(c, etn);
|
||||
};
|
||||
if (k == nkind.N_DOT) {
|
||||
let ft: *node = dotfieldtnode(c, scrut);
|
||||
if (ft == nil) { return nil; };
|
||||
return resolvetagged(c, ft);
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// matchspillsz — slot size for the @match_spill scratch a non-ident
|
||||
// scrutinee lands in. Mirrors cstage's `slot_size = (su->kind ==
|
||||
// TY_TAGGED) ? su->size : 16` (cmd/w6c/cgen.c cgmatch). 16 default
|
||||
// when the scrutinee type can't be resolved keeps the historical
|
||||
// alloc for non-tagged / unresolved cases. Used by both scanlocals
|
||||
// (counting) and cgmatch (emitting) per rule-10 align-to-cstage.
|
||||
fn matchspillsz(c: *cgen, scrutt: *node) i32 = {
|
||||
if (scrutt == nil) { return 16; };
|
||||
let sz: i32 = slotsize(c, scrutt);
|
||||
if (sz <= 0) { return 16; };
|
||||
return sz;
|
||||
};
|
||||
|
||||
// istaggedtype — alias-aware. Mirrors isstrtype: follow N_TNAME to its
|
||||
// underlying decl, then unwrap a leading N_TBANG so `type error =
|
||||
// !(invalid | overflow);` is still recognised as tagged. Without the
|
||||
|
||||
Reference in New Issue
Block a user