1 of 24

Escape Analysis in Turbofan

Tobias Tebbi

V8 Team, Google

Chrome

2 of 24

Chrome

Node.js

Ignition:�Interpreter

Turbofan:�Optimizing Compiler

Chrome

3 of 24

Escape Analysis: �Removing Temporary Objects

function diagonal(a) {

return abs({x:a, y:a});

}

function abs(v) {

return Math.sqrt(v.x*v.x + v.y*v.y);

}

return Math.sqrt(a*a + a*a);

How do we remove this object allocation?

Chrome

4 of 24

Step 1: Inlining

function diagonal(a) {

let v = {x:a, y:a};

return abs(v);

}

function abs(v) {

return Math.sqrt(v.x*v.x + v.y*v.y);

}

function diagonal(a) {

let v = {x:a, y:a};

return Math.sqrt(v.x*v.x + v.y*v.y);

}

Chrome

5 of 24

Step 2: Replacing Field Accesses

function diagonal(a) {

let v = {x:a, y:a};

return Math.sqrt(v.x*v.x + v.y*v.y);

}

function diagonal(a) {

let v = {x:a, y:a};

return Math.sqrt(a*a + a*a);

}

Chrome

6 of 24

Step 3: Removing unused allocation

function diagonal(a) {

let v = {x:a, y:a};

return Math.sqrt(a*a + a*a);

}

function diagonal(a) {

return Math.sqrt(a*a + a*a);

}

Chrome

7 of 24

It’s not always that easy...

Chrome

8 of 24

Writing to Fields

function write_field(x) {

let o = {a: 5};

while (x > 0) o.a += x--;

return o.a;

}

function write_field(x) {

let o_a = 5;

while (x > 0) o_a += x--;

return o_a;

}

Replace object fields with a local variable.

Chrome

9 of 24

Nested Objects

function nested_objects(b) {

let o = {a: {x: 5}};

o.a = {x: 7}

return o.a.x;

}

function nested_objects(b) {

return 7;

}

Chrome

10 of 24

Sometimes, we give up...

Chrome

11 of 24

Escaping Objects

function escaping_object(foo) {

let o = {};

foo(o);

}

When we can’t inline foo, then we cannot dematerialize o because foo could do anything with it.

Chrome

12 of 24

Index Access

let l = [1,2,3];

let sum = 0;

for(let i = 0; i < 3; ++i) sum += l[i];

Index access requires linear memory.

Thus we have to materialize l.

Chrome

13 of 24

Dynamic Object Identity

function object_identity(b) {

let o1 = {x: 5};

let o2 = {x: 7};

(b?o1:o2).x = 1;

return o1.x;

}

This computation uses the object identity of o1 and o2.

It’s impossible to map this to local variables: Local variables don’t have identity.

In principle, this could be solved with stack-allocation.

Chrome

14 of 24

The Magic of Deoptimization

function harmless(copy) {}

function foo(x) {

let copy = {};

copy.a = x + 1;

harmless(copy);

}

function foo(x) {

x + 1;

}

function evil(copy) {

global = copy;

}

foo({valueOf: () => harmless=evil});

?

While executing foo, the temporary object might suddenly escape.

Monkey patching can destroy any optimization,

while the optimized code is running.

Chrome

15 of 24

The Magic of Deoptimization

function harmless(copy) {}

function foo(x) {

let copy = {};

copy.a = x + 1;

harmless(copy);

}

function foo(x) {

if (typeof x != 'number')

%Deoptimize();

}

create object copy

When deoptimizing, we have to re-create dematerialized objects.

Escape Analysis needs to store the state of dematierialized objects at each deoptimization point.

Chrome

16 of 24

Escape Analysis in Turbofan

let a = {x: null};

let b = {y: 5};

a.x = b;

if (i > 10) {

a.x.y = i;

}

foo();

return a.y.x;

Chrome

17 of 24

Escape Analysis in Turbofan

let a = {x: null};

let b = {y: 5};

a.x = b;

if (i > 10) {

a.x.y = i;

}

