Fil-C on Nix

Filnix wiki

nanofil/docs/float.md

Floating point (M9)

Goal: compile Lua 5.4 with nanofil alone, matching a filcc-built Lua. This file is the contract between the three workstreams (IR and interpreter, backends, C frontend). Change it only with the orchestrating thread's agreement, as for the rest of ir.pl's grammar.

IR additions

The IR's floating point started from QBE's floating-point IL (QBE names are given below for reference); the qbe difftest has since been retired, and IR text now writes float constants as c(Bits) terms.

Integer-only IEEE helper API

src/ieee.pl exports the following concrete relations. Bits are unsigned integers; K, From and To are s/d. No host float enters these relations.

Semantics

The interpreter models x86-64 SSE2, so that a returned bit pattern compares exactly against native code:

C

Backends

x87 long double

C's long double on x86-64 is the x87 80-bit extended format: a sign, a 15-bit exponent with bias 16383, an explicit integer bit and 63 fraction bits, so LDBL_MANT_DIG is 64. At the pin clang gives it size 16 and alignment 16, stores its 10 bytes followed by 6 bytes of padding, checks each load and store as a 10-byte access with 8-byte alignment, and computes with fldt, fstpt and the x87 stack (abi-findings.md, x87 long double).

Decision: a real 80-bit semantics, as a new class x. The alternative, holding long double values as d and converting at memory, would print and compare differently from clang wherever the extra 11 significand bits matter. 00204's HFAs are bit patterns printf("%Lf") reads back, 1 + 0x1p-60L != 1, and (unsigned long)(long double) ULONG_MAX round-trips only with 64 bits. A narrower representation would make nanofil a different C implementation with the same <float.h>, not a cheaper one. The class is kept as small as the semantics allow:

The cost of the generic convention is a call's speed, not its meaning. Programs that print, compare or store long doubles get clang's bits. Exported long double functions have no fast signature alias, unlike clang's, so a // signatures progtest leaves them out.

Order

  1. IR workstream, first push: the grammar in ir.pl, the validator, qbe_parse/qbe_print (since replaced by ir_text.pl), op_kind and ieee.pl. The other two build on it.
  2. Then, in parallel: the interpreter's float semantics (IR workstream), the backends, and the C frontend.
  3. Then Lua: lua -e, its test suite, and a nix check like nix/hello.nix and nix/zlib.nix.