1 of 59

A Tale of TurboFan�Four years that changed V8 forever

Benedikt Meurer

@bmeurer

2 of 59

Overview

  1. How does V8 work?
  2. What is TurboFan?
  3. The optimizing compiler in detail
  4. Performance result

3 of 59

IGNITION

4 of 59

What is TurboFan?

  • Modern code generation architecture for growing number of platforms (8+)
  • Replacement for the aging Crankshaft compiler
  • Optimizing compiler with full ES2015+ language support
  • Predictable and consistent performance profile

5 of 59

launched in

and !

6 of 59

Unified code generation architecture

WASM

Compiler

TurboFan

Optimizing

Compiler

Built-in Operations

TurboFan code

generation

architecture

Instruction Selector

Code Generator

Register Allocator

Intel, ARM, PowerPC,

MIPS

Control flow graph

Scheduler

“Sea of nodes” graph

C++ DSL

CodeStubAssembler

Ignition Interpreter

Bytecodes

7 of 59

TurboFan Optimizing Compiler

Bytecode

Bytecode

GraphBuilder

“Sea of nodes” graph

Specialization

Inlining

Bytecode

Profiling Feedback

Frontend

Typed

Optimization

Redundancy Elimination

Escape Analysis

Representation Selection

Optimization

Machine-level Optimization

Scheduling

Control flow graph

Code Generation

Intel, ARM, PowerPC,

MIPS

Backend

8 of 59

Inlining

9 of 59

Inlining

function add(x, y) {

return x + y;�}

function three() {� return add(1, 2);�}

function three_add_inlined() {� var x = 1;

var y = 2;� var add_return_value = x + y;� return add_return_value;�}

function three_add_const_folded() {� return 3;�}

Constant-

folding

10 of 59

Inlining

leaq rcx,[rip+0x0]�movq rcx,[rcx-0x37]�testb [rcx+0xf],0x1�jnz CompileLazyDeoptimizedCode�push rbp�movq rbp,rsp�push rsi�push rdi�cmpq rsp,[r13+0xdb0]�jna StackCheck�movq rax,[rbp+0x18]�test al,0x1�jnz Deoptimize�movq rbx,[rbp+0x10]�testb rbx,0x1�jnz Deoptimize�movq rdx,rbx�shrq rdx, 32�movq rcx,rax�shrq rcx, 32�addl rdx,rcx�jo Deoptimize�shlq rdx, 32�movq rax,rdx�movq rsp,rbp�pop rbp�ret 0x18

function add(x, y) {

return x + y;�}

function three() {� return add(1, 2);�}

11 of 59

Inlining

leaq rcx,[rip+0x0]�movq rcx,[rcx-0x37]�testb [rcx+0xf],0x1�jnz CompileLazyDeoptimizedCode�push rbp�movq rbp,rsp�push rsi�push rdi�cmpq rsp,[r13+0xdb0]�jna StackCheck�movq rax,[rbp+0x18]�test al,0x1�jnz Deoptimize�movq rbx,[rbp+0x10]�testb rbx,0x1�jnz Deoptimize�movq rdx,rbx�shrq rdx, 32�movq rcx,rax�shrq rcx, 32�addl rdx,rcx�jo Deoptimize�shlq rdx, 32�movq rax,rdx�movq rsp,rbp�pop rbp�ret 0x18

function add(x, y) {

return x + y;�}

function three() {� return add(1, 2);�}

Prologue

12 of 59

Inlining

leaq rcx,[rip+0x0]�movq rcx,[rcx-0x37]�testb [rcx+0xf],0x1�jnz CompileLazyDeoptimizedCode�push rbp�movq rbp,rsp�push rsi�push rdi�cmpq rsp,[r13+0xdb0]�jna StackCheck�movq rax,[rbp+0x18]�test al,0x1�jnz Deoptimize�movq rbx,[rbp+0x10]�testb rbx,0x1�jnz Deoptimize�movq rdx,rbx�shrq rdx, 32�movq rcx,rax�shrq rcx, 32�addl rdx,rcx�jo Deoptimize�shlq rdx, 32�movq rax,rdx�movq rsp,rbp�pop rbp�ret 0x18

