cgen: tuple receive spills f64 word from XMM, not integer reg (both stages, #105)
A (f64,i64)/(i64,f64) tuple returns its f64 word in X0 (the SSE return reg) and its integer word in an integer reg (tuple_rseq AX/DX). All three tuple-from-call receive forms — single-var (cglet), destructure (N_MLET), reassign (N_MASSIGN) — share the #83 tuple_rseq cursor and all spilled the f64 word via MOVQ from the integer cursor; that reg holds garbage (the float is in X0), and #103-FACE-Z's field read (MOVSD slot,X0) then reads it. A single-return callee masked it (a float-literal return leaves the f64 bits in AX, and X0 stays live); a branched callee with a non- literal f64 word has an inner CALL clobber AX, exposing the corruption. Make every receive spill class-aware: an f64/f32 word spills MOVSD/MOVSS from X0 (the single SSE return reg, which survives the reg->mem stores regardless of the word's position), an integer word spills MOVQ from its tuple_rseq reg as before. cstage applies this at all three inline sites (cglet, N_MLET, N_MASSIGN); wwstage at the cglet branch and in the shared tupstore helper (covering cgmlet and cgmassign). The integer/str/slice path is byte-identical to before, so bootstrap codegen is unperturbed. Multi-float tuples collide on X0 at the RETURN (#107), out of scope here.
This commit is contained in:
@@ -6672,13 +6672,33 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
ins2(c, A_MOVQ, areg(D_CX), amem(D_BP, off + 16));
|
||||
break;
|
||||
}
|
||||
/* 2-tuple initialiser from a function call: SysV returns
|
||||
* a 16-byte aggregate in (AX, DX). Store both into the
|
||||
* tuple slot. */
|
||||
/* 2-tuple initialiser from a function call. An integer word
|
||||
* rides its tuple_rseq[] reg (AX, DX); a single f64/f32 word
|
||||
* rides X0, the SSE return reg — the RETURN leaves the float
|
||||
* in X0 and pushes garbage through that word's integer slot,
|
||||
* so a blanket MOVQ-from-integer spill stores garbage and the
|
||||
* #103-FACE-Z field read (MOVSD-from-slot) reads it (#105).
|
||||
* Spill each word from its real class. Multi-float tuples
|
||||
* collide on X0 at the RETURN (#107), out of scope here. */
|
||||
if (n->rhs && lu && lu->kind == TY_TUPLE && sz == 16) {
|
||||
Tparam *p0 = lu->params;
|
||||
Tparam *p1 = p0 ? p0->next : NULL;
|
||||
int f0_f32 = 0, f1_f32 = 0;
|
||||
int e0_f = p0 && fld_isfloat(p0->type, &f0_f32);
|
||||
int e1_f = p1 && fld_isfloat(p1->type, &f1_f32);
|
||||
cgexpr(c, n->rhs, *locals);
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off + 0));
|
||||
ins2(c, A_MOVQ, areg(D_DX), amem(D_BP, off + 8));
|
||||
if (e0_f)
|
||||
ins2(c, f0_f32 ? A_MOVSS : A_MOVSD,
|
||||
areg(D_X0), amem(D_BP, off + 0));
|
||||
else
|
||||
ins2(c, A_MOVQ, areg(tuple_rseq[0]),
|
||||
amem(D_BP, off + 0));
|
||||
if (e1_f)
|
||||
ins2(c, f1_f32 ? A_MOVSS : A_MOVSD,
|
||||
areg(D_X0), amem(D_BP, off + 8));
|
||||
else
|
||||
ins2(c, A_MOVQ, areg(tuple_rseq[1]),
|
||||
amem(D_BP, off + 8));
|
||||
break;
|
||||
}
|
||||
/* 32B tuple initialiser for `(scalar, str)` / `(str, scalar)`.
|
||||
@@ -7563,8 +7583,18 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
ins2(c, A_MOVQ, areg(tuple_rseq[cur + 2]),
|
||||
amem(D_BP, off + 16)); /* .cap */
|
||||
} else {
|
||||
ins2(c, A_MOVQ, areg(tuple_rseq[cur]),
|
||||
amem(D_BP, off));
|
||||
/* #105: an f64/f32 element rides X0, not its
|
||||
* integer cursor reg — MOVSD/MOVSS it, else
|
||||
* the slot gets garbage and the FACE-Z field
|
||||
* read sees it. X0 survives the reg->mem
|
||||
* stores. Single-float scope; #107 is multi. */
|
||||
int e_f32 = 0;
|
||||
if (fld_isfloat(t, &e_f32))
|
||||
ins2(c, e_f32 ? A_MOVSS : A_MOVSD,
|
||||
areg(D_X0), amem(D_BP, off));
|
||||
else
|
||||
ins2(c, A_MOVQ, areg(tuple_rseq[cur]),
|
||||
amem(D_BP, off));
|
||||
}
|
||||
cur += tuple_ebytes(wide);
|
||||
}
|
||||
@@ -7620,8 +7650,16 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
ins2(c, A_MOVQ, areg(tuple_rseq[cur + 2]),
|
||||
amem(D_BP, off + 16)); /* .cap */
|
||||
} else {
|
||||
ins2(c, A_MOVQ, areg(tuple_rseq[cur]),
|
||||
amem(D_BP, off));
|
||||
/* #105: f64/f32 element rides X0 (SSE),
|
||||
* not its integer cursor reg — see N_MLET. */
|
||||
int e_f32 = 0;
|
||||
if (fld_isfloat(et, &e_f32))
|
||||
ins2(c, e_f32 ? A_MOVSS : A_MOVSD,
|
||||
areg(D_X0), amem(D_BP, off));
|
||||
else
|
||||
ins2(c, A_MOVQ,
|
||||
areg(tuple_rseq[cur]),
|
||||
amem(D_BP, off));
|
||||
}
|
||||
}
|
||||
cur += tuple_ebytes(wide);
|
||||
|
||||
@@ -163,7 +163,7 @@ fn rettupleof(c: *cgen, rhs: *node) *node = {
|
||||
// header (ref/hare/rt/ensure.ha:4-8) at off/+8/+16 from consecutive
|
||||
// cursor registers; a scalar stores 1 word. Byte-identical to the cstage
|
||||
// N_MLET/N_MASSIGN store (cmd/w6c/cgen.c).
|
||||
fn tupstore(cur: i32, off: i32, wide: bool) void = {
|
||||
fn tupstore(c: *cgen, cur: i32, off: i32, wide: bool, tn: *node) void = {
|
||||
if (wide) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(tupreg(cur + 0));
|
||||
@@ -182,6 +182,20 @@ fn tupstore(cur: i32, off: i32, wide: bool) void = {
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
// #105: an f64/f32 element rides X0 (the SSE return reg), not its
|
||||
// integer cursor reg — MOVSD/MOVSS it, else the slot gets garbage and
|
||||
// the FACE-Z field read sees it. X0 survives the reg->mem stores.
|
||||
// Single-float scope; multi-float collides on X0 at RETURN (#107).
|
||||
if (isfloattype(c, tn)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, tn)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(tupreg(cur));
|
||||
emitline(", ");
|
||||
@@ -864,23 +878,55 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// 16B tuple init from a function call: SysV returns a
|
||||
// 16-byte aggregate across the integer cursor (AX, DX). Spill
|
||||
// BOTH eightbytes over the integer registers — an f64 element
|
||||
// also rides its eightbyte in AX/DX here and is re-read from
|
||||
// X0/XMM at field-read time, so no SSE cursor is needed (the
|
||||
// f64 read is already byte-id). Mirror of cstage cgen.c:6652.
|
||||
// Without this branch a 16B tuple receive (any element mix,
|
||||
// incl. all-integer) fell to the generic single-word store
|
||||
// below and dropped word1 — silent loss of t.1 (#102).
|
||||
if (rettupleof(c, rhs) != nil && sz == 16) {
|
||||
// 16B tuple init from a function call. An integer word rides
|
||||
// its integer cursor reg (AX, DX); a single f64/f32 word rides
|
||||
// X0, the SSE return reg — the RETURN leaves the float in X0
|
||||
// and pushes garbage through that word's integer slot, so a
|
||||
// blanket MOVQ-from-integer spill stores garbage and the #103-
|
||||
// FACE-Z field read (MOVSD-from-slot) reads it (#105). Spill
|
||||
// each word from its real class. Multi-float tuples collide on
|
||||
// X0 at the RETURN (#107), out of scope. Mirror of cstage
|
||||
// cgen.c. Without this branch a 16B tuple receive (any element
|
||||
// mix) fell to the generic single-word store below and dropped
|
||||
// word1 — silent loss of t.1 (#102).
|
||||
let rt16: *node = rettupleof(c, rhs);
|
||||
if (rt16 != nil && sz == 16) {
|
||||
let q0: *node = rt16.list;
|
||||
let q1: *node = nil;
|
||||
if (q0 != nil) { q1 = q0.next; };
|
||||
let q0t: *node = nil;
|
||||
let q1t: *node = nil;
|
||||
if (q0 != nil) { q0t = q0.lhs; };
|
||||
if (q1 != nil) { q1t = q1.lhs; };
|
||||
let e0f: bool = isfloattype(c, q0t);
|
||||
let e1f: bool = isfloattype(c, q1t);
|
||||
cgexpr(c, rhs);
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
if (e0f) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, q0t)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
if (e1f) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, q1t)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
@@ -1351,7 +1397,7 @@ fn cgmassign(c: *cgen, n: *node) void = {
|
||||
let off: i32 = 0;
|
||||
if (l.kind == nkind.N_IDENT) { off = localfind(c, l.str); };
|
||||
if (off != 0) {
|
||||
tupstore(cur, off, wide);
|
||||
tupstore(c, cur, off, wide, tn);
|
||||
};
|
||||
cur = cur + tupebytes(wide);
|
||||
l = l.next;
|
||||
@@ -1423,7 +1469,7 @@ fn cgmlet(c: *cgen, n: *node) void = {
|
||||
let sz: i32 = 8;
|
||||
if (wide) { sz = tyslicesize(): i32; };
|
||||
let off: i32 = localadd(c, l.str, sz, tn);
|
||||
tupstore(cur, off, wide);
|
||||
tupstore(c, cur, off, wide, tn);
|
||||
cur = cur + tupebytes(wide);
|
||||
l = l.next;
|
||||
if (pt != nil) { pt = pt.next; };
|
||||
|
||||
Reference in New Issue
Block a user