For over a decade, V8's optimizing compiler Turbofan has been a standout example of a production compiler employing the Sea of Nodes (SoN) intermediate representation (IR). However, starting around three years ago, the V8 team began transitioning away from SoN toward a more traditional Control-Flow Graph (CFG) IR, dubbed Turboshaft. Today, all JavaScript backend compilation in Turbofan uses Turboshaft, and WebAssembly pipelines rely on it entirely. Only two parts of Turbofan still retain some SoN usage: the builtin pipeline (gradually being replaced) and the JavaScript frontend (being replaced by Maglev, another CFG-based IR). This article delves into the reasons behind this significant architectural shift.
The Origins of Turbofan and Sea of Nodes
Twelve years ago, in 2013, V8 relied on a single optimizing compiler: Crankshaft, which used a CFG-based IR. While Crankshaft delivered notable performance gains, it faced mounting technical debt and several critical limitations:
- Excessive hand-written assembly: Every new IR operator required manual assembly implementations for all four supported architectures (x64, ia32, arm, arm64).
- asm.js optimization struggles: Crankshaft had difficulty optimizing asm.js, which was then considered vital for high-performance JavaScript.
- Rigid control flow: Control flow was fixed at graph construction time, preventing later lowerings from introducing new branches. For example, lowering
JSAdd(x, y)into a conditional string addition was impossible. - No try-catch support: Multiple engineers spent months attempting to support try-catch blocks without success.
- Performance cliffs and bailouts: Certain features or edge cases could degrade performance by a factor of 100, making code efficiency unpredictable.
- Deoptimization loops: Crankshaft would re-optimize functions with the same speculative assumptions that had just caused deoptimization, leading to cycles.
Challenges with the Sea of Nodes Approach
When Turbofan was introduced, Sea of Nodes was chosen to overcome Crankshaft's rigidity. SoN combined control and data dependencies in a single graph, theoretically enabling more flexible optimizations. However, in practice, it introduced new complexities:
- Debugging difficulties: The graph's non-linear nature made it hard to trace errors during optimization passes.
- Compilation performance overhead: Managing the graph structure consumed significant CPU time, slowing down JIT compilation.
- Memory usage: SoN's representation often required more memory than a CFG, impacting overall runtime.
- Limited parallelism: The graph's interdependencies constrained the ability to run optimization passes concurrently.
As V8 aimed to support more complex JavaScript features and improve compilation speed, these drawbacks became increasingly costly.
Why the Shift from Sea of Nodes?
The decision to move away from SoN was driven by several factors:
- Compiler maintainability: A CFG-based IR is easier to reason about, debug, and extend. New team members could contribute more quickly.
- Compilation speed: CFGs allow for simpler and faster optimization passes, reducing JIT latency—critical for startup performance.
- Better performance for modern workloads: SoN's theoretical advantages didn't always translate to faster code, especially with evolving JavaScript patterns.
- Integration with Maglev: The new mid-tier compiler Maglev uses a CFG, making it natural to unify the compiler pipeline around a single IR paradigm.
- Future-proofing: A CFG-based design aligns with most modern compilers, facilitating adoption of best practices and new techniques.
Turboshaft: A New Direction
Turboshaft is V8's custom CFG-based IR that replaces Sea of Nodes in Turbofan. It retains key advantages of SoN while addressing its shortcomings:
- Explicit control flow: Like traditional CFGs, Turboshaft exposes control edges, making optimizations and code generation more straightforward.
- Flexible lowering: It supports introducing new control flow during optimization passes, enabling richer transformations (e.g., conditionally inlining string operations).
- Reduced overhead: Simpler graph structure leads to faster compilation and lower memory footprint.
- Seamless integration: Turboshaft works hand-in-hand with Maglev and the existing Turbofan backend, minimizing disruption.
Current Status and Future Plans
As of early 2024, the JavaScript backend of Turbofan has fully adopted Turboshaft, and the WebAssembly pipeline has been entirely migrated. The builtin pipeline is in the process of being replaced, and the JavaScript frontend is transitioning to Maglev. The eventual goal is to eliminate Sea of Nodes entirely from V8's codebase. This shift represents a pragmatic evolution: leaving an innovative but complex IR for a simpler, more maintainable foundation that still delivers excellent performance.
Key Takeaways
- V8's Turbofan compiler originally used the advanced Sea of Nodes IR but faced maintenance and performance issues.
- The team developed Turboshaft, a CFG-based IR, to overcome these challenges while preserving optimization power.
- Most of V8's compilation pipeline now uses Turboshaft or the CFG-based Maglev compiler.
- The move improves compilation speed, debuggability, and developer productivity.
For more details, see the original V8 blog post Land ahoy: leaving the Sea of Nodes.