Lazy, Incremental JIT Compilation with Basic Block Versioning

104
Lazy, Incremental JIT Compilation with Basic Block Versioning Maxime Chevalier-Boisvert Dec 8th, 2014

Transcript of Lazy, Incremental JIT Compilation with Basic Block Versioning

Page 1: Lazy, Incremental JIT Compilation with Basic Block Versioning

Lazy, Incremental JIT Compilation with Basic Block Versioning

Maxime Chevalier-Boisvert

Dec 8th, 2014

Page 2: Lazy, Incremental JIT Compilation with Basic Block Versioning

2

Introduction

● PhD student at the UdeM, prof. Marc Feeley– Optimizing dynamic languages (speed)

– Eliminating dynamic type checks

● Higgs: experimental optimizing JIT for JS– Testbed for novel optimization techniques

● Type specialization without type analysis– Basic block versioning

– Lazy incremental compilation

– Typed object shapes

Page 3: Lazy, Incremental JIT Compilation with Basic Block Versioning

My M.Sc. Days

Page 4: Lazy, Incremental JIT Compilation with Basic Block Versioning
Page 5: Lazy, Incremental JIT Compilation with Basic Block Versioning
Page 6: Lazy, Incremental JIT Compilation with Basic Block Versioning
Page 7: Lazy, Incremental JIT Compilation with Basic Block Versioning

7

Page 8: Lazy, Incremental JIT Compilation with Basic Block Versioning

8

Page 9: Lazy, Incremental JIT Compilation with Basic Block Versioning

9

Page 10: Lazy, Incremental JIT Compilation with Basic Block Versioning

Functions to Versions

adptbeul

caprclos

crnidich

diffedit

fdtdfft

fiffmbrt

nb1dnb3d

nfrcnnet

playschr

sdkusvd

0

2

4

6

8

10

12

14

16

18

# functions# versions

Page 11: Lazy, Incremental JIT Compilation with Basic Block Versioning

McVM vs MATLAB & Fortran

adptbeul

caprclos

crnidich

diffedit

fdtdfft

fiffmbrt

nb1dnb3d

nfrcnnet

playschr

sdkusvd

0.00

0.01

0.10

1.00

10.00Vs. MATLABVs. Fortran

Re

l. sp

ee

du

p (

log

. sc

ale

)

Page 12: Lazy, Incremental JIT Compilation with Basic Block Versioning

JavaScript

Page 13: Lazy, Incremental JIT Compilation with Basic Block Versioning
Page 14: Lazy, Incremental JIT Compilation with Basic Block Versioning

14

// JS less-than comparison operator (x < y)function $rt_lt(x, y){ // If x is integer if ($ir_is_int32(x)) { if ($ir_is_int32(y)) return $ir_lt_i32(x, y);

if ($ir_is_float(y)) return $ir_lt_f64($ir_i32_to_f64(x), y); }

// If x is float if ($ir_is_float(x)) { if ($ir_is_int32(y)) return $ir_lt_f64(x, $ir_i32_to_f64(y));

if ($ir_is_float(y)) return $ir_lt_f64(x, y); }

…}

Page 15: Lazy, Incremental JIT Compilation with Basic Block Versioning

15

// JS less-than comparison operator (x < y)function $rt_lt(x, y){ // If x is integer if ($ir_is_int32(x)) { if ($ir_is_int32(y)) return $ir_lt_i32(x, y);

if ($ir_is_float(y)) return $ir_lt_f64($ir_i32_to_f64(x), y); }

// If x is float if ($ir_is_float(x)) { if ($ir_is_int32(y)) return $ir_lt_f64(x, $ir_i32_to_f64(y));

if ($ir_is_float(y)) return $ir_lt_f64(x, y); }

…}

Page 16: Lazy, Incremental JIT Compilation with Basic Block Versioning

16

// JS less-than comparison operator (x < y)function $rt_lt(x, y){ // If x is integer if ($ir_is_int32(x)) { if ($ir_is_int32(y)) return $ir_lt_i32(x, y);

if ($ir_is_float(y)) return $ir_lt_f64($ir_i32_to_f64(x), y); }

// If x is float if ($ir_is_float(x)) { if ($ir_is_int32(y)) return $ir_lt_f64(x, $ir_i32_to_f64(y));

if ($ir_is_float(y)) return $ir_lt_f64(x, y); }

…}

Page 17: Lazy, Incremental JIT Compilation with Basic Block Versioning

17

Page 18: Lazy, Incremental JIT Compilation with Basic Block Versioning

Basic Block Versioning

Page 19: Lazy, Incremental JIT Compilation with Basic Block Versioning

19

Basic Block Versioning

