On 2011-05-16, at 1:52 PM, Erick Lavoie wrote:
Le 11-05-15 23:36 , Marc Feeley a écrit :
This weekend I improved my prototype compiler which compiles JS to x86, using the global property jump optimization. The compiler is rather minimal and handles just enough to compile fib and similar simple code.
This prototype is mainly useful to explore various implementation approaches quickly, without the burden of the complete Tachyon compiler.
I would be interested in using it too, is it in scheme? Could you put it in our private repository?
It is in JS and I just pushed it to the repo. It is in source/parser. The main part of the compiler is js86-compile.js . Just cd source/parser and do
% ./js86 fib.js
It will generate fib.js.S and fib.js.exe, and executes it. I've tested it on Mac OS X and Linux. The compiler accepts a bunch of options to enable/disable various code generation features (see the end of js86-compile.js). For example, to call handlers through the context do:
% ./js86 -ctx-handlers fib.js
On my MacBook Pro, calling the handlers through the context gives a 10% performance boost for fib (and generates shorter code).
The results are very good. On fib(40), proto2 generate faster code than V8 (1.5x) and SpiderMonkey (2.3x), and Tachyon (18x). There is a 1.2x difference between proto1 and proto2 (in other words, there is a 20% overhead to setup and check argument counts). Here are the detailed running times:
relative time to proto2
Tachyon 29.613 (18.3) SM 3.700 ( 2.3) V8 2.397 ( 1.5) proto1 2.000 ( 1.2) proto2 1.614 ( 1.0)
Those are great news, how does that compare to gcc?
You try it!
If you have any questions please let me know.
You did not show how to handle closures. We would need to store the function object pointer in addition to the entry point of the code at the call site. It should only need a little modification of the patching code.
No modifications are necessary when closures are represented as a piece of code (see my paper "Closure generation based on viewing LAMBDA as EPSILON plus COMPILE" on my web page).
Also, what if the code or the function object is moved by the GC? Should we invalidate all the call site caches or should we patch them with the new addresses?
I don't know. That's the kind of thing that has to be tried to see what works best.
I could see a similar mecanism used to lazily compile functions. The function could be stored in an AST, HIR or string representation. When jumping to the function the first time, the compiler would first compile the function and then replace the entry point in the function object by the compiled code. This would be complimentary to the global call optimization by delaying the global function call site patching until the entry point of the compiled code is known.
Interesting idea.
Here's a question for Maxime and Erick... can you estimate the work needed to get Tachyon to generate similar x86 code?
Marc
To follow your current design, we could introduce a global function call instruction in the IR that would take a string property, a this object and the arguments (instead of the function object, the this object and the arguments for a regular CallInstr). This part should be simple.
Additionnally, we would need to modify the global object layout to have a pointer to the call site list in addition to the value of a property. Again, that should be simple with the layout system.
The tricky part, I think, is how to implement the different handlers. I would try to separate the implementation of those such that:
- The layout of the call sites is known only to the backend
- The patching code is done by the backend, including the list
traversal to invalidate the call sites 3. The global object layout and property lookup is implemented using the layout and primitive system
I would guess it could be done in a couple of days.
If it can be done quickly lets do it.
Marc