selfhost: port float lex + expression cgen — feature parity with C

Lexer: `lexnum` now parses the digit/exponent tail into an f64 via a
new `parsef64` (decimal-only, integer-arith driver + pow-10 multiply,
no strtod). The IEEE bits are also stashed in tok.uval via pointer
reinterpret so cgen consumers stay integer-only.

Parser: TK_FLOAT → N_FLOATLIT, carrying both fval and uval. Parser
state grows curfval to plumb the lexer's f64 through refill.

cgen:
  - cgfloatlit reads n.uval and materialises X0 via the standard
    MOVQ-PUSHQ-MOVSD-ADDQ trampoline.
  - cglet, cgident, cgassign learn float-typed branches: MOVSS/MOVSD
    for locals; LEAQ-indirect MOVSS/MOVSD for globals.
  - cgbin handles ADDSD/SUBSD/MULSD/DIVSD (+ SS variants) and
    UCOMISD/UCOMISS-based comparisons. cgun handles float negate
    via the `0 - X0` shape C cgen uses.
  - cgcast routes int↔float and f32↔f64 through CVTSI2SD/CVTTSD2SI/
    CVTSD2SS/CVTSS2SD and their SS twins.
  - cgcall + pushargsrev push float args via SUBQ+MOVSD and pop into
    the X0..X7 stream, tracked by a per-class counter alongside the
    int DI..R9 stream. cgfnparams loads float params from the same
    stream.
  - emitletdataw bakes FLOATLIT init bits into DATAW (4B for f32,
    8B for f64).

Tests: smoke programs (literal init, reassign, arithmetic, fn args/
returns, casts) produce byte-identical asm through `w6c` and
`wwdump_ww -c`, and the resulting binary exits with the same value
whether compiled by the C or wwstage toolchain. Full `make test` is
26/26 and `make bootstrap` still reaches its byte-identical
ww2==ww3==ww4 fixed point.
This commit is contained in:
2026-05-12 14:21:50 +09:00
parent a9b804935c
commit 5155ba55f3
10 changed files with 1767 additions and 69 deletions

View File

@@ -28,6 +28,17 @@ fn parseprimary(p: *parser) *node = {
advance(p);
return n;
};
if (p.curkind == tkind.TK_FLOAT) {
let n: *node = newnode(p.a, nkind.N_FLOATLIT, pf, pl, pc);
n.fval = p.curfval;
// uval carries the IEEE 754 bit pattern — the lexer sets
// both, and cgen consumers prefer the integer view so they
// don't need a float ABI to materialise the constant.
n.uval = p.curuval;
n.str = p.curtext;
advance(p);
return n;
};
if (p.curkind == tkind.TK_STR) {
let n: *node = newnode(p.a, nkind.N_STRLIT, pf, pl, pc);
n.str = p.curtext;

View File

@@ -30,6 +30,7 @@ type parser = struct {
curcol: i32,
curtext: str,
curuval: u64,
curfval: f64,
};
fn refill(p: *parser) void = {
@@ -41,6 +42,7 @@ fn refill(p: *parser) void = {
p.curcol = t.col;
p.curtext = t.text;
p.curuval = t.uval;
p.curfval = t.fval;
};
export fn parserinit(p: *parser, a: *arena, l: *lex) void = {