selfhost+test: route composite CALL return through nodeisslice (#24)
Wwstage call-arg-emit recognized slice args only when source was IDENT/SLICE/CAST/DOT. For N_CALL returning []T the natural-push fallthrough emitted one PUSHQ AX (lost .len/.cap) and cgcall's pop-count under-drained by 2 words — corrupting R8/R9 and every subsequent arg. Class A runtime miscompile with stack misalignment and 3-POPs-of-garbage at the receiving call. Sister to #21 (tagged-CALL arg) but for plain []u8 slice, not tagged-variant — wwstage's pushargsrev grew the tagged-CALL arm at #21 and never grew the plain-composite arm. Surfaced by lib/strings landing's `bytes.X(toutf8(in), p)` call sites: 995_self_rebuild's wwstage rebuild tripped on byte-id divergence at w6c_ww + wwdump_ww emit. 967_bytes_run was green because `ww run` exercises the cstage path. Corpus-coverage-blind on the wwstage side until lib/strings pulled the chain through wwstage compilation. Fix is minimal: `nodeisslice` (selfhost/cmd/wcc/cgenutil.ww) gains an N_CALL arm structurally identical to the existing N_CALL arm in `nodeisstr` (only swap: isslicetype for isstrtype). The downstream natural-push slice path (PUSHQ CX/BX/AX, extra=2 pop-count) was already correct — it just needed the N_CALL-of-slice-return shape to be recognized as a slice. pushargsrev and cgcall untouched. Polarity catalog: wwstage UNDER — missing N_CALL arm in slice shape recognition. Convergence wwstage → cstage per rule 10 (cstage reads typed-AST `type_isslice` natively). Tests: - 723_composite_call_arg pins the 3-PUSH order (CX, BX, AX) between `CALL view` and next CALL on canonical `f(g())` shape, plus cstage vs wwstage cmp -s byte-id. - 927_composite_call_arg_run runtime-pins 7 rows × 2 stages = 14 fixtures: canonical, slice-CALL + let-slice (hasprefix shape), two composite-CALL args (arg-shift collision), middle-argpos, nested composite-in-composite, slice + scalar pop-count mix, tagged-CALL regression alongside (confirms #21 still holds). 95/95 ok. 995_self_rebuild stays green (ww2==ww3==ww4 byte-id).
This commit is contained in:
@@ -481,6 +481,22 @@ fn nodeisslice(c: *cgen, n: *node) bool = {
|
||||
};
|
||||
if (k == nkind.N_SLICE) { return true; };
|
||||
if (k == nkind.N_CAST) { return isslicetype(c, n.rhs); };
|
||||
// #24: N_CALL returning a slice — cgexpr leaves (AX=ptr,
|
||||
// BX=len, CX=cap); pushargsrev's slice arm pushes CX/BX/AX
|
||||
// and cgcall pops 3 words. Without this arm the natural-push
|
||||
// fallthrough emits one PUSHQ AX (loses .len/.cap) and the pop
|
||||
// side under-drains by 2 words, leaving R8/R9 unset for the
|
||||
// receiver. Mirrors nodeisstr's N_CALL arm just below.
|
||||
if (k == nkind.N_CALL) {
|
||||
let callee: *node = n.lhs;
|
||||
if (callee != nil) {
|
||||
if (callee.kind == nkind.N_IDENT) {
|
||||
let rt: *node = fnretlookup(c, callee.str);
|
||||
return isslicetype(c, rt);
|
||||
};
|
||||
};
|
||||
return false;
|
||||
};
|
||||
// N_DOT to a slice field: resolve the field through the struct
|
||||
// (or *struct) the base ident / inner chain lands on, then check
|
||||
// the field tnode. Mirrors nodeisstr's N_DOT branch so call-arg
|
||||
|
||||
Reference in New Issue
Block a user