function add(x, y) {

return x + y;�}

function three() {� return add(1, 2);�}

Check x is a small integer

Prologue

13 of 59

Inlining

leaq rcx,[rip+0x0]�movq rcx,[rcx-0x37]�testb [rcx+0xf],0x1�jnz CompileLazyDeoptimizedCode�push rbp�movq rbp,rsp�push rsi�push rdi�cmpq rsp,[r13+0xdb0]�jna StackCheck�movq rax,[rbp+0x18]�test al,0x1�jnz Deoptimize�movq rbx,[rbp+0x10]�testb rbx,0x1�jnz Deoptimize�movq rdx,rbx�shrq rdx, 32�movq rcx,rax�shrq rcx, 32�addl rdx,rcx�jo Deoptimize�shlq rdx, 32�movq rax,rdx�movq rsp,rbp�pop rbp�ret 0x18

function add(x, y) {

return x + y;�}

function three() {� return add(1, 2);�}

Check x is a small integer

Check y is a small integer

Prologue

14 of 59

Inlining

leaq rcx,[rip+0x0]�movq rcx,[rcx-0x37]�testb [rcx+0xf],0x1�jnz CompileLazyDeoptimizedCode�push rbp�movq rbp,rsp�push rsi�push rdi�cmpq rsp,[r13+0xdb0]�jna StackCheck�movq rax,[rbp+0x18]�test al,0x1�jnz Deoptimize�movq rbx,[rbp+0x10]�testb rbx,0x1�jnz Deoptimize�movq rdx,rbx�shrq rdx, 32�movq rcx,rax�shrq rcx, 32�addl rdx,rcx�jo Deoptimize�shlq rdx, 32�movq rax,rdx�movq rsp,rbp�pop rbp�ret 0x18

function add(x, y) {

return x + y;�}

function three() {� return add(1, 2);�}

Check x is a small integer

Check y is a small integer

Convert y from tagged representation to word32

Prologue

15 of 59

Inlining

leaq rcx,[rip+0x0]�movq rcx,[rcx-0x37]�testb [rcx+0xf],0x1�jnz CompileLazyDeoptimizedCode�push rbp�movq rbp,rsp�push rsi�push rdi�cmpq rsp,[r13+0xdb0]�jna StackCheck�movq rax,[rbp+0x18]�test al,0x1�jnz Deoptimize�movq rbx,[rbp+0x10]�testb rbx,0x1�jnz Deoptimize�movq rdx,rbx�shrq rdx, 32�movq rcx,rax�shrq rcx, 32�addl rdx,rcx�jo Deoptimize�shlq rdx, 32�movq rax,rdx�movq rsp,rbp�pop rbp�ret 0x18

function add(x, y) {

return x + y;�}

function three() {� return add(1, 2);�}

Check x is a small integer

Check y is a small integer

Convert y from tagged representation to word32

Convert x from tagged representation to word32

Prologue

16 of 59

Inlining

leaq rcx,[rip+0x0]�movq rcx,[rcx-0x37]�testb [rcx+0xf],0x1�jnz CompileLazyDeoptimizedCode�push rbp�movq rbp,rsp�push rsi�push rdi�cmpq rsp,[r13+0xdb0]�jna StackCheck�movq rax,[rbp+0x18]�test al,0x1�jnz Deoptimize�movq rbx,[rbp+0x10]�testb rbx,0x1�jnz Deoptimize�movq rdx,rbx�shrq rdx, 32�movq rcx,rax�shrq rcx, 32�addl rdx,rcx�jo Deoptimize�shlq rdx, 32�movq rax,rdx�movq rsp,rbp�pop rbp�ret 0x18

function add(x, y) {

return x + y;�}

function three() {� return add(1, 2);�}

Check x is a small integer

Check y is a small integer

Convert y from tagged representation to word32

Convert x from tagged representation to word32

Add x and y (incl. overflow check)

Prologue

17 of 59

Inlining

