selfhost+cstage+test: graduate frame growth to first-use+fail-loud (#15)
Subsumes #36. Drop wwstage scanlocals pre-pass; both stages converge on first-use+fail-loud frame growth, rule-10 polarity DOWN to leaner side. #36's surfaces (frame-total divergence on match-arm case-let; sibling offset divergence in variadic+iter+match-prev compositions) close naturally — running-max c.frame includes every first-use binding. selfhost/cmd/wcc: add atlocals persistent @-prefix registry surviving cgblock save/restore; add cgoutbuf/cgoutmode/cgout_enable/disable/flush for deferred prologue (emit body to buffer, finalise c.frame, then TEXT/SUBQ + flush); localadd @-prefix dedups against atlocals + fail-louds on size-grow (rule 7 — no silent truncate); cgreturn-tagged routes through @retscr (was colliding with @tagscr on arg-widen sizes); variadic gather esz uses raw primsize (rune->4) not slotsize (rune->8) — matches cstage and fixes the #36 sibling runtime miscompile in non-leaf variadic+iter+match-prev callees. cmd/w6c/cgen.c: drop the over-allocation hack ("for byte-id with wwstage scanlocals reservation") since wwstage no longer over-reserves; add fail-loud on @sretscr size-grow; @tagscr sites pass actual slot_sz instead of stale c.tagscrsz. 748_size_strategy_convergence: table-driven 4 rows x 2 stages (tag_variadic_runearm, trim_iter_match_prev, variadic_gather_rune_stride, leaf_baseline). Each exercises a #36 surface shape; 8/8 ok. Net -1565 lines. Sister latents filed as cosmetic (cs/ws frame size drift on multiple-variadic-call fns): labelseq drift + varargseq stuck at 0 — both bootstrap-byte-id safe (ww2==ww3==ww4 holds since both ww2 and ww3 are wwstage outputs). make test 122/122; ww2==ww3==ww4 byte-id holds via 995_self_rebuild.
This commit is contained in:
102
cmd/w6c/cgen.c
102
cmd/w6c/cgen.c
@@ -51,17 +51,15 @@ static int cg_retscr;
|
||||
* cg_tagscr — sized scratch shared across THREE sites: cg_widen_
|
||||
* tagged_store via_outer write target, cg_widen_tagged_
|
||||
* push struct/tagged-source widen, N_INDEX tagged-element
|
||||
* assign. Mirrors wwstage @tagscr (cgenutil.ww:180 +
|
||||
* :2918, cgenexpr.ww:3524). Wwstage shares the slot via
|
||||
* localadd `@`-prefix dedup, sized to `c.tagscrsz`
|
||||
* (per-fn max computed by scanlocals pre-pass).
|
||||
* assign. Mirrors wwstage @tagscr — wwstage shares the
|
||||
* slot via localadd `@`-prefix dedup against c.atlocals.
|
||||
*
|
||||
* Cstage has no pre-pass: first call across the 3 sites sizes the
|
||||
* slot; subsequent calls (any of the 3 sites) reuse if sz ≤ cached,
|
||||
* fatal() if larger. Per rule 7: surface, don't silently corrupt the
|
||||
* frame. The size-strategy convergence (wwstage DOWN to first-use+
|
||||
* fail-loud on BOTH stages, or cstage UP to a scanlocals pre-pass) is
|
||||
* filed as #26c — separate concern. _sz tracks cached allocation size. */
|
||||
* Both stages now size at first use and fatal() if a later site asks
|
||||
* for more (rule 7: surface, don't silently corrupt the frame —
|
||||
* pinned offset can't grow in place once neighbours are allocated).
|
||||
* Per-fn convergence completed by #15 (#26c follow-up): wwstage
|
||||
* dropped its scanlocals pre-pass and aligned DOWN to cstage's
|
||||
* first-use shape. _sz tracks cached allocation size. */
|
||||
static int cg_tagbase;
|
||||
static int cg_tagbase_sz;
|
||||
static int cg_tagscr;
|
||||
@@ -1239,9 +1237,8 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
|
||||
if (cg_tagscr != 0) {
|
||||
if (sz > cg_tagscr_sz)
|
||||
fatal("cg_widen_tagged_store: @tagscr "
|
||||
"cached sz %d, need %d (per-fn slot "
|
||||
"growth needs scanlocals pre-pass — "
|
||||
"STATUS-4 #26c follow-up)",
|
||||
"cached sz %d, need %d (pinned offset "
|
||||
"can't grow in place; rule 7 — #15/#26c)",
|
||||
cg_tagscr_sz, sz);
|
||||
write_off = cg_tagscr;
|
||||
} else {
|
||||
@@ -1532,8 +1529,8 @@ cg_widen_tagged_push(Cg *c, Local **locals_p, Type *dst, Node *src, int sz)
|
||||
if (cg_tagscr != 0) {
|
||||
if (sz > cg_tagscr_sz)
|
||||
fatal("cg_widen_tagged_push: @tagscr cached sz %d, "
|
||||
"need %d (per-fn slot growth needs scanlocals "
|
||||
"pre-pass — STATUS-4 #26c follow-up)",
|
||||
"need %d (pinned offset can't grow in place; "
|
||||
"rule 7 — #15/#26c)",
|
||||
cg_tagscr_sz, sz);
|
||||
scr = cg_tagscr;
|
||||
} else {
|
||||
@@ -3518,10 +3515,9 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
if (ssz > cg_tagscr_sz)
|
||||
fatal("N_INDEX tagged: "
|
||||
"@tagscr cached sz %d, "
|
||||
"need %d (per-fn slot "
|
||||
"growth needs scanlocals "
|
||||
"pre-pass — STATUS-4 #26c "
|
||||
"follow-up)",
|
||||
"need %d (pinned offset "
|
||||
"can't grow in place; "
|
||||
"rule 7 — #15/#26c)",
|
||||
cg_tagscr_sz, ssz);
|
||||
scr = cg_tagscr;
|
||||
} else {
|
||||
@@ -4318,8 +4314,8 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
? (int)velem->size : 8;
|
||||
/* Allocate dname BEFORE sname so the
|
||||
* descriptor lives below the element
|
||||
* buffer, matching the wwstage scanlocals
|
||||
* reservation order (rule 10). */
|
||||
* buffer, matching wwstage's emit-time
|
||||
* order (rule 10). */
|
||||
int doff = 0;
|
||||
if (nvar > 0) {
|
||||
const char *dname = mklabel(c, "vararg_d");
|
||||
@@ -4612,21 +4608,31 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
sret_call_sz = cg_sret_retsize(ret);
|
||||
}
|
||||
if (sret_call_sz > 0) {
|
||||
/* Always pre-allocate @sretscr at the first sret CALL
|
||||
* regardless of whether cg_sret_dest_off is set — keeps
|
||||
* cstage's frame in lockstep with wwstage's
|
||||
* scanlocals-based reservation. Single-slot SSoT (cg_
|
||||
* sretscr_off) mirrors @retscr / @tagscr conventions. */
|
||||
if (cg_sretscr_off == 0) {
|
||||
cg_sretscr_off = local_alloc(c,
|
||||
&locals, "@sretscr",
|
||||
sret_call_sz, cg_frame);
|
||||
cg_sretscr_sz = sret_call_sz;
|
||||
}
|
||||
/* @sretscr is only needed when the result is dropped
|
||||
* (no `let x = f();` receiver wired the call's dest into
|
||||
* cg_sret_dest_off). Allocate first-use per #15/#26c
|
||||
* size-strategy convergence — wwstage's scanlocals pre-
|
||||
* pass that used to reserve this slot unconditionally is
|
||||
* gone; cstage matches by skipping the allocation when a
|
||||
* dest is already wired. fatal() on a later sret CALL
|
||||
* needing a bigger slot (rule 7 — pinned offset can't
|
||||
* grow in place). */
|
||||
if (cg_sret_dest_off != 0) {
|
||||
sret_call_off = cg_sret_dest_off;
|
||||
cg_sret_dest_off = 0;
|
||||
} else {
|
||||
if (cg_sretscr_off == 0) {
|
||||
cg_sretscr_off = local_alloc(c,
|
||||
&locals, "@sretscr",
|
||||
sret_call_sz, cg_frame);
|
||||
cg_sretscr_sz = sret_call_sz;
|
||||
} else if (sret_call_sz > cg_sretscr_sz) {
|
||||
fatal("cgcall: @sretscr cached sz "
|
||||
"%d, need %d (per-fn slot growth "
|
||||
"post-#15 — pinned offset can't "
|
||||
"grow in place)",
|
||||
cg_sretscr_sz, sret_call_sz);
|
||||
}
|
||||
sret_call_off = cg_sretscr_off;
|
||||
}
|
||||
}
|
||||
@@ -4708,9 +4714,9 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* forwards through an sret callee, source RDI from outer's
|
||||
* saved @sretarg — inner writes directly into outer's
|
||||
* caller-prealloc dest. No temporary in outer's frame.
|
||||
* The @sretscr slot was still allocated above for byte-id
|
||||
* lockstep with wwstage's scanlocals reservation; it goes
|
||||
* unused on the forwarding branch. */
|
||||
* Post-#15 @sretscr is skipped entirely on the forwarding
|
||||
* branch (no allocation, no frame growth) — earlier scan-
|
||||
* lockstep reservation is gone. */
|
||||
if (sret_call_sz > 0) {
|
||||
if (cg_sret_forward) {
|
||||
ins2(c, A_MOVQ,
|
||||
@@ -6768,19 +6774,19 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
if (cg_retscr != 0) {
|
||||
scr = cg_retscr;
|
||||
} else {
|
||||
/* STATUS-3 #15: use the fixed
|
||||
* "@retscr" SSoT name (mirrors
|
||||
* wwstage's c.retscroff pattern in
|
||||
* selfhost/cmd/wcc/cgen.ww). Pre-
|
||||
* fix mklabel(c, "retscr") consumed
|
||||
/* Fixed "@retscr" SSoT name —
|
||||
* mirrors wwstage's localadd
|
||||
* @-prefix dedup. Pre-fix
|
||||
* mklabel(c, "retscr") consumed
|
||||
* one labelseq counter slot per
|
||||
* function with a tagged return,
|
||||
* pushing every subsequent ct/ce/
|
||||
* else/end label 1 ahead of wwstage.
|
||||
* Site 1 sentinel masked by latent
|
||||
* struct-widen offset divergence
|
||||
* (STATUS-3 #20/#21); fix is
|
||||
* preventive symmetry per rule 10. */
|
||||
* else/end label 1 ahead of
|
||||
* wwstage. Site 1 sentinel
|
||||
* masked by latent struct-widen
|
||||
* offset divergence (#20/#21);
|
||||
* fix is preventive symmetry per
|
||||
* rule 10. */
|
||||
scr = local_alloc(c, locals,
|
||||
"@retscr", sz, cg_frame);
|
||||
cg_retscr = scr;
|
||||
@@ -6936,9 +6942,9 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
|| n->lhs->kind == N_STRUCTLIT)) {
|
||||
int sz = (int)rt->size;
|
||||
/* Single-slot @retscr (#14): see tagged arm
|
||||
* above for rationale. STATUS-3 #15: fixed
|
||||
* "@retscr" name avoids bumping labelseq;
|
||||
* mirrors wwstage's c.retscroff SSoT. */
|
||||
* above for rationale. Fixed "@retscr" name
|
||||
* avoids bumping labelseq; mirrors wwstage's
|
||||
* localadd @-prefix dedup. */
|
||||
int scr;
|
||||
if (cg_retscr != 0) {
|
||||
scr = cg_retscr;
|
||||
|
||||
Reference in New Issue
Block a user