● Similar to tracing, procedure cloning● As you compile code, accumulate facts

– Type tests add type information

● Specialize based on accumulated facts– Low-level type information (type tags)

– Object type/shape, global variable types

● May compile multiple versions of code

Page 20: Lazy, Incremental JIT Compilation with Basic Block Versioning

20

if (is_int32(n)) // A{ // B ... }else{ // C ...}

// D...

Page 21: Lazy, Incremental JIT Compilation with Basic Block Versioning

21

is_int32(n)?

CB

D

falsetrue

Page 22: Lazy, Incremental JIT Compilation with Basic Block Versioning

22

is_int32(n)?

CB

D

falsetrue

n is int32

Page 23: Lazy, Incremental JIT Compilation with Basic Block Versioning

23

is_int32(n)?

CB

D

falsetrue

n is not int32n is int32

Page 24: Lazy, Incremental JIT Compilation with Basic Block Versioning

24

is_int32(n)?

CB

D

falsetrue

n is not int32n is int32

n is ???

Page 25: Lazy, Incremental JIT Compilation with Basic Block Versioning

25

is_int32(n)?

CB

D'

falsetrue

n is not int32n is int32

D''

n is int32 n is not int32

Page 26: Lazy, Incremental JIT Compilation with Basic Block Versioning

26

var v = 4294967296;for (var i = 0; i < 600000; i++) v = v & i;

// From the SunSpider bitwise-and benchmark

Page 27: Lazy, Incremental JIT Compilation with Basic Block Versioning

27

var v = 4294967296;for (var i = 0; less_than(i,600000); i = add(i,1)) v = bitwise_and(v,i);

Page 28: Lazy, Incremental JIT Compilation with Basic Block Versioning

28

var v = 4294967296;for (var i = 0; less_than(i,600000); i = add(i,1)) v = bitwise_and(v,i);

function bitwise_and(x,y) {if (is_int32(x) && is_int32(y))

return bitwise_and_int32(x,y); // Fast path

return bitwise_and_int32(toInt32(x), toInt32(y));}

Page 29: Lazy, Incremental JIT Compilation with Basic Block Versioning

29

var v = 4294967296;for (var i = 0; less_than(i,600000); i = add(i,1)) v = bitwise_and(v,i);

function bitwise_and(x,y) {if (is_int32(x) && is_int32(y))

return bitwise_and_int32(x,y); // Fast path

return bitwise_and_int32(toInt32(x), toInt32(y));}

function add(x,y) {if (is_int32(x) && is_int32(y)){

var r = add_int32(x,y); // Fast pathif (cpu_overflow_flag)

r = add_double(toDouble(x), toDouble(y));return r;

}

return add_general(x,y);}

Page 30: Lazy, Incremental JIT Compilation with Basic Block Versioning

30

var v = 4294967296;for (var i = 0; less_than(i,600000); i = add(i,1)) v = bitwise_and(v,i);

function bitwise_and(x,y) {if (is_int32(x) && is_int32(y))

return bitwise_and_int32(x,y); // Fast path

return bitwise_and_int32(toInt32(x), toInt32(y));}

function add(x,y) {if (is_int32(x) && is_int32(y)){

var r = add_int32(x,y); // Fast pathif (cpu_overflow_flag)

r = add_double(toDouble(x), toDouble(y));return r;

}

return add_general(x,y);}

Page 31: Lazy, Incremental JIT Compilation with Basic Block Versioning

31

var v = 4294967296;var i = 0;for (;;) { // if (i >= 600000) break;

if (is_int32(i) && is_int32(600000))if (greater_eq_int32(i, 600000)) break;

elseif (greater_eq_general(i, 600000) break;

// v = v & iif (is_int32(v) && is_int32(i))

v = bitwise_and_int32(v,i);else

v = bitwise_and_int32(toInt32(v), toInt32(i));

// i = i + 1if (is_int32(i) && is_int32(1)) {

i = add_int32(i,1);if (cpu_overflow_flag)

i = add_double(toDouble(i), toDouble(1));}else

i = add_general(i,1);}

Page 32: Lazy, Incremental JIT Compilation with Basic Block Versioning

32

var v = 4294967296;var i = 0;for (;;) { // if (i >= 600000) break;

if (is_int32(i) && is_int32(600000))if (greater_eq_int32(i, 600000)) break;

elseif (greater_eq_general(i, 600000) break;

// v = v & iif (is_int32(v) && is_int32(i))

v = bitwise_and_int32(v,i);else

v = bitwise_and_int32(toInt32(v), toInt32(i));

// i = i + 1if (is_int32(i) && is_int32(1)) {

i = add_int32(i,1);if (cpu_overflow_flag)

i = add_double(toDouble(i), toDouble(1));}else

i = add_general(i,1);}

