cstage+selfhost+test: zero unused ABI words in cgreturn variant-widen (#18)
cgreturn's variant-widen arm only filled the registers each variant's payload needed: scalar variants left CX and R8 stale; str variant left R8 stale. The receiver (cg_widen_tagged_store non-N_IDENT branch) writes all four ABI words to the dst slot unconditionally, so caller-side residue in CX/R8 (the array-index IMULQ being the canonical primer) landed at slot+16 and slot+24. Worker-fmtparser surfaced this through fprintf's loop body where array indexing primed CX and a 24B-return helper failed to clear it; bug isn't loop-specific — straight-line repro at /tmp/wcrs_repro/ repro8.ww confirms. Patch: emit `MOVQ $0, CX` after the variant's register shuffle when the slot exceeds 16B and the variant doesn't fill CX; same for R8 when the slot exceeds 24B. Symmetric across cstage cgen.c and wwstage cgenstmt.ww. Order: zero-MOVQs precede `MOVQ $tag, AX` so AX-as-staging stays safe. Inline comment at cg_widen_tagged_store non-N_IDENT branch documents the producer-zero contract. Test 707 (cgreturn_variant_zero): 6 rows × both stages = 12 fixtures. Covers scalar/bool/str returns after array-index priming in straight-line / single-loop body / nested-loop body / mixed-variant loop. Asm byte-identity check intentionally omitted; wwstage taggedvariantindex divergence on str/bool N_IDENT is filed as task #20. 995_self_rebuild covers the broader cross-stage drift surface. ww2 == ww3 == ww4 byte-identical post-fix. 67/67 green. Deferred (followups filed): #20 wwstage taggedvariantindex, #21 [N]str/[N]bool array-literal non-pointer-half writes, #22 consolidate variant-widen into uniform scratch-slot path.
This commit is contained in:
@@ -1082,6 +1082,12 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
|
||||
amem(D_BP, write_off + k));
|
||||
}
|
||||
} else {
|
||||
/* Tagged source returned via the tagged-return ABI
|
||||
* (AX=tag, DX=word0, CX=word1, R8=word2). The unused
|
||||
* ABI words are zeroed by the producer (#18 cgreturn
|
||||
* variant-widen) so the unconditional store here is
|
||||
* safe even when the source variant has fewer payload
|
||||
* words than the dst slot. */
|
||||
cgexpr(c, src, *locals_p);
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, write_off + 0));
|
||||
@@ -6065,8 +6071,15 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
* Tagged-return ABI: AX=tag, DX=word0,
|
||||
* CX=word1, R8=word2. Slice payload uses
|
||||
* all four; str uses three; scalar uses
|
||||
* two. */
|
||||
* two. Unused ABI words must still be
|
||||
* zeroed because the receiver
|
||||
* (cg_widen_tagged_store call-source arm)
|
||||
* writes AX/DX/CX/R8 unconditionally sized
|
||||
* by the dst slot; stale CX/R8 from the
|
||||
* caller (e.g. a slice-stride IMULQ) would
|
||||
* land in slot+16 / slot+24. (Task #18.) */
|
||||
int tag = cg_tag_for_variant(rt, vt);
|
||||
int rsz = (int)rt->size;
|
||||
cgexpr(c, n->lhs, *locals);
|
||||
if (type_isslice(vt)) {
|
||||
/* cgexpr leaves (AX=ptr, BX=len,
|
||||
@@ -6083,9 +6096,23 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
areg(D_CX));
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
areg(D_DX));
|
||||
/* str fills DX,CX. Zero R8 if dst
|
||||
* slot covers slot+24. */
|
||||
if (rsz > 24)
|
||||
ins2(c, A_MOVQ, aimm(0),
|
||||
areg(D_R8));
|
||||
} else {
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
areg(D_DX));
|
||||
/* scalar fills DX only. Zero
|
||||
* CX / R8 if dst slot covers
|
||||
* slot+16 / slot+24. */
|
||||
if (rsz > 16)
|
||||
ins2(c, A_MOVQ, aimm(0),
|
||||
areg(D_CX));
|
||||
if (rsz > 24)
|
||||
ins2(c, A_MOVQ, aimm(0),
|
||||
areg(D_R8));
|
||||
}
|
||||
ins2(c, A_MOVQ, aimm(tag < 0 ? 0 : tag),
|
||||
areg(D_AX));
|
||||
|
||||
Reference in New Issue
Block a user