1 of 71

What’s inside of TurboFan?

Benedikt Meurer

@bmeurer

2 of 71

Overview

  • How does V8 work?
  • What is TurboFan?
  • The optimizing compiler in detail
  • Performance result

3 of 71

IGNITION

4 of 71

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 71

launched in

and !

6 of 71

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 71

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 71

Inlining

9 of 71

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 71

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 71

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 71

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 71

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 71

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 71

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 71

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 71

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 71

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 71

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 71

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 71

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 71

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 71

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 71

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 71

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 71

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 71

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 71

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 71

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 71

Inlining

  • Crucial to reduce small function overhead
    • Not just in JavaScript!
  • Makes other optimizations more effective
    • Constant folding
    • Strength reduction
    • Redundancy elimination
    • Escape Analysis and Scalar Replacement of Aggregates

31 of 71

Builtin Inlining

32 of 71

Higher-order Array Builtins

function allPositive(a) {� return a.every(x => x >= 0);�}

allPositive([-1, 0, 1]);

// => false

allPositive([1, 2, 3, 4]);

// => true

33 of 71

Higher-order Array Builtins

function allPositive(a) {� return a.every(x => x >= 0);�}

function allPositiveManual(a) {� var l = a.length;� for (var i = 0; i < l; ++i) {� if (a[i] < 0) {

return false;� }� }� return true;�}

vs

34 of 71

Higher-order Array Builtins

function allPositive(a) {� return a.every(x => x >= 0);�}

35 of 71

Higher-order Array Builtins

function allPositive(a) {� return a.every(x => x >= 0);�}

function allPositive_every(a) {� var c = x => x >= 0;� var l = a.length;� if (typeof c !== "function") {� throw new TypeError();� }� for (var i = 0; i < l; ++i) {� if (i in a) {� if (!c(a[i])) {

return false;� }� }� }� return true;�}

36 of 71

Higher-order Array Builtins

function allPositive_every(a) {� var c = x => x >= 0;� var l = a.length;� if (typeof c !== "function") {� throw new TypeError();� }� for (var i = 0; i < l; ++i) {� if (i in a) {� if (!c(a[i])) {

return false;� }� }� }� return true;�}

37 of 71

Higher-order Array Builtins

function allPositive_every(a) {� var c = x => x >= 0;� var l = a.length;� if (typeof c !== "function") {� throw new TypeError();� }� for (var i = 0; i < l; ++i) {� if (i in a) {� if (!c(a[i])) {

return false;� }� }� }� return true;�}

  • Callable check is redundant

38 of 71

Higher-order Array Builtins

function allPositive_every(a) {� var c = x => x >= 0;� var l = a.length;� if (typeof c !== "function") {� throw new TypeError();� }� for (var i = 0; i < l; ++i) {� if (i in a) {� if (!c(a[i])) {

return false;� }� }� }� return true;�}

  • Callable check is redundant
  • Assuming that a is non-holey (learned from the a.every property access) and not mutated (speculated on), the i in a check is redundant

39 of 71

Higher-order Array Builtins

function allPositive_every(a) {� var c = x => x >= 0; var l = a.length;� if (typeof c !== "function") {� throw new TypeError();� }� for (var i = 0; i < l; ++i) {� if (i in a) {� if (!(a[i] >= 0)) {

return false;� }� }� }� return true;�}

  • Callable check is redundant
  • Assuming that a is non-holey (learned from the a.every property access) and not mutated (speculated on), the i in a check is redundant
  • Regular inlining can inline c at it’s callsite

40 of 71

Higher-order Array Builtins

function allPositive_every(a) {� var c = x => x >= 0; var l = a.length;� if (typeof c !== "function") {� throw new TypeError();� }� for (var i = 0; i < l; ++i) {� if (i in a) {� if (!(a[i] >= 0)) {

return false;� }� }� }� return true;�}

  • Callable check is redundant
  • Assuming that a is non-holey (learned from the a.every property access) and not mutated (speculated on), the i in a check is redundant
  • Regular inlining can inline c at it’s callsite
  • Escape analysis and scalar replacement eliminate the closure allocation

41 of 71

Higher-order Array Builtins

function allPositive(a) {� return a.every(x => x >= 0);�}

function allPositive_every(a) {� var l = a.length;� for (var i = 0; i < l; ++i) {� if (!(a[i] >= 0)) {

return false;� }� }� return true;�}

42 of 71

Higher-order Array Builtins - Performance

43 of 71

Higher-order Array Builtins - Performance

Full support planned for Chrome 66 / Node 10

  • Array.prototype.map
  • Array.prototype.filter
  • Array.prototype.every
  • Array.prototype.some
  • Array.prototype.reduce
  • Array.prototype.reduceRight
  • Array.prototype.forEach
  • Array.prototype.find
  • Array.prototype.findIndex

44 of 71

Higher-order Array Builtins - Performance

45 of 71

Not just Array builtins...

46 of 71

Function builtins

function dispatch(f, ...args) {� return f.apply(this, args);�}

args are forwarded to f

47 of 71

Function builtins

