diff --git a/cmd/w6c/cgen.c b/cmd/w6c/cgen.c index 06980c05..a2a333fb 100644 --- a/cmd/w6c/cgen.c +++ b/cmd/w6c/cgen.c @@ -2284,7 +2284,16 @@ cgexpr(Cg *c, Node *n, Local *locals) /* Handled in the pre-cgexpr early-exit above. */ break; case TK_STAR: /* deref */ - ins2(c, A_MOVQ, amem(D_AX, 0), areg(D_AX)); + /* f64/f32 result rides X0 (SSE), not AX — an integer + * MOVQ strands the value off the float ABI and the + * caller's MOVSD X0 reads stale bits (#96). Mirrors the + * float field/ident load idiom at 1462/1838. */ + if (node_isfloat(n)) { + ins2(c, node_isf32(n) ? A_MOVSS : A_MOVSD, + amem(D_AX, 0), areg(D_X0)); + } else { + ins2(c, A_MOVQ, amem(D_AX, 0), areg(D_AX)); + } break; default: break; } diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index d3c7bc10..683af0fb 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -16759,7 +16759,20 @@ fn cgun(c: *cgen, n: *node) void = { }; return; }; - if (n.op == tkind.TK_STAR) { emitline("\tMOVQ\t(AX), AX\n"); return; }; + if (n.op == tkind.TK_STAR) { + // f64/f32 result rides X0 (SSE), not AX — an integer MOVQ + // strands the value off the float ABI and the caller's + // MOVSD X0 reads stale bits (#96). Mirrors the float + // field/ident load idiom. + if (isfloattype(c, n)) { + let mov: str = "MOVSD"; + if (isf32type(c, n)) { mov = "MOVSS"; }; + emitline("\t"); emitline(mov); emitline("\t(AX), X0\n"); + } else { + emitline("\tMOVQ\t(AX), AX\n"); + }; + return; + }; if (n.op == tkind.TK_NOT) { let t: str = mklabel(c, "tt"); let e: str = mklabel(c, "te"); diff --git a/selfhost/cmd/wcc/cgenexpr.ww b/selfhost/cmd/wcc/cgenexpr.ww index 3b15759c..9493c5a6 100644 --- a/selfhost/cmd/wcc/cgenexpr.ww +++ b/selfhost/cmd/wcc/cgenexpr.ww @@ -2736,7 +2736,20 @@ fn cgun(c: *cgen, n: *node) void = { }; return; }; - if (n.op == tkind.TK_STAR) { emitline("\tMOVQ\t(AX), AX\n"); return; }; + if (n.op == tkind.TK_STAR) { + // f64/f32 result rides X0 (SSE), not AX — an integer MOVQ + // strands the value off the float ABI and the caller's + // MOVSD X0 reads stale bits (#96). Mirrors the float + // field/ident load idiom. + if (isfloattype(c, n)) { + let mov: str = "MOVSD"; + if (isf32type(c, n)) { mov = "MOVSS"; }; + emitline("\t"); emitline(mov); emitline("\t(AX), X0\n"); + } else { + emitline("\tMOVQ\t(AX), AX\n"); + }; + return; + }; if (n.op == tkind.TK_NOT) { let t: str = mklabel(c, "tt"); let e: str = mklabel(c, "te"); diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 5bc152ab..9dd53a74 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -16759,7 +16759,20 @@ fn cgun(c: *cgen, n: *node) void = { }; return; }; - if (n.op == tkind.TK_STAR) { emitline("\tMOVQ\t(AX), AX\n"); return; }; + if (n.op == tkind.TK_STAR) { + // f64/f32 result rides X0 (SSE), not AX — an integer MOVQ + // strands the value off the float ABI and the caller's + // MOVSD X0 reads stale bits (#96). Mirrors the float + // field/ident load idiom. + if (isfloattype(c, n)) { + let mov: str = "MOVSD"; + if (isf32type(c, n)) { mov = "MOVSS"; }; + emitline("\t"); emitline(mov); emitline("\t(AX), X0\n"); + } else { + emitline("\tMOVQ\t(AX), AX\n"); + }; + return; + }; if (n.op == tkind.TK_NOT) { let t: str = mklabel(c, "tt"); let e: str = mklabel(c, "te");