leaq rcx,[rip+0x0]�movq rcx,[rcx-0x37]�testb [rcx+0xf],0x1�jnz CompileLazyDeoptimizedCode�push rbp�movq rbp,rsp�push rsi�push rdi�cmpq rsp,[r13+0xdb0]�jna StackCheck�movq rax,[rbp+0x18]�test al,0x1�jnz Deoptimize�movq rbx,[rbp+0x10]�testb rbx,0x1�jnz Deoptimize�movq rdx,rbx�shrq rdx, 32�movq rcx,rax�shrq rcx, 32�addl rdx,rcx�jo Deoptimize�shlq rdx, 32�movq rax,rdx�movq rsp,rbp�pop rbp�ret 0x18

function add(x, y) {

return x + y;�}

function three() {� return add(1, 2);�}

Check x is a small integer

Check y is a small integer

Convert y from tagged representation to word32

Convert x from tagged representation to word32

Add x and y (incl. overflow check)

Convert result to tagged representation

Prologue

18 of 59

Inlining

leaq rcx,[rip+0x0]�movq rcx,[rcx-0x37]�testb [rcx+0xf],0x1�jnz CompileLazyDeoptimizedCode�push rbp�movq rbp,rsp�push rsi�push rdi�cmpq rsp,[r13+0xdb0]�jna StackCheck�movq rax,[rbp+0x18]�test al,0x1�jnz Deoptimize�movq rbx,[rbp+0x10]�testb rbx,0x1�jnz Deoptimize�movq rdx,rbx�shrq rdx, 32�movq rcx,rax�shrq rcx, 32�addl rdx,rcx�jo Deoptimize�shlq rdx, 32�movq rax,rdx�movq rsp,rbp�pop rbp�ret 0x18

function add(x, y) {

return x + y;�}

function three() {� return add(1, 2);�}

Check x is a small integer

Check y is a small integer

Convert y from tagged representation to word32

Convert x from tagged representation to word32

Add x and y (incl. overflow check)

Convert result to tagged representation

Epilogue

Prologue

19 of 59

Inlining

leaq rcx,[rip+0x0]�movq rcx,[rcx-0x37]�testb [rcx+0xf],0x1�jnz CompileLazyDeoptimizedCode�push rbp�movq rbp,rsp�push rsi�push rdi�cmpq rsp,[r13+0xdb0]�jna StackCheck�movq rdi,<JSFunction add>�movq rsi,[rdi+0x1f]�movq rax,<JSGlobal Object>�push rax�movq rax,0x100000000�push rax�movq rax,0x200000000�push rax�movq rdx,[r13-0x60]�movl rax,0x2�movq rcx,[rdi+0x2f]�addq rcx,0x5f�call rcx�movq rsp,rbp�pop rbp�ret 0x8

function add(x, y) {

return x + y;�}

function three() {� return add(1, 2);�}

20 of 59

Inlining

leaq rcx,[rip+0x0]�movq rcx,[rcx-0x37]�testb [rcx+0xf],0x1�jnz CompileLazyDeoptimizedCode�push rbp�movq rbp,rsp�push rsi�push rdi�cmpq rsp,[r13+0xdb0]�jna StackCheck�movq rdi,<JSFunction add>�movq rsi,[rdi+0x1f]�movq rax,<JSGlobal Object>�push rax�movq rax,0x100000000�push rax�movq rax,0x200000000�push rax�movq rdx,[r13-0x60]�movl rax,0x2�movq rcx,[rdi+0x2f]�addq rcx,0x5f�call rcx�movq rsp,rbp�pop rbp�ret 0x8

function add(x, y) {

return x + y;�}

function three() {� return add(1, 2);�}

Prologue

21 of 59

Inlining

leaq rcx,[rip+0x0]�movq rcx,[rcx-0x37]�testb [rcx+0xf],0x1�jnz CompileLazyDeoptimizedCode�push rbp�movq rbp,rsp�push rsi�push rdi�cmpq rsp,[r13+0xdb0]�jna StackCheck�movq rdi,<JSFunction add>�movq rsi,[rdi+0x1f]�movq rax,<JSGlobal Object>�push rax�movq rax,0x100000000�push rax�movq rax,0x200000000�push rax�movq rdx,[r13-0x60]�movl rax,0x2�movq rcx,[rdi+0x2f]�addq rcx,0x5f�call rcx�movq rsp,rbp�pop rbp�ret 0x8

