V8pedia

TurboFan (top-tier)

TurboFan is V8's most powerful compiler. It produces the fastest code in the engine, and it pays for that with the most compile time — so the tiering manager reserves it for functions proven both hot and type-stable. Its two defining features are the Sea of Nodes IR and speculative optimization driven by runtime feedback.

::: info Ubiquitous language Sea of Nodes: an IR where the program is a graph of value and control nodes with explicit dependency edges, not a fixed instruction order. Nodes "float" until a scheduling phase places them. This frees the optimizer to move code aggressively. :::

The Sea of Nodes

In TurboFan, everything — arithmetic, loads, control flow, effects — is a Node with typed input edges:

class Node final {
 public:
  const Operator* op() const;
  constexpr IrOpcode::Value opcode() const;
  int InputCount() const;
  Node* InputAt(int index) const;
  void ReplaceInput(int index, Node* new_to);
  int UseCount() const;
};

src/compiler/node.h#L41-L100

Because nodes are connected by data, effect, and control edges rather than laid out in a fixed order, the optimizer can perform global value numbering, dead-code elimination, redundancy elimination, and code motion without fighting a rigid instruction sequence. A node only needs to run after the things it depends on — nothing more. A later scheduling phase turns the floating graph back into ordered basic blocks:

enum Control {
  kNone, kGoto, kCall, kBranch, kSwitch,
  kDeoptimize, kTailCall, kReturn, kThrow
};

src/compiler/schedule.h#L28-L100

Note kDeoptimize is a first-class block terminator: bailing out to the interpreter is part of the control-flow vocabulary.

The pipeline

TurboFan is organized as a sequence of phases, orchestrated by PipelineImpl:

bool InitializeHeapBroker();          // A.1  read the heap safely off-thread
bool CreateGraph(Linkage*);           // A.2  bytecode + feedback → graph
bool OptimizeTurbofanGraph(Linkage*); // B    the optimization passes
bool ComputeScheduledGraph();         // B.1  graph → ordered basic blocks
bool AllocateRegisters(…);            // B.3  assign machine registers
MaybeDirectHandle<Code> FinalizeCode(…); // C  emit machine code
bool CommitDependencies(Handle<Code>);   // D  install deopt dependencies

src/compiler/pipeline.cc#L161-L200

Two things are worth dwelling on:

  • The heap broker. Most optimization runs on a background thread, where it may not touch the heap directly (the main thread could be mutating it). The broker takes a consistent snapshot of the heap facts the compiler needs (Maps, feedback, constants) up front, so the heavy passes run concurrently and safely.

  • CommitDependencies. Optimized code is valid only while its assumptions hold ("this Map is stable", "this prototype hasn't changed"). TurboFan registers these as code dependencies; if one is invalidated, the code is thrown away (deoptimized). This is the bookkeeping that makes speculation sound.

Speculative optimization

This is the core idea. TurboFan reads the FeedbackVector and compiles code that assumes the observed types keep holding. If a site only ever loaded property x from objects of one Map, TurboFan emits: check the Map; if it matches, load the field at its known offset directly; if not, deoptimize. No hash lookup, no IC — just a compare and a load.

It speculates on much more than shapes: that a value stays a Smi, that an array stays packed, that a called function is a specific target (enabling inlining), that arithmetic does not overflow. Each speculation is a cheap guard plus a deopt exit. The payoff is enormous: monomorphic, type-stable JavaScript compiles down to code competitive with statically-typed languages.

::: warning Speculation is a bet on stability TurboFan's wins evaporate if a function's types are unstable. A site that sees many Maps (polymorphic/megamorphic) cannot be specialized to one; a counter that overflows Smi range forces float paths; a "packed" array that gets a hole deoptimizes. The optimizer is only as good as the shape discipline of the code feeding it. :::

Why the top tier is also the most dangerous to over-use

Every TurboFan compile costs CPU and memory, and every deopt wastes a compile. That is exactly why V8 (a) only sends very hot, stable functions here, via high invocation thresholds, and (b) added Maglev beneath it so fewer functions need the full treatment. TurboFan is the sledgehammer; the tier ladder exists so V8 swings it rarely and accurately.

See also