Because even a just-in-time compiler is slower than native performance quite frequently. A just-in-time WebAssembly compiler can be guaranteed to be as fast as V8 in practice.
Branch prediction on a CPU can be quite helpful, but if a program stops hitting the same branches it can impact performance. The point of a just in time compiler is that it compiles on the fly, which does take a certain amount of time. That could be loading time for your application.
In any case, the purpose of this development would be to design something that is faster than V8. And I'm specifically speaking of compiling with inferred types or from asm.js or TypeScript, not embedding a VM or a garbage collector or an emulator.
This compiler might only accept programs that would be faster.
Well, you'll need a garbage collector anyway, and with AOT compilation it would have to be conservative (which isn't that much of an issue, but precise collectors are generally a little more efficient).
The problem is that you can't fully trust your type inference. JIT compilers can get away with this because they can escape to the interpreter if a type check fails. With an AOT compiler you can optimize for your inferred type but you still always have to include complete code for the slow path too. At that point 80%+ of your instruction cache is gonna be probably-unused code. You can never eliminate as many type checks as a tracing or block versioning JIT.
TypeScript doesn't help either:
function foo(a: int, b: int) { return a + b; }
window['fo' + String.fromCharCode(111)]('nice ', 'try);
Whoops. TypeScript is (intentionally) unsound, which doesn't help your compilation much.
If AOT compilation were a way to get JavaScript faster than V8, V8 would compile ahead-of-time (well, it would probably compile in the background and use an interpreter on first load, but you get the idea).
asm.js is a different since it is strongly typed and explicitly controls memory layout. It's not JavaScript from the perspective of implementation.
Well, then I should use some regular expressions, a parser generator, AST walker and syntax analyzer, with the help of a few directive comments, to turn my strict subset of JavaScript code into C, and then use emscripten to generate WebAssembly.
Then I will have faster (and smaller) code on platforms that support WebAssembly and regular JavaScript on platforms that don't.
What a wonderful way to deploy JavaScript to WebAssembly for enlightened developers, which doesn't involve a "compiler."
Obviously, we are thinking of different scenarios. My subset of JavaScript may be different than yours, which might be different than asm.js. Asm.js would be great except it isn't idiomatic and having a fixed heap isn't always great. That doesn't mean there aren't middle grounds which are as of yet unexplored. This is sort of a limited area, which is probably why no one's started a project on this yet.
By the window['foo'] argument, minifiers are unnecessary because then you can't use eval().
That is interesting, thx. ... but it seems to me, they failed, because they wanted to mix strong typed js with weak typed ...
"the main problem here was the interoperability with weak classes: we need to allow inheriting strong from weak classes and vice versa"
> turn my strict subset of JavaScript code into C, and then use emscripten to generate WebAssembly.
Well, the problem there is that your subset would be... C. JavaScript without automatic memory management, closures, prototype inheritance[1], and dynamic types isn't really JS anymore.
> By the window['foo'] argument, minifiers are unnecessary because then you can't use eval().
Most minifiers don't touch object properties or global variables for this reason. Closure compiler is the only one that does and it's quite strict (and doesn't work on a lot of JS code because of it).
1. Assuming objects are hash tables in your implementation, you might be able to do inheritance by reference counting a list of parent hash tables but that could get messy.
The code that I have to work with is static. It just happens to be JavaScript. Aside from writing some type casts and a linked list implementation, the standard implementation for a project like this doesn't have to be large. And yes, it boils down to a trivial C compiler.
Also, Uglify 2 modifies global properties if you tell it to, and neither uglify nor closure compiler do well with object properties, which can be renamed.
All of these arguments assume that a developer is unaware or not understanding of the mechanisms that their code uses. It seems no one is interested in using anything other than emscripten/LLVM.
>Because even a just-in-time compiler is slower than native performance quite frequently.
No that's not how it works. The problem with most JIT'd languages is that every variable or field is a pointer, every function call is a virtual call and there are no type signatures to help a compiler. These are the main reasons. If you added structs, nonvirtual functions and type hints you will quickly enter C performance territory (this is basically what asm.js does).
JIT compilers heavily use profiler guided optimizations to avoid the overheads associated with dynamic languages. They can often inline polymorphic function calls because usually 98% the variable points to an instance of type X and the remaining 2% to type Y. Falling back to a normal polymorphic call if the type is not Y. This is something an AOT compiler cannot do.
Yes, it can. You know the part where the JIT decides whether to execute X or Y? That can easily be implemented in a conditional in compiled code with the two code paths, X and Y.
But I'm more interested in the C performance territory. I'm not even interested in cross-compiling code where globals are used or variables change types.
It's possible to strip the functions and properties off of a JavaScript prototype and if it's well behaved, which user code is more likely to be than JavaScript libraries, it can be compiled fairly well.
> You know the part where the JIT decides whether to execute X or Y? That can easily be implemented in a conditional in compiled code with the two code paths, X and Y.
And that's the difference with a JIT compiler: if the value is almost certainly X, then it just emits the code for X and a stub for Y. The stub just bails back into the interpreter. The check is a couple machine instructions and the fast path can be pipelined (since branch prediction is unlikely to fail).
An AOT compiler has to include the full code for both X and Y, no matter how unlikely Y is. AOT compiling dynamically typed languages gets you easily 80%-90% unused code.
Then why isn't your argument against code size rather than performance? Seems like an odd position to take. The fast path would also be branch predicted in an AOT case.
The only difference in this hypothetical is that the slower path would run faster.
Because with 80%+ unused code, your instruction cache is gonna choke.
Also JITs can remove many more type checks, because it knows when a check would be redundant. See e.g. Lazy Basic Block Versioning[1], but existing tracing JITs do quite well also.
Native performance is only better in the cases where you can explicitly control memory layout & access. That's why asm.js runs so fast. You're explicitly translating C/C++ to arrays.
I don't really see how a js -> wasm compiler fundamentally changes things. You're still working in JS which has poor memory placement semantics.
Argh, I see this keep coming up and it really needs to be put to rest.
Compilers don't know how to layout your data. They don't know how you'll access it or the traversal patterns. Your whiz-bang compiler isn't going to strip out class members and AoS from SoA because that's a fundamental data structure problem.
Compilers are great, they fold things down, inline and unroll loops. When it comes to memory placement they can't know your intent and it's up to you to lay out your data appropriately. They might do padding but anything else is going to have memory & complexity implications and they shouldn't make those decisions for you.
I don't know, you have a real mix of arguments here, as do most people. There's nothing a JIT does that an ahead of time compiler can't do without the burden of a larger footprint.
This is really an issue of backend optimization to "make things like asm.js."
For example, there are obvious examples of cases where a JavaScript object's properties and property types would be transparent to a compiler at compile time, with a complete graph of where those values would be stored and used. This would be stored in the WebAssembly stack, analogous to an asm.js fixed array.
The WebAssembly sandbox is designed to run code directly. The JIT is designed to build code to run directly in an iterative fashion. If system designers knows how software is expected to operate, they can make choices to instrument that process and also make software which is designed intelligently.
Java runs in a VM. Someone could write a just in time compiler for .java code that performs well. That's not a sufficient argument not to develop or continue to develop a javac compiler for byte code. Additionally, whether or not a language is designed to use GC doesn't mean there couldn't be research or design into avoiding or augmenting that situation for performance (in the way that a just-in-time compiler would relieve the situation). Nor reason not to build a platform and engineering methodology to deploy to two independent and cooperating runtime environments.
Neither Asm.js and emscripten or C are the end of all things. Neither is dynamicism.
If you compile ahead of time, you can tune your optimizations. With the right tooling you can tell the compiler that a certain path is hot and should be optimized from the beginning, or catch deoptimizations better, or just get rid of the compilation cost. Right now I'm not sure how much practical benefit there is, but there's a lot of potential.
AOT compiling highly dynamic languages gives you very little room for optimizations.