Have you heard about Hack Frontend Community?Join us on Telegram!

How V8 Works: from JavaScript to Machine Code

V8 is a high-performance JavaScript engine by Google, used in Chrome and Node.js. Understanding its architecture is critical for writing optimized code.

V8 Pipeline: Three-Level Compilation

Parser → AST

function add(a, b) {
  return a + b;
}

AST (Abstract Syntax Tree):

{
  "type": "FunctionDeclaration",
  "id": { "name": "add" },
  "params": [
    { "name": "a" },
    { "name": "b" }
  ],
  "body": {
    "type": "ReturnStatement",
    "argument": {
      "type": "BinaryExpression",
      "operator": "+",
      "left": { "name": "a" },
      "right": { "name": "b" }
    }
  }
}

Ignition Interpreter

Ignition converts AST to bytecode and starts execution:

// Bytecode for add(a, b)
Ldar a0        // Load argument 0 (a) into accumulator
Add a1, [0]    // Add argument 1 (b)
Return         // Return result

Why bytecode?

  • Fast startup (no compilation needed)
  • Memory efficient (more compact than AST)
  • Easy to deoptimize back

Sparkplug - Baseline Compiler

Sparkplug (added in V8 9.1) compiles frequently called bytecode to unoptimized machine code:

Bytecode → Machine Code (1:1 mapping)

Advantages:

  • Faster than interpreter (~2x)
  • No profiling overhead
  • Intermediate level before TurboFan

TurboFan - Optimizing Compiler

TurboFan creates highly optimized machine code based on feedback:

function add(a, b) {
  return a + b;
}

// After 1000+ calls with numbers
add(1, 2);    // V8 notices: always numbers!
add(5, 10);
add(100, 200);

// TurboFan optimizes: assuming numbers
// Generates machine code for numbers directly

TurboFan Optimizations:

  • Type specialization
  • Inline caching
  • Function inlining
  • Loop unrolling
  • Dead code elimination

Optimization and Deoptimization

When Does Optimization Occur?

function calculate(x) {
  return x * 2;
}

// Call 1-100: Ignition (bytecode)
for (let i = 0; i < 100; i++) calculate(i);

// Call 100+: Sparkplug (baseline)
// V8: "This function is hot, compile to baseline"

// Call 1000+: TurboFan (optimized)
// V8: "Always numbers! Optimize for numbers"

Deoptimization (Optimization Rollback)

function calculate(x) {
  return x * 2;
}

// V8 optimized for numbers
for (let i = 0; i < 10000; i++) {
  calculate(i); // Numbers - optimized code
}

// Unexpected type!
calculate("hello"); // String - DEOPT!

// V8 rolls back to bytecode and collects feedback again

Deopt Cost:

  • Rollback to bytecode (slow)
  • Loss of optimized code
  • Re-collecting statistics

Hidden Classes (Shapes/Maps)

V8 optimizes property access through Hidden Classes:

class Point {
  constructor(x, y) {
    this.x = x; // Hidden Class C0 → C1
    this.y = y; // Hidden Class C1 → C2
  }
}

const p1 = new Point(1, 2);
const p2 = new Point(3, 4);
// p1 and p2 have same Hidden Class C2

Hidden Class stores:

  • List of properties and their offsets
  • Value types
  • Transitions (changes when adding properties)

Performance Killers

// Bad: different Hidden Classes
const p1 = { x: 1, y: 2 };
const p2 = { y: 2, x: 1 }; // Different order!

// Bad: dynamic properties
const p3 = { x: 1, y: 2 };
p3.z = 3; // New Hidden Class!

// Good: same structure
const p4 = { x: 1, y: 2 };
const p5 = { x: 3, y: 4 }; // Same Hidden Class

Inline Caching (IC)

Inline Cache speeds up property access by caching their location:

function getX(point) {
  return point.x;
}

// First call
getX({ x: 1, y: 2 }); 
// V8: "x is at offset 0 for Hidden Class C2"

// Subsequent calls
getX({ x: 3, y: 4 });
// V8: "Same Hidden Class! Use cache - offset 0"

IC Types:

  1. Monomorphic (best case):
// Always one Hidden Class
getX({ x: 1, y: 2 });
getX({ x: 3, y: 4 });
  1. Polymorphic (2-4 different classes):
getX({ x: 1, y: 2 });
getX({ x: 1, y: 2, z: 3 }); // Different Hidden Class
  1. Megamorphic (5+ classes - slow!):
// Bad: too many different structures
for (let i = 0; i < 10; i++) {
  getX({ x: 1, [i]: i }); // New Hidden Class every time!
}

Memory Management: Heap Organization

Generational Garbage Collection

Idea: Most objects die young.

Scavenge GC (Young Generation):

// Create temporary objects
function process() {
  const temp = { data: new Array(1000) }; // Dies immediately
  return temp.data.length;
}
// temp destroyed by Scavenge GC (~1-2ms)

Mark-Sweep-Compact (Old Generation):

// Long-lived objects
const cache = new Map(); // Survives Scavenge → Old Gen
cache.set('key', largeData);
// Removed by Major GC (~50-100ms)

Performance Best Practices

1

Avoid deoptimization

Don't change function argument types. Use TypeScript for type control.

2

Keep object shape

Initialize all properties in constructor. Don't add properties dynamically.

3

Monomorphic > Polymorphic > Megamorphic

Functions should work with objects of same structure (Hidden Class).

4

Avoid delete

delete obj.prop creates new Hidden Class. Use obj.prop = undefined.

Optimization Examples

// Bad: different types
function add(a, b) {
  return a + b;
}
add(1, 2);        // Numbers
add("a", "b");    // Strings - DEOPT!

// Good: one type
function addNumbers(a, b) {
  return a + b;
}
function addStrings(a, b) {
  return a + b;
}

// Bad: dynamic properties
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}
const p = new Point(1, 2);
p.z = 3; // New Hidden Class!

// Good: all properties in constructor
class Point3D {
  constructor(x, y, z = 0) {
    this.x = x;
    this.y = y;
    this.z = z; // Always same structure
  }
}

Analysis Tools

Chrome DevTools

// Check function optimization
%OptimizeFunctionOnNextCall(myFunction); // --allow-natives-syntax
myFunction();
%GetOptimizationStatus(myFunction);

Node.js

# Run with V8 flags
node --trace-opt --trace-deopt app.js

# Output:
# [optimizing: myFunction / 0x...]
# [bailout: myFunction - type mismatch]

Summary:

V8 uses multi-tier compilation (Ignition → Sparkplug → TurboFan) for balance between startup speed and performance. Understanding Hidden Classes, Inline Caching, and deoptimization conditions helps write code that V8 can efficiently optimize.

By continuing to use the platform, you accept the terms of the Privacy Policy and the use of cookies.