function add(x, y) {

return x + y;�}

function three() {� return add(1, 2);�}

Load call target add

Prologue

22 of 59

Inlining

leaq rcx,[rip+0x0]�movq rcx,[rcx-0x37]�testb [rcx+0xf],0x1�jnz CompileLazyDeoptimizedCode�push rbp�movq rbp,rsp�push rsi�push rdi�cmpq rsp,[r13+0xdb0]�jna StackCheck�movq rdi,<JSFunction add>�movq rsi,[rdi+0x1f]�movq rax,<JSGlobal Object>�push rax�movq rax,0x100000000�push rax�movq rax,0x200000000�push rax�movq rdx,[r13-0x60]�movl rax,0x2�movq rcx,[rdi+0x2f]�addq rcx,0x5f�call rcx�movq rsp,rbp�pop rbp�ret 0x8

function add(x, y) {

return x + y;�}

function three() {� return add(1, 2);�}

Load call target add

Load call parameters

Prologue

23 of 59

Inlining

leaq rcx,[rip+0x0]�movq rcx,[rcx-0x37]�testb [rcx+0xf],0x1�jnz CompileLazyDeoptimizedCode�push rbp�movq rbp,rsp�push rsi�push rdi�cmpq rsp,[r13+0xdb0]�jna StackCheck�movq rdi,<JSFunction add>�movq rsi,[rdi+0x1f]�movq rax,<JSGlobal Object>�push rax�movq rax,0x100000000�push rax�movq rax,0x200000000�push rax�movq rdx,[r13-0x60]�movl rax,0x2�movq rcx,[rdi+0x2f]�addq rcx,0x5f�call rcx�movq rsp,rbp�pop rbp�ret 0x8

function add(x, y) {

return x + y;�}

function three() {� return add(1, 2);�}

Load call target add

Load call parameters

Call sequence

Prologue

24 of 59

Inlining

leaq rcx,[rip+0x0]�movq rcx,[rcx-0x37]�testb [rcx+0xf],0x1�jnz CompileLazyDeoptimizedCode�push rbp�movq rbp,rsp�push rsi�push rdi�cmpq rsp,[r13+0xdb0]�jna StackCheck�movq rdi,<JSFunction add>�movq rsi,[rdi+0x1f]�movq rax,<JSGlobal Object>�push rax�movq rax,0x100000000�push rax�movq rax,0x200000000�push rax�movq rdx,[r13-0x60]�movl rax,0x2�movq rcx,[rdi+0x2f]�addq rcx,0x5f�call rcx�movq rsp,rbp�pop rbp�ret 0x8

function add(x, y) {

return x + y;�}

function three() {� return add(1, 2);�}

Load call target add

Load call parameters

Call sequence

Epilogue

Prologue

25 of 59

Inlining

function add(x, y) {

return x + y;�}

function three() {� return add(1, 2);�}

leaq rcx,[rip+0x0]�movq rcx,[rcx-0x37]�testb [rcx+0xf],0x1�jnz CompileLazyDeoptimizedCode�push rbp�movq rbp,rsp�push rsi�push rdi�cmpq rsp,[r13+0xdb0]�jna StackCheck�movq rax,0x300000000�movq rsp,rbp�pop rbp�ret 0x8

26 of 59

Inlining

function add(x, y) {

return x + y;�}

function three() {� return add(1, 2);�}

leaq rcx,[rip+0x0]�movq rcx,[rcx-0x37]�testb [rcx+0xf],0x1�jnz CompileLazyDeoptimizedCode�push rbp�movq rbp,rsp�push rsi�push rdi�cmpq rsp,[r13+0xdb0]�jna StackCheck�movq rax,0x300000000�movq rsp,rbp�pop rbp�ret 0x8