foo();

return a.y.x;

Virtual Object 1

+0: var_a_shape

+8: …

+16: …

+24: var_a_x

Current Variable Value

var_a_shape

shape of {x:?}

var_a_x

null

Chrome

18 of 24

Escape Analysis in Turbofan

let a = {x: null};

let b = {y: 5};

a.x = b;

if (i > 10) {

a.x.y = i;

}

foo();

return a.y.x;

Virtual Object 1

+0: var_a_shape

+8: …

+16: …

+24: var_a_x

Virtual Object 2

+0: var_b_shape

+8: …

+16: …

+24: var_b_y

Current Variable Value

var_a_shape

shape of {x:?}

var_a_x

null

var_b_shape

shape of {y:?}

var_b_y

5

Chrome

19 of 24

Escape Analysis in Turbofan

let a = {x: null};

let b = {y: 5};

a.x = b;

if (i > 10) {

a.x.y = i;

}

foo();

return a.y.x;

Virtual Object 1

+0: var_a_shape

+8: …

+16: …

+24: var_a_x

Virtual Object 2

+0: var_b_shape

+8: …

+16: …

+24: var_b_y

Current Variable Value

var_a_shape

shape of {x:?}

var_a_x

b

var_b_shape

shape of {y:?}

var_b_y

5

Chrome

20 of 24

Escape Analysis in Turbofan

let a = {x: null};

let b = {y: 5};

a.x = b;

if (i > 10) {

a.x.y = i;

}

foo();

return a.y.x;

Virtual Object 1

+0: var_a_shape

+8: …

+16: …

+24: var_a_x

Virtual Object 2

+0: var_b_shape

+8: …

+16: …

+24: var_b_y

Current Variable Value

var_a_shape

shape of {x:?}

var_a_x

b

var_b_shape

shape of {y:?}

var_b_y

i

Chrome

21 of 24

Escape Analysis in Turbofan

let a = {x: null};

let b = {y: 5};

a.x = b;

if (i > 10) {

a.x.y = i;

}

foo();

return a.y.x;

Virtual Object 1

+0: var_a_shape

+8: …

+16: …

+24: var_a_x

Virtual Object 2

+0: var_b_shape

+8: …

+16: …

+24: var_b_y

Current Variable Value

var_a_shape

shape of {x:?}

var_a_x

b

var_b_shape

shape of {y:?}

var_b_y

Φ(5,i)

Chrome

22 of 24

Escape Analysis in Turbofan

let a = {x: null};

let b = {y: 5};

a.x = b;

if (i > 10) {

a.x.y = i;

}

foo();

return a.y.x;

Virtual Object 1

+0: var_a_shape

+8: …

+16: …

+24: var_a_x

Virtual Object 2

+0: var_b_shape

+8: …

+16: …

+24: var_b_y

Current Variable Value

var_a_shape

shape of {x:?}

var_a_x

b

var_b_shape

shape of {y:?}

var_b_y

Φ(5,i)

+0: shape of {x:?}

+8: …

+16: …

+24:

+0: shape of {y:?}

+8: …

+16: …

+24: Φ(5,i)

Remember Deoptimization Data

Chrome

23 of 24

Escape Analysis in Turbofan

let a = {x: null};

let b = {y: 5};

a.x = b;

if (i > 10) {

a.x.y = i;

}

foo();

return a.y.x; Φ(5,i)

Virtual Object 1

+0: var_a_shape

+8: …

+16: …

+24: var_a_x

Virtual Object 2

+0: var_b_shape

+8: …

+16: …

+24: var_b_y

Current Variable Value

var_a_shape

shape of {x:?}

var_a_x

b

var_b_shape

shape of {y:?}

var_b_y

Φ(5,i)

Chrome

24 of 24

Conclusion

  • Escape analysis avoids allocating temporary object.
  • This allows for simpler code without performance penalty.
  • V8 can dematerialize objects despite deoptimization.
  • Limits of escape analysis are: escaping uses, index access, and using the object identity dynamically.

Chrome