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.
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.
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().
1. https://developers.google.com/v8/experiments#strong-mode
2. https://groups.google.com/forum/#!msg/strengthen-js/ojj3TDxb...
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.
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.
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.
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.
The only difference in this hypothetical is that the slower path would run faster.
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.
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.
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.
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.
AOT compiling highly dynamic languages gives you very little room for optimizations.