cstage+test: graduate hidden-name mklabel sites to @-prefix SSoT (#26)
Class A frame-layout landmine pre-located; #26c queued for size-
strategy convergence per rule 10.
Cstage's tagged-scratch sites previously stamped per-call labels
via mklabel "tagbase"/"tagscr"/"argscr"/"idxscr", bumping labelseq
once per call and allocating a fresh frame slot. Wwstage routes
the same sites through localadd("@tagbase", ...) and
localadd("@tagscr", c.tagscrsz, nil) — the @-dedup shares ONE
slot per name per fn and never touches labelseq. @tagscr is
shared across THREE wwstage sites: cgenutil.ww:180 pushargsrev
struct-payload widen, cgenutil.ww:2918 cgwidentaggedstore
via_outer, cgenexpr.ww:3524 cgindex tagged-element. Worker's
initial draft introduced cg_argscr / cg_idxscr as separate
cache vars — names that don't exist in wwstage. Per rob's rule-10
amendment those collapsed to a single cg_tagscr shared across
the 3 sites, matching wwstage's @tagscr SSoT exactly.
Cstage now caches two slots matching wwstage's namespace exactly:
cg_tagbase (8B base spill, 1 site at cgwidentaggedstore via_outer)
and cg_tagscr (sized scratch shared across the 3 sites above).
Eliminates per-call labelseq bumps and per-call frame churn.
Class A byte-id drift (silent corpus-coverage-blind landmine)
closed for the 1-name shape match. Model: STATUS-3 #15 commit
987391b routed @retscr through the same SSoT via cg_retscr;
this commit extends the carve-out to @tagbase and @tagscr.
Size strategy: cstage has no scanlocals pre-pass (wwstage's
c.tagscrsz pre-pass at cgendecl.ww:32 tagscrbump computes the
per-fn max). First call across the 3 @tagscr sites sizes the
slot; subsequent calls reuse if sz <= cached, fatal() if larger
(rule 7: surface-don't-silently-corrupt). Long-term rule-10
convergence — wwstage DOWN from scanlocals to first-use+fail-loud
on BOTH stages (per rob: aligning richer DOWN to leaner) — is
filed as #26c, separate concern from #26's name-SSoT graduation.
Tests:
- 736_cstage_label_ssot succ_rows: pins cstage-vs-wwstage cmp -s
byte-id on the canonical pointer-rooted two-tagged-store shape
(two `c.v = (...: bag);` writes through *cell). Pre-fix cstage
frame was 16B+48B larger (2*@tagbase + 2*@tagscr per call);
post-fix single-slot SSoT matches wwstage byte-for-byte.
- 736_cstage_label_ssot fail_rows: pre-locates the size-grow
landmine. A fn with two unions of different slot sizes (16B
then 24B) routed through @tagscr; cstage must fatal() with
"@tagscr cached sz" + size mismatch + #26c follow-up cite.
Gates corpus growth into this shape against silent miscompile.
110/110 ok. 995_self_rebuild byte-id holds (ww2 == ww3 == ww4).
This commit is contained in:
@@ -39,6 +39,33 @@ static int *cg_frame;
|
||||
* semantics for synthetic scratches). 0 means "not yet allocated";
|
||||
* negative offsets returned by local_alloc are the live value. */
|
||||
static int cg_retscr;
|
||||
/* Per-fn @-prefix scratch SSoT (task #26, follow-up to #15-cstage's
|
||||
* @retscr). Pre-#26 each site allocated a labelseq-stamped fresh slot
|
||||
* per call (mklabel "tagbase" / "tagscr" / "argscr" / "idxscr"); the
|
||||
* labelseq bumps drifted cstage's ct/ce/end labels ahead of wwstage,
|
||||
* and the per-call frame growth drifted cstage's framesize ahead too.
|
||||
*
|
||||
* Two cached slots match wwstage's `@`-prefix namespace exactly:
|
||||
* cg_tagbase — 8B base-register spill for cg_widen_tagged_store
|
||||
* via_outer (mirrors wwstage @tagbase, 1 site).
|
||||
* 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).
|
||||
*
|
||||
* 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. */
|
||||
static int cg_tagbase;
|
||||
static int cg_tagbase_sz;
|
||||
static int cg_tagscr;
|
||||
static int cg_tagscr_sz;
|
||||
/* System V AMD64 sret discipline (task #23). Plain TY_STRUCT returns
|
||||
* with size > 24B are passed via a hidden first-arg pointer (RDI) to
|
||||
* a caller-prealloc dest; the callee writes through that pointer and
|
||||
@@ -1186,11 +1213,29 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
|
||||
int base_spill = 0;
|
||||
int write_off = slot_off;
|
||||
if (via_outer) {
|
||||
const char *spname = mklabel(c, "tagbase");
|
||||
base_spill = local_alloc(c, locals_p, spname, 8, cg_frame);
|
||||
if (cg_tagbase != 0) {
|
||||
base_spill = cg_tagbase;
|
||||
} else {
|
||||
base_spill = local_alloc(c, locals_p, "@tagbase", 8,
|
||||
cg_frame);
|
||||
cg_tagbase = base_spill;
|
||||
cg_tagbase_sz = 8;
|
||||
}
|
||||
ins2(c, A_MOVQ, areg(base_reg), amem(D_BP, base_spill));
|
||||
const char *scname = mklabel(c, "tagscr");
|
||||
write_off = local_alloc(c, locals_p, scname, sz, cg_frame);
|
||||
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)",
|
||||
cg_tagscr_sz, sz);
|
||||
write_off = cg_tagscr;
|
||||
} else {
|
||||
write_off = local_alloc(c, locals_p, "@tagscr", sz,
|
||||
cg_frame);
|
||||
cg_tagscr = write_off;
|
||||
cg_tagscr_sz = sz;
|
||||
}
|
||||
/* Pre-zero so str/scalar branches (which leave high words
|
||||
* untouched when sz exceeds the variant's footprint) still
|
||||
* deliver a clean slot to the copy-out. */
|
||||
@@ -1469,8 +1514,19 @@ cg_widen_tagged_push(Cg *c, Local **locals_p, Type *dst, Node *src, int sz)
|
||||
ins1(c, A_PUSHQ, areg(D_AX)); /* tag at +0 */
|
||||
return;
|
||||
}
|
||||
const char *scr_name = mklabel(c, "argscr");
|
||||
int scr = local_alloc(c, locals_p, scr_name, sz, cg_frame);
|
||||
int scr;
|
||||
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)",
|
||||
cg_tagscr_sz, sz);
|
||||
scr = cg_tagscr;
|
||||
} else {
|
||||
scr = local_alloc(c, locals_p, "@tagscr", sz, cg_frame);
|
||||
cg_tagscr = scr;
|
||||
cg_tagscr_sz = sz;
|
||||
}
|
||||
/* Zero the scratch slot first so any pad word the store path
|
||||
* leaves untouched (struct payload shorter than the slot's value
|
||||
* area) reads as 0 on the callee. The store path then writes the
|
||||
@@ -3443,9 +3499,23 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* the function frame; no cleanup needed. */
|
||||
if ((is_arr || is_sl || is_ptr) && elem_tagged) {
|
||||
int ssz = esz;
|
||||
const char *scrn = mklabel(c, "idxscr");
|
||||
int scr = local_alloc(c, &locals, scrn, ssz,
|
||||
cg_frame);
|
||||
int scr;
|
||||
if (cg_tagscr != 0) {
|
||||
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)",
|
||||
cg_tagscr_sz, ssz);
|
||||
scr = cg_tagscr;
|
||||
} else {
|
||||
scr = local_alloc(c, &locals,
|
||||
"@tagscr", ssz, cg_frame);
|
||||
cg_tagscr = scr;
|
||||
cg_tagscr_sz = ssz;
|
||||
}
|
||||
ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
|
||||
for (int k = 0; k < ssz; k += 8)
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
@@ -7180,6 +7250,10 @@ cgfn(Cg *c, FILE *out, Node *fn)
|
||||
nloops = 0;
|
||||
cg_ret_type = fn->type ? fn->type->ret : NULL;
|
||||
cg_retscr = 0;
|
||||
cg_tagbase = 0;
|
||||
cg_tagbase_sz = 0;
|
||||
cg_tagscr = 0;
|
||||
cg_tagscr_sz = 0;
|
||||
cg_sret_arg_off = 0;
|
||||
cg_sret_dest_off = 0;
|
||||
cg_sretscr_off = 0;
|
||||
|
||||
Reference in New Issue
Block a user