wcc: f32 array-element store from X0 (#122)

Float array-element stores (array-literal init, [v...] repeat-fill, and
arr[i]=v) now route from X0 via MOVSS/MOVSD in both stages; the AX path
stored the raw double low-bits, garbage for f32 (f64 worked by accident).
A clobbering call-index (a[geti()]=v) loses the X0 value — deferred to #125.
This commit is contained in:
2026-05-26 14:46:01 +09:00
parent 0917fee48d
commit 7d7ed964b0
6 changed files with 188 additions and 19 deletions

View File

@@ -21,13 +21,16 @@
*
* Each row carries (a) a cstage `ww build` + run asserting the exit
* code, and (b) a w6c vs w6c_ww `.s` cmp (rule-10 byte-id). A row whose
* want_exit is RUN_SKIP runs only the byte-id leg: the f32 row exercises
* the f32 LOAD + cs==ww but its runtime VALUE is blocked by a SEPARATE,
* pre-existing bug — the f32 array-element STORE writes AX (the raw
* double low-bits) instead of the CVTSD2SS-narrowed X0 single, so every
* f32 array slot reads back 0.0f. That store-side twin is filed
* separately (#119-store); this probe still proves the f32 LOAD shape +
* cs==ww byte-identity.
* want_exit is RUN_SKIP runs only the byte-id leg.
*
* #122 fixes the store-side twin the #119 commit deferred: the f32
* array-element STORE wrote AX (the raw double low-bits) instead of the
* CVTSD2SS-narrowed X0 single, so every f32 array slot read back garbage.
* Both the array-literal-init store (cgen.c:6889 / cgenstmt:949) and the
* arr[i]= index store (cgen.c:3818 / cgenexpr:4209) now route FROM X0 via
* MOVSS/MOVSD, mirroring the scalar float store. The f32 rows below now
* assert the runtime VALUE (not byte-id only) and add an arr[i]= store
* plus a [v...] repeat-fill init (a distinct cgen arm #122 also fixed).
*/
#include <stdio.h>
#include <stdlib.h>
@@ -75,17 +78,42 @@ static const struct row rows[] = {
" if (a[2] != 9.0) { return 1; };\n"
" return 0;\n"
"};\n", 0 },
/* f32 element load (MOVSS into X0) + add, suffixed literals so
* fold-1 narrows them. BYTE-ID ONLY: the runtime value is blocked by
* the f32 array-element STORE bug (#119-store), so we assert only
* that both stages emit the same (correct-load) asm. */
{ "f32_arith_byteid",
/* f32 array-literal-init store + element load + add: suffixed
* literals so fold-1 narrows them. Pre-#122 the init store wrote
* MOVL AX (raw double low-bits) so the slots read garbage; #122
* routes the store from X0 via MOVSS, so 1.5 + 2.5 == 4.0. */
{ "f32_arith",
"package main;\n"
"export fn main() i32 = {\n"
" let b: [2]f32 = [1.5f32, 2.5f32];\n"
" if ((b[0] + b[1]): f64 != 4.0) { return 1; };\n"
" return 0;\n"
"};\n", RUN_SKIP },
"};\n", 0 },
/* f32 arr[i]= index store (#122): assign each slot, read back.
* The index-store path popped the value to AX and wrote MOVL (raw
* double low-bits, garbage for f32); #122 stores from X0 via MOVSS.
* The [0.0f32,0.0f32] init also exercises the array-lit store. */
{ "f32_index_store",
"package main;\n"
"export fn main() i32 = {\n"
" let b: [2]f32 = [0.0f32, 0.0f32];\n"
" b[0] = 1.5f32;\n"
" b[1] = 2.5f32;\n"
" if ((b[0] + b[1]): f64 != 4.0) { return 1; };\n"
" return 0;\n"
"};\n", 0 },
/* f32 [v...] repeat-fill init store (#122): the repeat marker
* fills every slot from the last element's X0 single; pre-#122 the
* fill wrote MOVL AX (raw double low-bits) per slot so each read
* back garbage. Distinct cgen arm from the per-element list store.
* 1.5 * 3 == 4.5 (exact in IEEE). */
{ "f32_repeat_fill",
"package main;\n"
"export fn main() i32 = {\n"
" let c: [3]f32 = [1.5f32...];\n"
" if ((c[0] + c[1] + c[2]): f64 != 4.5) { return 1; };\n"
" return 0;\n"
"};\n", 0 },
{ NULL, NULL, 0 }
};