JavaScript V8 JIT Bytecode Compilation Interpretation

Is JavaScript Really Interpreted or Compiled?

Aliaksei Kankou Aliaksei Kankou
· July 10, 2024 · 6 min read

Is JavaScript really interpreted or compiled? JavaScript is neither purely interpreted nor compiled. Modern V8 uses a four-tier pipeline — Ignition Interpreter, Sparkplug, Maglev, and TurboFan — to dynamically decide how to execute your code.

Exploring Google's V8 Engine: JavaScript's Powerhouse

The V8 engine, developed by Google, is an open-source JavaScript engine that powers both Google Chrome and Node.js. Its primary role is to translate JavaScript code into machine code, enabling fast execution on the hardware. Modern V8 uses a sophisticated four-tier pipeline to balance startup speed and peak performance.

V8 Engine Compiler Pipeline Architecture — Ignition, Sparkplug, Maglev, TurboFan
Modern V8 Engine compiler pipeline: from source code to native machine code via four execution tiers.

From Source Code to Machine Code: The Lifecycle of JavaScript in V8

1

Source Code (e.g., script.js)

  • The journey begins with the JavaScript source code, which is the script written by the developer.
2

Parser

  • The parser reads the source code and converts it into an Abstract Syntax Tree (AST). This tree structure represents the syntactic structure of the code.
3

Bytecode Generator and Interpreter (Ignition)

  • Bytecode Generation: Ignition transforms the AST into bytecode — a compact, platform-independent intermediate representation.
  • Interpretation: Ignition executes the bytecode by dispatching each opcode to a pre-compiled machine code handler. No new machine code is generated at this stage — Ignition uses V8's own built-in handlers to carry out each operation.
  • This is the default execution tier for cold code — functions that are called rarely or only once.
4

Sparkplug — Baseline JIT (Warm Code)

  • When a function is called enough times to be considered warm, V8 compiles it with Sparkplug — a non-optimizing baseline JIT compiler introduced in V8 v9.1.
  • Sparkplug performs a single linear pass over the bytecode and emits native machine code directly, without building any intermediate representation (IR).
  • It makes zero optimizations, but eliminates interpreter overhead — bytecode decoding and opcode dispatch — resulting in a meaningful speedup over Ignition.
  • The compiled machine code is cached on the V8 heap (Code Space) and reused on subsequent calls.
5

Maglev — Fast Optimizing JIT (Hot Code)

  • For functions called frequently enough to be considered hot, V8 uses Maglev — a mid-tier optimizing JIT compiler introduced in Chrome M117.
  • Maglev builds an SSA (Static Single Assignment) graph using runtime type feedback collected by Ignition, and applies moderate optimizations such as type specialization and representation selection.
  • It compiles roughly 10x slower than Sparkplug but 10x faster than TurboFan, making it ideal for the large middle ground of real-world web app code that is too hot for Sparkplug but not hot enough to justify TurboFan's cost.
  • If speculative assumptions are violated at runtime (e.g. a variable changes type), Maglev deoptimizes and falls back to bytecode execution.
6

TurboFan — Aggressive Optimizing JIT (Very Hot Code)

  • For code that runs thousands of times — tight loops, compute-heavy functions — V8 applies TurboFan, its top-tier optimizing compiler.
  • TurboFan performs deep, aggressive optimizations including inlining, escape analysis, loop optimizations, and speculative type narrowing based on accumulated feedback.
  • It takes significantly longer to compile than Maglev, but produces the fastest possible native machine code.
  • Like Maglev, TurboFan can deoptimize back to bytecode if its speculative assumptions are invalidated.
7

Optimization and Deoptimization

  • Both Maglev and TurboFan make speculative optimizations based on observed types. If those assumptions are later violated — for example, a function that always received numbers suddenly receives a string — the optimized code is discarded and execution falls back to Ignition's bytecode.
  • Bytecode always remains the stable fallback. It is never discarded as long as the function exists.

So, Is JavaScript Interpreted or Compiled?

The V8 engine employs a hybrid approach that blurs the line between interpretation and compilation. Every function starts life in Ignition's interpreter. As it gets called more frequently, V8 progressively promotes it through Sparkplug, Maglev, and finally TurboFan — each tier trading compilation cost for faster output. This tiered JIT pipeline allows JavaScript to achieve rapid startup times for cold code while reaching near-native performance for hot code.

Conclusion

Modern V8 is far more sophisticated than a simple interpreter or a single JIT compiler. Its four-tier pipeline — Ignition, Sparkplug, Maglev, and TurboFan — carefully balances compilation cost against execution speed at every level. JavaScript is neither purely interpreted nor purely compiled: it is a dynamically tiered language, continuously optimizing itself based on runtime behavior. This is what makes JavaScript fast enough to power everything from simple web pages to complex server-side applications.

Aliaksei Kankou

Aliaksei Kankou

Lead Full-Stack Engineer and Cloud Architect with a Bachelor's degree in Software Engineering and 15+ years of experience.