SpiderMonkey's secret object sauce

Well not really secret since SpiderMonkey is open source, but I tried to think of a cool title. A lot of the benchmarks show that SpiderMonkey is significantly faster than Tamarin. This has been mostly attributed to the way SpiderMonkey accesses object properties. SpiderMonkey has an implementation of the shape idea. A shape is a unique identifier which details the structure of an object, and allows for a quick lookup of a given property. I think it will be easier to explain shapes by showing how SpiderMonkey uses them. When SpiderMonkey looks up a property, for example with the opcode JSOP_GETPROP, it does the following:

jsinterp.c
do_getprop_with_obj:
do {
JSPropCacheEntry *entry;

if (JS_LIKELY(obj->map->ops->getProperty == js_GetProperty)) {
PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom);

The macro PROPERTY_CACHE_TEST expands into a large chunk of code, but the important line is here:
jsinterp.c  
JSPropertyCache *cache_ = &JS_PROPERTY_CACHE(cx); \
uint32 kshape_ = (JS_ASSERT(OBJ_IS_NATIVE(obj)), \
OBJ_SCOPE(obj)->shape); \
entry = &cache_->table[PROPERTY_CACHE_HASH_PC(pc, kshape_)]; \
Therefore, properties and the shape, are attached to a Scope. Hence global variables are attached to the global scope, and object properties are attached to the object instance's scope. The PROPERTY_CACHE_HASH_PC expands to:
jsinterp.h
#define PROPERTY_CACHE_HASH(pc,kshape) \
((((jsuword)(pc) >> PROPERTY_CACHE_LOG2) ^ (jsuword)(pc) ^ (kshape)) & \
PROPERTY_CACHE_MASK)

#define PROPERTY_CACHE_HASH_PC(pc,kshape) \
PROPERTY_CACHE_HASH(pc, kshape)

With a few bit operations, given an object's shape and the current program location, we can find a property's address. However, what happens if we have a property cache miss? SpiderMonkey tries to use another combination which includes the object instance of the property. If the quick property cache fails we see the following:
jsinterp.c
#define PROPERTY_CACHE_TEST
// Test quick access

// if property not found
atom = js_FullTestPropertyCache(cx, pc, &obj, &pobj, &entry); \
if (atom) \
PCMETER(cache_->misses++);

jsinterp.c
js_FullTestPropertyCache
entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_ATOM(atom, obj, NULL)];

#define PROPERTY_CACHE_HASH_ATOM(atom,obj,pobj) \
PROPERTY_CACHE_HASH((jsuword)(atom) >> 2, OBJ_SCOPE(obj)->shape)

If the property cannot be found, SpiderMonkey does a full property lookup in the property tree. This includes going up through the prototype chain to find the property. After enough local properties have been added, the object property lookup simply becomes a hash lookup. In quick summary, based on the current program counter and scope shape, we look for a given property. If it is not found, we include the object instance the property should be in. Finally, if nothing is found, we do a full property look up.

Now the only question is, how do we determine what to put in the cache, and when? The function that fills the property cache is in jsinterp.c in function js_FillPropertyCache. This function will take a given shape and allocate some space in the property cache associated with the given shape. js_FillPropertyCache is called upon the setting/getting of a property when the property could not be found in the cache, and a full property lookup occurred.