The tags are mainly used for the dynamic typing required at run time to
ensure the types are correct for the operation being executed. For operations like +, they are used to detect the common case (adding two fixnums) and treat it inline.
I would make a reasonable guess that the most common cases would be testing for immediate integers, testing if something is an object with properties (object, function, array), and testing is something is a function (eg: before making a call).
Why do we need to reserve a tag for 4 constants? I could see having a
single tag for all the false values to make testing for false quick.
The way I'd originally proposed was to have a specific bit pattern for true/false/null/undefined. This way, we don't need to do any tag-masking, we can compare the boxed value against the pattern for false to know if its false (eg: box == FALSE_BIT_PATTERN).
That being said, these constants could possibly be fit under the "other" category, to allow for 1 bit longer integer constants.
how do you intend to implement setter/getters?
Probably by having some kind of object with references to two functions inside it. References to getter-setter objects can then be stored in hash tables as normal values.
Why do references to hash-tables need to be tagged?
They would fit under "other" so that the GC only ever has to deal with references in the form of boxed values. They wouldn't have a specific tag of their own. They would all be lumped under the same tag.
I thought we had said "strings" would be included in "objects". If
there is an advantage to split them, what is it?
Strings can't have properties of their own. They're not actual objects in JavaScript (they don't have the type object).
There is a question of how fast we want to be able to test if something is a string. Are we willing to pay the cost of having to read the object's header to do so, or should we have a tag for strings?
I suspect that the test "is_a_function" will occur often in unoptimized code (because it is logically part of all function calls), so it would be good to find ways to reduce its cost. In a previous message I explained how this can be done for the global object (i.e. calling f(X) will jump to a trampoline for f in the global object, which will jump to an error handler if f is not bound to a function). To handle dynamic calls efficiently we could start all objects with machine code. For all object types except "function", the machine code would be a short sequence (probably 3 bytes) which calls an error handler. That way a function call would always jump to the object, and it is the object that signals that it is not a function.
I have a few concerns about that sort of scheme:
1. I'm not sure how we could integrate this with the back-end in a convenient and portable way.
2. It's also rather convoluted. It means special-casing the global object, and all objects that may end up being passed as potential callees.
3. The scheme may also be flawed. Immediate integers, for example, aren't pointers to anything, so there still always needs to be a test before function calls (is_not_integer)... We might as well, in my opinion, implement an efficient is_function, it would be simpler, and probably comparable in speed to is_not_integer. Not to mention the performance implications of having unnecessary instructions getting executed, trampoline calls, and bloating objects a little more.
I guess the message I want to convey is that any proposed tagging scheme should come with a justification of why it is efficient, i.e. machine code templates for the frequent operations.
Well, it's the usual chicken and egg problem. Not yet having a working JavaScript engine, we can't benchmark it... To benchmark it, we need to implement something. So we'll probably want to make educated guesses. They probably won't be such bad guesses seeing we have your experience with Gambit, papers about typical JavaScript behavior, and lots of other existing compiler implementations to base ourselves on.
If we lump the constants together with the "other" category, we can free up a bit for immediate integers. Also, if we free up another tag (eg: string), or lump strings with the objects, we could make it so that testing if something is an object only requires checking if the lowest bit is 1. This is assuming that testing if the lowest bit is 1 is faster than checking if the tag is greater than some constant on x86 (eg: box & TAG_MASK > 4).
Tags needed: - immediate int X-----X00
- object X----X011 - function obj X----X101 - array obj X----X111
- float X----X110 - other X----X010 - string - constants (true/false/undef/null) - other heap objects (getter-setter, hash table, etc.)
As an interesting aside, V8 only has one tag bit I believe. Lowest bit 0 for integers, 1 for references.
- Maxime