The solution
Modern sites like Google Docs and Gmail execute so much JavaScript that engine speed had two separate bottlenecks. Firefox's C++ bytecode interpreter was slow and collected no type information, while the Baseline JIT compiled each function to machine code; on heavy pages that meant compiling thousands of functions, so time went to compilation instead of running. The optimizing Ion JIT only got fast once the Baseline JIT had gathered type data.
The obvious fixes each cost something: speed up the C++ interpreter and it still gives the optimizer no type data; delay or move Baseline compilation and cold paths or bailouts become risky; resume Ion bailouts in the middle of JIT code and every feature needs extra metadata and complex code for debuggers and exceptions. Mozilla instead added a new tier, the Baseline Interpreter, sitting between the C++ interpreter and the Baseline JIT.
The move that made this cheap was not writing an interpreter from scratch. The Baseline JIT is a template JIT: each bytecode instruction already maps to a mostly fixed sequence of machine code. Mozilla generated those same sequences into a threaded interpreter loop, sharing one templated code generator, the same frame layout, and one JitScript structure holding the inline caches and profiling data both tiers use.
Because interpretation now gathered type information, the Baseline JIT warm-up threshold could rise from 10 to 100, sharply cutting how many functions got compiled. Firefox Nightly measured 2-8% faster page loads, 2-10% faster devtools tests, memory savings, and simpler code: Ion bailouts resumed in the interpreter, and much of the complicated debugger and exception-handling machinery was deleted.
What it achieved
Generate the cheap tier from the same code generator as the fast tier, so caches and type data stay shared
Why it worked
Reusing the JIT's code generator means interpreter behavior and inline caches match the JIT by construction, with no second implementation to keep in sync.
Interpreting now gathers the same type data the JIT used to, so the optimizing tier gets its profile without waiting for compilation.
Raising the JIT warm-up threshold tenfold slashed how many functions got compiled on JS-heavy pages.
Bailouts, debugger support and exception handling got simpler because Ion resumes in the interpreter instead of mid-JIT code, needing less metadata.
Entering the middle tier is cheap: JitScript allocation runs at least 10 times faster than Baseline JIT compilation.
What can be applied
Do not hand-write the middle tier between slow and fast: generate it from the fast tier's own templates, and caches, frames and behavior stay identical by construction
Aftermath
The Baseline Interpreter landed in Firefox Nightly in July 2019 and was headed for Firefox 70's general release that October. Mozilla's measurements showed 2-8% faster page loads and 2-10% faster devtools tests, with memory savings on top. The clean-up it enabled also sped up Baseline JIT compilation, and the team's stated next step was moving that compilation off the main thread.
FOLLOW THE EVIDENCE
The sources
- The Baseline Interpreter: a faster JS interpreter in Firefox 70 hacks.mozilla.org
- Hacker News discussion: The Baseline Interpreter: a faster JS interpreter in Firefox 70 news.ycombinator.com