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:
@@ -1295,6 +1295,25 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
|
||||
if (via_outer) goto copy_out;
|
||||
return;
|
||||
}
|
||||
/* Float arm: cgexpr on an f64/f32 source leaves the bit pattern in
|
||||
* X0 only — the AX-store 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.
|
||||
* Same kind-specific dispatch as the str/slice branches above and
|
||||
* the structlit field-flow at the top of this function. */
|
||||
int wid_isf32 = 0;
|
||||
if (fld_isfloat(st, &wid_isf32)) {
|
||||
int mov = wid_isf32 ? A_MOVSS : A_MOVSD;
|
||||
cgexpr(c, src, *locals_p);
|
||||
ins2(c, mov, areg(D_X0), amem(D_BP, write_off + 8));
|
||||
int tag = cg_tag_for_variant(du, st);
|
||||
ins2(c, A_MOVQ, aimm(tag < 0 ? 0 : tag),
|
||||
amem(D_BP, write_off + 0));
|
||||
if (via_outer) goto copy_out;
|
||||
return;
|
||||
}
|
||||
/* Scalar / pointer / etc. The high slot word (when sz > 16) is
|
||||
* left untouched here — match dispatches on the tag word first
|
||||
* and only the str branch reads slot+16, so leaving the pad
|
||||
|
||||
Reference in New Issue
Block a user