wcc: float arr[i]= spills X0 across index eval (#125)
Fix value-loss bug introduced as a #122 boundary in the float arr[i]=v store: when the index sub-expr clobbers X0 (e.g. `a[geti()]=1.5f32`), the value is lost. Mirror the scalar-deref X0-spill template (cstage cgen.c:4187; line shifted from the brief's stale :3859 cite by intervening #133/#135/#138 commits): for float element only, replace PUSHQ AX (junk for floats — value is in X0) with SUBQ $8,SP + MOVSS/MOVSD X0,(SP) before the idx/base eval; mirror replace POPQ AX with MOVSS/MOVSD (SP),X0 + ADDQ $8,SP after. Wwstage parallel. Non-float keeps PUSHQ/POPQ AX so the str/slice 3-word {ptr,len,cap} pop order at the end of the branch is preserved. #122 trailing-store comment updated from "Deferred to #125" to a positive cite. Test 916: 5 rows — f64_call_index + f32_call_index canonical repros (geti's body clobbers X0; pre-fix exit=2 from post-call residue, post-fix exit=1 from the spilled 1.5) + f64_lit_index / _localvar / _arith control rows for non-X0-clobbering index paths. f32_call_index uses an int-arg call to dodge the sibling cs/ww f32-arg-push MOVSD-vs-MOVSS divergence (#143, task #36 — orthogonal, filed). Bootstrap NEUTRAL (zero current float arr[i]= callers in lib; only [N]u8 byte-buffers like f64tos_buf). cs==ww byte-identical both stages (990-997 + 916 inline cmp). Closes the #122 boundary-doc loose end; completes the #122 family.
This commit is contained in:
@@ -3951,7 +3951,20 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
ins1(c, A_PUSHQ, areg(D_CX)); /* cap */
|
||||
ins1(c, A_PUSHQ, areg(D_BX)); /* len */
|
||||
}
|
||||
ins1(c, A_PUSHQ, areg(D_AX));
|
||||
/* Float element: spill X0 (not AX — AX is junk
|
||||
* for floats) across the idx/base eval. A call-
|
||||
* index (`a[geti()]=v`) clobbers X0 and would
|
||||
* otherwise lose the value. Mirrors the *p=v
|
||||
* float deref store at cgen.c:4187 (#125). */
|
||||
int sp_isfloat = type_isfloat(esub);
|
||||
int sp_mov = sp_isfloat
|
||||
? (type_isf32(esub) ? A_MOVSS : A_MOVSD) : 0;
|
||||
if (sp_isfloat) {
|
||||
ins2(c, A_SUBQ, aimm(8), areg(D_SP));
|
||||
ins2(c, sp_mov, areg(D_X0), amem(D_SP, 0));
|
||||
} else {
|
||||
ins1(c, A_PUSHQ, areg(D_AX));
|
||||
}
|
||||
cgexpr(c, n->lhs->rhs, locals); /* idx → AX */
|
||||
if (esz > 1) {
|
||||
ins2(c, A_MOVQ, aimm(esz), areg(D_CX));
|
||||
@@ -3993,7 +4006,15 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
}
|
||||
ins1(c, A_POPQ, areg(D_AX)); /* scaled idx */
|
||||
ins2(c, A_ADDQ, areg(D_AX), areg(D_BX));
|
||||
ins1(c, A_POPQ, areg(D_AX)); /* value (ptr if str) */
|
||||
/* Reload value: float reloads X0 from the spill
|
||||
* slot; non-float pops AX. Twin of the value-spill
|
||||
* site above (#125). */
|
||||
if (sp_isfloat) {
|
||||
ins2(c, sp_mov, amem(D_SP, 0), areg(D_X0));
|
||||
ins2(c, A_ADDQ, aimm(8), areg(D_SP));
|
||||
} else {
|
||||
ins1(c, A_POPQ, areg(D_AX)); /* value (ptr if str) */
|
||||
}
|
||||
if (elem_is_str || elem_is_slice) {
|
||||
/* str/slice: store ptr/len/cap (#1/Phase 3, #7). */
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BX, 0));
|
||||
@@ -4008,12 +4029,10 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* CVTSD2SS narrowing only touches X0 — the AX path
|
||||
* below would store the raw double low-bits (garbage
|
||||
* for f32). Float-ness from esub, mirroring the read
|
||||
* side at cgen.c:6423 (#122). 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 above. Deferred to
|
||||
* #125. */
|
||||
* side at cgen.c:6423 (#122). #125: the value-spill
|
||||
* pair above keeps X0 live across the idx/base eval
|
||||
* so this MOVSS/MOVSD is correct even on call-index
|
||||
* shapes. */
|
||||
if (type_isfloat(esub)) {
|
||||
int mov = type_isf32(esub) ? A_MOVSS : A_MOVSD;
|
||||
ins2(c, mov, areg(D_X0), amem(D_BX, 0));
|
||||
|
||||
Reference in New Issue
Block a user