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:
@@ -18215,7 +18215,24 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node, memphase: bool) i32 = {
|
||||
emitline("\tPUSHQ\tDX\n");
|
||||
pp -= 8;
|
||||
};
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
// #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
|
||||
// — why #48 with a no-payload-read arm passed but #49
|
||||
// reading `d == 2.5` did not). Both stages (#263);
|
||||
// cstage cg_widen_tagged_push twin.
|
||||
if (isfloattype(c, arg)) {
|
||||
emitline("\tSUBQ\t$8, SP\n");
|
||||
let fmov: str = "MOVSD";
|
||||
if (isf32type(c, arg)) { fmov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(fmov);
|
||||
emitline("\tX0, (SP)\n");
|
||||
} else {
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(widentag: i64);
|
||||
emitline(", AX\n");
|
||||
|
||||
Reference in New Issue
Block a user