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:
@@ -749,6 +749,11 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
let idx: *node = n.rhs;
|
||||
let esz: i32 = 8;
|
||||
let signed_elem: bool = false;
|
||||
// #119: float element loads route to MOVSS/MOVSD into X0, not the
|
||||
// integer loadopsz into AX. float_elem/f32_elem are set per-branch
|
||||
// from the SAME tinfo esz reads — never a fresh node-stamp (#121).
|
||||
let float_elem: bool = false;
|
||||
let f32_elem: bool = false;
|
||||
// #1/Phase 3: str and slice are both 24B (and a >16B struct is
|
||||
// 24B+ too), so the header branches below MUST gate on KIND
|
||||
// (elemisstr/elemisslice, mirroring cstage's elem_is_str||
|
||||
@@ -774,6 +779,8 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
if (baselocal != nil) {
|
||||
esz = elemsizeofc(c, baselocal.tnode);
|
||||
signed_elem = elemissignedc(c, baselocal.tnode);
|
||||
float_elem = elemisfloatc(c, baselocal.tnode);
|
||||
f32_elem = elemisf32c(c, baselocal.tnode);
|
||||
} else {
|
||||
let tn: *node = letvartnode(c, bn);
|
||||
if (tn != nil) {
|
||||
@@ -782,12 +789,16 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
signed_elem = elemissignedc(c, tn);
|
||||
float_elem = elemisfloatc(c, tn);
|
||||
f32_elem = elemisf32c(c, tn);
|
||||
};
|
||||
if (tn.kind == nkind.N_TPTR) {
|
||||
isglobalptr = true;
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
signed_elem = elemissignedc(c, tn);
|
||||
float_elem = elemisfloatc(c, tn);
|
||||
f32_elem = elemisf32c(c, tn);
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -799,7 +810,7 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
// cgen.c:3517-18). esz-only — N_DOT-base signedness
|
||||
// stays unset, as before.
|
||||
let dt: *tinfo = n.type_: *tinfo;
|
||||
if (dt != nil) { esz = dt.size: i32; elemisstr = typeisstr(dt); elemisslice = typeisslice(dt); };
|
||||
if (dt != nil) { esz = dt.size: i32; elemisstr = typeisstr(dt); elemisslice = typeisslice(dt); float_elem = typeisfloat(dt); f32_elem = typeisf32(dt); };
|
||||
} else { if (base.kind == nkind.N_INDEX) {
|
||||
// #60: chained `names[i][k]` — n.type_ is the checker-
|
||||
// stamped outer element tinfo (indexresult over the inner
|
||||
@@ -810,6 +821,8 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
if (et != nil) {
|
||||
esz = et.size: i32;
|
||||
signed_elem = typeissigned(et);
|
||||
float_elem = typeisfloat(et);
|
||||
f32_elem = typeisf32(et);
|
||||
};
|
||||
};};};
|
||||
};
|
||||
@@ -891,6 +904,18 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
cgslicehdr(c, "BX");
|
||||
return;
|
||||
};
|
||||
// #119: float element → MOVSS/MOVSD into X0 (the consumer's
|
||||
// ADDSD/MOVSD spill machinery already expects X0); the integer
|
||||
// loadopsz below would leave it in AX and the SSE side reads
|
||||
// stale. Twin of cgen.c:2014's scalar-float global load.
|
||||
if (float_elem) {
|
||||
let fop1: str = "MOVSD";
|
||||
if (f32_elem) { fop1 = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(fop1);
|
||||
emitline("\t(BX), X0\n");
|
||||
return;
|
||||
};
|
||||
let lop1: str = loadopsz(signed_elem, esz);
|
||||
emitline("\t");
|
||||
emitline(lop1);
|
||||
@@ -930,6 +955,15 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
cgslicehdr(c, "BX");
|
||||
return;
|
||||
};
|
||||
// #119: float element → X0 (see the global arm above).
|
||||
if (float_elem) {
|
||||
let fop2: str = "MOVSD";
|
||||
if (f32_elem) { fop2 = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(fop2);
|
||||
emitline("\t(BX), X0\n");
|
||||
return;
|
||||
};
|
||||
let lop2: str = loadopsz(signed_elem, esz);
|
||||
emitline("\t");
|
||||
emitline(lop2);
|
||||
@@ -961,6 +995,16 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
cgslicehdr(c, "AX");
|
||||
return;
|
||||
};
|
||||
// #119: float element → X0 (see the global arm above). The base
|
||||
// address is in AX; MOVSS/MOVSD reads the element into X0.
|
||||
if (float_elem) {
|
||||
let fop3: str = "MOVSD";
|
||||
if (f32_elem) { fop3 = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(fop3);
|
||||
emitline("\t(AX), X0\n");
|
||||
return;
|
||||
};
|
||||
let lop3: str = loadopsz(signed_elem, esz);
|
||||
emitline("\t");
|
||||
emitline(lop3);
|
||||
|
||||
Reference in New Issue
Block a user