The Prepack Compiler

Prepack is an experimental optimizing Javascript compiler. The existing major Javascript compilers, like Babel or Closure, focus on other things: code size reduction for the former, backwards compatibility for the latter. Prepack is all about optimization: function inlining, loop unrolling, and other transformations that shift some of the work to compile time.

How it works

It runs your code, then generates an optimized version that returns the same results. This makes it very good at optimizing, for example, a call to fib(10), which returns the 10th Fibonacci number.

It’s limited, by default, to code along the global path — ie, what’s run in the initialization phase — but it can also run specific code that you direct it to do so, as long as any state it depends on are defined by the time the initialization phase in complete.

Symbolic Execution Engine

Certain results will depend on state, e.g., Date.now(), that can’t be determined at compile time. These can be substituted with abstract values, allowing optimizations to still be made over code that depends on them.

Here are some examples that I tried with the Prepack repl that could in principle be optimized, but aren’t:

Eg 1:

let now = Date.now();
now += 1;
now -= 1;

In this case, the compiler can deduce that the value of now (which could be any integer) is the same whether or not lines 2 and 3 execute, so it should optimize them away.

Eg 2:

function f() {
    let now = Date.now()
    if(now * 2 == 12) {
        return 0;
    } else {
        return 1;
    }
}

Here, the result only depends on whether now is equal to 6. The abstract interpreter can replace the result with an abstract data type equivalent to (Date.now == 6 ? 0 : 1).

(This shows the result of a control flow merge that results in a conditional abstract value.)