Prologue

27 of 59

Inlining

function add(x, y) {

return x + y;�}

function three() {� return add(1, 2);�}

leaq rcx,[rip+0x0]�movq rcx,[rcx-0x37]�testb [rcx+0xf],0x1�jnz CompileLazyDeoptimizedCode�push rbp�movq rbp,rsp�push rsi�push rdi�cmpq rsp,[r13+0xdb0]�jna StackCheck�movq rax,0x300000000�movq rsp,rbp�pop rbp�ret 0x8

Load tagged value 3

Prologue

28 of 59

Inlining

function add(x, y) {

return x + y;�}

function three() {� return add(1, 2);�}

leaq rcx,[rip+0x0]�movq rcx,[rcx-0x37]�testb [rcx+0xf],0x1�jnz CompileLazyDeoptimizedCode�push rbp�movq rbp,rsp�push rsi�push rdi�cmpq rsp,[r13+0xdb0]�jna StackCheck�movq rax,0x300000000�movq rsp,rbp�pop rbp�ret 0x8

Load tagged value 3

Epilogue

Prologue

29 of 59

Inlining

leaq rcx,[rip+0x0]�movq rcx,[rcx-0x37]�testb [rcx+0xf],0x1�jnz CompileLazyDeoptimizedCode�push rbp�movq rbp,rsp�push rsi�push rdi�cmpq rsp,[r13+0xdb0]�jna StackCheck�movq rax,0x300000000�movq rsp,rbp�pop rbp�ret 0x8

leaq rcx,[rip+0x0]�movq rcx,[rcx-0x37]�testb [rcx+0xf],0x1�jnz CompileLazyDeoptimizedCode�push rbp�movq rbp,rsp�push rsi�push rdi�cmpq rsp,[r13+0xdb0]�jna StackCheck�movq rdi,<JSFunction add>�movq rsi,[rdi+0x1f]�movq rax,<JSGlobal Object>�push rax�movq rax,0x100000000�push rax�movq rax,0x200000000�push rax�movq rdx,[r13-0x60]�movl rax,0x2�movq rcx,[rdi+0x2f]�addq rcx,0x5f�call rcx�movq rsp,rbp�pop rbp�ret 0x8

leaq rcx,[rip+0x0]�movq rcx,[rcx-0x37]�testb [rcx+0xf],0x1�jnz CompileLazyDeoptimizedCode�push rbp�movq rbp,rsp�push rsi�push rdi�cmpq rsp,[r13+0xdb0]�jna StackCheck�movq rax,[rbp+0x18]�test al,0x1�jnz Deoptimize�movq rbx,[rbp+0x10]�testb rbx,0x1�jnz Deoptimize�movq rdx,rbx�shrq rdx, 32�movq rcx,rax�shrq rcx, 32�addl rdx,rcx�jo Deoptimize�shlq rdx, 32�movq rax,rdx�movq rsp,rbp�pop rbp�ret 0x18

+

vs

30 of 59

Inlining

  • Crucial to reduce small function overhead
    • Not just in JavaScript!
  • Makes other optimizations more effective
    • Constant folding
    • Strength reduction
    • Redundancy elimination
    • Escape analysis / scalar replacement of aggregates

31 of 59

Escape Analysis and Scalar Replacement

32 of 59

Escape Analysis and Scalar Replacement

  • Avoid allocating local objects in the heap
  • Escape Analysis determines whether the lifetime of an object can be proven to be restricted only to the current function
    • Object is allocated inside that function
    • Object doesn’t escape the function
  • If an object is found to be accessed in ways that do not require the object to exist as a sequential memory structure, Scalar Replacement may allow its fields be stored in CPU registers instead of in memory

33 of 59

Escape Analysis and Scalar Replacement

class Point {� constructor(x, y) {

this.x = x;

this.y = y;� }�� distance(that) {� return Math.abs(this.x - that.x)� + Math.abs(this.y - that.y);� }�}��function manhattan(x1, y1, x2, y2) {� const a = new Point(x1, y1);� const b = new Point(x2, y2);� return a.distance(b);�}

