Back to V8

TurboFan: V8's Optimizing Compiler

docs/compiler/turbofan/compiler-turbofan.md

15.0.107.4 KB
Original Source

TurboFan: V8's Optimizing Compiler

TurboFan is V8's top-tier optimizing compiler. It is responsible for generating the most efficient machine code possible for hot functions, utilizing all available type information and aggressive optimization techniques.

Architecture

TurboFan has historically used a "Sea of Nodes" Intermediate Representation (IR). V8 has transitioned to a new IR framework called Turboshaft, which is now enabled by default for JavaScript. The current pipeline often uses a hybrid approach, starting with Sea of Nodes and lowering to Turboshaft for later phases.

Sea of Nodes IR

In the Sea of Nodes representation, there is no strict control flow graph (CFG) during the early and middle optimization phases. Instead:

  • Nodes represent operations (e.g., additions, loads, constants).
  • Edges represent data flow, effect flow (ordering of operations with side effects), and control flow.
  • This representation allows the compiler to freely reorder operations as long as data and effect dependencies are respected, enabling powerful optimizations like code motion and redundant load elimination.

Turboshaft

Turboshaft is the next-generation IR for V8. It uses a more structured control flow graph with basic blocks and linear sequences of operations within blocks. This makes certain optimizations easier to implement and reason about compared to the Sea of Nodes.

The Compilation Pipeline

The compilation process is managed by the Pipeline class (defined in src/compiler/pipeline.h) and turboshaft::Pipeline (defined in src/compiler/turboshaft/pipelines.h). It consists of several phases, often spanning both IRs:

  1. Graph Building: The initial graph is built from Ignition bytecode or from Maglev IR in the Sea of Nodes representation.
  2. Sea of Nodes Optimizations: The graph goes through a series of optimization phases in the Sea of Nodes IR, including:
    • Inlining: Replacing function calls with the body of the called function.
    • Escape Analysis: Determining if objects allocated in the function escape to the heap.
    • Simplified Lowering: Lowering high-level JavaScript operations to simpler machine-level operations based on type feedback.
    • Loop Peeling: Peeling loops to enable further optimizations.
  3. Turboshaft Conversion: The optimized Sea of Nodes graph is converted to the Turboshaft IR.
  4. Turboshaft Optimizations: Further optimizations are performed in Turboshaft, such as:
    • Machine Lowering: Lowering to machine-level operations.
    • Load Elimination: Removing redundant loads.
    • Loop Unrolling: Unrolling loops for better performance.
  5. Instruction Selection: Mapping the IR nodes to specific machine instructions for the target architecture.
  6. Register Allocation: Assigning physical registers to virtual registers.
  7. Code Generation: Emitting the final machine code.

Deoptimization

TurboFan generates highly specialized code based on optimistic assumptions (e.g., assuming a specific hidden class). To ensure correctness, it inserts checks before specialized operations.

If a check fails at runtime (e.g., a function receives an object with a Map it hasn't seen before), the code cannot continue. TurboFan triggers a Deoptimization (Deopt).

  • The current state of the optimized frame is translated back into an unoptimized frame compatible with Ignition or Sparkplug.
  • Execution resumes in the unoptimized tier.
  • The FeedbackVector is updated to reflect the new reality, so that a future re-optimization can take it into account.

External Resources

Articles and blog posts

Talks

Design documents

These are design documents that are mostly concerned with TurboFan internals.

These are design documents that also affect TurboFan in a significant way.

See Also