Skip to content

V8 Engine Intro

The JS engine used by Chromium is called V8. It is an open-source JavaScript and WebAssembly engine developed by Google and written in C++. It implements ECMAScript and WebAssembly, and can run on X64, IA-32, and ARM architectures on Windows, macOS, and Linux systems.

V8 is essentially a JavaScript virtual machine. Its development history is as follows:

  • Initially (2008), it ran JS code through full just-in-time compilation via a JIT compiler. The generated code is called Baseline Code.
  • In 2010, a new JIT compiler named Crankshaft was introduced, which optimized based on the results of full code generation. It featured type feedback and deoptimization capabilities. Correspondingly, we call the optimized code Optimized Code.
  • In 2015, a new JIT compiler named TurboFan was introduced, intended to replace Crankshaft. It is the primary JIT compiler used by V8 today, featuring advanced capabilities such as type analysis and sea of nodes.
  • In 2016, a JS interpreter named Ignition was introduced, which is now V8's primary way of running JS code.
  • In 2017, the compilation pipeline underwent a major overhaul, leaving only the streamlined Ignition + TurboFan structure, which continues to be used today.

V8 itself can be treated as a standalone C++ project and can be easily integrated into other C++ applications. Currently, V8 Engine supports nine different instruction set architectures and is widely used in large projects such as Node.js and Chrome.

Architecture & Compilation Pipeline

The top-level architecture design of V8 Engine is shown in the diagram below:

  • After reading in JavaScript source code, the Parser first parses it into an intermediate representation such as AST.
  • The intermediate representation is handed to the interpreter named Ignition Interpreter for translation, generating compact bytecode (only 25~50% the size of baseline machine code).
  • JavaScript is an interpreted language, so after bytecode translation is complete, Ignition interprets and executes it.
  • For frequently executed code or code deemed to need optimization for other reasons, the Ignition Interpreter passes the translated bytecode to the TurboFan Compiler for Just-In-Time Compilation: directly generating the corresponding machine code in memory.

Additionally, there is a special design in V8 Engine called Deoptimization: reverse-translating the machine code generated by TurboFan back to bytecode and handing it back to Ignition for interpreted execution. This typically occurs in the following two scenarios:

  • Eager Deoptimization: Occurs in situations such as JIT code execution errors (e.g., dynamic checks failing). In this case, execution of JIT code is immediately abandoned, the deoptimizer is called to translate it back to bytecode, and it is handed to the interpreter for interpreted execution.
  • Lazy Deoptimization: For example, when a global variable changes, code that depends on that variable is considered to need deoptimization, but it is first only patched, and the deoptimizer is called when the corresponding code is reached during execution.

Under construction.

Reference

https://arttnba3.cn

What Are Rendering Engines: An In-Depth Guide

RenderingNG deep-dive: BlinkNG

How Does the Browser Render HTML?

Browser's Rendering Pipeline

Inside look at modern web browser (part 1)

JavaScript engine fundamentals: Shapes and Inline Caches

Winty's blog - Modern Browser Architecture Overview

Firing up the Ignition interpreter

Digging into the TurboFan JIT

Ignition: Jump-starting an Interpreter for V8

Ignition: An Interpreter for V8

Deoptimization in V8

A New Crankshaft for V8

TurboFan

Sea of Nodes

TurboFan: A new code generation architecture for V8