function manhattan_inl(x1, y1, x2, y2) {� const a = {x:x1, y:y1};� const b = {x:x2, y:y2};� return Math.abs(a.x - b.x)� + Math.abs(a.y - b.y);�}

34 of 59

Escape Analysis and Scalar Replacement

function manhattan_inl(x1, y1, x2, y2) {� const a = {x:x1, y:y1};� const b = {x:x2, y:y2};� return Math.abs(a.x - b.x)� + Math.abs(a.y - b.y);�}

Over-simplified conditions for non escaping objects:

  1. Allocated inside the function (after inlining)
  2. All uses are property accesses
    • Not returned from the function
    • Not yielded
    • Not passed to non-inlined function
    • Not referenced from an escaping object

a is allocated and doesn’t escape

b is allocated and doesn’t escape

35 of 59

Escape Analysis and Scalar Replacement

function manhattan_inl(x1, y1, x2, y2) {� const a = {x:x1, y:y1};� const b = {x:x2, y:y2};� return Math.abs(a.x - b.x)� + Math.abs(a.y - b.y);�}

function manhattan_ea(x1, y1, x2, y2) {� var a_x = x1;� var a_y = y1;� var b_x = x2;� var b_y = y2;� return Math.abs(a_x - b_x)� + Math.abs(a_y - b_y);�}

Fields

x

y

a

a_x

a_y

b

b_x

b_y

Map fields to local variables

Benefits:

  • No need for allocation or costly property accesses anymore!
  • Happens under the hood!

36 of 59

Example: Rest Parameters

function count(...args) {� return args.length;�}

args doesn’t escape and only its length field is accessed

37 of 59

Example: Rest Parameters

function count(...args) {� return args.length;�}

leaq rcx,[rip+0x0]� movq rcx,[rcx-0x37]� testb [rcx+0xf],0x1� jnz CompileLazyDeoptimizedCode� push rbp� movq rbp,rsp� push rsi� push rdi� cmpq rsp,[r13+0xdb0]� jna StackCheck� movq rax,rbp� movq rbx,[rax]� cmpq [rbx-0x8],0x20� jz L0� movq rbx,rax�L0: cmpq rax,rbx� jz L1� movq rax,[rbx-0x18]� cmpq rax,0x0� jge L2L1: xorl rax, rax�L2: movq rsp,rbp� pop rbp� ret 0x8

38 of 59

Example: Rest Parameters

function count(...args) {� return args.length;�}

leaq rcx,[rip+0x0]� movq rcx,[rcx-0x37]� testb [rcx+0xf],0x1� jnz CompileLazyDeoptimizedCode� push rbp� movq rbp,rsp� push rsi� push rdi� cmpq rsp,[r13+0xdb0]� jna StackCheck� movq rax,rbp� movq rbx,[rax]� cmpq [rbx-0x8],0x20� jz L0� movq rbx,rax�L0: cmpq rax,rbx� jz L1� movq rax,[rbx-0x18]� cmpq rax,0x0� jge L2L1: xorl rax, rax�L2: movq rsp,rbp� pop rbp� ret 0x8

Prologue

39 of 59

Example: Rest Parameters

function count(...args) {� return args.length;�}

leaq rcx,[rip+0x0]� movq rcx,[rcx-0x37]� testb [rcx+0xf],0x1� jnz CompileLazyDeoptimizedCode� push rbp� movq rbp,rsp� push rsi� push rdi� cmpq rsp,[r13+0xdb0]� jna StackCheck� movq rax,rbp� movq rbx,[rax]� cmpq [rbx-0x8],0x20� jz L0� movq rbx,rax�L0: cmpq rax,rbx� jz L1� movq rax,[rbx-0x18]� cmpq rax,0x0� jge L2L1: xorl rax, rax�L2: movq rsp,rbp� pop rbp� ret 0x8

Prologue

Find the parameters

40 of 59

Example: Rest Parameters

function count(...args) {� return args.length;�}

