wcc: widen-push spills the float payload from X0, both stages
Widening a runtime f64 into a tagged slot pushed a stale AX as the payload while the value sat in X0 — both stages shared the push bug (float literals dodged it because TK_FLOAT loads AX too); the divergent pop sides then produced different garbage. Spill the payload from X0 (MOVSD) with the variant tag. Review item #49. Both stages move in one commit: one emission contract; splitting would leave the byte-id gates red between the halves.
This commit is contained in:
@@ -3108,7 +3108,20 @@ cg_widen_tagged_push(Cg *c, Local **locals_p, Type *dst, Node *src, int sz)
|
||||
ins2(c, A_XORQ, areg(D_DX), areg(D_DX));
|
||||
ins1(c, A_PUSHQ, areg(D_DX));
|
||||
}
|
||||
ins1(c, A_PUSHQ, areg(D_AX)); /* value at +8 */
|
||||
/* #49: an f64/f32 payload sits in X0 (cgexpr left it there),
|
||||
* not AX — spill it through the stack so the callee reads the
|
||||
* real bits. A plain PUSHQ AX pushed whatever AX last held
|
||||
* (stale for a runtime float producer; only a const folder
|
||||
* leaves the bits in AX, which is why #48's no-payload-read arm
|
||||
* passed but #49's `d == 2.5` read did not). Both stages
|
||||
* (#263); wwstage cgenutil.ww scalar-variant twin. */
|
||||
if (node_isfloat(src)) {
|
||||
int fmov = node_isf32(src) ? A_MOVSS : A_MOVSD;
|
||||
ins2(c, A_SUBQ, aimm(8), areg(D_SP));
|
||||
ins2(c, fmov, areg(D_X0), amem(D_SP, 0));
|
||||
} else {
|
||||
ins1(c, A_PUSHQ, areg(D_AX)); /* value at +8 */
|
||||
}
|
||||
ins2(c, A_MOVQ, aimm(tag), areg(D_AX));
|
||||
ins1(c, A_PUSHQ, areg(D_AX)); /* tag at +0 */
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user