Page 33: Lazy, Incremental JIT Compilation with Basic Block Versioning

33

var v = 4294967296;var i = 0;for (;;) { // if (i >= 600000) break;

if (is_int32(i))if (greater_eq_int32(i, 600000)) break;

elseif (greater_eq_general(i, 600000) break;

// v = v & iif (is_int32(v) && is_int32(i))

v = bitwise_and_int32(v,i);else

v = bitwise_and_int32(toInt32(v), toInt32(i));

// i = i + 1if (is_int32(i)) {

i = add_int32(i,1);if (cpu_overflow_flag)

i = add_double(toDouble(i), toDouble(1));}else

i = add_general(i,1);}

Page 34: Lazy, Incremental JIT Compilation with Basic Block Versioning

34

var v = 4294967296;var i = 0; // when we enter the loop, i is int32for (;;) { // if (i >= 600000) break;

if (is_int32(i))if (greater_eq_int32(i, 600000)) break;

elseif (greater_eq_general(i, 600000) break;

// v = v & iif (is_int32(v) && is_int32(i))

v = bitwise_and_int32(v,i);else

v = bitwise_and_int32(toInt32(v), toInt32(i));

// i = i + 1if (is_int32(i)) {

i = add_int32(i,1);if (cpu_overflow_flag)

i = add_double(toDouble(i), toDouble(1));}else

i = add_general(i,1);}

Page 35: Lazy, Incremental JIT Compilation with Basic Block Versioning

35

var v = 4294967296;var i = 0;for (;;) { // assume i is int32 when entering loop // if (i >= 600000) break;

if (is_int32(i))if (greater_eq_int32(i, 600000)) break;

elseif (greater_eq_general(i, 600000) break;

// v = v & iif (is_int32(v) && is_int32(i))

v = bitwise_and_int32(v,i);else

v = bitwise_and_int32(toInt32(v), toInt32(i));

// i = i + 1if (is_int32(i)) {

i = add_int32(i,1);if (cpu_overflow_flag)

i = add_double(toDouble(i), toDouble(1));}else

i = add_general(i,1);}

Page 36: Lazy, Incremental JIT Compilation with Basic Block Versioning

36

var v = 4294967296;var i = 0;for (;;) { // assume i is int32 when entering loop // if (i >= 600000) break;

if (is_int32(i))if (greater_eq_int32(i, 600000)) break;

elseif (greater_eq_general(i, 600000) break;

// v = v & iif (is_int32(v) && is_int32(i))

v = bitwise_and_int32(v,i);else

v = bitwise_and_int32(toInt32(v), toInt32(i));

// i = i + 1if (is_int32(i)) {

i = add_int32(i,1);if (cpu_overflow_flag)

i = add_double(toDouble(i), toDouble(1));}else

i = add_general(i,1);}

Page 37: Lazy, Incremental JIT Compilation with Basic Block Versioning

37

var v = 4294967296;var i = 0;for (;;) { // if (i >= 600000) break;

if (greater_eq_int32(i, 600000)) break;

// v = v & iif (is_int32(v) && is_int32(i))

v = bitwise_and_int32(v,i);else

v = bitwise_and_int32(toInt32(v), toInt32(i));

// i = i + 1if (is_int32(i)) {

i = add_int32(i,1);if (cpu_overflow_flag)

i = add_double(toDouble(i), toDouble(1));}else

i = add_general(i,1);}

Page 38: Lazy, Incremental JIT Compilation with Basic Block Versioning

38

var v = 4294967296;var i = 0;for (;;) { // if (i >= 600000) break;

if (greater_eq_int32(i, 600000)) break;

// v = v & iif (is_int32(v) && is_int32(i))

v = bitwise_and_int32(v,i);else

v = bitwise_and_int32(toInt32(v), toInt32(i));

// i = i + 1if (is_int32(i)) {

i = add_int32(i,1);if (cpu_overflow_flag)

i = add_double(toDouble(i), toDouble(1));}else

i = add_general(i,1);}

Page 39: Lazy, Incremental JIT Compilation with Basic Block Versioning

39

var v = 4294967296;var i = 0;for (;;) { // assume both i and v are int32 // if (i >= 600000) break;

if (greater_eq_int32(i, 600000)) break;

// v = v & iif (is_int32(v) && is_int32(i))

v = bitwise_and_int32(v,i);else

v = bitwise_and_int32(toInt32(v), toInt32(i));

// i = i + 1if (is_int32(i)) {

i = add_int32(i,1);if (cpu_overflow_flag)

i = add_double(toDouble(i), toDouble(1));}else

i = add_general(i,1);}

