On 2010-07-12, at 10:44 AM, Maxime Chevalier-Boisvert wrote:
I'm progressing fairly well in the IR translation. I now have code generation for closures, and exception handling. I also believe I'm supporting all primitive operators. I added code to Marc's ast pass 1 to transform expressions of the form x += y into x = x + y, to minimize the code generation effort.
Be careful... that transformation is not valid, i.e.
obj[i++] += 1; is not equivalent to obj[i++] = obj[i++] + 1
And because JavaScript lacks C++ references and pointers, there is no other way to express x += y in general (for arbitrary x and y).
However, I think it is valid to transform ++x into x += 1 and x++ into (x += 1) - 1 . Similar for decrement.
I'm in the process of implementing code generation for the with statement. After this, I'll be missing the for-in loop and the switch statement, which I hope to complete by Wednesday.
I wanted to run some ideas by you guys:
- To implement the for-in loop, we will need some way to iterate over
the property names of an object. This will require some sort of iterator object, and property iteration instructions at the IR level. I am considering using a simple index integer as an iterator, and having two IR instructions, one to find the next property index, and one to get the property name at the current index. The index would be initialized at 0, and could possibly go to -1 when there are no more properties to be found.
Another possibility is to make the iteration work with an abstract iterator object, with iterator manipulations to get an iterator on an object, get the next property, and check if the iterator is still valid.
It is very hard to implement an efficient for-in loop. The reason is that the semantics of JavaScript (if I recall correctly) requires that all the properties of the object *at the moment the for-in loop is started* must be iterated on by the loop. Properties that are added during the loop need not be iterated on. The problem is that it is possible that the layout of the hashtable will change during the loop (due to a GC, a property being added or deleted which causes the hashtable to change size and thus the position of the keys). So... the best I can think of is the conversion of the hashtable into an array at the moment the for-in loop is entered, and then the for-in loop is just a normal for loop iterating over the elements of the array.
While I'm at it, whenever there is a choice, please write loops that count down rather than up, as this requires fewer live variables (less register pressure). So
for (var i=t.length-1; i>=0; i--) { ... t[i] ... }
is better than
for (var i=0; i<t.length; i++) { ... t[i] ... }
if it doesn't matter in which order the elements are processed.
- Many of the IR instruction classes I wrote so far are very
resemblant. I am considering writing a function to generate a "generic" instruction constructor using a closure (an instruction class maker function), in order to minimize the amount of code. This way, it would actually be easy to have an instruction class for each arithmetic operator, for example, and the "instanceof" check would still work on instruction classes created this way.
Seems fine. How will this impact performance?
- I've been brainstorming about inline IR, and how to code handlers to
implement things like function calls and arithmetic operators. It seems to me like it's still not 100% clear how to factor these in terms of JavaScript code and inline IR, or even how to structure the lower levels of IR to make them most convenient to use.
I was discussing with Erick, and I was thinking that, for a first version of our compiler, we could write all handlers for function calls, arithmetic operators, etc., in x86 directly. Once we see what kind of code has to actually go in there, it will become clearer how we can refactor this into higher level IR to make it more optimizable/specializable/portable. For a first compiler version, we could write handlers to only support a few simple types, and a mimimal subset of the language in this way.
That's what I did for the first version of Gambit. I ended up with more than 5000 lines of assembly code... not a good thing.
But I'm OK with the idea. If we use our x86 assembler for this, we can even write abstractions (in JavaScript) for particular patterns of x86 code (in effect a kind of virtual machine layer). For example, we could define that ARG1 will refer to the register that contains the first parameter of a function, so that it is easy to change this without having to rewrite the assembly code.
Marc