leaq rcx,[rip+0x0]� movq rcx,[rcx-0x37]� testb [rcx+0xf],0x1� jnz CompileLazyDeoptimizedCode� push rbp� movq rbp,rsp� push rsi� push rdi� cmpq rsp,[r13+0xdb0]� jna StackCheck� movq rax,rbp� movq rbx,[rax]� cmpq [rbx-0x8],0x20� jz L0� movq rbx,raxL0: cmpq rax,rbx� jz L1� movq rax,[rbx-0x18]� cmpq rax,0x0� jge L2L1: xorl rax, raxL2: movq rsp,rbp� pop rbp� ret 0x8

Prologue

Find the parameters

Count the number of parameters

41 of 59

Example: Rest Parameters

function count(...args) {� return args.length;�}

leaq rcx,[rip+0x0]� movq rcx,[rcx-0x37]� testb [rcx+0xf],0x1� jnz CompileLazyDeoptimizedCode� push rbp� movq rbp,rsp� push rsi� push rdi� cmpq rsp,[r13+0xdb0]� jna StackCheck� movq rax,rbp� movq rbx,[rax]� cmpq [rbx-0x8],0x20� jz L0� movq rbx,rax�L0: cmpq rax,rbx� jz L1� movq rax,[rbx-0x18]� cmpq rax,0x0� jge L2L1: xorl rax, rax�L2: movq rsp,rbp� pop rbp� ret 0x8

Prologue

Find the parameters

Count the number of parameters

Epilogue

42 of 59

Example: Rest Parameters

function count(...args) {� return args.length;�}

leaq rcx,[rip+0x0]� movq rcx,[rcx-0x37]� testb [rcx+0xf],0x1� jnz CompileLazyDeoptimizedCode� push rbp� movq rbp,rsp� push rsi� push rdi� cmpq rsp,[r13+0xdb0]� jna StackCheck� movq rax,rbp� movq rbx,[rax]� cmpq [rbx-0x8],0x20� jz L0� movq rbx,rax�L0: cmpq rax,rbx� jz L1� movq rax,[rbx-0x18]� cmpq rax,0x0� jge L2L1: xorl rax, rax�L2: movq rsp,rbp� pop rbp� ret 0x8

Prologue

Find the parameters

Count the number of parameters

Epilogue

Array args is never materialized!

43 of 59

Escape Analysis and Scalar Replacement

  • Reduces pressure on the garbage collector (GC)
  • Important optimization for a number of ES2015+ features
    • Rest parameters
    • for..of loops
    • Destructuring
  • Makes other optimizations more effective
    • Constant folding
    • Strength reduction
    • Representation Selection
    • Instruction Selection

44 of 59

Predictable

Performance

45 of 59

Performance cliffs in Crankshaft

(function good() {� const start = Date.now();� for (var i = 0; i < 1e8; i++) {}� console.log(Date.now() - start);�})();

(function bad() {� const start = Date.now();� for (var i = 0; i < 1e8; i++) {}� console.log(Date.now() - start);� const whatever = 1;�})();

Runs ~80ms

Runs ~230ms

~3x slowdown�with Crankshaft

46 of 59

47 of 59

Optimization Killers

48 of 59

Optimization Killers

  • Generators and async functions
  • for-of and destructuring
  • try-catch and try-finally
  • Compound let or const assignment
  • Object literals that contain __proto__, or get or set declarations.
  • debugger or with statements
  • Literal calls to eval()

49 of 59

It’s not just�optimized code...

50 of 59

Octane/Crypto test

51 of 59

TypeScript 2.1.6 compiling itself

Octane/Crypto test

52 of 59

Loading www.nytimes.com

Octane/Crypto test

53 of 59

Performance Results

54 of 59

60% faster web developer tools

55 of 59

1.7× faster on ARES-6

56 of 59

57 of 59

Takeaways

  • Write modern, idiomatic JavaScript
  • Let us worry about making it fast

58 of 59

Follow us!

@v8js

59 of 59

Benedikt Meurer�@bmeurer

Thanks!��AMA on V8 & JS Performance�Tuesday 16:00–17:00, Room B