Dynamic Languages need Dynamic Compilers Maxime Chevalier-Boisvert mloc.js 2014
Introduction ● PhD candidate at the Université de Montréal, prof. Marc Feeley ● Research: compilers, optimizing dynamic languages, JS ● This talk is about: – Dynamic languages – JIT compilation & optimization – The Higgs JS compiler – Basic block versioning – Lazy incremental compilation 2
Dynamic languages ● JS is not an "interpreted language" – Originally ran in an interpreter, back in 1995 – Today, all browsers have JIT compilers ● Family of languages sharing “dynamic” characteristics – e.g.: JS, Python, Ruby, PHP, Perl, MATLAB, LISPs ● Huge gain in popularity with web development – Rapid prototyping, short development cycles 3
Defining dynamic languages ● Dynamic typing – Variables can change type over time – Types associated with values – No type annotations ● Late binding – Symbols resolved dynamically (e.g.: globals) – Dynamic loading of code (eval, load) ● Dynamic growth of objects – Dynamic arrays – Objects as dictionaries/maps 4
"I think of a programming language as a tool to convert a programmer's mental images into precise operations that a machine can perform. The main idea is to match the user's intuition as well as possible . […]" – Donald Knuth 5
Higher-level thinking ● Reduce the cognitive burden on programmers ● Hope to make you more productive – Write less code for the same result – Each line of code conveys more meaning ● Leave more details implicit, assumed – Invisible work happen behind the scenes – This invisible work carries extra overhead 6
Dynamic type tests ● Operators change meaning based on types – Different operations for different inputs ● For example, the + operator could mean: – Integer addition – Floating-point addition – String concatenation – Array appending ● Inside each operator hide many type tests – Functions with cascades of if statements testing types 7
8
9
JIT compilation ● Key advantages over static compilers – Observe, monitor programs as they run – Modify code at run time ● In theory, JITs can produce faster code – Code adapted to program's behavior – Don't account for unexecuted code ● Opinion: modern JITs still overly static – Compiler books are all about AOT compilers 11
Higgs ● JavaScript JIT compiler for x86-64 – Open source, mostly written in D – Self-hosted runtime library, in JS – Relatively small codebase (~ 50 KLOC) ● Support for most of ECMAScript 5 – No with statement, no getters/setters – Extras: Foreign Function Interface (FFI) ● Experimental platform – Try new ideas, uncharted territory – Basic block versioning, lazy incremental compilation 12
Self-hosting ● Runtime and standard library are self-hosted ● JS primitives (e.g.: + operator) written in extended JS – Exposes low-level operations (e.g.: machine integer ops) ● Primitives compiled & inlined like any other JS code – Avoids opaque calls into C or D code ● Easy to change & extend runtime ● Inlining critical for performance 13
Basic block versioning ● Similarities with tracing, procedure cloning ● As you compile code, accumulate facts – e.g.: type information – Branch tests add type & shape information ● Specialize based on accumulated facts – Type information: type tags, variables, objects – Potentially: register allocation state ● May compile multiple versions of code
true false A B C D 15
true false is_int32(n)? B C D 16
if ( is_int32(n) ) { // B ... } else { // C ... } // D ... 17
true false is_int32(n)? B C D 18
true false is_int32(n)? n is int32 B C D 19
true false is_int32(n)? n is int32 n is not int32 B C D 20
true false is_int32(n)? n is int32 n is not int32 B C n is ??? D 21
true false is_int32(n)? n is int32 n is not int32 B C n is int32 n is not int32 D' D'' 22
var v = 4294967296; for (var i = 0; i < 600000; i++) v = v & i; // From the SunSpider bitwise-and benchmark 23
var v = 4294967296; for (var i = 0; less_than (i,600000); i = add (i,1)) v = bitwise_and (v,i); 24
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)); } 25
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 path if (cpu_overflow_flag) r = add_double(toDouble(x), toDouble(y)); return r; } return add_general(x,y); 26 }
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 path if ( cpu_overflow_flag ) r = add_double(toDouble(x), toDouble(y)); return r; } return add_general(x,y); 27 }
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; else if (greater_eq_general(i, 600000) break; // v = v & i if ( is_int32 (v) && is_int32 (i)) v = bitwise_and_int32(v,i); else v = bitwise_and _int32 (toInt32(v), toInt32(i)); // i = i + 1 if ( 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); 28 }
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; else if (greater_eq_general(i, 600000) break; // v = v & i if (is_int32(v) && is_int32(i)) v = bitwise_and_int32(v,i); else v = bitwise_and _int32 (toInt32(v), toInt32(i)); // i = i + 1 if (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); 29 }
var v = 4294967296; var i = 0; for (;;) { // if (i >= 600000) break; if ( is_int32 (i)) if (greater_eq_int32(i, 600000)) break; else if (greater_eq_general(i, 600000) break; // v = v & i if ( is_int32 (v) && is_int32 (i)) v = bitwise_and_int32(v,i); else v = bitwise_and _int32 (toInt32(v), toInt32(i)); // i = i + 1 if ( 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); 30 }
var v = 4294967296; var i = 0; // when we enter the loop, i is int32 for (;;) { // if (i >= 600000) break; if ( is_int32 (i)) if (greater_eq_int32(i, 600000)) break; else if (greater_eq_general(i, 600000) break; // v = v & i if ( is_int32 (v) && is_int32 (i)) v = bitwise_and_int32(v,i); else v = bitwise_and _int32 (toInt32(v), toInt32(i)); // i = i + 1 if ( 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); 31 }
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; else if (greater_eq_general(i, 600000) break; // v = v & i if ( is_int32 (v) && is_int32 (i)) v = bitwise_and_int32(v,i); else v = bitwise_and _int32 (toInt32(v), toInt32(i)); // i = i + 1 if ( 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); 32 }
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; else if (greater_eq_general(i, 600000) break; // v = v & i if ( is_int32 (v) && is_int32 (i)) v = bitwise_and_int32(v,i); else v = bitwise_and _int32 (toInt32(v), toInt32(i)); // i = i + 1 if ( 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); 33 }
var v = 4294967296; var i = 0; for (;;) { // if (i >= 600000) break; if (greater_eq_int32(i, 600000)) break; // v = v & i if ( is_int32 (v) && is_int32 (i)) v = bitwise_and_int32(v,i); else v = bitwise_and_int32(toInt32(v), toInt32(i)); // i = i + 1 if ( 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); } 34
var v = 4294967296; var i = 0; for (;;) { // if (i >= 600000) break; if (greater_eq_int32(i, 600000)) break; // v = v & i if ( is_int32 (v) && is_int32 (i)) v = bitwise_and_int32(v,i); else v = bitwise_and_int32(toInt32(v), toInt32(i)); // i = i + 1 if ( 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); } 35
Recommend
More recommend