cstage+selfhost+test: f64 variant-widen via MOVSD from X0 (#30)
Initializing a tagged-union variant slot with a runtime f64 source (let, cast, fn call, unary, struct field, etc.) stored the i64 bit pattern in the payload, not the float bit pattern. cgexpr leaves f64 in X0; the existing scalar-fallback MOVQ-from-AX wrote whatever was last in AX (typically pre-conversion integer or stale residue). Worker-fmtfloat surfaced this during #17 pre-flight (probe at .ai/probe_f64_union_widen.ww). Blocks #17 fmt.float dispatch arm. TK_FLOAT literals were coincidentally correct because the lowering loads bits into AX before passing through X0 — the literal_1_0 test row pins that as the principled MOVSD path now. cstage cg_widen_tagged_store: add fld_isfloat arm between the slice and scalar fallbacks. Emit MOVSD (f64) / MOVSS (f32) from X0 to the payload offset, then the tag MOVQ. Mirrors existing str/slice/ structlit field-flow dispatchers. Wwstage cgwidentaggedstorebp: mirror via exprfloatkind. Resolves a secondary gap by looking up the variant tag directly via flatvariantidx(c, dt, "f64"/"f32") — rhstargetname has no N_FLOATLIT / N_CALL / N_DOT branch and would fall through to str-fallback returning tag 0. No in-tree consumer triggered this pre-fix (no f64 in any tagged union yet) — hence latent silence. arr[i]= and append() have the same class gap but no in-tree exerciser today; same shape if/when [N]f64 / []f64 land. Test 715 (tagged_widen_f64): 7 rows × 2 stages = 14 fixtures with bit-pinning via *u8 punning. literal_1_0 (regression lock-in), cast_1_f64, call_makeone, unary_neg_f64, ident_f64, field_f64 (rob's extra row), i64_rhs_still_integer (negative control). Diagnosable 0/1/2 return codes distinguish pass / wrong-tag / wrong-payload. ww2 == ww3 == ww4 byte-identical post-fix.
This commit is contained in:
@@ -9110,6 +9110,37 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
// Float arm: cgexpr on an f64/f32 source leaves the bit pattern in
|
||||
// X0 only — the AX-store fallback below would silently write whatever
|
||||
// was loaded into AX before the SSE conversion. Literal `1.0` works
|
||||
// by coincidence (TK_FLOAT lowering loads the f64 bit pattern into AX
|
||||
// before MOVSD'ing into X0); every runtime f64 shape (cast, call,
|
||||
// unary, ident, struct-field load) needs the explicit MOVSD path.
|
||||
// Mirror of cstage cg_widen_tagged_store's float arm. Wwstage has no
|
||||
// checker so we classify via exprfloatkind (same shape used by cgcast)
|
||||
// and resolve the variant tag by name directly — rhstargetname has no
|
||||
// N_FLOATLIT / N_CALL / N_DOT branch and would fall through to the
|
||||
// str-shape fallback that picks tag 0 for an `(i64 | f64)` union.
|
||||
let fkind: i32 = exprfloatkind(c, src);
|
||||
if (fkind != 0) {
|
||||
let fmov: str = "MOVSD";
|
||||
let fname: str = "f64";
|
||||
if (fkind == 1) { fmov = "MOVSS"; fname = "f32"; };
|
||||
cgexpr(c, src);
|
||||
emitline("\t");
|
||||
emitline(fmov);
|
||||
emitline("\tX0, ");
|
||||
emitoff((slot_off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
let ftag: i32 = flatvariantidx(c, dt, fname);
|
||||
if (ftag < 0) { ftag = 0; };
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(ftag: i64);
|
||||
emitline(", ");
|
||||
emitoff(slot_off: i64);
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
// Scalar payload.
|
||||
cgexpr(c, src);
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
|
||||
@@ -2867,6 +2867,37 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
// Float arm: cgexpr on an f64/f32 source leaves the bit pattern in
|
||||
// X0 only — the AX-store fallback below would silently write whatever
|
||||
// was loaded into AX before the SSE conversion. Literal `1.0` works
|
||||
// by coincidence (TK_FLOAT lowering loads the f64 bit pattern into AX
|
||||
// before MOVSD'ing into X0); every runtime f64 shape (cast, call,
|
||||
// unary, ident, struct-field load) needs the explicit MOVSD path.
|
||||
// Mirror of cstage cg_widen_tagged_store's float arm. Wwstage has no
|
||||
// checker so we classify via exprfloatkind (same shape used by cgcast)
|
||||
// and resolve the variant tag by name directly — rhstargetname has no
|
||||
// N_FLOATLIT / N_CALL / N_DOT branch and would fall through to the
|
||||
// str-shape fallback that picks tag 0 for an `(i64 | f64)` union.
|
||||
let fkind: i32 = exprfloatkind(c, src);
|
||||
if (fkind != 0) {
|
||||
let fmov: str = "MOVSD";
|
||||
let fname: str = "f64";
|
||||
if (fkind == 1) { fmov = "MOVSS"; fname = "f32"; };
|
||||
cgexpr(c, src);
|
||||
emitline("\t");
|
||||
emitline(fmov);
|
||||
emitline("\tX0, ");
|
||||
emitoff((slot_off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
let ftag: i32 = flatvariantidx(c, dt, fname);
|
||||
if (ftag < 0) { ftag = 0; };
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(ftag: i64);
|
||||
emitline(", ");
|
||||
emitoff(slot_off: i64);
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
// Scalar payload.
|
||||
cgexpr(c, src);
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
|
||||
@@ -9110,6 +9110,37 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
// Float arm: cgexpr on an f64/f32 source leaves the bit pattern in
|
||||
// X0 only — the AX-store fallback below would silently write whatever
|
||||
// was loaded into AX before the SSE conversion. Literal `1.0` works
|
||||
// by coincidence (TK_FLOAT lowering loads the f64 bit pattern into AX
|
||||
// before MOVSD'ing into X0); every runtime f64 shape (cast, call,
|
||||
// unary, ident, struct-field load) needs the explicit MOVSD path.
|
||||
// Mirror of cstage cg_widen_tagged_store's float arm. Wwstage has no
|
||||
// checker so we classify via exprfloatkind (same shape used by cgcast)
|
||||
// and resolve the variant tag by name directly — rhstargetname has no
|
||||
// N_FLOATLIT / N_CALL / N_DOT branch and would fall through to the
|
||||
// str-shape fallback that picks tag 0 for an `(i64 | f64)` union.
|
||||
let fkind: i32 = exprfloatkind(c, src);
|
||||
if (fkind != 0) {
|
||||
let fmov: str = "MOVSD";
|
||||
let fname: str = "f64";
|
||||
if (fkind == 1) { fmov = "MOVSS"; fname = "f32"; };
|
||||
cgexpr(c, src);
|
||||
emitline("\t");
|
||||
emitline(fmov);
|
||||
emitline("\tX0, ");
|
||||
emitoff((slot_off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
let ftag: i32 = flatvariantidx(c, dt, fname);
|
||||
if (ftag < 0) { ftag = 0; };
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(ftag: i64);
|
||||
emitline(", ");
|
||||
emitoff(slot_off: i64);
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
// Scalar payload.
|
||||
cgexpr(c, src);
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
|
||||
Reference in New Issue
Block a user