Page 40: Lazy, Incremental JIT Compilation with Basic Block Versioning

40

var v = 4294967296;var i = 0;for (;;) { // if (i >= 600000) break;

if (greater_eq_int32(i, 600000)) break;

// v = v & iv = bitwise_and_int32(v,i);

// i = i + 1if (is_int32(i)) {

i = add_int32(i,1);if (cpu_overflow_flag)

i = add_double(toDouble(i), toDouble(1));}else

i = add_general(i,1);}

Page 41: Lazy, Incremental JIT Compilation with Basic Block Versioning

41

var v = 4294967296;var i = 0;for (;;) { // if (i >= 600000) break;

if (greater_eq_int32(i, 600000)) break;

// v = v & iv = bitwise_and_int32(v,i);

// i = i + 1if (is_int32(i)) {

i = add_int32(i,1);if (cpu_overflow_flag)

i = add_double(toDouble(i), toDouble(1));}else

i = add_general(i,1);}

Page 42: Lazy, Incremental JIT Compilation with Basic Block Versioning

42

var v = 4294967296;var i = 0;for (;;) { // if (i >= 600000) break;

if (greater_eq_int32(i, 600000)) break;

// v = v & iv = bitwise_and_int32(v,i); // v remains int32 after this

// i = i + 1i = add_int32(i,1);if (cpu_overflow_flag)

i = add_double(toDouble(i), toDouble(1));}

Page 43: Lazy, Incremental JIT Compilation with Basic Block Versioning

43

