cgen: f64-typed int-literal + tuple-field materialize in X0 (both stages, #103)
Two sites, same class: an f64 value failing to reach XMM (X0) before an SSE op. Both gate-blind — cstage and wwstage emitted the same wrong asm — so the fix touches both stages identically. FACE X — a no-decimal float-typed integer literal (`0f64`, `8f64`) is an N_INTLIT carrying float TYPE. The integer-immediate path stranded it in AX, so `n == 0f64` compared a stale X0 (true for all n) and `(8f64 * 10.0): i32` read garbage. Route the float-typed N_INTLIT through the float-constant-in-X0 emit (cgen.c cgexpr_float, factored from N_FLOATLIT; cgenexpr.ww cgfloatbits). The wwstage also needs the exprfloatkind N_INTLIT arm so the downstream f64->i32 cast emits CVTTSD2SI not MOVSXD — cstage reads the checker-stamped type directly, so this is the same #101 structural-vs-stamped asymmetry. FACE Z — a tuple positional f64 field read (`r.0`, r:(f64,i64)) loaded via the integer op into AX, so `r.0 == 0.0` was wrongly true. Add a fld_isfloat branch -> MOVSD/MOVSS into X0 (cgen.c:5910 tuple arm; cgenexpr.ww tuple arm), mirroring the struct-field float load at cgen.c:1462,1838 (the #96 pattern).
This commit is contained in:
@@ -1230,6 +1230,23 @@ cgexpr_int(Cg *c, long long v)
|
|||||||
ins2(c, A_MOVQ, aimm(v), areg(D_AX));
|
ins2(c, A_MOVQ, aimm(v), areg(D_AX));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Materialise a float constant in X0: MOVQ the IEEE bits into AX, PUSH,
|
||||||
|
* MOVSD off the stack into X0. Shared by N_FLOATLIT and the f64/f32-typed
|
||||||
|
* N_INTLIT arm (#103 FACE X): a no-decimal `0f64`/`8f64` is an N_INTLIT
|
||||||
|
* carrying float TYPE, so it must reach X0 like a true float literal does
|
||||||
|
* — the integer-immediate path left the value stranded in AX, so an SSE
|
||||||
|
* compare/mul read a stale X0. */
|
||||||
|
static void
|
||||||
|
cgexpr_float(Cg *c, double val)
|
||||||
|
{
|
||||||
|
union { double d; u64 u; } x;
|
||||||
|
x.d = val;
|
||||||
|
ins2(c, A_MOVQ, aimm((long long)x.u), areg(D_AX));
|
||||||
|
ins1(c, A_PUSHQ, areg(D_AX));
|
||||||
|
ins2(c, A_MOVSD, amem(D_SP, 0), areg(D_X0));
|
||||||
|
ins2(c, A_ADDQ, aimm(8), areg(D_SP));
|
||||||
|
}
|
||||||
|
|
||||||
/* cg_widen_tag_remap — when widening from one tagged union to another,
|
/* cg_widen_tag_remap — when widening from one tagged union to another,
|
||||||
* rewrite the source's variant tag at BP+slot_off+0 to use the dst
|
* rewrite the source's variant tag at BP+slot_off+0 to use the dst
|
||||||
* union's variant indices. No-op when src and dst index orders coincide.
|
* union's variant indices. No-op when src and dst index orders coincide.
|
||||||
@@ -1868,17 +1885,15 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
switch (n->kind) {
|
switch (n->kind) {
|
||||||
case N_INTLIT:
|
case N_INTLIT:
|
||||||
case N_RUNELIT:
|
case N_RUNELIT:
|
||||||
|
if (node_isfloat(n)) {
|
||||||
|
cgexpr_float(c, (double)(long long)n->uval);
|
||||||
|
break;
|
||||||
|
}
|
||||||
cgexpr_int(c, (long long)n->uval);
|
cgexpr_int(c, (long long)n->uval);
|
||||||
break;
|
break;
|
||||||
case N_FLOATLIT: {
|
case N_FLOATLIT:
|
||||||
union { double d; u64 u; } x;
|
cgexpr_float(c, n->fval);
|
||||||
x.d = n->fval;
|
|
||||||
ins2(c, A_MOVQ, aimm((long long)x.u), areg(D_AX));
|
|
||||||
ins1(c, A_PUSHQ, areg(D_AX));
|
|
||||||
ins2(c, A_MOVSD, amem(D_SP, 0), areg(D_X0));
|
|
||||||
ins2(c, A_ADDQ, aimm(8), areg(D_SP));
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case N_STRLIT: {
|
case N_STRLIT: {
|
||||||
/* str IS []u8: the (ptr, len, cap) triple — ptr in AX, len in
|
/* str IS []u8: the (ptr, len, cap) triple — ptr in AX, len in
|
||||||
* BX, cap in CX. A static literal has no spare storage, so
|
* BX, cap in CX. A static literal has no spare storage, so
|
||||||
@@ -5924,6 +5939,17 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
? tp->type->under : tp->type;
|
? tp->type->under : tp->type;
|
||||||
int op = fldloadop(tp->type, fsz);
|
int op = fldloadop(tp->type, fsz);
|
||||||
int off = localfind(locals, n->lhs->str);
|
int off = localfind(locals, n->lhs->str);
|
||||||
|
/* f64/f32 tuple field must ride X0 via MOVSD/MOVSS;
|
||||||
|
* the integer fldloadop left it in AX (#103 FACE Z).
|
||||||
|
* Mirrors the struct-field float load at cgen.c:1462,
|
||||||
|
* 1838 (the #96 pattern). */
|
||||||
|
int tup_isf32 = 0;
|
||||||
|
if (fld_isfloat(tp->type, &tup_isf32)) {
|
||||||
|
int mov = tup_isf32 ? A_MOVSS : A_MOVSD;
|
||||||
|
ins2(c, mov, amem(D_BP, off + foff),
|
||||||
|
areg(D_X0));
|
||||||
|
break;
|
||||||
|
}
|
||||||
/* str IS []u8 — load (ptr, len, cap) into
|
/* str IS []u8 — load (ptr, len, cap) into
|
||||||
* (AX, BX, CX), the canonical slice-header ABI,
|
* (AX, BX, CX), the canonical slice-header ABI,
|
||||||
* so chains like `t.1.len` propagate through the
|
* so chains like `t.1.len` propagate through the
|
||||||
|
|||||||
@@ -21,11 +21,37 @@ import typ;
|
|||||||
import sym;
|
import sym;
|
||||||
import strconv;
|
import strconv;
|
||||||
|
|
||||||
|
// cgfloatbits — materialise a float constant in X0: MOVQ the IEEE bits
|
||||||
|
// into AX, PUSH, MOVSD off the stack into X0. Shared by N_FLOATLIT (bits
|
||||||
|
// already in n.uval from the lexer's bitcast) and the f64/f32-typed
|
||||||
|
// N_INTLIT arm (#103 FACE X).
|
||||||
|
fn cgfloatbits(c: *cgen, bits: u64) void = {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(bits: i64);
|
||||||
|
emitline(", AX\n");
|
||||||
|
emitline("\tPUSHQ\tAX\n");
|
||||||
|
emitline("\tMOVSD\t(SP), X0\n");
|
||||||
|
emitline("\tADDQ\t$8, SP\n");
|
||||||
|
};
|
||||||
|
|
||||||
fn cgexpr(c: *cgen, n: *node) void = {
|
fn cgexpr(c: *cgen, n: *node) void = {
|
||||||
if (n == nil) { return; };
|
if (n == nil) { return; };
|
||||||
let k: nkind = n.kind;
|
let k: nkind = n.kind;
|
||||||
|
|
||||||
if (k == nkind.N_INTLIT) {
|
if (k == nkind.N_INTLIT) {
|
||||||
|
// A no-decimal `0f64`/`8f64` is an N_INTLIT carrying float
|
||||||
|
// TYPE; it must reach X0 like a true float literal, not the
|
||||||
|
// integer-immediate path (which strands it in AX and an SSE
|
||||||
|
// compare/mul reads a stale X0 — #103 FACE X). The bits are
|
||||||
|
// the IEEE pattern of the integer value, mirroring cstage's
|
||||||
|
// `(double)(long long)n->uval`; the (&fv):*u64 bitcast is the
|
||||||
|
// lex.ww idiom (lib/ww/lex/lex.ww).
|
||||||
|
if (isfloattype(c, n)) {
|
||||||
|
let fv: f64 = (n.uval: i64): f64;
|
||||||
|
let pu: *u64 = (&fv): *u64;
|
||||||
|
cgfloatbits(c, *pu);
|
||||||
|
return;
|
||||||
|
};
|
||||||
// Print signed (i64), not unsigned (u64). C cgen uses
|
// Print signed (i64), not unsigned (u64). C cgen uses
|
||||||
// `$%lld` so 64-bit constants with bit 63 set show up as
|
// `$%lld` so 64-bit constants with bit 63 set show up as
|
||||||
// negative — e.g. FNV-1a's offset basis prints as
|
// negative — e.g. FNV-1a's offset basis prints as
|
||||||
@@ -36,19 +62,11 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (k == nkind.N_FLOATLIT) {
|
if (k == nkind.N_FLOATLIT) {
|
||||||
// Materialise the f64 bit pattern in AX, push, then MOVSD it
|
// The bits come from n.uval — the parser populates it from
|
||||||
// into X0. The bits come from n.uval — the parser populates
|
// the lexer's bitcast of t.fval. The f32 narrowing is handled
|
||||||
// it from the lexer's bitcast of t.fval, so this path stays
|
// at the consumer site, not here — the literal always carries
|
||||||
// integer-only (no SSE in the cgen source). The f32
|
// the full double precision until typed by context.
|
||||||
// narrowing is handled at the consumer site, not here — the
|
cgfloatbits(c, n.uval);
|
||||||
// literal always carries the full double precision until
|
|
||||||
// typed by context.
|
|
||||||
emitline("\tMOVQ\t$");
|
|
||||||
emitint(n.uval: i64);
|
|
||||||
emitline(", AX\n");
|
|
||||||
emitline("\tPUSHQ\tAX\n");
|
|
||||||
emitline("\tMOVSD\t(SP), X0\n");
|
|
||||||
emitline("\tADDQ\t$8, SP\n");
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (k == nkind.N_RUNELIT) {
|
if (k == nkind.N_RUNELIT) {
|
||||||
@@ -1652,6 +1670,21 @@ fn cgdot(c: *cgen, n: *node) void = {
|
|||||||
emitline("(BP), CX\n");
|
emitline("(BP), CX\n");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
// f64/f32 tuple field must ride X0 via
|
||||||
|
// MOVSD/MOVSS; the integer load op left it
|
||||||
|
// in AX (#103 FACE Z). Mirrors the float
|
||||||
|
// local load above and cstage cgen.c:1462,
|
||||||
|
// 1838 (the #96 pattern).
|
||||||
|
if (isfloattype(c, tpt)) {
|
||||||
|
let mov: str = "MOVSD";
|
||||||
|
if (isf32type(c, tpt)) { mov = "MOVSS"; };
|
||||||
|
emitline("\t");
|
||||||
|
emitline(mov);
|
||||||
|
emitline("\t");
|
||||||
|
emitoff((lc.off + foff): i64);
|
||||||
|
emitline("(BP), X0\n");
|
||||||
|
return;
|
||||||
|
};
|
||||||
let sz: i32 = slotsize(c, tpt);
|
let sz: i32 = slotsize(c, tpt);
|
||||||
let op: str = tnodeloadop(c, tpt, sz);
|
let op: str = tnodeloadop(c, tpt, sz);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
|
|||||||
@@ -1847,6 +1847,18 @@ export fn exprfloatkind(c: *cgen, n: *node) i32 = {
|
|||||||
if (n == nil) { return 0; };
|
if (n == nil) { return 0; };
|
||||||
let k: nkind = n.kind;
|
let k: nkind = n.kind;
|
||||||
if (k == nkind.N_FLOATLIT) { return 2; };
|
if (k == nkind.N_FLOATLIT) { return 2; };
|
||||||
|
if (k == nkind.N_INTLIT) {
|
||||||
|
// A float-typed integer literal (`8f64`/`0f32`) now
|
||||||
|
// materialises in X0 (#103 FACE X), so the cast / spill /
|
||||||
|
// arith-recursion sides must classify it as float — else
|
||||||
|
// `(8f64 * 10.0): i32` recurses to this N_INTLIT lhs and
|
||||||
|
// falls through to integer, emitting MOVSXD not CVTTSD2SI
|
||||||
|
// (the #101 structural-vs-stamped asymmetry). cstage reads
|
||||||
|
// the checker-stamped type via node_isfloat directly.
|
||||||
|
if (isf32type(c, n)) { return 1; };
|
||||||
|
if (isfloattype(c, n)) { return 2; };
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
if (k == nkind.N_CAST) {
|
if (k == nkind.N_CAST) {
|
||||||
if (isf32type(c, n.rhs)) { return 1; };
|
if (isf32type(c, n.rhs)) { return 1; };
|
||||||
if (isfloattype(c, n.rhs)) { return 2; };
|
if (isfloattype(c, n.rhs)) { return 2; };
|
||||||
|
|||||||
Reference in New Issue
Block a user