wcc: narrow f32-typed float literals at materialisation (#104 fold-1)
Both stages materialise a float literal as a 64-bit double in X0 (MOVQ bits -> MOVSD), ignoring the node type. For an f32-typed literal the downstream MOVSS reads the low 4 bytes of that double — garbage (0.0f for clean values, which is why 0.0 survived the bug and 951's f32 rows, which only assert NaN ordering, never caught it). Append CVTSD2SS X0,X0 at both literal sites (N_FLOATLIT + the float-typed N_INTLIT arm) when the node is f32-typed, so the value reaches X0 as a true single. Mirror in cgenexpr.ww (rule-10) and regen the w6c/wwdump combined.ww embeds. Covers literals carrying an explicit f32 type (the `f32` suffix and the no-decimal `8f32` N_INTLIT arm). An un-suffixed literal in an f32 context (`let x: f32 = 1.0`) stays ty_untyped_float through the checker, so its node is never f32-typed and this branch can't fire — that needs fold-2 (checker untyped-float -> f32 lowering, both checkers). 964_f32lit_run: cstage run + cs==ww byte-id probe over concrete f32 values (suffixed), the hole 951 leaves open.
This commit is contained in:
@@ -14209,6 +14209,12 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
let fv: f64 = (n.uval: i64): f64;
|
||||
let pu: *u64 = (&fv): *u64;
|
||||
cgfloatbits(c, *pu);
|
||||
// #104: cgfloatbits materialises a DOUBLE in X0; an
|
||||
// f32-typed literal must narrow with hardware single-
|
||||
// rounding so the downstream MOVSS reads a true single.
|
||||
if (isf32type(c, n)) {
|
||||
emitline("\tCVTSD2SS\tX0, X0\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
// Print signed (i64), not unsigned (u64). C cgen uses
|
||||
@@ -14222,10 +14228,12 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
};
|
||||
if (k == nkind.N_FLOATLIT) {
|
||||
// The bits come from n.uval — the parser populates it from
|
||||
// the lexer's bitcast of t.fval. The f32 narrowing is handled
|
||||
// at the consumer site, not here — the literal always carries
|
||||
// the full double precision until typed by context.
|
||||
// the lexer's bitcast of t.fval.
|
||||
cgfloatbits(c, n.uval);
|
||||
// #104: narrow the double in X0 to single for an f32 literal.
|
||||
if (isf32type(c, n)) {
|
||||
emitline("\tCVTSD2SS\tX0, X0\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (k == nkind.N_RUNELIT) {
|
||||
|
||||
@@ -50,6 +50,12 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
let fv: f64 = (n.uval: i64): f64;
|
||||
let pu: *u64 = (&fv): *u64;
|
||||
cgfloatbits(c, *pu);
|
||||
// #104: cgfloatbits materialises a DOUBLE in X0; an
|
||||
// f32-typed literal must narrow with hardware single-
|
||||
// rounding so the downstream MOVSS reads a true single.
|
||||
if (isf32type(c, n)) {
|
||||
emitline("\tCVTSD2SS\tX0, X0\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
// Print signed (i64), not unsigned (u64). C cgen uses
|
||||
@@ -63,10 +69,12 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
};
|
||||
if (k == nkind.N_FLOATLIT) {
|
||||
// The bits come from n.uval — the parser populates it from
|
||||
// the lexer's bitcast of t.fval. The f32 narrowing is handled
|
||||
// at the consumer site, not here — the literal always carries
|
||||
// the full double precision until typed by context.
|
||||
// the lexer's bitcast of t.fval.
|
||||
cgfloatbits(c, n.uval);
|
||||
// #104: narrow the double in X0 to single for an f32 literal.
|
||||
if (isf32type(c, n)) {
|
||||
emitline("\tCVTSD2SS\tX0, X0\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (k == nkind.N_RUNELIT) {
|
||||
|
||||
@@ -14209,6 +14209,12 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
let fv: f64 = (n.uval: i64): f64;
|
||||
let pu: *u64 = (&fv): *u64;
|
||||
cgfloatbits(c, *pu);
|
||||
// #104: cgfloatbits materialises a DOUBLE in X0; an
|
||||
// f32-typed literal must narrow with hardware single-
|
||||
// rounding so the downstream MOVSS reads a true single.
|
||||
if (isf32type(c, n)) {
|
||||
emitline("\tCVTSD2SS\tX0, X0\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
// Print signed (i64), not unsigned (u64). C cgen uses
|
||||
@@ -14222,10 +14228,12 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
};
|
||||
if (k == nkind.N_FLOATLIT) {
|
||||
// The bits come from n.uval — the parser populates it from
|
||||
// the lexer's bitcast of t.fval. The f32 narrowing is handled
|
||||
// at the consumer site, not here — the literal always carries
|
||||
// the full double precision until typed by context.
|
||||
// the lexer's bitcast of t.fval.
|
||||
cgfloatbits(c, n.uval);
|
||||
// #104: narrow the double in X0 to single for an f32 literal.
|
||||
if (isf32type(c, n)) {
|
||||
emitline("\tCVTSD2SS\tX0, X0\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (k == nkind.N_RUNELIT) {
|
||||
|
||||
Reference in New Issue
Block a user