function dispatch(f, ...args) {� return f.apply(this, args);�}

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+0xe60]�jna StackCheck�movq rbx,[rbp+0x10]�testb rbx,0x1�jz Deoptimize�movq rax,<JSFunction hidden class>�cmpq [rbx-0x1],rax�jnz Deoptimize�movq rdx,[rbp+0x18]�push rdx�xorl rax,rax�movl rcx,0x1�movq rdi,rbx�movq rsi,[rbp-0x8]�call CallForwardVarargs�movq rsp,rbp�pop rbp�ret 0x10

48 of 71

Function builtins

function dispatch(f, ...args) {� return f.apply(this, args);�}

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+0xe60]�jna StackCheck�movq rbx,[rbp+0x10]�testb rbx,0x1�jz Deoptimize�movq rax,<JSFunction hidden class>�cmpq [rbx-0x1],rax�jnz Deoptimize�movq rdx,[rbp+0x18]�push rdx�xorl rax,rax�movl rcx,0x1�movq rdi,rbx�movq rsi,[rbp-0x8]�call CallForwardVarargs�movq rsp,rbp�pop rbp�ret 0x10

Prologue

49 of 71

Function builtins

function dispatch(f, ...args) {� return f.apply(this, args);�}

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+0xe60]�jna StackCheck�movq rbx,[rbp+0x10]�testb rbx,0x1�jz Deoptimize�movq rax,<JSFunction hidden class>�cmpq [rbx-0x1],rax�jnz Deoptimize�movq rdx,[rbp+0x18]�push rdx�xorl rax,rax�movl rcx,0x1�movq rdi,rbx�movq rsi,[rbp-0x8]�call CallForwardVarargs�movq rsp,rbp�pop rbp�ret 0x10

Prologue

Check f is not a small integer

50 of 71

Function builtins

function dispatch(f, ...args) {� return f.apply(this, args);�}

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+0xe60]�jna StackCheck�movq rbx,[rbp+0x10]�testb rbx,0x1�jz Deoptimize�movq rax,<JSFunction hidden class>�cmpq [rbx-0x1],rax�jnz Deoptimize�movq rdx,[rbp+0x18]�push rdx�xorl rax,rax�movl rcx,0x1�movq rdi,rbx�movq rsi,[rbp-0x8]�call CallForwardVarargs�movq rsp,rbp�pop rbp�ret 0x10

Prologue

Check f is not a small integer

Check f is a JSFunction

51 of 71

Function builtins

function dispatch(f, ...args) {� return f.apply(this, args);�}

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+0xe60]�jna StackCheck�movq rbx,[rbp+0x10]�testb rbx,0x1�jz Deoptimize�movq rax,<JSFunction hidden class>�cmpq [rbx-0x1],rax�jnz Deoptimize�movq rdx,[rbp+0x18]�push rdx�xorl rax,rax�movl rcx,0x1�movq rdi,rbx�movq rsi,[rbp-0x8]�call CallForwardVarargs�movq rsp,rbp�pop rbp�ret 0x10

Prologue

Check f is not a small integer

Check f is a JSFunction

Call f with incoming parameters (skipping the first)

52 of 71

Function builtins

function dispatch(f, ...args) {� return f.apply(this, args);�}

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+0xe60]�jna StackCheck�movq rbx,[rbp+0x10]�testb rbx,0x1�jz Deoptimize�movq rax,<JSFunction hidden class>�cmpq [rbx-0x1],rax�jnz Deoptimize�movq rdx,[rbp+0x18]�push rdx�xorl rax,rax�movl rcx,0x1�movq rdi,rbx�movq rsi,[rbp-0x8]�call CallForwardVarargs�movq rsp,rbp�pop rbp�ret 0x10

Prologue

Check f is not a small integer

Check f is a JSFunction

Call f with incoming parameters (skipping the first)

Epilogue

53 of 71

Function builtins

function dispatch(f, ...args) {� return f.apply(this, args);�}

function dispatchManual() {� var l = arguments.length;� var f = arguments[0];� switch (l) {� case 1: return f.call(this);� case 2: return f.call(this,

arguments[1]);� default: {� var a = new Array(l - 1);� for (var i = 1; i < l; ++i)

a[i - 1] = arguments[i];� return f.apply(this, a);� }� }�}

vs

54 of 71

Function builtins

55 of 71

Builtin Inlining

  • Makes idiomatic JavaScript performant
    • Crucial for functional-style programming
  • Again makes other optimizations more effective
    • Inlining
    • Strength reduction
    • Redundancy elimination
    • Escape Analysis and Scalar Replacement

56 of 71

Predictable

Performance

57 of 71

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

58 of 71

59 of 71

\o/

60 of 71

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()

61 of 71

It’s not just�optimized code...

62 of 71

Octane/Crypto test

63 of 71

TypeScript 2.1.6 compiling itself

Octane/Crypto test

64 of 71

Loading www.nytimes.com

Octane/Crypto test

65 of 71

Performance Results

66 of 71

60% faster web developer tools

67 of 71

1.7× faster on ARES-6

68 of 71

69 of 71

What’s coming in 2018?

  • Better asynchronous performance
  • BigInt, class features, async iteration
  • Reducing memory footprint

70 of 71

Follow us!

@v8js

71 of 71

Benedikt Meurer�@bmeurer

Thanks!