wcc: tagged float-variant return packs float bits via X0-spill (#157)
The N_RETURN tagged-pack scalar-variant arm did MOVQ AX,DX, but a float variant's value is in X0 not AX -> packed stale int (broke stof64/stof32 return (f64|invalid|overflow)). Fix: float variant bridges X0->DX via a stack slot (SUBQ $8,SP; MOVQ $0,(SP); MOVSS|MOVSD X0,(SP); MOVQ (SP),DX; ADDQ $8,SP), gated type_isfloat/exprfloatkind. No MOVQ-xmm->gp form exists, hence the spill (715-class, cgreturn-register-pack twin of 715's store-to-slot). Zero-slot-first -> deterministic f32 high-4. AX-independent -> also resolves the multi-variant cs!=ww. Bootstrap-NEUTRAL (compiler has no float-tagged-return). Test 707 +3 rows (f64/f32/multi, slot+8 bit-exact; f32 no-f32-arg to isolate #143). Make test 184/184 incl 990-997 byte-id.
This commit is contained in:
@@ -7716,6 +7716,35 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
|||||||
areg(D_CX));
|
areg(D_CX));
|
||||||
ins2(c, A_MOVQ, areg(D_AX),
|
ins2(c, A_MOVQ, areg(D_AX),
|
||||||
areg(D_DX));
|
areg(D_DX));
|
||||||
|
} else if (type_isfloat(vt)) {
|
||||||
|
/* #157: float variant. cgexpr left
|
||||||
|
* the value in X0, not AX; there is
|
||||||
|
* no MOVQ-xmm->gp encoding, so bridge
|
||||||
|
* X0->DX through a stack slot (same
|
||||||
|
* SUBQ/MOVSD/ADDQ idiom as the arg-
|
||||||
|
* push at cgen.c:5367). Zero the slot
|
||||||
|
* first so the f32 case (MOVSS writes
|
||||||
|
* only the low 4 bytes) leaves a
|
||||||
|
* deterministic high-4 — cs==ww byte-
|
||||||
|
* id, matching f64's MOVSD which fills
|
||||||
|
* all 8. The AX-independent spill also
|
||||||
|
* removes the stale-AX cs!=ww on
|
||||||
|
* multi-variant returns. */
|
||||||
|
int isf32 = type_isf32(vt);
|
||||||
|
ins2(c, A_SUBQ, aimm(8), areg(D_SP));
|
||||||
|
ins2(c, A_MOVQ, aimm(0),
|
||||||
|
amem(D_SP, 0));
|
||||||
|
ins2(c, isf32 ? A_MOVSS : A_MOVSD,
|
||||||
|
areg(D_X0), amem(D_SP, 0));
|
||||||
|
ins2(c, A_MOVQ, amem(D_SP, 0),
|
||||||
|
areg(D_DX));
|
||||||
|
ins2(c, A_ADDQ, aimm(8), areg(D_SP));
|
||||||
|
if (rsz > 16)
|
||||||
|
ins2(c, A_MOVQ, aimm(0),
|
||||||
|
areg(D_CX));
|
||||||
|
if (rsz > 24)
|
||||||
|
ins2(c, A_MOVQ, aimm(0),
|
||||||
|
areg(D_R8));
|
||||||
} else {
|
} else {
|
||||||
ins2(c, A_MOVQ, areg(D_AX),
|
ins2(c, A_MOVQ, areg(D_AX),
|
||||||
areg(D_DX));
|
areg(D_DX));
|
||||||
|
|||||||
@@ -22060,6 +22060,31 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tMOVQ\tCX, R8\n");
|
emitline("\tMOVQ\tCX, R8\n");
|
||||||
emitline("\tMOVQ\tBX, CX\n");
|
emitline("\tMOVQ\tBX, CX\n");
|
||||||
emitline("\tMOVQ\tAX, DX\n");
|
emitline("\tMOVQ\tAX, DX\n");
|
||||||
|
} else { if (exprfloatkind(c, rhs) != 0) {
|
||||||
|
// #157: float variant — cgexpr left the value
|
||||||
|
// in X0, not AX. No MOVQ-xmm->gp encoding, so
|
||||||
|
// bridge X0->DX through a stack slot (same arg-
|
||||||
|
// push idiom). Zero the slot first so the f32
|
||||||
|
// case (MOVSS writes only the low 4 bytes)
|
||||||
|
// leaves a deterministic high-4 — cs==ww byte-
|
||||||
|
// id, matching f64's MOVSD which fills all 8.
|
||||||
|
// The AX-independent spill also removes the
|
||||||
|
// stale-AX cs!=ww on multi-variant returns.
|
||||||
|
emitline("\tSUBQ\t$8, SP\n");
|
||||||
|
emitline("\tMOVQ\t$0, (SP)\n");
|
||||||
|
let mov: str = "MOVSD";
|
||||||
|
if (exprfloatkind(c, rhs) == 1) { mov = "MOVSS"; };
|
||||||
|
emitline("\t");
|
||||||
|
emitline(mov);
|
||||||
|
emitline("\tX0, (SP)\n");
|
||||||
|
emitline("\tMOVQ\t(SP), DX\n");
|
||||||
|
emitline("\tADDQ\t$8, SP\n");
|
||||||
|
if (rsz > 16) {
|
||||||
|
emitline("\tMOVQ\t$0, CX\n");
|
||||||
|
};
|
||||||
|
if (rsz > 24) {
|
||||||
|
emitline("\tMOVQ\t$0, R8\n");
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
emitline("\tMOVQ\tAX, DX\n");
|
emitline("\tMOVQ\tAX, DX\n");
|
||||||
// scalar fills DX only. Zero CX / R8 if dst
|
// scalar fills DX only. Zero CX / R8 if dst
|
||||||
@@ -22070,7 +22095,7 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
|||||||
if (rsz > 24) {
|
if (rsz > 24) {
|
||||||
emitline("\tMOVQ\t$0, R8\n");
|
emitline("\tMOVQ\t$0, R8\n");
|
||||||
};
|
};
|
||||||
};};
|
};};};
|
||||||
emitline("\tMOVQ\t$");
|
emitline("\tMOVQ\t$");
|
||||||
if (idx < 0) { idx = 0; };
|
if (idx < 0) { idx = 0; };
|
||||||
emitint(idx: i64);
|
emitint(idx: i64);
|
||||||
|
|||||||
@@ -422,6 +422,31 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tMOVQ\tCX, R8\n");
|
emitline("\tMOVQ\tCX, R8\n");
|
||||||
emitline("\tMOVQ\tBX, CX\n");
|
emitline("\tMOVQ\tBX, CX\n");
|
||||||
emitline("\tMOVQ\tAX, DX\n");
|
emitline("\tMOVQ\tAX, DX\n");
|
||||||
|
} else { if (exprfloatkind(c, rhs) != 0) {
|
||||||
|
// #157: float variant — cgexpr left the value
|
||||||
|
// in X0, not AX. No MOVQ-xmm->gp encoding, so
|
||||||
|
// bridge X0->DX through a stack slot (same arg-
|
||||||
|
// push idiom). Zero the slot first so the f32
|
||||||
|
// case (MOVSS writes only the low 4 bytes)
|
||||||
|
// leaves a deterministic high-4 — cs==ww byte-
|
||||||
|
// id, matching f64's MOVSD which fills all 8.
|
||||||
|
// The AX-independent spill also removes the
|
||||||
|
// stale-AX cs!=ww on multi-variant returns.
|
||||||
|
emitline("\tSUBQ\t$8, SP\n");
|
||||||
|
emitline("\tMOVQ\t$0, (SP)\n");
|
||||||
|
let mov: str = "MOVSD";
|
||||||
|
if (exprfloatkind(c, rhs) == 1) { mov = "MOVSS"; };
|
||||||
|
emitline("\t");
|
||||||
|
emitline(mov);
|
||||||
|
emitline("\tX0, (SP)\n");
|
||||||
|
emitline("\tMOVQ\t(SP), DX\n");
|
||||||
|
emitline("\tADDQ\t$8, SP\n");
|
||||||
|
if (rsz > 16) {
|
||||||
|
emitline("\tMOVQ\t$0, CX\n");
|
||||||
|
};
|
||||||
|
if (rsz > 24) {
|
||||||
|
emitline("\tMOVQ\t$0, R8\n");
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
emitline("\tMOVQ\tAX, DX\n");
|
emitline("\tMOVQ\tAX, DX\n");
|
||||||
// scalar fills DX only. Zero CX / R8 if dst
|
// scalar fills DX only. Zero CX / R8 if dst
|
||||||
@@ -432,7 +457,7 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
|||||||
if (rsz > 24) {
|
if (rsz > 24) {
|
||||||
emitline("\tMOVQ\t$0, R8\n");
|
emitline("\tMOVQ\t$0, R8\n");
|
||||||
};
|
};
|
||||||
};};
|
};};};
|
||||||
emitline("\tMOVQ\t$");
|
emitline("\tMOVQ\t$");
|
||||||
if (idx < 0) { idx = 0; };
|
if (idx < 0) { idx = 0; };
|
||||||
emitint(idx: i64);
|
emitint(idx: i64);
|
||||||
|
|||||||
@@ -22060,6 +22060,31 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
|||||||
emitline("\tMOVQ\tCX, R8\n");
|
emitline("\tMOVQ\tCX, R8\n");
|
||||||
emitline("\tMOVQ\tBX, CX\n");
|
emitline("\tMOVQ\tBX, CX\n");
|
||||||
emitline("\tMOVQ\tAX, DX\n");
|
emitline("\tMOVQ\tAX, DX\n");
|
||||||
|
} else { if (exprfloatkind(c, rhs) != 0) {
|
||||||
|
// #157: float variant — cgexpr left the value
|
||||||
|
// in X0, not AX. No MOVQ-xmm->gp encoding, so
|
||||||
|
// bridge X0->DX through a stack slot (same arg-
|
||||||
|
// push idiom). Zero the slot first so the f32
|
||||||
|
// case (MOVSS writes only the low 4 bytes)
|
||||||
|
// leaves a deterministic high-4 — cs==ww byte-
|
||||||
|
// id, matching f64's MOVSD which fills all 8.
|
||||||
|
// The AX-independent spill also removes the
|
||||||
|
// stale-AX cs!=ww on multi-variant returns.
|
||||||
|
emitline("\tSUBQ\t$8, SP\n");
|
||||||
|
emitline("\tMOVQ\t$0, (SP)\n");
|
||||||
|
let mov: str = "MOVSD";
|
||||||
|
if (exprfloatkind(c, rhs) == 1) { mov = "MOVSS"; };
|
||||||
|
emitline("\t");
|
||||||
|
emitline(mov);
|
||||||
|
emitline("\tX0, (SP)\n");
|
||||||
|
emitline("\tMOVQ\t(SP), DX\n");
|
||||||
|
emitline("\tADDQ\t$8, SP\n");
|
||||||
|
if (rsz > 16) {
|
||||||
|
emitline("\tMOVQ\t$0, CX\n");
|
||||||
|
};
|
||||||
|
if (rsz > 24) {
|
||||||
|
emitline("\tMOVQ\t$0, R8\n");
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
emitline("\tMOVQ\tAX, DX\n");
|
emitline("\tMOVQ\tAX, DX\n");
|
||||||
// scalar fills DX only. Zero CX / R8 if dst
|
// scalar fills DX only. Zero CX / R8 if dst
|
||||||
@@ -22070,7 +22095,7 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
|||||||
if (rsz > 24) {
|
if (rsz > 24) {
|
||||||
emitline("\tMOVQ\t$0, R8\n");
|
emitline("\tMOVQ\t$0, R8\n");
|
||||||
};
|
};
|
||||||
};};
|
};};};
|
||||||
emitline("\tMOVQ\t$");
|
emitline("\tMOVQ\t$");
|
||||||
if (idx < 0) { idx = 0; };
|
if (idx < 0) { idx = 0; };
|
||||||
emitint(idx: i64);
|
emitint(idx: i64);
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
/*
|
/*
|
||||||
* 707_cgreturn_variant_zero — tagged-return ABI variant-widen zero-pad
|
* 707_cgreturn_variant_zero — tagged-return ABI variant-widen zero-pad
|
||||||
* for unused AX/DX/CX/R8 words (task #18) plus aliased-tagged variant-
|
* for unused AX/DX/CX/R8 words (task #18) plus aliased-tagged variant-
|
||||||
* index lookup (task #20).
|
* index lookup (task #20) plus float-variant payload-from-X0 pack
|
||||||
|
* (#157, rows 10-12): the same scalar-variant arm packed stale AX into
|
||||||
|
* the payload word for an f64/f32 value (which lives in X0), not the
|
||||||
|
* float bits — fixed by an X0->stack->DX spill bridge.
|
||||||
*
|
*
|
||||||
* Pre-fix (#18): cgreturn's `!istagged && !isstruct` variant-widen arm
|
* Pre-fix (#18): cgreturn's `!istagged && !isstruct` variant-widen arm
|
||||||
* only filled the registers a given variant actually uses (scalar → DX;
|
* only filled the registers a given variant actually uses (scalar → DX;
|
||||||
@@ -247,6 +250,53 @@ static const struct row rows[] = {
|
|||||||
" return (*pt): i32;\n"
|
" return (*pt): i32;\n"
|
||||||
"};\n",
|
"};\n",
|
||||||
2 },
|
2 },
|
||||||
|
/* 10-12: #157 — float variant of the same `!istagged && !isstruct`
|
||||||
|
* return arm. Pre-fix it did `MOVQ AX, DX` even when the value was
|
||||||
|
* a float in X0 (no MOVQ-xmm->gp form), so the payload word held
|
||||||
|
* stale AX, not the float bits. Fix spills X0 through a stack slot
|
||||||
|
* (zero-slot-first so the f32 MOVSS low-4 write leaves a determin-
|
||||||
|
* istic high-4). Probe slot+8 as the float directly (bit-exact
|
||||||
|
* f64/f32 equality) — isolates the return-pack from the match-
|
||||||
|
* receive. The f32 row avoids f32 ARGS (pre-existing #143 f32-arg
|
||||||
|
* MOVSD/MOVSS divergence is unrelated; f32 here comes from a global
|
||||||
|
* with an int arg, so the byte-id check pins THIS fix). */
|
||||||
|
{ "f64_variant_return_payload",
|
||||||
|
"type ft = (f64 | i32);\n"
|
||||||
|
"fn addf(a: f64, b: f64) ft = { return a + b; };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let a: ft = addf(1.5, 2.0);\n"
|
||||||
|
" let p: u64 = (&a): u64;\n"
|
||||||
|
" let pf: *f64 = (p + 8u64): *f64;\n"
|
||||||
|
" let fv: f64 = *pf;\n"
|
||||||
|
" if (fv == 3.5) { return 1; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
1 },
|
||||||
|
{ "f32_variant_return_payload",
|
||||||
|
"let g: f32 = 1.5f32;\n"
|
||||||
|
"type ft = (f32 | i32);\n"
|
||||||
|
"fn mkf(k: i32) ft = { if (k > 0) { return g + 2.0f32; }; return k; };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let a: ft = mkf(1);\n"
|
||||||
|
" let p: u64 = (&a): u64;\n"
|
||||||
|
" let pf: *f32 = (p + 8u64): *f32;\n"
|
||||||
|
" let fv: f32 = *pf;\n"
|
||||||
|
" if (fv == 3.5f32) { return 1; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
1 },
|
||||||
|
{ "f64_variant_return_multivariant",
|
||||||
|
"type ft = (f64 | i32 | bool);\n"
|
||||||
|
"fn mulf(a: f64, b: f64) ft = { return a * b; };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let a: ft = mulf(2.5, 4.0);\n"
|
||||||
|
" let p: u64 = (&a): u64;\n"
|
||||||
|
" let pf: *f64 = (p + 8u64): *f64;\n"
|
||||||
|
" let fv: f64 = *pf;\n"
|
||||||
|
" if (fv == 10.0) { return 1; };\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"};\n",
|
||||||
|
1 },
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|||||||
Reference in New Issue
Block a user