← board

Track O: float expression temporaries in registers

Where the 4x goes

The x86-64 float binop path is an accumulator machine: each IR_BINOP loads operands from stack slots, computes in xmm0/xmm1, and spills the result back (EmitFloatSpill386-style patterns on x86-64 too). The mandelbrot inner loop (zrzr - zizi + cr etc.) round-trips every temporary through memory; FPC keeps the whole escape iteration in registers.

Recon (2026-07-15 morning — root confirmed)

The x86-64 VALUE MODEL carries a Double as raw bits in RAX. A float IR_BINOP therefore emits: eval left -> rax, push rax; eval right -> rax, mov rcx<-pop; movq xmm1, rax; movq xmm0, rcx; <op>; movq rax, xmm0. Three GPR<->XMM transfers plus a stack round-trip PER OPERATION — the whole 4.2x against FPC's xmm-resident code. The narrow "fuse within one tree" idea still pays, but the honest fix is an xmm-resident float accumulator (xmm0 = the float accumulator; nested left operands spill to the stack as today, but as MOVSD spills, no GPR transit), which touches every float consumer: binops, compares, call args/returns, stores, writeln. Sized as a MULTI-SESSION Track O arc — do not start it as a night-tail.

User constraints (2026-07-15)

Shape (per the regcall/residency precedents)

Re-measured 2026-07-18 night (fable-O) — gap HALVED since opening, arc still valid

Same box, same checksum (74607393270), quiet machine, hyperfine w2/r7:

build time vs FPC -O2
pxx -O2 1.399 s ± 0.009 4.2x (unchanged — ticket baseline)
pxx -O3 0.664 s ± 0.005 1.97x
FPC -O2 0.337 s ± 0.003 1.00x