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

@@ -18444,6 +18444,24 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tMOVQ\tCX, 16(BX)\n");
return;
};
// float element → store FROM X0 (MOVSS/MOVSD): cgexpr
// left the value in X0. X0 survives the index/base
// eval only for literal/local-var indices; a call-
// index (a[geti()]=v) clobbers X0 and loses the value
// — this path does not spill X0 across the eval,
// unlike the *p=v float deref store. Deferred to #125.
// For f32 the #104 CVTSD2SS narrowing only touches X0,
// so the AX store below would write raw double low-
// bits, garbage for f32 (#122, mirrors cstage cgen.c
// arr[i]= float store).
if (isfloattype(c, elemtn)) {
let fmov: str = "MOVSD";
if (isf32type(c, elemtn)) { fmov = "MOVSS"; };
emitline("\t");
emitline(fmov);
emitline("\tX0, (BX)\n");
return;
};
let isop: str = tnodestoreop(c, elemtn, esz);
emitline("\t");
emitline(isop);
@@ -21435,6 +21453,14 @@ fn cglet(c: *cgen, n: *node) void = {
};
};
let mop: str = tnodestoreop(c, elemn, esz);
// float element → store FROM X0 (MOVSS/MOVSD): cgexpr
// leaves a float in X0 and for f32 the #104 CVTSD2SS
// narrowing only touches X0; the AX store (mop) would
// write the raw double low-bits, garbage for f32 (#122,
// mirrors cstage cgen.c:6889 arr-lit float store).
let isfloatel: bool = isfloattype(c, elemn);
let fmov: str = "MOVSD";
if (isf32type(c, elemn)) { fmov = "MOVSS"; };
let idx: i32 = 0;
let repeat: bool = false;
let e: *node = rhs.list;
@@ -21457,13 +21483,19 @@ fn cglet(c: *cgen, n: *node) void = {
emitline("\tMOVQ\tBX, ");
emitoff((off + idx * esz + 8): i64);
emitline("(BP)\n");
} else { if (isfloatel) {
emitline("\t");
emitline(fmov);
emitline("\tX0, ");
emitoff((off + idx * esz): i64);
emitline("(BP)\n");
} else {
emitline("\t");
emitline(mop);
emitline("\tAX, ");
emitoff((off + idx * esz): i64);
emitline("(BP)\n");
};
}; };
idx += 1;
e = e.next;
};
@@ -21489,13 +21521,19 @@ fn cglet(c: *cgen, n: *node) void = {
emitline("\tMOVQ\tBX, ");
emitoff((off + idx * esz + 8): i64);
emitline("(BP)\n");
} else { if (isfloatel) {
emitline("\t");
emitline(fmov);
emitline("\tX0, ");
emitoff((off + idx * esz): i64);
emitline("(BP)\n");
} else {
emitline("\t");
emitline(mop);
emitline("\tAX, ");
emitoff((off + idx * esz): i64);
emitline("(BP)\n");
};
}; };
idx += 1;
};
};

View File

@@ -4206,6 +4206,24 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tMOVQ\tCX, 16(BX)\n");
return;
};
// float element → store FROM X0 (MOVSS/MOVSD): cgexpr
// left the value in X0. X0 survives the index/base
// eval only for literal/local-var indices; a call-
// index (a[geti()]=v) clobbers X0 and loses the value
// — this path does not spill X0 across the eval,
// unlike the *p=v float deref store. Deferred to #125.
// For f32 the #104 CVTSD2SS narrowing only touches X0,
// so the AX store below would write raw double low-
// bits, garbage for f32 (#122, mirrors cstage cgen.c
// arr[i]= float store).
if (isfloattype(c, elemtn)) {
let fmov: str = "MOVSD";
if (isf32type(c, elemtn)) { fmov = "MOVSS"; };
emitline("\t");
emitline(fmov);
emitline("\tX0, (BX)\n");
return;
};
let isop: str = tnodestoreop(c, elemtn, esz);
emitline("\t");
emitline(isop);

View File

