cstage+selfhost+test: enforce single-slot @retscr both stages (#14)
wwstage's $64 frame was 24B below required — the second struct-return's @retscr write at -88(BP) landed below SP. Silent miscompile masked by bootstrap-window luck. The fix retires the stomp by enforcing single-slot @retscr at emit-time. cstage was per-site-fresh (wasteful but safe, frame $96); aligned UP to single-slot for ABI consistency with wwstage's @-prefix convention, not for correctness. Both stages now produce $64 frame; second return reuses the first's -64..-48(BP) slot. Generalizes #38's c.tagscrsz SSoT pattern to c.retscroff (wwstage) and cg_retscr (cstage). Returns are terminal — only one fires per call, so the two slots' lifetimes never overlap; single-slot is structurally correct. wwstage's emit-side dedup was incomplete post-#27 (cgblock save/restore unwinds the @-prefix stub); the @retscr fast path in localadd bypasses the c.locals walk. Test 718: 4 rows × {cstage runtime, wwstage runtime, byte-id, stomp sentinel}. Stomp sentinel scans .s for any -N(BP) where N>64 and fails the row if found — catches below-SP writes that bootstrap byte-id would miss in a lucky window. Row 2 (3-return) byte-id disabled per task #15 (pre-existing label-counter skew, unrelated to #14). Polarity catalog this session: - #9 wwstage OVER (tagged-return slot) - #11 wwstage UNDER (struct-by-value param decompose) - #14 wwstage UNDER (struct multi-return @retscr — silent stomp)
This commit is contained in:
@@ -32,6 +32,13 @@ static Type *cg_ret_type;
|
||||
* needs this to allocate scratch slots (e.g. match bindings) without
|
||||
* threading it through every signature. */
|
||||
static int *cg_frame;
|
||||
/* Per-fn @retscr offset (single-slot SSoT, task #14). Returns are
|
||||
* terminal: at most one return path fires per call, so all retscr
|
||||
* uses share one slot. Mirrors wwstage's `@retscr` convention
|
||||
* (cgen.ww localadd '@'-prefix dedup; #38 ratified single-slot
|
||||
* 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 defer stack: pushed in registration order, popped (emitted)
|
||||
* in reverse at each return. */
|
||||
@@ -6322,11 +6329,23 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
* stores; struct ident: word copy;
|
||||
* tagged subset: copy + tag remap.
|
||||
* 4th word in R8 covers slice payload
|
||||
* variants (slot >= 32B). */
|
||||
* variants (slot >= 32B).
|
||||
*
|
||||
* Single-slot @retscr (#14): returns are
|
||||
* terminal, so all retscr uses in this fn
|
||||
* share one slot. Pre-fix per-site fresh
|
||||
* allocation over-grew the frame by sz
|
||||
* bytes per extra return. */
|
||||
int sz = (int)rt->size;
|
||||
const char *scrn = mklabel(c, "retscr");
|
||||
int scr = local_alloc(c, locals, scrn,
|
||||
sz, cg_frame);
|
||||
int scr;
|
||||
if (cg_retscr != 0) {
|
||||
scr = cg_retscr;
|
||||
} else {
|
||||
const char *scrn = mklabel(c, "retscr");
|
||||
scr = local_alloc(c, locals, scrn,
|
||||
sz, cg_frame);
|
||||
cg_retscr = scr;
|
||||
}
|
||||
ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
|
||||
for (int k = 0; k < sz; k += 8)
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
@@ -6372,9 +6391,17 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
&& (n->lhs->kind == N_IDENT
|
||||
|| n->lhs->kind == N_STRUCTLIT)) {
|
||||
int sz = (int)rt->size;
|
||||
const char *scrn = mklabel(c, "retscr");
|
||||
int scr = local_alloc(c, locals, scrn, 24,
|
||||
cg_frame);
|
||||
/* Single-slot @retscr (#14): see tagged arm
|
||||
* above for rationale. */
|
||||
int scr;
|
||||
if (cg_retscr != 0) {
|
||||
scr = cg_retscr;
|
||||
} else {
|
||||
const char *scrn = mklabel(c, "retscr");
|
||||
scr = local_alloc(c, locals, scrn, 24,
|
||||
cg_frame);
|
||||
cg_retscr = scr;
|
||||
}
|
||||
ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, scr + 0));
|
||||
@@ -6782,6 +6809,7 @@ cgfn(Cg *c, FILE *out, Node *fn)
|
||||
ndefers = 0;
|
||||
nloops = 0;
|
||||
cg_ret_type = fn->type ? fn->type->ret : NULL;
|
||||
cg_retscr = 0;
|
||||
|
||||
int frame = 0;
|
||||
Local *locals = NULL;
|
||||
|
||||
Reference in New Issue
Block a user