From b74384b5c86d61f50366306405ec5930268483cf Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Fri, 18 Sep 2009 08:55:01 -0500 Subject: [PATCH] Backed out changeset 3f508cfdfa36 (bug 500431) due to tinderbox orangeness --- js/src/Makefile.in | 2 - js/src/jsbuiltins.cpp | 1 - js/src/jscntxt.cpp | 3 +- js/src/jscntxt.h | 6 + js/src/jsinterp.cpp | 1 - js/src/jsobj.cpp | 18 ++- js/src/jsops.cpp | 306 ++++++++++++++++++++---------------- js/src/jspropcache.cpp | 210 ++++++++++++++----------- js/src/jspropcache.h | 220 +++++++++++++++----------- js/src/jspropcacheinlines.h | 223 -------------------------- js/src/jsscript.cpp | 4 +- js/src/jstracer.cpp | 5 +- 12 files changed, 438 insertions(+), 561 deletions(-) delete mode 100644 js/src/jspropcacheinlines.h diff --git a/js/src/Makefile.in b/js/src/Makefile.in index b2eb3829353..054e0d2bd01 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -201,8 +201,6 @@ INSTALLED_HEADERS = \ jsotypes.h \ jsparse.h \ jsprf.h \ - jspropcache.h \ - jspropcacheinlines.h \ jsproto.tbl \ jsprvtd.h \ jspubtd.h \ diff --git a/js/src/jsbuiltins.cpp b/js/src/jsbuiltins.cpp index 02714169ea1..c065608c747 100644 --- a/js/src/jsbuiltins.cpp +++ b/js/src/jsbuiltins.cpp @@ -61,7 +61,6 @@ #include "jsvector.h" #include "jsatominlines.h" -#include "jspropcacheinlines.h" using namespace avmplus; using namespace nanojit; diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index b0af20f8de9..c4f4f5efd3b 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -98,6 +98,7 @@ FinishThreadData(JSThreadData *data) #endif js_FinishGSNCache(&data->gsnCache); + js_FinishPropertyCache(&data->propertyCache); #if defined JS_TRACER js_FinishJIT(&data->traceMonitor); #endif @@ -109,7 +110,7 @@ PurgeThreadData(JSContext *cx, JSThreadData *data) js_PurgeGSNCache(&data->gsnCache); /* FIXME: bug 506341. */ - data->propertyCache.purge(cx); + js_PurgePropertyCache(cx, &data->propertyCache); # ifdef JS_TRACER JSTraceMonitor *tm = &data->traceMonitor; diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 5d9970adfee..612019ba904 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -1702,6 +1702,12 @@ js_GetTopStackFrame(JSContext *cx) return cx->fp; } +static JS_INLINE JSBool +js_IsPropertyCacheDisabled(JSContext *cx) +{ + return cx->runtime->shapeGen >= SHAPE_OVERFLOW_BIT; +} + static JS_INLINE uint32 js_RegenerateShapeForGC(JSContext *cx) { diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index e3785f99b90..04db29144e1 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -84,7 +84,6 @@ #endif #include "jsatominlines.h" -#include "jspropcacheinlines.h" #include "jsscriptinlines.h" #include "jsautooplen.h" diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index d6e0b4dd4ab..d6be9d3d493 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -3838,7 +3838,7 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, if (defineHow & JSDNP_CACHE_RESULT) { JS_ASSERT_NOT_ON_TRACE(cx); JSPropCacheEntry *entry; - entry = JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, sprop, added); + entry = js_FillPropertyCache(cx, obj, 0, 0, obj, sprop, added); TRACE_2(SetPropHit, entry, sprop); } if (propp) @@ -4090,8 +4090,9 @@ js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult, } #endif if (cacheResult) { - entry = JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, protoIndex, pobj, - (JSScopeProperty *) prop, false); + entry = js_FillPropertyCache(cx, scopeChain, + scopeIndex, protoIndex, pobj, + (JSScopeProperty *) prop, false); } SCOPE_DEPTH_ACCUM(&rt->scopeSearchDepthStats, scopeIndex); goto out; @@ -4172,8 +4173,9 @@ js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id) #ifdef DEBUG JSPropCacheEntry *entry = #endif - JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, protoIndex, pobj, - (JSScopeProperty *) prop, false); + js_FillPropertyCache(cx, scopeChain, + scopeIndex, protoIndex, pobj, + (JSScopeProperty *) prop, false); JS_ASSERT(entry); JS_UNLOCK_OBJ(cx, pobj); return obj; @@ -4419,7 +4421,7 @@ js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN getHow, if (getHow & JSGET_CACHE_RESULT) { JS_ASSERT_NOT_ON_TRACE(cx); - JS_PROPERTY_CACHE(cx).fill(cx, aobj, 0, protoIndex, obj2, sprop, false); + js_FillPropertyCache(cx, aobj, 0, protoIndex, obj2, sprop, false); } if (!js_NativeGet(cx, obj, obj2, sprop, getHow, vp)) @@ -4597,7 +4599,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, if (attrs & JSPROP_SHARED) { if (defineHow & JSDNP_CACHE_RESULT) { JSPropCacheEntry *entry; - entry = JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, protoIndex, pobj, sprop, false); + entry = js_FillPropertyCache(cx, obj, 0, protoIndex, pobj, sprop, false); TRACE_2(SetPropHit, entry, sprop); } @@ -4699,7 +4701,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, if (defineHow & JSDNP_CACHE_RESULT) { JSPropCacheEntry *entry; - entry = JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, sprop, added); + entry = js_FillPropertyCache(cx, obj, 0, 0, obj, sprop, added); TRACE_2(SetPropHit, entry, sprop); } diff --git a/js/src/jsops.cpp b/js/src/jsops.cpp index cb730513edd..2557b9ecac3 100644 --- a/js/src/jsops.cpp +++ b/js/src/jsops.cpp @@ -1682,8 +1682,11 @@ entry = NULL; atom = NULL; if (JS_LIKELY(obj->map->ops->setProperty == js_SetProperty)) { + JSPropertyCache *cache = &JS_PROPERTY_CACHE(cx); + uint32 kshape = OBJ_SHAPE(obj); + /* - * Open-code JSPropertyCache::test, specializing for two + * Open-code PROPERTY_CACHE_TEST, specializing for two * important set-property cases. First: * * function f(a, b, c) { @@ -1701,149 +1704,161 @@ * will (possibly after the first iteration) always exist * in native object o. */ - if (JS_PROPERTY_CACHE(cx).testForSet(cx, regs.pc, &obj, - &obj2, &entry, &atom)) { - JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); - sprop = PCVAL_TO_SPROP(entry->vword); - JS_ASSERT(!(sprop->attrs & JSPROP_READONLY)); - JS_ASSERT_IF(!(sprop->attrs & JSPROP_SHARED), - PCVCAP_TAG(entry->vcap) == 0); + entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)]; + PCMETER(cache->pctestentry = entry); + PCMETER(cache->tests++); + PCMETER(cache->settests++); + if (entry->kpc == regs.pc && entry->kshape == kshape) { + JS_ASSERT(PCVCAP_TAG(entry->vcap) <= 1); + if (JS_LOCK_OBJ_IF_SHAPE(cx, obj, kshape)) { + JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); + sprop = PCVAL_TO_SPROP(entry->vword); + JS_ASSERT(!(sprop->attrs & JSPROP_READONLY)); + JS_ASSERT_IF(!(sprop->attrs & JSPROP_SHARED), + PCVCAP_TAG(entry->vcap) == 0); - JSScope *scope = OBJ_SCOPE(obj); - JS_ASSERT(!scope->sealed()); + JSScope *scope = OBJ_SCOPE(obj); + JS_ASSERT(!scope->sealed()); - /* - * Fastest path: check whether the cached sprop is - * already in scope and call NATIVE_SET and break to - * get out of the do-while(0). But we can call - * NATIVE_SET only if obj owns scope or sprop is - * shared. - */ - bool checkForAdd; - if (sprop->attrs & JSPROP_SHARED) { - if (PCVCAP_TAG(entry->vcap) == 0 || - ((obj2 = OBJ_GET_PROTO(cx, obj)) && - OBJ_IS_NATIVE(obj2) && - OBJ_SHAPE(obj2) == PCVCAP_SHAPE(entry->vcap))) { - goto fast_set_propcache_hit; + /* + * Fastest path: check whether the cached sprop is + * already in scope and call NATIVE_SET and break + * to get out of the do-while(0). But we can call + * NATIVE_SET only if obj owns scope or sprop is + * shared. + */ + bool checkForAdd; + if (sprop->attrs & JSPROP_SHARED) { + if (PCVCAP_TAG(entry->vcap) == 0 || + ((obj2 = OBJ_GET_PROTO(cx, obj)) && + OBJ_IS_NATIVE(obj2) && + OBJ_SHAPE(obj2) == PCVCAP_SHAPE(entry->vcap))) { + goto fast_set_propcache_hit; + } + + /* The cache entry doesn't apply. vshape mismatch. */ + checkForAdd = false; + } else if (scope->owned()) { + if (sprop == scope->lastProp || scope->has(sprop)) { + fast_set_propcache_hit: + PCMETER(cache->pchits++); + PCMETER(cache->setpchits++); + NATIVE_SET(cx, obj, sprop, entry, &rval); + JS_UNLOCK_SCOPE(cx, scope); + break; + } + checkForAdd = + !(sprop->attrs & JSPROP_SHARED) && + sprop->parent == scope->lastProp && + !scope->hadMiddleDelete(); + } else { + scope = js_GetMutableScope(cx, obj); + if (!scope) { + JS_UNLOCK_OBJ(cx, obj); + goto error; + } + checkForAdd = !sprop->parent; } - /* The cache entry doesn't apply. vshape mismatch. */ - checkForAdd = false; - } else if (scope->owned()) { - if (sprop == scope->lastProp || scope->has(sprop)) { - fast_set_propcache_hit: + if (checkForAdd && + SPROP_HAS_STUB_SETTER(sprop) && + (slot = sprop->slot) == scope->freeslot) { + /* + * Fast path: adding a plain old property that + * was once at the frontier of the property + * tree, whose slot is next to claim among the + * allocated slots in obj, where scope->table + * has not been created yet. + * + * We may want to remove hazard conditions + * above and inline compensation code here, + * depending on real-world workloads. + */ + JS_ASSERT(!(obj->getClass()->flags & + JSCLASS_SHARE_ALL_PROPERTIES)); + PCMETER(cache->pchits++); - PCMETER(cache->setpchits++); - NATIVE_SET(cx, obj, sprop, entry, &rval); + PCMETER(cache->addpchits++); + + /* + * Beware classes such as Function that use + * the reserveSlots hook to allocate a number + * of reserved slots that may vary with obj. + */ + if (slot < STOBJ_NSLOTS(obj) && + !OBJ_GET_CLASS(cx, obj)->reserveSlots) { + ++scope->freeslot; + } else { + if (!js_AllocSlot(cx, obj, &slot)) { + JS_UNLOCK_SCOPE(cx, scope); + goto error; + } + } + + /* + * If this obj's number of reserved slots + * differed, or if something created a hash + * table for scope, we must pay the price of + * JSScope::add. + * + * If slot does not match the cached sprop's + * slot, update the cache entry in the hope + * that obj and other instances with the same + * number of reserved slots are now "hot". + */ + if (slot != sprop->slot || scope->table) { + JSScopeProperty *sprop2 = + scope->add(cx, sprop->id, + sprop->getter, sprop->setter, + slot, sprop->attrs, + sprop->flags, sprop->shortid); + if (!sprop2) { + js_FreeSlot(cx, obj, slot); + JS_UNLOCK_SCOPE(cx, scope); + goto error; + } + if (sprop2 != sprop) { + PCMETER(cache->slotchanges++); + JS_ASSERT(slot != sprop->slot && + slot == sprop2->slot && + sprop2->id == sprop->id); + entry->vword = SPROP_TO_PCVAL(sprop2); + } + sprop = sprop2; + } else { + scope->extend(cx, sprop); + } + + /* + * No method change check here because here we + * are adding a new property, not updating an + * existing slot's value that might contain a + * method of a branded scope. + */ + TRACE_2(SetPropHit, entry, sprop); + LOCKED_OBJ_SET_SLOT(obj, slot, rval); JS_UNLOCK_SCOPE(cx, scope); + + /* + * Purge the property cache of the id we may + * have just shadowed in obj's scope and proto + * chains. We do this after unlocking obj's + * scope to avoid lock nesting. + */ + js_PurgeScopeChain(cx, obj, sprop->id); break; } - checkForAdd = - !(sprop->attrs & JSPROP_SHARED) && - sprop->parent == scope->lastProp && - !scope->hadMiddleDelete(); - } else { - scope = js_GetMutableScope(cx, obj); - if (!scope) { - JS_UNLOCK_OBJ(cx, obj); - goto error; - } - checkForAdd = !sprop->parent; - } - - if (checkForAdd && - SPROP_HAS_STUB_SETTER(sprop) && - (slot = sprop->slot) == scope->freeslot) { - /* - * Fast path: adding a plain old property that was - * once at the frontier of the property tree, whose - * slot is next to claim among the allocated slots - * in obj, where scope->table has not been created - * yet. - * - * We may want to remove hazard conditions above - * and inline compensation code here, depending on - * real-world workloads. - */ - JS_ASSERT(!(obj->getClass()->flags & - JSCLASS_SHARE_ALL_PROPERTIES)); - - PCMETER(cache->pchits++); - PCMETER(cache->addpchits++); - - /* - * Beware classes such as Function that use the - * reserveSlots hook to allocate a number of - * reserved slots that may vary with obj. - */ - if (slot < STOBJ_NSLOTS(obj) && - !OBJ_GET_CLASS(cx, obj)->reserveSlots) { - ++scope->freeslot; - } else { - if (!js_AllocSlot(cx, obj, &slot)) { - JS_UNLOCK_SCOPE(cx, scope); - goto error; - } - } - - /* - * If this obj's number of reserved slots differed, - * or if something created a hash table for scope, - * we must pay the price of JSScope::add. - * - * If slot does not match the cached sprop's slot, - * update the cache entry in the hope that obj and - * other instances with the same number of reserved - * slots are now "hot". - */ - if (slot != sprop->slot || scope->table) { - JSScopeProperty *sprop2 = - scope->add(cx, sprop->id, - sprop->getter, sprop->setter, - slot, sprop->attrs, - sprop->flags, sprop->shortid); - if (!sprop2) { - js_FreeSlot(cx, obj, slot); - JS_UNLOCK_SCOPE(cx, scope); - goto error; - } - if (sprop2 != sprop) { - PCMETER(cache->slotchanges++); - JS_ASSERT(slot != sprop->slot && - slot == sprop2->slot && - sprop2->id == sprop->id); - entry->vword = SPROP_TO_PCVAL(sprop2); - } - sprop = sprop2; - } else { - scope->extend(cx, sprop); - } - - /* - * No method change check here because here we are - * adding a new property, not updating an existing - * slot's value that might contain a method of a - * branded scope. - */ - TRACE_2(SetPropHit, entry, sprop); - LOCKED_OBJ_SET_SLOT(obj, slot, rval); JS_UNLOCK_SCOPE(cx, scope); - - /* - * Purge the property cache of the id we may have - * just shadowed in obj's scope and proto - * chains. We do this after unlocking obj's scope - * to avoid lock nesting. - */ - js_PurgeScopeChain(cx, obj, sprop->id); - break; + PCMETER(cache->setpcmisses++); } - JS_UNLOCK_SCOPE(cx, scope); - PCMETER(cache->setpcmisses++); } - if (!atom) { + atom = js_FullTestPropertyCache(cx, regs.pc, &obj, &obj2, + &entry); + if (atom) { + PCMETER(cache->misses++); + PCMETER(cache->setmisses++); + } else { ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry); sprop = NULL; if (obj == obj2) { @@ -3448,16 +3463,37 @@ JS_ASSERT(!(obj->getClass()->flags & JSCLASS_SHARE_ALL_PROPERTIES)); do { + JSScope *scope; + uint32 kshape; + JSPropertyCache *cache; + JSPropCacheEntry *entry; + JS_LOCK_OBJ(cx, obj); - JSScope *scope = OBJ_SCOPE(obj); + scope = OBJ_SCOPE(obj); // FIXME: bug 513291 -- uncomment this assertion and remove the // (!scope->owned()) => js_GetMutableScope code further // below. // JS_ASSERT(scope->object == obj); JS_ASSERT(!scope->sealed()); + kshape = scope->shape; + cache = &JS_PROPERTY_CACHE(cx); + entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)]; + PCMETER(cache->pctestentry = entry); + PCMETER(cache->tests++); + PCMETER(cache->initests++); + + if (entry->kpc == regs.pc && + entry->kshape == kshape && + PCVCAP_SHAPE(entry->vcap) == rt->protoHazardShape) { + JS_ASSERT(PCVCAP_TAG(entry->vcap) == 0); + + PCMETER(cache->pchits++); + PCMETER(cache->inipchits++); + + JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); + sprop = PCVAL_TO_SPROP(entry->vword); + JS_ASSERT(!(sprop->attrs & JSPROP_READONLY)); - JSPropCacheEntry *entry; - if (JS_PROPERTY_CACHE(cx).testForInit(cx, regs.pc, obj, &entry, &sprop)) { /* * If this property has a non-stub setter, it must be * __proto__, __parent__, or another "shared prototype" diff --git a/js/src/jspropcache.cpp b/js/src/jspropcache.cpp index 3b96963da58..c6a11c7ff28 100644 --- a/js/src/jspropcache.cpp +++ b/js/src/jspropcache.cpp @@ -1,5 +1,5 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99: + * vim: set ts=8 sw=4 et tw=79: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -42,28 +42,31 @@ #include "jsapi.h" #include "jscntxt.h" #include "jsinterp.h" +#include "jsobj.h" #include "jsscope.h" -#include "jspropcacheinlines.h" JS_REQUIRES_STACK JSPropCacheEntry * -JSPropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, uintN protoIndex, - JSObject *pobj, JSScopeProperty *sprop, JSBool adding) +js_FillPropertyCache(JSContext *cx, JSObject *obj, + uintN scopeIndex, uintN protoIndex, JSObject *pobj, + JSScopeProperty *sprop, JSBool adding) { + JSPropertyCache *cache; jsbytecode *pc; JSScope *scope; - jsuword kshape, vshape; + jsuword kshape, vshape, khash; JSOp op; const JSCodeSpec *cs; jsuword vword; ptrdiff_t pcoff; JSAtom *atom; + JSPropCacheEntry *entry; JS_ASSERT(!cx->runtime->gcRunning); - JS_ASSERT(this == &JS_PROPERTY_CACHE(cx)); + cache = &JS_PROPERTY_CACHE(cx); /* FIXME bug 489098: consider enabling the property cache for eval. */ if (js_IsPropertyCacheDisabled(cx) || (cx->fp->flags & JSFRAME_EVAL)) { - PCMETER(disfills++); + PCMETER(cache->disfills++); return JS_NO_PROP_CACHE_FILL; } @@ -73,7 +76,7 @@ JSPropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, uintN prot */ scope = OBJ_SCOPE(pobj); if (!scope->has(sprop)) { - PCMETER(oddfills++); + PCMETER(cache->oddfills++); return JS_NO_PROP_CACHE_FILL; } @@ -109,7 +112,7 @@ JSPropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, uintN prot * mutate in arbitrary way without changing any shapes. */ if (!tmp || !OBJ_IS_NATIVE(tmp)) { - PCMETER(noprotos++); + PCMETER(cache->noprotos++); return JS_NO_PROP_CACHE_FILL; } if (tmp == pobj) @@ -119,7 +122,7 @@ JSPropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, uintN prot } if (scopeIndex > PCVCAP_SCOPEMASK || protoIndex > PCVCAP_PROTOMASK) { - PCMETER(longchains++); + PCMETER(cache->longchains++); return JS_NO_PROP_CACHE_FILL; } @@ -171,7 +174,7 @@ JSPropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, uintN prot * object will result in shape being regenerated. */ if (!scope->branded()) { - PCMETER(brandfills++); + PCMETER(cache->brandfills++); #ifdef DEBUG_notme fprintf(stderr, "branding %p (%s) for funobj %p (%s), shape %lu\n", @@ -266,6 +269,7 @@ JSPropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, uintN prot vshape = scope->shape; } + khash = PROPERTY_CACHE_HASH_PC(pc, kshape); if (obj == pobj) { JS_ASSERT(scopeIndex == 0 && protoIndex == 0); } else { @@ -284,6 +288,12 @@ JSPropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, uintN prot #endif if (scopeIndex != 0 || protoIndex != 1) { + khash = PROPERTY_CACHE_HASH_ATOM(atom, obj); + PCMETER(if (PCVCAP_TAG(cache->table[khash].vcap) <= 1) + cache->pcrecycles++); + pc = (jsbytecode *) atom; + kshape = (jsuword) obj; + /* * Make sure that a later shadowing assignment will enter * PurgeProtoChain and invalidate this entry, bug 479198. @@ -295,16 +305,31 @@ JSPropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, uintN prot * because the property cache and JIT cache are thread-local. */ obj->setDelegate(); - - return fillByAtom(atom, obj, vshape, scopeIndex, protoIndex, vword); } } - return fillByPC(pc, kshape, vshape, scopeIndex, protoIndex, vword); + entry = &cache->table[khash]; + PCMETER(PCVAL_IS_NULL(entry->vword) || cache->recycles++); + entry->kpc = pc; + entry->kshape = kshape; + entry->vcap = PCVCAP_MAKE(vshape, scopeIndex, protoIndex); + entry->vword = vword; + + cache->empty = JS_FALSE; + PCMETER(cache->fills++); + + /* + * The modfills counter is not exact. It increases if a getter or setter + * recurse into the interpreter. + */ + PCMETER(entry == cache->pctestentry || cache->modfills++); + PCMETER(cache->pctestentry = NULL); + return entry; } JS_REQUIRES_STACK JSAtom * -JSPropertyCache::fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp, JSObject **pobjp, +js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc, + JSObject **objp, JSObject **pobjp, JSPropCacheEntry **entryp) { JSOp op; @@ -329,15 +354,15 @@ JSPropertyCache::fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp, JSObje obj = *objp; JS_ASSERT(OBJ_IS_NATIVE(obj)); - entry = &table[PROPERTY_CACHE_HASH_ATOM(atom, obj)]; + entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_ATOM(atom, obj)]; *entryp = entry; vcap = entry->vcap; if (entry->kpc != (jsbytecode *) atom) { - PCMETER(idmisses++); + PCMETER(JS_PROPERTY_CACHE(cx).idmisses++); #ifdef DEBUG_notme - entry = &table[PROPERTY_CACHE_HASH_PC(pc, OBJ_SHAPE(obj))]; + entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_PC(pc, OBJ_SHAPE(obj))]; fprintf(stderr, "id miss for %s from %s:%u" " (pc %u, kpc %u, kshape %u, shape %u)\n", @@ -357,7 +382,7 @@ JSPropertyCache::fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp, JSObje } if (entry->kshape != (jsuword) obj) { - PCMETER(komisses++); + PCMETER(JS_PROPERTY_CACHE(cx).komisses++); return atom; } @@ -395,104 +420,107 @@ JSPropertyCache::fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp, JSObje return NULL; } - PCMETER(vcmisses++); + PCMETER(JS_PROPERTY_CACHE(cx).vcmisses++); return atom; } -inline void -JSPropertyCache::assertEmpty() -{ #ifdef DEBUG - JS_ASSERT(empty); - for (uintN i = 0; i < PROPERTY_CACHE_SIZE; i++) { - JS_ASSERT(!table[i].kpc); - JS_ASSERT(!table[i].kshape); - JS_ASSERT(!table[i].vcap); - JS_ASSERT(!table[i].vword); - } +#define ASSERT_CACHE_IS_EMPTY(cache) \ + JS_BEGIN_MACRO \ + JSPropertyCache *cache_ = (cache); \ + uintN i_; \ + JS_ASSERT(cache_->empty); \ + for (i_ = 0; i_ < PROPERTY_CACHE_SIZE; i_++) { \ + JS_ASSERT(!cache_->table[i_].kpc); \ + JS_ASSERT(!cache_->table[i_].kshape); \ + JS_ASSERT(!cache_->table[i_].vcap); \ + JS_ASSERT(!cache_->table[i_].vword); \ + } \ + JS_END_MACRO +#else +#define ASSERT_CACHE_IS_EMPTY(cache) ((void)0) #endif -} - JS_STATIC_ASSERT(PCVAL_NULL == 0); void -JSPropertyCache::purge(JSContext *cx) +js_PurgePropertyCache(JSContext *cx, JSPropertyCache *cache) { - JS_ASSERT(this == &JS_PROPERTY_CACHE(cx)); - - if (empty) { - assertEmpty(); + if (cache->empty) { + ASSERT_CACHE_IS_EMPTY(cache); return; } - memset(table, 0, sizeof table); - empty = JS_TRUE; + memset(cache->table, 0, sizeof cache->table); + cache->empty = JS_TRUE; #ifdef JS_PROPERTY_CACHE_METERING - { - static FILE *fp; - if (!fp) - fp = fopen("/tmp/propcache.stats", "w"); - if (fp) { - fputs("Property cache stats for ", fp); -# ifdef JS_THREADSAFE - fprintf(fp, "thread %lu, ", (unsigned long) cx->thread->id); -# endif - fprintf(fp, "GC %u\n", cx->runtime->gcNumber); + { static FILE *fp; + if (!fp) + fp = fopen("/tmp/propcache.stats", "w"); + if (fp) { + fputs("Property cache stats for ", fp); +#ifdef JS_THREADSAFE + fprintf(fp, "thread %lu, ", (unsigned long) cx->thread->id); +#endif + fprintf(fp, "GC %u\n", cx->runtime->gcNumber); -# define P(mem) fprintf(fp, "%11s %10lu\n", #mem, (unsigned long)mem) - P(fills); - P(nofills); - P(rofills); - P(disfills); - P(oddfills); - P(modfills); - P(brandfills); - P(noprotos); - P(longchains); - P(recycles); - P(pcrecycles); - P(tests); - P(pchits); - P(protopchits); - P(initests); - P(inipchits); - P(inipcmisses); - P(settests); - P(addpchits); - P(setpchits); - P(setpcmisses); - P(slotchanges); - P(setmisses); - P(idmisses); - P(komisses); - P(vcmisses); - P(misses); - P(flushes); - P(pcpurges); +# define P(mem) fprintf(fp, "%11s %10lu\n", #mem, (unsigned long)cache->mem) + P(fills); + P(nofills); + P(rofills); + P(disfills); + P(oddfills); + P(modfills); + P(brandfills); + P(noprotos); + P(longchains); + P(recycles); + P(pcrecycles); + P(tests); + P(pchits); + P(protopchits); + P(initests); + P(inipchits); + P(inipcmisses); + P(settests); + P(addpchits); + P(setpchits); + P(setpcmisses); + P(slotchanges); + P(setmisses); + P(idmisses); + P(komisses); + P(vcmisses); + P(misses); + P(flushes); + P(pcpurges); # undef P - fprintf(fp, "hit rates: pc %g%% (proto %g%%), set %g%%, ini %g%%, full %g%%\n", - (100. * pchits) / tests, - (100. * protopchits) / tests, - (100. * (addpchits + setpchits)) / settests, - (100. * inipchits) / initests, - (100. * (tests - misses)) / tests); - fflush(fp); - } + fprintf(fp, "hit rates: pc %g%% (proto %g%%), set %g%%, ini %g%%, full %g%%\n", + (100. * cache->pchits) / cache->tests, + (100. * cache->protopchits) / cache->tests, + (100. * (cache->addpchits + cache->setpchits)) + / cache->settests, + (100. * cache->inipchits) / cache->initests, + (100. * (cache->tests - cache->misses)) / cache->tests); + fflush(fp); } + } #endif - PCMETER(flushes++); + PCMETER(cache->flushes++); } void -JSPropertyCache::purgeForScript(JSContext *cx, JSScript *script) +js_PurgePropertyCacheForScript(JSContext *cx, JSScript *script) { - JS_ASSERT(this == &JS_PROPERTY_CACHE(cx)); + JSPropertyCache *cache; + JSPropCacheEntry *entry; - for (JSPropCacheEntry *entry = table; entry < table + PROPERTY_CACHE_SIZE; entry++) { + cache = &JS_PROPERTY_CACHE(cx); + for (entry = cache->table; entry < cache->table + PROPERTY_CACHE_SIZE; + entry++) { if (JS_UPTRDIFF(entry->kpc, script->code) < script->length) { entry->kpc = NULL; entry->kshape = 0; diff --git a/js/src/jspropcache.h b/js/src/jspropcache.h index bfb45974298..9c09a8c662f 100644 --- a/js/src/jspropcache.h +++ b/js/src/jspropcache.h @@ -46,33 +46,46 @@ * polymorphic callsite method/get/set speedups. For details, see * . */ -const int PROPERTY_CACHE_LOG2 = 12; -const jsuword PROPERTY_CACHE_SIZE = JS_BIT(PROPERTY_CACHE_LOG2); -const jsuword PROPERTY_CACHE_MASK = JS_BITMASK(PROPERTY_CACHE_LOG2); +#define PROPERTY_CACHE_LOG2 12 +#define PROPERTY_CACHE_SIZE JS_BIT(PROPERTY_CACHE_LOG2) +#define PROPERTY_CACHE_MASK JS_BITMASK(PROPERTY_CACHE_LOG2) + +/* + * Add kshape rather than xor it to avoid collisions between nearby bytecode + * that are evolving an object by setting successive properties, incrementing + * the object's scope->shape on each set. + */ +#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) + +#define PROPERTY_CACHE_HASH_ATOM(atom,obj) \ + PROPERTY_CACHE_HASH((jsuword)(atom) >> 2, OBJ_SHAPE(obj)) /* * Property cache value capability macros. */ -const int PCVCAP_PROTOBITS = 4; -const jsuword PCVCAP_PROTOSIZE = JS_BIT(PCVCAP_PROTOBITS); -const jsuword PCVCAP_PROTOMASK = JS_BITMASK(PCVCAP_PROTOBITS); +#define PCVCAP_PROTOBITS 4 +#define PCVCAP_PROTOSIZE JS_BIT(PCVCAP_PROTOBITS) +#define PCVCAP_PROTOMASK JS_BITMASK(PCVCAP_PROTOBITS) -const int PCVCAP_SCOPEBITS = 4; -const jsuword PCVCAP_SCOPESIZE = JS_BIT(PCVCAP_SCOPEBITS); -const jsuword PCVCAP_SCOPEMASK = JS_BITMASK(PCVCAP_SCOPEBITS); +#define PCVCAP_SCOPEBITS 4 +#define PCVCAP_SCOPESIZE JS_BIT(PCVCAP_SCOPEBITS) +#define PCVCAP_SCOPEMASK JS_BITMASK(PCVCAP_SCOPEBITS) -const jsuword PCVCAP_TAGBITS = PCVCAP_PROTOBITS + PCVCAP_SCOPEBITS; -const jsuword PCVCAP_TAGMASK = JS_BITMASK(PCVCAP_TAGBITS); -inline jsuword PCVCAP_TAG(jsuword t) { return t & PCVCAP_TAGMASK; } +#define PCVCAP_TAGBITS (PCVCAP_PROTOBITS + PCVCAP_SCOPEBITS) +#define PCVCAP_TAGMASK JS_BITMASK(PCVCAP_TAGBITS) +#define PCVCAP_TAG(t) ((t) & PCVCAP_TAGMASK) -inline jsuword PCVCAP_MAKE(jsuword t, jsuword s, jsuword p) -{ - return (uint32(t) << PCVCAP_TAGBITS) | (s << PCVCAP_PROTOBITS) | p; -} +#define PCVCAP_MAKE(t,s,p) ((uint32(t) << PCVCAP_TAGBITS) | \ + ((s) << PCVCAP_PROTOBITS) | \ + (p)) +#define PCVCAP_SHAPE(t) ((t) >> PCVCAP_TAGBITS) -inline jsuword PCVCAP_SHAPE(jsuword t) { return t >> PCVCAP_TAGBITS; } - -const jsuword SHAPE_OVERFLOW_BIT = JS_BIT(32 - PCVCAP_TAGBITS); +#define SHAPE_OVERFLOW_BIT JS_BIT(32 - PCVCAP_TAGBITS) struct JSPropCacheEntry { jsbytecode *kpc; /* pc if vcap tag is <= 1, else atom */ @@ -93,62 +106,15 @@ struct JSPropCacheEntry { * Special value for functions returning JSPropCacheEntry * to distinguish * between failure and no no-cache-fill cases. */ -JSPropCacheEntry * const JS_NO_PROP_CACHE_FILL = (JSPropCacheEntry *) NULL + 1; +#define JS_NO_PROP_CACHE_FILL ((JSPropCacheEntry *) NULL + 1) #if defined DEBUG_brendan || defined DEBUG_brendaneich #define JS_PROPERTY_CACHE_METERING 1 #endif -class JSPropertyCache { -public: - JS_ALWAYS_INLINE JS_REQUIRES_STACK void - test(JSContext *cx, jsbytecode *pc, JSObject **objp, - JSObject **pobjp, JSPropCacheEntry **entryp, JSAtom **atomp); - - JS_ALWAYS_INLINE JS_REQUIRES_STACK bool - testForSet(JSContext *cx, jsbytecode *pc, JSObject **objp, - JSObject **pobjp, JSPropCacheEntry **entryp, JSAtom **atomp); - - JS_ALWAYS_INLINE bool - testForInit(JSContext *cx, jsbytecode *pc, JSObject *obj, - JSPropCacheEntry **entryp, JSScopeProperty **spropp); - - /* - * Fill property cache entry for key cx->fp->pc, optimized value word - * computed from obj and sprop, and entry capability forged from 24-bit - * OBJ_SHAPE(obj), 4-bit scopeIndex, and 4-bit protoIndex. - * - * Return the filled cache entry or JS_NO_PROP_CACHE_FILL if caching was - * not possible. - */ - JS_REQUIRES_STACK JSPropCacheEntry * - fill(JSContext *cx, JSObject *obj, uintN scopeIndex, uintN protoIndex, JSObject *pobj, - JSScopeProperty *sprop, JSBool adding); - - void purge(JSContext *cx); - void purgeForScript(JSContext *cx, JSScript *script); - -private: - JS_REQUIRES_STACK JSAtom * - fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp, JSObject **pobjp, - JSPropCacheEntry **entryp); - - inline void assertEmpty(); - - inline JSPropCacheEntry *fillEntry(jsuword khash, jsbytecode *pc, jsuword kshape, - jsuword vcap, jsuword vword); - - inline JSPropCacheEntry *fillByPC(jsbytecode *pc, jsuword kshape, - jsuword vshape, jsuword scopeIndex, jsuword protoIndex, - jsuword vword); - - inline JSPropCacheEntry *fillByAtom(JSAtom *atom, JSObject *obj, - jsuword vshape, jsuword scopeIndex, jsuword protoIndex, - jsuword vword); - +typedef struct JSPropertyCache { JSPropCacheEntry table[PROPERTY_CACHE_SIZE]; JSBool empty; - #ifdef JS_PROPERTY_CACHE_METERING JSPropCacheEntry *pctestentry; /* entry of the last PC-based test */ uint32 fills; /* number of cache entry fills */ @@ -187,43 +153,109 @@ private: #else # define PCMETER(x) ((void)0) #endif -}; +} JSPropertyCache; /* * Property cache value tagging/untagging macros. */ -const jsuword PCVAL_OBJECT = 0; -const jsuword PCVAL_SLOT = 1; -const jsuword PCVAL_SPROP = 2; +#define PCVAL_OBJECT 0 +#define PCVAL_SLOT 1 +#define PCVAL_SPROP 2 -const int PCVAL_TAGBITS = 2; -const jsuword PCVAL_TAGMASK = JS_BITMASK(PCVAL_TAGBITS); +#define PCVAL_TAGBITS 2 +#define PCVAL_TAGMASK JS_BITMASK(PCVAL_TAGBITS) +#define PCVAL_TAG(v) ((v) & PCVAL_TAGMASK) +#define PCVAL_CLRTAG(v) ((v) & ~(jsuword)PCVAL_TAGMASK) +#define PCVAL_SETTAG(v,t) ((jsuword)(v) | (t)) -inline jsuword PCVAL_TAG(jsuword v) { return v & PCVAL_TAGMASK; } -inline jsuword PCVAL_CLRTAG(jsuword v) { return v & ~PCVAL_TAGMASK; } -inline jsuword PCVAL_SETTAG(jsuword v, jsuword t) { return v | t; } +#define PCVAL_NULL 0 +#define PCVAL_IS_NULL(v) ((v) == PCVAL_NULL) -const jsuword PCVAL_NULL = 0; -inline bool PCVAL_IS_NULL(jsuword v) { return v == PCVAL_NULL; } +#define PCVAL_IS_OBJECT(v) (PCVAL_TAG(v) == PCVAL_OBJECT) +#define PCVAL_TO_OBJECT(v) ((JSObject *) (v)) +#define OBJECT_TO_PCVAL(obj) ((jsuword) (obj)) -inline bool PCVAL_IS_OBJECT(jsuword v) { return PCVAL_TAG(v) == PCVAL_OBJECT; } -inline JSObject *PCVAL_TO_OBJECT(jsuword v) { return (JSObject *) v; } -inline jsuword OBJECT_TO_PCVAL(JSObject *obj) { return (jsuword) obj; } +#define PCVAL_OBJECT_TO_JSVAL(v) OBJECT_TO_JSVAL(PCVAL_TO_OBJECT(v)) +#define JSVAL_OBJECT_TO_PCVAL(v) OBJECT_TO_PCVAL(JSVAL_TO_OBJECT(v)) -inline jsval PCVAL_OBJECT_TO_JSVAL(jsuword v) { return OBJECT_TO_JSVAL(PCVAL_TO_OBJECT(v)); } -inline jsuword JSVAL_OBJECT_TO_PCVAL(jsval v) { return OBJECT_TO_PCVAL(JSVAL_TO_OBJECT(v)); } +#define PCVAL_IS_SLOT(v) ((v) & PCVAL_SLOT) +#define PCVAL_TO_SLOT(v) ((jsuint)(v) >> 1) +#define SLOT_TO_PCVAL(i) (((jsuword)(i) << 1) | PCVAL_SLOT) -inline bool PCVAL_IS_SLOT(jsuword v) { return (v & PCVAL_SLOT) != 0; } -inline jsuint PCVAL_TO_SLOT(jsuword v) { return (jsuint) v >> 1; } -inline jsuword SLOT_TO_PCVAL(jsuint i) { return ((jsuword) i << 1) | PCVAL_SLOT; } +#define PCVAL_IS_SPROP(v) (PCVAL_TAG(v) == PCVAL_SPROP) +#define PCVAL_TO_SPROP(v) ((JSScopeProperty *) PCVAL_CLRTAG(v)) +#define SPROP_TO_PCVAL(sprop) PCVAL_SETTAG(sprop, PCVAL_SPROP) -inline bool PCVAL_IS_SPROP(jsuword v) { return PCVAL_TAG(v) == PCVAL_SPROP; } -inline JSScopeProperty *PCVAL_TO_SPROP(jsuword v) { return (JSScopeProperty *) PCVAL_CLRTAG(v); } +/* + * Fill property cache entry for key cx->fp->pc, optimized value word computed + * from obj and sprop, and entry capability forged from 24-bit OBJ_SHAPE(obj), + * 4-bit scopeIndex, and 4-bit protoIndex. + * + * Return the filled cache entry or JS_NO_PROP_CACHE_FILL if caching was not + * possible. + */ +extern JS_REQUIRES_STACK JSPropCacheEntry * +js_FillPropertyCache(JSContext *cx, JSObject *obj, + uintN scopeIndex, uintN protoIndex, JSObject *pobj, + JSScopeProperty *sprop, JSBool adding); -inline jsuword -SPROP_TO_PCVAL(JSScopeProperty *sprop) -{ - return PCVAL_SETTAG((jsuword) sprop, PCVAL_SPROP); -} +/* + * Property cache lookup macros. PROPERTY_CACHE_TEST is designed to inline the + * fast path in js_Interpret, so it makes "just-so" restrictions on parameters, + * e.g. pobj and obj should not be the same variable, since for JOF_PROP-mode + * opcodes, obj must not be changed because of a cache miss. + * + * On return from PROPERTY_CACHE_TEST, if atom is null then obj points to the + * scope chain element in which the property was found, pobj is locked, and + * entry is valid. If atom is non-null then no object is locked but entry is + * still set correctly for use, e.g., by js_FillPropertyCache and atom should + * be used as the id to find. + * + * We must lock pobj on a hit in order to close races with threads that might + * be deleting a property from its scope, or otherwise invalidating property + * caches (on all threads) by re-generating scope->shape. + */ +#define PROPERTY_CACHE_TEST(cx, pc, obj, pobj, entry, atom) \ + do { \ + JSPropertyCache *cache_ = &JS_PROPERTY_CACHE(cx); \ + uint32 kshape_ = (JS_ASSERT(OBJ_IS_NATIVE(obj)), OBJ_SHAPE(obj)); \ + entry = &cache_->table[PROPERTY_CACHE_HASH_PC(pc, kshape_)]; \ + PCMETER(cache_->pctestentry = entry); \ + PCMETER(cache_->tests++); \ + JS_ASSERT(&obj != &pobj); \ + if (entry->kpc == pc && entry->kshape == kshape_) { \ + JSObject *tmp_; \ + pobj = obj; \ + JS_ASSERT(PCVCAP_TAG(entry->vcap) <= 1); \ + if (PCVCAP_TAG(entry->vcap) == 1 && \ + (tmp_ = OBJ_GET_PROTO(cx, pobj)) != NULL) { \ + pobj = tmp_; \ + } \ + \ + if (JS_LOCK_OBJ_IF_SHAPE(cx, pobj, PCVCAP_SHAPE(entry->vcap))) { \ + PCMETER(cache_->pchits++); \ + PCMETER(!PCVCAP_TAG(entry->vcap) || cache_->protopchits++); \ + atom = NULL; \ + break; \ + } \ + } \ + atom = js_FullTestPropertyCache(cx, pc, &obj, &pobj, &entry); \ + if (atom) \ + PCMETER(cache_->misses++); \ + } while (0) + +extern JS_REQUIRES_STACK JSAtom * +js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc, + JSObject **objp, JSObject **pobjp, + JSPropCacheEntry **entryp); + +/* The property cache does not need a destructor. */ +#define js_FinishPropertyCache(cache) ((void) 0) + +extern void +js_PurgePropertyCache(JSContext *cx, JSPropertyCache *cache); + +extern void +js_PurgePropertyCacheForScript(JSContext *cx, JSScript *script); #endif /* jspropcache_h___ */ diff --git a/js/src/jspropcacheinlines.h b/js/src/jspropcacheinlines.h deleted file mode 100644 index 2788491ae87..00000000000 --- a/js/src/jspropcacheinlines.h +++ /dev/null @@ -1,223 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jspropcacheinlines_h___ -#define jspropcacheinlines_h___ - -inline JSBool -js_IsPropertyCacheDisabled(JSContext *cx) -{ - return cx->runtime->shapeGen >= SHAPE_OVERFLOW_BIT; -} - -/* - * Add kshape rather than xor it to avoid collisions between nearby bytecode - * that are evolving an object by setting successive properties, incrementing - * the object's scope->shape on each set. - */ -inline jsuword -PROPERTY_CACHE_HASH(jsuword pc, jsuword kshape) -{ - return (((pc >> PROPERTY_CACHE_LOG2) ^ pc) + (kshape)) & PROPERTY_CACHE_MASK; -} - -inline jsuword -PROPERTY_CACHE_HASH_PC(jsbytecode *pc, jsuword kshape) -{ - return PROPERTY_CACHE_HASH((jsuword) pc, kshape); -} - -inline jsuword -PROPERTY_CACHE_HASH_ATOM(JSAtom *atom, JSObject *obj) -{ - return PROPERTY_CACHE_HASH((jsuword) atom >> 2, OBJ_SHAPE(obj)); -} - -/* - * Property cache lookup. - * - * On return from PROPERTY_CACHE_TEST, if atom is null then obj points to the - * scope chain element in which the property was found, pobj is locked, and - * entry is valid. If atom is non-null then no object is locked but entry is - * still set correctly for use, e.g., by JSPropertyCache::fill and atom should - * be used as the id to find. - * - * We must lock pobj on a hit in order to close races with threads that might - * be deleting a property from its scope, or otherwise invalidating property - * caches (on all threads) by re-generating scope->shape. - */ -#define PROPERTY_CACHE_TEST(cx, pc, obj, pobj, entry, atom) \ - JS_PROPERTY_CACHE(cx).test(cx, pc, &obj, &pobj, &entry, &atom) - -JS_ALWAYS_INLINE void -JSPropertyCache::test(JSContext *cx, jsbytecode *pc, JSObject **objp, - JSObject **pobjp, JSPropCacheEntry **entryp, JSAtom **atomp) -{ - JS_ASSERT(this == &JS_PROPERTY_CACHE(cx)); - JS_ASSERT(OBJ_IS_NATIVE(*objp)); - - uint32 kshape = OBJ_SHAPE(*objp); - JSPropCacheEntry *entry = &table[PROPERTY_CACHE_HASH_PC(pc, kshape)]; - PCMETER(pctestentry = entry); - PCMETER(tests++); - JS_ASSERT(objp != pobjp); - if (entry->kpc == pc && entry->kshape == kshape) { - JSObject *tmp; - JSObject *pobj = *objp; - - JS_ASSERT(PCVCAP_TAG(entry->vcap) <= 1); - if (PCVCAP_TAG(entry->vcap) == 1 && (tmp = pobj->getProto()) != NULL) - pobj = tmp; - - if (JS_LOCK_OBJ_IF_SHAPE(cx, pobj, PCVCAP_SHAPE(entry->vcap))) { - PCMETER(pchits++); - PCMETER(!PCVCAP_TAG(entry->vcap) || protopchits++); - *pobjp = pobj; - *entryp = entry; - *atomp = NULL; - return; - } - } - *atomp = fullTest(cx, pc, objp, pobjp, entryp); - if (*atomp) - PCMETER(misses++); -} - -JS_ALWAYS_INLINE bool -JSPropertyCache::testForSet(JSContext *cx, jsbytecode *pc, JSObject **objp, - JSObject **pobjp, JSPropCacheEntry **entryp, JSAtom **atomp) -{ - uint32 kshape = OBJ_SHAPE(*objp); - JSPropCacheEntry *entry = &table[PROPERTY_CACHE_HASH_PC(pc, kshape)]; - PCMETER(pctestentry = entry); - PCMETER(tests++); - PCMETER(settests++); - - *entryp = entry; - if (entry->kpc == pc && entry->kshape == kshape) { - JS_ASSERT(PCVCAP_TAG(entry->vcap) <= 1); - if (JS_LOCK_OBJ_IF_SHAPE(cx, *objp, kshape)) - return true; - } - - *atomp = JS_PROPERTY_CACHE(cx).fullTest(cx, pc, objp, pobjp, entryp); - if (*atomp) { - PCMETER(cache->misses++); - PCMETER(cache->setmisses++); - } - - return false; -} - -JS_ALWAYS_INLINE bool -JSPropertyCache::testForInit(JSContext *cx, jsbytecode *pc, JSObject *obj, - JSPropCacheEntry **entryp, JSScopeProperty **spropp) -{ - uint32 shape = OBJ_SCOPE(obj)->shape; - JSPropCacheEntry *entry = &table[PROPERTY_CACHE_HASH_PC(pc, shape)]; - PCMETER(pctestentry = entry); - PCMETER(tests++); - PCMETER(initests++); - - if (entry->kpc == pc && - entry->kshape == shape && - PCVCAP_SHAPE(entry->vcap) == cx->runtime->protoHazardShape) - { - JS_ASSERT(PCVCAP_TAG(entry->vcap) == 0); - PCMETER(cache->pchits++); - PCMETER(cache->inipchits++); - JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); - JSScopeProperty *sprop = PCVAL_TO_SPROP(entry->vword); - JS_ASSERT(!(sprop->attrs & JSPROP_READONLY)); - *spropp = sprop; - *entryp = entry; - return true; - } - return false; -} - -inline JSPropCacheEntry * -JSPropertyCache::fillEntry(jsuword khash, jsbytecode *pc, jsuword kshape, - jsuword vcap, jsuword vword) -{ - JSPropCacheEntry *entry = &table[khash]; - PCMETER(PCVAL_IS_NULL(entry->vword) || recycles++); - entry->kpc = pc; - entry->kshape = kshape; - entry->vcap = vcap; - entry->vword = vword; - - empty = JS_FALSE; - PCMETER(fills++); - - /* - * The modfills counter is not exact. It increases if a getter or setter - * recurse into the interpreter. - */ - PCMETER(entry == pctestentry || modfills++); - PCMETER(pctestentry = NULL); - return entry; -} - -inline JSPropCacheEntry * -JSPropertyCache::fillByPC(jsbytecode *pc, jsuword kshape, - jsuword vshape, jsuword scopeIndex, jsuword protoIndex, - jsuword vword) -{ - return fillEntry(PROPERTY_CACHE_HASH_PC(pc, kshape), pc, kshape, - PCVCAP_MAKE(vshape, scopeIndex, protoIndex), vword); -} - -inline JSPropCacheEntry * -JSPropertyCache::fillByAtom(JSAtom *atom, JSObject *obj, - jsuword vshape, jsuword scopeIndex, jsuword protoIndex, - jsuword vword) -{ - JS_ASSERT(obj->isDelegate()); - jsuword khash = PROPERTY_CACHE_HASH_ATOM(atom, obj); - PCMETER(if (PCVCAP_TAG(table[khash].vcap) <= 1) - pcrecycles++); - return fillEntry(khash, - reinterpret_cast(atom), - reinterpret_cast(obj), - PCVCAP_MAKE(vshape, scopeIndex, protoIndex), - vword); -} - -#endif /* jspropcacheinlines_h___ */ diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 9994a53bb24..0c2d8eb1ffb 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -1619,7 +1619,7 @@ js_DestroyScript(JSContext *cx, JSScript *script) * regenerating shapes, so we don't have to purge fragments if the GC is * currently running. * - * JS_THREADSAFE note: JSPropertyCache::purgeForScript purges only the + * JS_THREADSAFE note: js_PurgePropertyCacheForScript purges only the * current thread's property cache, so a script not owned by a function * or object, which hands off lifetime management for that script to the * GC, must be used by only one thread over its lifetime. @@ -1638,7 +1638,7 @@ js_DestroyScript(JSContext *cx, JSScript *script) JSStackFrame *fp = js_GetTopStackFrame(cx); if (!(fp && (fp->flags & JSFRAME_EVAL))) { - JS_PROPERTY_CACHE(cx).purgeForScript(cx, script); + js_PurgePropertyCacheForScript(cx, script); #ifdef CHECK_SCRIPT_OWNER JS_ASSERT(script->owner == cx->thread); diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 8b27b06e05f..4d120704143 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -77,7 +77,6 @@ #include "jsxml.h" #include "jsatominlines.h" -#include "jspropcacheinlines.h" #include "jsscriptinlines.h" #include "jsautooplen.h" // generated headers last @@ -8522,8 +8521,8 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2 obj2->dropProperty(cx, prop); ABORT_TRACE("property found on non-native object"); } - entry = JS_PROPERTY_CACHE(cx).fill(cx, aobj, 0, protoIndex, obj2, - (JSScopeProperty*) prop, false); + entry = js_FillPropertyCache(cx, aobj, 0, protoIndex, obj2, + (JSScopeProperty*) prop, false); JS_ASSERT(entry); if (entry == JS_NO_PROP_CACHE_FILL) entry = NULL;