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:
@@ -791,6 +791,34 @@ fn elemissignedc(c: *cgen, t: *node) bool = {
|
||||
return typeissigned(ti.sub);
|
||||
};
|
||||
|
||||
// elemisfloatc — given an indexable type-AST (`*T`, `[]T`, `[N]T`), is
|
||||
// its element an f32/f64? Used by cgindex to route the element load to
|
||||
// MOVSS/MOVSD into X0 instead of the integer loadopsz into AX (#119 —
|
||||
// the array-element twin of the scalar-float global load at cgen.c:
|
||||
// 2014). Reads through the stamped tinfo, peeling TY_NAMED before the
|
||||
// .sub read exactly as elemissignedc does (#64/#65). Float-ness comes
|
||||
// from the SAME tinfo the esz already reads — never a fresh node-stamp
|
||||
// (the unstamped-base trap that broke the exprfloatkind collapse, #121).
|
||||
fn elemisfloatc(c: *cgen, t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let ti: *tinfo = t.type_: *tinfo;
|
||||
if (ti == nil) { return false; };
|
||||
for (ti != nil && ti.kind == tykind.TY_NAMED) { ti = ti.under; };
|
||||
if (ti == nil) { return false; };
|
||||
return typeisfloat(ti.sub);
|
||||
};
|
||||
|
||||
// elemisf32c — narrower elemisfloatc: true only when the element is f32,
|
||||
// so cgindex picks MOVSS over MOVSD at the #119 element load.
|
||||
fn elemisf32c(c: *cgen, t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let ti: *tinfo = t.type_: *tinfo;
|
||||
if (ti == nil) { return false; };
|
||||
for (ti != nil && ti.kind == tykind.TY_NAMED) { ti = ti.under; };
|
||||
if (ti == nil) { return false; };
|
||||
return typeisf32(ti.sub);
|
||||
};
|
||||
|
||||
// fieldissignedc — does this field/element type-AST need sign-
|
||||
// extension on a sub-word load? One-liner via typeissigned (cstage
|
||||
// cgen.c:240 `fld_issigned` SSoT). t.type_ is stamped at check.ww
|
||||
@@ -1865,6 +1893,21 @@ export fn exprfloatkind(c: *cgen, n: *node) i32 = {
|
||||
if (isfloattype(c, n.rhs)) { return 2; };
|
||||
return 0;
|
||||
};
|
||||
if (k == nkind.N_INDEX) {
|
||||
// #119: a float array/slice element feeds cgbin / cgcast through
|
||||
// X0 (the #119 load is MOVSS/MOVSD into X0). Without this arm the
|
||||
// wwstage consumer falls to integer (PUSHQ/ADDQ, MOVSXD) while
|
||||
// the cstage reads the stamped operand type and uses ADDSD/
|
||||
// CVTTSD2SI — a rule-10 divergence the #119 load fix exposes (the
|
||||
// consumer was never aligned for indexed float operands). The
|
||||
// index-result type_ IS checker-stamped (cgindex reads it for
|
||||
// esz at the N_DOT/N_INDEX-base arms), so this is NOT the
|
||||
// unstamped-N_MLET-base trap that deferred the broader collapse
|
||||
// (#121) — only the always-stamped N_INDEX case is classified.
|
||||
if (isf32type(c, n)) { return 1; };
|
||||
if (isfloattype(c, n)) { return 2; };
|
||||
return 0;
|
||||
};
|
||||
if (k == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, n.str);
|
||||
if (lc != nil) {
|
||||
|
||||
Reference in New Issue
Block a user