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:
@@ -845,21 +845,37 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
let fsz: i32 = letvarisfloat(c, nm);
|
||||
if (fsz > 0) {
|
||||
// Float global: 4B (f32) or 8B (f64).
|
||||
// The selfhost parser doesn't lex
|
||||
// N_FLOATLIT yet, so only zero-init
|
||||
// reaches this path. C cgen emits
|
||||
// identical bytes for the zero-init
|
||||
// case; FLOATLIT-init lives in C cgen
|
||||
// only.
|
||||
// Two init shapes:
|
||||
// - no rhs: emit fsz zero bytes
|
||||
// - N_FLOATLIT: bake the IEEE bits the
|
||||
// parser stashed in r.uval (lexer
|
||||
// bit-casts t.fval into t.uval). f32
|
||||
// emits the low 4 bytes; f64 emits 8.
|
||||
let bits: u64 = 0u64;
|
||||
let ok: bool = true;
|
||||
if (d.rhs != nil) { ok = false; };
|
||||
if (d.rhs != nil) {
|
||||
let r: *node = d.rhs;
|
||||
for (r != nil) {
|
||||
if (r.kind != nkind.N_CAST) { break; };
|
||||
r = r.lhs;
|
||||
};
|
||||
ok = false;
|
||||
if (r != nil) {
|
||||
if (r.kind == nkind.N_FLOATLIT) {
|
||||
bits = r.uval;
|
||||
ok = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (ok) {
|
||||
emitline("DATAW ");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB),\"");
|
||||
let i: i32 = 0;
|
||||
let nb: u64 = bits;
|
||||
for (i < fsz) {
|
||||
emitdatawbyte(0u8);
|
||||
emitdatawbyte((nb & 255u64): u8);
|
||||
nb = nb >> 8u64;
|
||||
i += 1;
|
||||
};
|
||||
emitline("\"\n");
|
||||
@@ -1385,3 +1401,18 @@ fn argregname(i: i32) str = {
|
||||
if (i == 5) { return "R9"; };
|
||||
return "?";
|
||||
};
|
||||
|
||||
// fargregname — XMM scalar-float arg registers (SysV: X0..X7).
|
||||
// Parallel to argregname / sysv_argregs; float args advance their
|
||||
// own counter so int and float arg slots don't conflict.
|
||||
export fn fargregname(i: i32) str = {
|
||||
if (i == 0) { return "X0"; };
|
||||
if (i == 1) { return "X1"; };
|
||||
if (i == 2) { return "X2"; };
|
||||
if (i == 3) { return "X3"; };
|
||||
if (i == 4) { return "X4"; };
|
||||
if (i == 5) { return "X5"; };
|
||||
if (i == 6) { return "X6"; };
|
||||
if (i == 7) { return "X7"; };
|
||||
return "?";
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user