Parser & preparser
source → AST
The scanner reads the source once. The preparser skims function bodies it can defer, recording only the scope information needed to compile them later, and a full parse happens the first time a function is actually called. Lazy parsing is why a large bundle costs less than its byte count suggests.
function f() { … } // pre-parsed, skimmed
f() // now fully parsedCompiles the AST into a compact register-based bytecode with one implicit accumulator, then interprets it. The handlers themselves are generated machine code, so the interpreter is fast — but the reason it exists is size. Bytecode is a fraction of what full machine code would cost in memory.
LdaSmi [1] // accumulator ← 1
Star r0 // r0 ← accumulator
Sparkplug
the baseline tier
A non-optimising compiler that walks the bytecode once and emits machine code for each instruction. No IR, no register allocation, no analysis — and it keeps the interpreter’s exact stack frame layout, so a function can be swapped between tiers mid-run. It buys a flat speed-up for almost no compile time.
// bytecode → machine code, one pass
// same frame layout as Ignition
The tier between baseline and full optimisation. It builds a real SSA control-flow graph and specialises on collected feedback, but skips the expensive global reordering TurboFan does. Warm functions get most of the win long before they are hot enough to justify the top tier.
--maglev // default since Chrome 117
TurboFan
the optimising tier
A sea-of-nodes IR where value, effect and control are separate edge kinds, so most nodes float free of any order until scheduling places them. It optimises speculatively on feedback — this argument has only ever been a small integer — inlines aggressively, and guards every assumption it made.
// nodes float; only effect and control
// edges pin them to an order
Deoptimisation
the escape hatch
When a guard fails, V8 discards the optimised frame and rebuilds an interpreter frame from it in place, resuming in Ignition at the exact bytecode offset. Nothing observable happens. Speculation is only a sane strategy because being wrong is recoverable rather than fatal.
--trace-deopt
// deoptimizing … reason: wrong map