wcc: tagged float-variant return packs float bits via X0-spill (#157)

The N_RETURN tagged-pack scalar-variant arm did MOVQ AX,DX, but a float
variant's value is in X0 not AX -> packed stale int (broke stof64/stof32
return (f64|invalid|overflow)). Fix: float variant bridges X0->DX via a
stack slot (SUBQ $8,SP; MOVQ $0,(SP); MOVSS|MOVSD X0,(SP); MOVQ (SP),DX;
ADDQ $8,SP), gated type_isfloat/exprfloatkind. No MOVQ-xmm->gp form
exists, hence the spill (715-class, cgreturn-register-pack twin of 715's
store-to-slot). Zero-slot-first -> deterministic f32 high-4. AX-independent
-> also resolves the multi-variant cs!=ww. Bootstrap-NEUTRAL (compiler has
no float-tagged-return). Test 707 +3 rows (f64/f32/multi, slot+8 bit-exact;
f32 no-f32-arg to isolate #143). Make test 184/184 incl 990-997 byte-id.
This commit is contained in:
2026-05-27 13:04:02 +09:00
parent cbeffea7d8
commit 4a91bdc8db
5 changed files with 158 additions and 4 deletions

View File

@@ -1,7 +1,10 @@
/*
* 707_cgreturn_variant_zero — tagged-return ABI variant-widen zero-pad
* for unused AX/DX/CX/R8 words (task #18) plus aliased-tagged variant-
* index lookup (task #20).
* index lookup (task #20) plus float-variant payload-from-X0 pack
* (#157, rows 10-12): the same scalar-variant arm packed stale AX into
* the payload word for an f64/f32 value (which lives in X0), not the
* float bits — fixed by an X0->stack->DX spill bridge.
*
* Pre-fix (#18): cgreturn's `!istagged && !isstruct` variant-widen arm
* only filled the registers a given variant actually uses (scalar → DX;
@@ -247,6 +250,53 @@ static const struct row rows[] = {
" return (*pt): i32;\n"
"};\n",
2 },
/* 10-12: #157 — float variant of the same `!istagged && !isstruct`
* return arm. Pre-fix it did `MOVQ AX, DX` even when the value was
* a float in X0 (no MOVQ-xmm->gp form), so the payload word held
* stale AX, not the float bits. Fix spills X0 through a stack slot
* (zero-slot-first so the f32 MOVSS low-4 write leaves a determin-
* istic high-4). Probe slot+8 as the float directly (bit-exact
* f64/f32 equality) — isolates the return-pack from the match-
* receive. The f32 row avoids f32 ARGS (pre-existing #143 f32-arg
* MOVSD/MOVSS divergence is unrelated; f32 here comes from a global
* with an int arg, so the byte-id check pins THIS fix). */
{ "f64_variant_return_payload",
"type ft = (f64 | i32);\n"
"fn addf(a: f64, b: f64) ft = { return a + b; };\n"
"fn main() i32 = {\n"
" let a: ft = addf(1.5, 2.0);\n"
" let p: u64 = (&a): u64;\n"
" let pf: *f64 = (p + 8u64): *f64;\n"
" let fv: f64 = *pf;\n"
" if (fv == 3.5) { return 1; };\n"
" return 0;\n"
"};\n",
1 },
{ "f32_variant_return_payload",
"let g: f32 = 1.5f32;\n"
"type ft = (f32 | i32);\n"
"fn mkf(k: i32) ft = { if (k > 0) { return g + 2.0f32; }; return k; };\n"
"fn main() i32 = {\n"
" let a: ft = mkf(1);\n"
" let p: u64 = (&a): u64;\n"
" let pf: *f32 = (p + 8u64): *f32;\n"
" let fv: f32 = *pf;\n"
" if (fv == 3.5f32) { return 1; };\n"
" return 0;\n"
"};\n",
1 },
{ "f64_variant_return_multivariant",
"type ft = (f64 | i32 | bool);\n"
"fn mulf(a: f64, b: f64) ft = { return a * b; };\n"
"fn main() i32 = {\n"
" let a: ft = mulf(2.5, 4.0);\n"
" let p: u64 = (&a): u64;\n"
" let pf: *f64 = (p + 8u64): *f64;\n"
" let fv: f64 = *pf;\n"
" if (fv == 10.0) { return 1; };\n"
" return 0;\n"
"};\n",
1 },
};
static int