Navigating Away from Sea of Nodes: Why V8's Turbofan Embraces a Control-Flow Graph

V8's optimizing compilers have undergone a significant transformation over the past few years. The famous Sea of Nodes (SoN) intermediate representation, once a hallmark of Turbofan, is being phased out in favor of a more traditional Control-Flow Graph (CFG) called Turboshaft. This shift is already complete for the JavaScript backend and WebAssembly, with only remnants of SoN remaining in the builtin pipeline and the JavaScript frontend (both being replaced by CFG-based alternatives). Below, we explore the reasons behind this evolution through key questions.

What is the Sea of Nodes and why did V8 adopt it?

The Sea of Nodes (SoN) is an intermediate representation (IR) that merges control flow and data dependencies into a single graph, allowing flexible reordering and optimization. V8 adopted SoN for its Turbofan compiler about 12 years ago to overcome severe limitations in its predecessor, Crankshaft. Crankshaft used a rigid Control-Flow Graph (CFG) that made it impossible to introduce new control flow during lowering—a common compiler task. For example, lowering a high-level JSAdd(x,y) into a conditional if (x is String and y is String) { StringAdd } else { ... } was not feasible. SoN provided the flexibility to dynamically create control flow, support try-catch constructs, and reduce performance cliffs. It was a bold architectural choice that enabled Turban to achieve high performance for asm.js and beyond.

Navigating Away from Sea of Nodes: Why V8's Turbofan Embraces a Control-Flow Graph
Source: v8.dev

What were the main problems with the Crankshaft compiler?

Crankshaft, V8's first optimizing compiler, had several critical issues. First, it relied heavily on hand-written assembly for each of the four supported architectures (x64, ia32, arm, arm64), making it tedious to add new operators. Second, it could not introduce control flow during lowering; control flow was fixed at graph-building time, limiting optimizations. Third, try-catch blocks were unsupported—multiple engineers spent months trying to add them without success. Fourth, it suffered from severe performance cliffs: using certain features or hitting edge cases could cause a 100x slowdown. Finally, deoptimization loops were common—Crankshaft would repeatedly reoptimize functions with the same faulty assumptions, leading to endless cycles. These problems made it difficult for developers to write predictable, high-performance JavaScript.

How did the Sea of Nodes address Crankshaft's shortcomings?

The Sea of Nodes solved many of Crankshaft's limitations by treating control flow as a mutable part of the graph. It allowed dynamic introduction of control flow during lowering, enabling operations like JSAdd to be decomposed into type-checking branches. This made try-catch support feasible and reduced the need for hand-written assembly by enabling generic lowering patterns. The graph-based representation also facilitated better speculative optimizations and deoptimization handling, reducing performance cliffs. However, SoN introduced its own complexities: the graph could become large and hard to analyze, and certain optimizations like scheduling and register allocation required extra passes. While it was a powerful tool, maintainability challenges eventually led the V8 team to reconsider its use.

Why is V8 now moving away from the Sea of Nodes?

Despite its initial benefits, the Sea of Nodes grew increasingly complex to maintain and optimize. The graph structure made it difficult to implement certain passes, and the team found that a well-designed CFG could achieve comparable performance with much simpler engineering. The success of Turboshaft and Maglev—both CFG-based IRs—proved that a traditional control-flow graph could handle modern JavaScript and WebAssembly optimizations efficiently. Additionally, the SoN's flexibility often led to unexpected interactions between passes, making debugging and performance tuning harder. The V8 team decided that moving to a CFG would reduce technical debt, speed up development, and lower the barrier for contributions. After three years of work, the JavaScript backend and WebAssembly pipeline have fully migrated to Turboshaft, with the remaining SoN parts being phased out.

What is Turboshaft and how does it differ from the Sea of Nodes?

Turboshaft is a new Control-Flow Graph (CFG) intermediate representation designed as a replacement for the Sea of Nodes in V8. Unlike the monolithic graph of SoN, Turboshaft uses a more traditional block-structured CFG where control flow is explicit and separate from data flow. This makes it easier to implement standard compiler passes like scheduling, register allocation, and code generation. Turboshaft also simplifies debugging and incremental optimization because the IR is closer to the final machine code. It has been adopted for the entire JavaScript backend of Turbofan and the full WebAssembly pipeline, demonstrating its versatility. The team reports that Turboshaft achieves equivalent or better performance than SoN while being significantly easier to maintain and extend. It is the cornerstone of V8's modern compiler infrastructure.

What parts of Turbofan still use Sea of Nodes?

As of now, only two parts of Turbofan still rely on the Sea of Nodes. The first is the builtin pipeline, which handles built-in JavaScript functions like Array.prototype.forEach. The V8 team is slowly replacing this pipeline with Turboshaft, migrating builtins one by one. The second is the frontend of the JavaScript pipeline, which parses and lowers JavaScript source code into IR. This frontend is being replaced by Maglev, another CFG-based IR that integrates smoothly with Turboshaft. Maglev is designed for fast, non-optimizing compilation but shares the same CFG philosophy. Once these replacements are complete, the Sea of Nodes will be completely removed from V8. The transition highlights the team's commitment to a unified, maintainable compiler architecture.

What does the future hold for V8's compiler infrastructure?

The future of V8's compilation is firmly based on Control-Flow Graph IRs. Turboshaft will handle all optimized compilation for JavaScript and WebAssembly, while Maglev will provide a fast, baseline compilation path. This dual-approach gives V8 the flexibility to optimize hot code thoroughly while quickly compiling cold code. The complete removal of the Sea of Nodes is expected to reduce complexity, improve compilation speed, and lower maintenance costs. Moreover, the CFG architecture makes it easier for external contributors to understand and enhance V8. The team is also exploring new optimizations that leverage Turboshaft's explicit control flow, such as better inlining and vectorization. Overall, V8 is moving toward a simpler, more robust compiler stack that can adapt to future JavaScript features and performance demands.

Tags:

Recommended

Discover More

The Secret Digital Diary: 8 Surprising Things Windows Logs About Your AppsNeanderthal Brains: Size Doesn't Tell the Full StoryWarp Terminal Goes Open Source: AI-Driven Development Model UnveiledLion vs Tiger: New Research Reveals Surprising Genetic and Behavioral DifferencesA Step-by-Step Guide to Adding Rich Structured Data to Your Web Pages with the Block Protocol