@@ -962,6 +962,14 @@ fn cglet(c: *cgen, n: *node) void = {
};
};
let mop: str = tnodestoreop(c, elemn, esz);
// float element → store FROM X0 (MOVSS/MOVSD): cgexpr
// leaves a float in X0 and for f32 the #104 CVTSD2SS
// narrowing only touches X0; the AX store (mop) would
// write the raw double low-bits, garbage for f32 (#122,
// mirrors cstage cgen.c:6889 arr-lit float store).
let isfloatel: bool = isfloattype(c, elemn);
let fmov: str = "MOVSD";
if (isf32type(c, elemn)) { fmov = "MOVSS"; };
let idx: i32 = 0;
let repeat: bool = false;
let e: *node = rhs.list;
@@ -984,13 +992,19 @@ fn cglet(c: *cgen, n: *node) void = {
emitline("\tMOVQ\tBX, ");
emitoff((off + idx * esz + 8): i64);
emitline("(BP)\n");
} else { if (isfloatel) {
emitline("\t");
emitline(fmov);
emitline("\tX0, ");
emitoff((off + idx * esz): i64);
emitline("(BP)\n");
} else {
emitline("\t");
emitline(mop);
emitline("\tAX, ");
emitoff((off + idx * esz): i64);
emitline("(BP)\n");
};
}; };
idx += 1;
e = e.next;
};
@@ -1016,13 +1030,19 @@ fn cglet(c: *cgen, n: *node) void = {
emitline("\tMOVQ\tBX, ");
emitoff((off + idx * esz + 8): i64);
emitline("(BP)\n");
} else { if (isfloatel) {
emitline("\t");
emitline(fmov);
emitline("\tX0, ");
emitoff((off + idx * esz): i64);
emitline("(BP)\n");
} else {
emitline("\t");
emitline(mop);
emitline("\tAX, ");
emitoff((off + idx * esz): i64);
emitline("(BP)\n");
};
}; };
idx += 1;
};
};

View File

@@ -18444,6 +18444,24 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tMOVQ\tCX, 16(BX)\n");
return;
};
// float element → store FROM X0 (MOVSS/MOVSD): cgexpr
// left the value in X0. X0 survives the index/base
// eval only for literal/local-var indices; a call-
// index (a[geti()]=v) clobbers X0 and loses the value
// — this path does not spill X0 across the eval,
// unlike the *p=v float deref store. Deferred to #125.
// For f32 the #104 CVTSD2SS narrowing only touches X0,
// so the AX store below would write raw double low-
// bits, garbage for f32 (#122, mirrors cstage cgen.c
// arr[i]= float store).
if (isfloattype(c, elemtn)) {
let fmov: str = "MOVSD";
if (isf32type(c, elemtn)) { fmov = "MOVSS"; };
emitline("\t");
emitline(fmov);
emitline("\tX0, (BX)\n");
return;
};
let isop: str = tnodestoreop(c, elemtn, esz);
emitline("\t");
emitline(isop);
@@ -21435,6 +21453,14 @@ fn cglet(c: *cgen, n: *node) void = {
};
};
let mop: str = tnodestoreop(c, elemn, esz);
// float element → store FROM X0 (MOVSS/MOVSD): cgexpr
// leaves a float in X0 and for f32 the #104 CVTSD2SS
// narrowing only touches X0; the AX store (mop) would
// write the raw double low-bits, garbage for f32 (#122,
// mirrors cstage cgen.c:6889 arr-lit float store).
let isfloatel: bool = isfloattype(c, elemn);
let fmov: str = "MOVSD";
if (isf32type(c, elemn)) { fmov = "MOVSS"; };
let idx: i32 = 0;
let repeat: bool = false;
let e: *node = rhs.list;
@@ -21457,13 +21483,19 @@ fn cglet(c: *cgen, n: *node) void = {
emitline("\tMOVQ\tBX, ");
emitoff((off + idx * esz + 8): i64);
emitline("(BP)\n");
} else { if (isfloatel) {
emitline("\t");
emitline(fmov);
emitline("\tX0, ");
emitoff((off + idx * esz): i64);
emitline("(BP)\n");
} else {
emitline("\t");
emitline(mop);
emitline("\tAX, ");
emitoff((off + idx * esz): i64);
emitline("(BP)\n");
};
}; };
idx += 1;
e = e.next;
};
@@ -21489,13 +21521,19 @@ fn cglet(c: *cgen, n: *node) void = {
emitline("\tMOVQ\tBX, ");
emitoff((off + idx * esz + 8): i64);
emitline("(BP)\n");
} else { if (isfloatel) {
emitline("\t");
emitline(fmov);
emitline("\tX0, ");
emitoff((off + idx * esz): i64);
emitline("(BP)\n");
} else {
emitline("\t");
emitline(mop);
emitline("\tAX, ");
emitoff((off + idx * esz): i64);
emitline("(BP)\n");
};
}; };
idx += 1;
};
};