Every byte of machine code is Floati's own, a news site that showed blank renders, Gust's code is half the size, and closures and ?? compile
Floati now writes all of its machine code itself. The five compilers that turn hot JavaScript into machine code build Updraft IR, Floati's own compiler core, which Floati lowers to ARM64 and x86-64 code with no outside code generator in the browser any more: compiles take about half the time, the code is smaller and faster, and the browser program is 2.45 MB smaller. A large news site that showed as a blank strip in 0.1.359 renders in full again: a function's own variables were confused with a global of the same name declared inside one of its inner functions, so a framework's library read as undefined. Functions whose inner functions capture their variables, and functions that use ?? or ?., now compile instead of staying in the interpreter. Gust, the instant compiler, makes half the code it did, and its x86-64 version now runs correctly on Windows. Strings are faster and lighter (building class names 151 -> 105 ms with 3.7 times fewer allocations, a long-key hash 47 -> 31 ms), a script declaring 16,000 global functions starts in 22 ms instead of 935, callbacks over arrays run 5 times faster than in the reference engine, and a paused Map or Set iterator no longer skips an entry.
Why Floati now writes all of its own machine code
Updraft is the machine-code compiler Floati builds for itself. Its first tiers shipped earlier this month: Gust, which turns a function into machine code in under a microsecond per operation, and Thermal, which keeps a hot loop's numbers in the processor's registers. Until this release the other five compilers (hot loops, callbacks and recursion, batch drivers, whole functions, and typed-array vector kernels) still handed their work to a general-purpose code generator written for other projects. Now all five build Updraft IR, and Floati lowers it itself: registers chosen by linear scan, repeated values merged, loop-invariant code hoisted out of loops, checks fused into the branches that read them, and 128-bit vectors on both CPUs. Why build our own: the outside generator spent about 100 times longer per operation than a page can afford on code that runs only a few times, it knew nothing about JavaScript values or Floati's page objects, it kept code memory we could not free, and it was the last large part of the engine Floati did not own. The business decision was to own the whole engine, as Floati owns its layout, network and media code, but to remove the outside generator only once ours matched or beat it on every axis we measure. It did: an integer loop 29 -> 23 ms on ARM64, callbacks over arrays 120 -> 101 ms, entry code 238 -> 216 bytes per function in half the compile time, 1.2 times faster code on x86-64, and vector sums on Windows 175 -> 4.5 ms (the old generator could not build them there at all). Every compiled program is checked against what the IR means: 100,000 random programs with 8 inputs each, plus every test map in 13 tier configurations.
A blank news site renders again, and closures compile
A news site with a front page built by a JavaScript framework showed as a 64-pixel strip in 0.1.359, with 30 copies of one error: a property read on undefined. The cause was in how a function gets its fast plan: a var declared inside one of its inner functions was counted as the outer function's own variable, so the outer function's reads of a global with the same name, the framework's library here, read that empty variable instead. Inner functions are now left out of the scan, and the fix came with a larger change: a function whose inner functions capture its variables used to stay in the interpreter, and now gets a plan. The captured variables live in one scope made when the function is called, the way a function context works, and everything else stays in registers. Functions that use ?? and ?. compile as well. On a recorded load of the same site, functions refused a plan fell from 343 to 232.
Gust's code halved
Gust copied every slow path at every place it could happen: each reference count drop, each stack check and each call into the runtime carried its own save, call and restore sequence. Now each kind of slow path is one routine per function, and a site makes a short jump into it, so Gust's code for 1,500 warm functions fell from 18.5 to 9.05 MB, which also means less to compile and more of it fits in the processor's cache. Gust's x86-64 version for Intel and Windows machines now uses the Windows calling convention there, and its shared routines belong to one thread each: a routine shared across threads could read another thread's view of a replaced method, a wrong answer the new test caught 241 times in 600 runs before the fix. x86-64 Gust stays behind a switch while its code is made smaller still.
Strings, start-up and iterators
A string literal in a hot function was boxed into a new string on every call: 'a' + i + 'b' made three strings where one is the answer. It is now the literal's own value, so building class names makes 3.7 times fewer allocations. Reading s.length no longer copies the string, Map and Set hash their string keys 8 bytes at a time with a seed chosen per run, and Thermal now compiles hash loops that read strings. A script declaring many global functions checked each new name against all the earlier ones, so setting up 16,000 took almost a second; it is a single pass now. And a Map or Set iterator paused while its collection was compacted many times resumed at the wrong place and skipped an entry; it now keeps an ordinal and resumes exactly.
What changed 9 changes
Try it in your browser
Each check runs live in the browser you are reading with. Nothing is sent anywhere.