var v = 4294967296;var i = 0;for (;;) { // if (i >= 600000) break;

if (greater_eq_int32(i, 600000)) break;

// v = v & iv = bitwise_and_int32(v,i);

// i = i + 1i = add_int32(i,1); // the add could overflow!if (cpu_overflow_flag)

i = add_double(toDouble(i), toDouble(1)); // i becomes a double}

Page 44: Lazy, Incremental JIT Compilation with Basic Block Versioning

44

var v = 4294967296;var i = 0;for (;;) { // if (i >= 600000) break;

if (greater_eq_int32(i, 600000)) break;

// v = v & iv = bitwise_and_int32(v,i);

// i = i + 1i = add_int32(i,1);if (cpu_overflow_flag) {

i = add_double(toDouble(i), toDouble(1));

// Jump to a loop version where i is a doubleNEW_LOOP_VERSION = gen_new_version({'i':'double'});goto NEW_LOOP_VERSION;

}

// If we make it here, i is still int32}

Page 45: Lazy, Incremental JIT Compilation with Basic Block Versioning

45

var v = 4294967296;var i = 0;for (;;) { // if (i >= 600000) break;

if (greater_eq_int32(i, 600000)) break;

// v = v & iv = bitwise_and_int32(v,i);

// i = i + 1i = add_int32(i,1);if (cpu_overflow_flag) {

i = add_double(toDouble(i), toDouble(1));

// Jump to a loop version where i is a doubleNEW_LOOP_VERSION = gen_new_version({'i':'double'});goto NEW_LOOP_VERSION;

}

// If we make it here, i is still int32}

Page 46: Lazy, Incremental JIT Compilation with Basic Block Versioning

46

var v = 4294967296;var i = 0;for (;;) { // if (i >= 600000) break;

if (greater_eq_int32(i, 600000)) break; // i < INT32_MAX

// v = v & iv = bitwise_and_int32(v,i);

// i = i + 1i = add_int32(i,1);if (cpu_overflow_flag) {

i = add_double(toDouble(i), toDouble(1));

// Jump to a loop version where i is a doubleNEW_LOOP_VERSION = gen_new_version({'i':'double'});goto NEW_LOOP_VERSION;

}

// If we make it here, i is still int32}

Page 47: Lazy, Incremental JIT Compilation with Basic Block Versioning

47

var v = 4294967296;var i = 0;for (;;) { // if (i >= 600000) break;

if (greater_eq_int32(i, 600000)) break; // i < INT32_MAX

// v = v & iv = bitwise_and_int32(v,i);

// i = i + 1i = add_int32(i,1); // i + 1 <= INT32_MAXif (cpu_overflow_flag) {

i = add_double(toDouble(i), toDouble(1));

// Jump to a loop version where i is a doubleNEW_LOOP_VERSION = gen_new_version({'i':'double'});goto NEW_LOOP_VERSION;

}

// If we make it here, i is still int32}

Page 48: Lazy, Incremental JIT Compilation with Basic Block Versioning

48

var v = 4294967296;var i = 0;for (;;) { // if (i >= 600000) break;

if (greater_eq_int32(i, 600000)) break; // i < INT32_MAX

// v = v & iv = bitwise_and_int32(v,i);

// i = i + 1i = add_int32(i,1); // i + 1 <= INT32_MAX

// If we make it here, i is still int32}

Page 49: Lazy, Incremental JIT Compilation with Basic Block Versioning

49

A “multi-world” Approach

● Traditional type analysis– Fixed-point on types

– Types found must agree with all inputs

– Pessimistic, conservative answer

● Basic block versioning– Multiple solutions possible for each block

– Don't necessarily have to sacrifice

– Fixed-point on versioning of blocks

Page 50: Lazy, Incremental JIT Compilation with Basic Block Versioning

50

There is no Explosion

● Obvious criticism: versions explosion● Over 70% of blocks have one version● Large number of versions quite rare

– Few blocks have large version counts

– Worst cases not that bad

● KISS: hard limit on versions per block– Most common versions often occur first

● After limit, look for imperfect match

Page 51: Lazy, Incremental JIT Compilation with Basic Block Versioning

51

Unseen Benefits

● Hoists redundant type tests out of loop bodies● More powerful than traditional type analysis

– Multiple separate optimized code paths

– Works on code poorly amenable to analysis

– Gives answers where a type analysis can't

● No iterative fixed-point, very fast● Many interesting extensions possible

Page 52: Lazy, Incremental JIT Compilation with Basic Block Versioning

Lazy Incremental Compilation

Page 53: Lazy, Incremental JIT Compilation with Basic Block Versioning

53

Eager Versioning is No Good

● Generating versions eagerly is problematic– Guess ahead of time what will be executed

– Must remain conservative, overestimate

– Generate versions that will never be used

● Compiles blocks lazily: when first executed– Interleaves compilation and execution

– The running program's behavior drives versioning

● Avoid compiling unneeded blocks/versions– No floating-point code in your integer benchmark

– Never executed error handling is never compiled

Page 54: Lazy, Incremental JIT Compilation with Basic Block Versioning

54

is_int32(n)?

CB

D'

falsetrue

n is not int32n is int32

D''

n is int32 n is not int32

Page 55: Lazy, Incremental JIT Compilation with Basic Block Versioning

55

is_int32(n)?

CB

D'

falsetrue

n is not int32n is int32

D''

n is int32 n is not int32

Page 56: Lazy, Incremental JIT Compilation with Basic Block Versioning

56

Not Quite Tracing

● A bit like “eager tracing”● Small linear code fragments● No interpreter● No recording of traces● It's all in the branches

– Keep compiling when direction determined

– When unknown, jump to stubs, resume execution

– Jumping to a stub resumes compilation

● Write code linearly and patch it

Page 57: Lazy, Incremental JIT Compilation with Basic Block Versioning

Incremental Codegen Example

Page 58: Lazy, Incremental JIT Compilation with Basic Block Versioning

function sumInts(n){ var sum = 0; for (var i = 0; i < n; i++) sum += i;

return sum;}

sumInts(600);

Page 59: Lazy, Incremental JIT Compilation with Basic Block Versioning

function sumInts(n){ var sum = 0; for (var i = 0; i < n; i++) sum += i;

return sum;}

sumInts(600);

Page 60: Lazy, Incremental JIT Compilation with Basic Block Versioning

60

entry(2241F):; $2 = phi 0; $3 = phi 0xor ecx, ecx;xor edx, edx;

for_test(22426):; $0 = is_int32 $3; $0 = is_int32 $26cmp [byte r13 + 26], 1;jne not_int_stubje is_int_stub

Page 61: Lazy, Incremental JIT Compilation with Basic Block Versioning

61

entry(2241F):; $2 = phi 0; $3 = phi 0xor ecx, ecx;xor edx, edx;

for_test(22426):; $0 = is_int32 $3; $0 = is_int32 $26cmp [byte r13 + 26], 1;jne not_int_stubje is_int_stub

Page 62: Lazy, Incremental JIT Compilation with Basic Block Versioning

62

entry(2241F):; $2 = phi 0; $3 = phi 0xor ecx, ecx;xor edx, edx;

for_test(22426):; $0 = is_int32 $3; $0 = is_int32 $26cmp [byte r13 + 26], 1;jne not_int_stubje is_int_stub

Page 63: Lazy, Incremental JIT Compilation with Basic Block Versioning

63

entry(2241F):; $2 = phi 0; $3 = phi 0xor ecx, ecx;xor edx, edx;

for_test(22426):; $0 = is_int32 $3; $0 = is_int32 $26cmp [byte r13 + 26], 1;jne branch_if_join(22456);

if_true(22453):; $0 = lt_i32 $3, $26mov r12, [qword r14 + 208];cmp edx, r12d;jl branch_for_body(22427);jmp branch_for_exit(22429);

Page 64: Lazy, Incremental JIT Compilation with Basic Block Versioning

64

entry(2241F):; $2 = phi 0; $3 = phi 0xor ecx, ecx;xor edx, edx;

for_test(22426):; $0 = is_int32 $3; $0 = is_int32 $26cmp [byte r13 + 26], 1;jne branch_if_join(22456);

if_true(22453):; $0 = lt_i32 $3, $26mov r12, [qword r14 + 208];cmp edx, r12d;jl branch_for_body(22427);jmp branch_for_exit(22429);

Page 65: Lazy, Incremental JIT Compilation with Basic Block Versioning

65

entry(2241F):; $2 = phi 0; $3 = phi 0xor ecx, ecx;xor edx, edx;

for_test(22426):; $0 = is_int32 $3; $0 = is_int32 $26cmp [byte r13 + 26], 1;jne branch_if_join(22456);

if_true(22453):; $0 = lt_i32 $3, $26mov r12, [qword r14 + 208];cmp edx, r12d;jge branch_for_exit(22429);

for_body(22427):; $0 = is_int32 $2; $0 = is_int32 $3; $8 = add_i32_ovf $2, $3add ecx, edx;jo branch_if_false(2249D);jmp branch_call_merge(22485);

Page 66: Lazy, Incremental JIT Compilation with Basic Block Versioning

66

entry(2241F):; $2 = phi 0; $3 = phi 0xor ecx, ecx;xor edx, edx;

for_test(22426):; $0 = is_int32 $3; $0 = is_int32 $26cmp [byte r13 + 26], 1;jne branch_if_join(22456);

if_true(22453):; $0 = lt_i32 $3, $26mov r12, [qword r14 + 208];cmp edx, r12d;jge branch_for_exit(22429);

for_body(22427):; $0 = is_int32 $2; $0 = is_int32 $3; $8 = add_i32_ovf $2, $3add ecx, edx;jo branch_if_false(2249D);jmp branch_call_merge(22485);

Page 67: Lazy, Incremental JIT Compilation with Basic Block Versioning

67

entry(2241F):; $2 = phi 0; $3 = phi 0xor ecx, ecx;xor edx, edx;

for_test(22426):; $0 = is_int32 $3; $0 = is_int32 $26cmp [byte r13 + 26], 1;jne branch_if_join(22456);

if_true(22453):; $0 = lt_i32 $3, $26mov r12, [qword r14 + 208];cmp edx, r12d;jge branch_for_exit(22429);

for_body(22427):; $0 = is_int32 $2; $0 = is_int32 $3; $8 = add_i32_ovf $2, $3add ecx, edx;jo branch_if_false(2249D);

call_merge(22485):; $0 = is_int32 $3; $15 = add_i32_ovf $3, 1add edx, 1;

for_test(22426):; $0 = is_int32 $3; $0 = is_int32 $26jmp if_true(22453);

Page 68: Lazy, Incremental JIT Compilation with Basic Block Versioning

68

entry(2241F):; $2 = phi 0; $3 = phi 0xor ecx, ecx;xor edx, edx;

for_test(22426):; $0 = is_int32 $3; $0 = is_int32 $26cmp [byte r13 + 26], 1;jne branch_if_join(22456);

if_true(22453):; $0 = lt_i32 $3, $26mov r12, [qword r14 + 208];cmp edx, r12d;jge branch_for_exit(22429);

for_body(22427):; $0 = is_int32 $2; $0 = is_int32 $3; $8 = add_i32_ovf $2, $3add ecx, edx;jo branch_if_false(2249D);

call_merge(22485):; $0 = is_int32 $3; $15 = add_i32_ovf $3, 1add edx, 1;

for_test(22426):; $0 = is_int32 $3; $0 = is_int32 $26jmp if_true(22453);

Page 69: Lazy, Incremental JIT Compilation with Basic Block Versioning

69

entry(2241F):; $2 = phi 0; $3 = phi 0xor ecx, ecx;xor edx, edx;

for_test(22426):; $0 = is_int32 $3; $0 = is_int32 $26cmp [byte r13 + 26], 1;jne branch_if_join(22456);

if_true(22453):; $0 = lt_i32 $3, $26mov r12, [qword r14 + 208];cmp edx, r12d;jge branch_for_exit(22429);

for_body(22427):; $0 = is_int32 $2; $0 = is_int32 $3; $8 = add_i32_ovf $2, $3add ecx, edx;jo branch_if_false(2249D);

call_merge(22485):; $0 = is_int32 $3; $15 = add_i32_ovf $3, 1add edx, 1;

for_test(22426):; $0 = is_int32 $3; $0 = is_int32 $26jmp if_true(22453);

Page 70: Lazy, Incremental JIT Compilation with Basic Block Versioning

70

entry(2241F):; $2 = phi 0; $3 = phi 0xor ecx, ecx;xor edx, edx;

for_test(22426):; $0 = is_int32 $3; $0 = is_int32 $26cmp [byte r13 + 26], 1;jne branch_if_join(22456);

if_true(22453):; $0 = lt_i32 $3, $26mov r12, [qword r14 + 208];cmp edx, r12d;jge branch_for_exit(22429);

for_body(22427):; $0 = is_int32 $2; $0 = is_int32 $3; $8 = add_i32_ovf $2, $3add ecx, edx;jo branch_if_false(2249D);

call_merge(22485):; $0 = is_int32 $3; $15 = add_i32_ovf $3, 1add edx, 1;

for_test(22426):; $0 = is_int32 $3; $0 = is_int32 $26jmp if_true(22453);

Page 71: Lazy, Incremental JIT Compilation with Basic Block Versioning

71

entry(2241F):; $2 = phi 0; $3 = phi 0xor ecx, ecx;xor edx, edx;

for_test(22426):; $0 = is_int32 $3; $0 = is_int32 $26cmp [byte r13 + 26], 1;jne branch_if_join(22456);

if_true(22453):; $0 = lt_i32 $3, $26mov r12, [qword r14 + 208];cmp edx, r12d;jge branch_for_exit(22429);

for_body(22427):; $0 = is_int32 $2; $0 = is_int32 $3; $8 = add_i32_ovf $2, $3add ecx, edx;jo branch_if_false(2249D);

call_merge(22485):; $0 = is_int32 $3; $15 = add_i32_ovf $3, 1add edx, 1;

for_test(22426):; $0 = is_int32 $3; $0 = is_int32 $26jmp if_true(22453);

Page 72: Lazy, Incremental JIT Compilation with Basic Block Versioning

72

entry(2241F):; $2 = phi 0; $3 = phi 0xor ecx, ecx;xor edx, edx;

for_test(22426):; $0 = is_int32 $3; $0 = is_int32 $26cmp [byte r13 + 26], 1;jne branch_if_join(22456);

if_true(22453):; $0 = lt_i32 $3, $26mov r12, [qword r14 + 208];cmp edx, r12d;jge branch_for_exit(22429);

for_body(22427):; $0 = is_int32 $2; $0 = is_int32 $3; $8 = add_i32_ovf $2, $3add ecx, edx;jo branch_if_false(2249D);

call_merge(22485):; $0 = is_int32 $3; $15 = add_i32_ovf $3, 1add edx, 1;

for_test(22426):; $0 = is_int32 $3; $0 = is_int32 $26jmp if_true(22453);

for_exit(22429):; ret $2mov dl, 1;mov eax, [dword r14 + 200];sub eax, 1;xor ebx, ebx;cmp eax, 0;cmovl eax, ebx;add eax, 27;mov rbx, [qword r14 + 176];add r13, rax;shl rax, 3;add r14, rax;jmp rbx;

Page 73: Lazy, Incremental JIT Compilation with Basic Block Versioning

But Wait, There's More!

Page 74: Lazy, Incremental JIT Compilation with Basic Block Versioning

Typed Object Shapes

Page 75: Lazy, Incremental JIT Compilation with Basic Block Versioning

JavaScript Objects

● Are dynamic with complex semantics– Dynamic addition & deletion of properties

– Dynamic typing of properties

– Modifiable property attributes

– Dynamically installable getters & setters

– Indexable, behave like dictionaries

● Need to be efficient– Must perform better than dictionaries

Page 76: Lazy, Incremental JIT Compilation with Basic Block Versioning

Objects as Dictionaries

WEC A 5

WEC B 1.5

-- -- --

E C “foo”

WE D null

-- -- --

Attribute flags Keys Values

Page 77: Lazy, Incremental JIT Compilation with Basic Block Versioning

Shapes

5

1.5

“foo”

null

A

B

C

D

slots

shape pointer

shapenodes

empty

Page 78: Lazy, Incremental JIT Compilation with Basic Block Versioning

The Empty Object

empty

empty object

empty shape

Page 79: Lazy, Incremental JIT Compilation with Basic Block Versioning

Growing Objects

5

A

empty

+A

Page 80: Lazy, Incremental JIT Compilation with Basic Block Versioning

Growing Objects

5

1.5

A

B

empty

+A

+B

Page 81: Lazy, Incremental JIT Compilation with Basic Block Versioning

Growing Objects

5

1.5

“foo”

A

B

C

empty

+A

+C

+B

Page 82: Lazy, Incremental JIT Compilation with Basic Block Versioning

Growing Objects

5

1.5

“foo”

null

A

B

C

D

empty

+A

+C

+D

+B

Page 83: Lazy, Incremental JIT Compilation with Basic Block Versioning

Inline Caching

5

1.5

“foo”

null

A

B

C

D

empty

0

1

2

3

3

2

1

0

Page 84: Lazy, Incremental JIT Compilation with Basic Block Versioning

Shape Trees and Sharing

5

1.5

“foo”

null

A

B

C

D

empty

E2

0.1

foo()

A

B

E

A

B

C

D

+A

+C+E

+B

+D

Page 85: Lazy, Incremental JIT Compilation with Basic Block Versioning

Typed Shapes

5

1.5

“foo”

null

A

B

C

D

empty

float64

int32

string

const

A

B

C

D

Page 86: Lazy, Incremental JIT Compilation with Basic Block Versioning

Typed Shapes

● Extend shapes to store property types– Type tags

– Function pointers

● Allows versioning based on shapes● Permits the elimination of:

– Missing property checks

– Getter/setter checks

– Property type checks

– Boxing/unboxing overhead

– Dynamic dispatch on function calls

Page 87: Lazy, Incremental JIT Compilation with Basic Block Versioning

Experimental Results

Page 88: Lazy, Incremental JIT Compilation with Basic Block Versioning

Experimental Setup

● Benchmarks from SunSpider and V8 suites– Excluded two which Higgs could not run

– Recently: excluded RegExp benchmarks

● Questions to answer:– How many type tests does BBV eliminate?

– How does BBV compare to a type analysis?

– Impact of BBV on code size

– Can we get measurable speedups?

– Impact on compilation time

– How does BBV perform against trace compilation?

Page 89: Lazy, Incremental JIT Compilation with Basic Block Versioning
Page 90: Lazy, Incremental JIT Compilation with Basic Block Versioning
Page 91: Lazy, Incremental JIT Compilation with Basic Block Versioning
Page 92: Lazy, Incremental JIT Compilation with Basic Block Versioning

92

2013

Page 93: Lazy, Incremental JIT Compilation with Basic Block Versioning

93

2013 worst case: almost 300%

Page 94: Lazy, Incremental JIT Compilation with Basic Block Versioning
Page 95: Lazy, Incremental JIT Compilation with Basic Block Versioning
Page 96: Lazy, Incremental JIT Compilation with Basic Block Versioning
Page 97: Lazy, Incremental JIT Compilation with Basic Block Versioning
Page 98: Lazy, Incremental JIT Compilation with Basic Block Versioning

Future Work

Page 99: Lazy, Incremental JIT Compilation with Basic Block Versioning

Interprocedural Versioning

● Straightforward extension:– Multiple function entry block versions

– Typed shapes give us callee identity

– Can jump directly to correct entry block version

● Further extensions:– Pass return value info

– Threading the global object

– Shape-preserving calls

● Will this explode?

Page 100: Lazy, Incremental JIT Compilation with Basic Block Versioning

Incremental Inlining

● Incremental compilation + inlining● Inline functions one block at a time

– Only what gets executed

– Particularly useful for self-hosted runtimes

● Avoid expensive CFG transformations● Avoid needing to recompile whole functions

– No need for separate inlining pass

– No need for on-stack replacement

Page 101: Lazy, Incremental JIT Compilation with Basic Block Versioning

IR-level Versioning

● In Higgs, versioning happens in the backend– Backend maintains basic block “instances”

– Each instance has a context object, type info

● Backend's view of code is rather myopic– Too late for complex code transformations

● BBV should be done by transforming the IR– Context is implicitly threaded in the IR

– Higher-level optimizations possible

– e.g.: property access optimizations

Page 102: Lazy, Incremental JIT Compilation with Basic Block Versioning

Code Collection & Compaction

● Some functions become unreachable– All compiled code is then dead

● Some block versions may become irrelevant– Object shape which doesn't exist anymore

● Dead machine code should be removed– Space is limited, compactness is good for i-cache

● In Higgs: machine code is relocatable– Eventually: compacting machine code GC

Page 103: Lazy, Incremental JIT Compilation with Basic Block Versioning

103

github.com/maximecb/Higgs

#higgsjs on freenode IRC

pointersgonewild.com

[email protected]

Love2Code on twitter

Page 104: Lazy, Incremental JIT Compilation with Basic Block Versioning

104

Special thanks to:

Prof. Marc FeeleyTommy Everett @tach4n

Brett FraleyPaul Fryzel @paulfryzel

Zimbabao Borbaki @zimbabaoSorella @robotlolita

Óscar Toledo @nanochessLuke Wagner (blog.mozilla.org/luke)