wcc: float array-element loads to X0 + indexed-float consumer (#119)

cgindex's element-load sites ended in the integer loadopsz (MOVQ/MOVL
into AX), with no float branch — so an f32/f64 array element landed in
a GPR while the consumer's ADDSD/MOVSD read a stale X0. Add a float-
element branch (MOVSS f32 / MOVSD f64 into X0) at all three wwstage
cgindex sites (global, baselocal, fallback) and both cstage N_INDEX
element-load sites, deriving float-ness from the SAME stamped element
tinfo the esz already reads: new elemisfloatc/elemisf32c helpers
(mirroring elemissignedc) for ident bases, typeisfloat/typeisf32(n.type_)
for N_DOT/N_INDEX bases — never a fresh node-stamp that could hit an
unstamped base (#121).

The load fix cannot land alone: the wwstage consumer (cgbin/cgcast)
classified an indexed float operand as INTEGER (no exprfloatkind N_INDEX
arm) and fell to PUSHQ/ADDQ/MOVSXD, while the cstage read the stamped
operand type and used ADDSD/CVTTSD2SI. That divergence is pre-existing
on master (proven: master cs vs ww already differ on `a[0]+a[1]`),
contradicting the original "consumer already expects X0, cs==ww"
premise; load-only would leave the wwstage incoherent (value in X0,
consumed from AX) and still cs!=ww. So this also adds the exprfloatkind
N_INDEX arm — safe because the index-result type_ IS checker-stamped
(cgindex reads it for esz), unlike the unstamped-N_MLET case deferred
under #121. With both, f64 arrays are runtime-correct and both stages
emit byte-identical asm.

946_floatarr_run: f64 element add / trunc / non-adjacent index assert
the value + cs==ww; the f32 row asserts cs==ww only — its runtime value
is blocked by a SEPARATE store-side bug (f32 array-element store writes
AX raw double low-bits instead of CVTSD2SS-narrowed X0), filed as
#119-store. Regen w6c/wwdump combined.ww (cgenexpr.ww + cgenutil.ww
embedded).
This commit is contained in:
2026-05-26 13:06:42 +09:00
parent a1dff13ec1
commit 0917fee48d
7 changed files with 499 additions and 3 deletions

View File

@@ -6414,6 +6414,17 @@ cgexpr(Cg *c, Node *n, Local *locals)
ins2(c, A_MOVQ, amem(D_BX, 0), areg(D_AX));
break;
}
/* float element → MOVSS/MOVSD into X0: the consumer's
* ADDSD/MOVSD spill machinery already expects X0, but the
* integer fldloadop below would leave it in AX and the SSE
* side reads stale (#119). Float-ness from esub — the same
* type the esz above reads. Twin of the scalar-float global
* load at cgen.c:2014. */
if (type_isfloat(esub)) {
int op = type_isf32(esub) ? A_MOVSS : A_MOVSD;
ins2(c, op, amem(D_BX, 0), areg(D_X0));
break;
}
int load_op = fldloadop(esub, esz);
ins2(c, load_op, amem(D_BX, 0), areg(D_AX));
break;
@@ -6456,6 +6467,13 @@ cgexpr(Cg *c, Node *n, Local *locals)
ins2(c, A_MOVQ, amem(D_BX, 0), areg(D_AX));
break;
}
/* float element via fallback base → X0 (see Site A, #119). The
* base address is in AX; MOVSS/MOVSD reads the element into X0. */
if (type_isfloat(esub)) {
int op = type_isf32(esub) ? A_MOVSS : A_MOVSD;
ins2(c, op, amem(D_AX, 0), areg(D_X0));
break;
}
{
int load_op = fldloadop(esub, esz);
ins2(c, load_op, amem(D_AX, 0), areg(D_AX));