Seed new empty scope shape from prototype to handle foreshadowing, enable deep propcache hits keyed by shapes, simplify code (497789, r=igor).

This commit is contained in:
Brendan Eich 2009-06-25 12:05:09 -07:00
parent b96eed42da
commit 579d4f6d24
4 changed files with 64 additions and 94 deletions

View File

@ -115,12 +115,10 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj,
JSPropertyCache *cache;
jsbytecode *pc;
JSScope *scope;
jsuword kshape, vshape, khash;
jsuword kshape, vshape;
JSOp op;
const JSCodeSpec *cs;
jsuword vword;
ptrdiff_t pcoff;
JSAtom *atom;
JSPropCacheEntry *entry;
JS_ASSERT(!cx->runtime->gcRunning);
@ -313,19 +311,11 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj,
vshape = scope->shape;
}
khash = PROPERTY_CACHE_HASH_PC(pc, kshape);
if (obj == pobj) {
JS_ASSERT(scopeIndex == 0 && protoIndex == 0);
JS_ASSERT(OBJ_SCOPE(obj)->object == obj);
JS_ASSERT(kshape != 0);
} else {
if (op == JSOP_LENGTH) {
atom = cx->runtime->atomState.lengthAtom;
} else {
pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0;
GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
}
#ifdef DEBUG
if (scopeIndex == 0) {
JS_ASSERT(protoIndex != 0);
@ -334,12 +324,6 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj,
#endif
if (scopeIndex != 0 || protoIndex != 1) {
khash = PROPERTY_CACHE_HASH_ATOM(atom, obj, pobj);
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.
@ -354,7 +338,7 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj,
}
}
entry = &cache->table[khash];
entry = &cache->table[PROPERTY_CACHE_HASH_PC(pc, kshape)];
PCMETER(PCVAL_IS_NULL(entry->vword) || cache->recycles++);
entry->kpc = pc;
entry->kshape = kshape;
@ -373,42 +357,41 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj,
return entry;
}
static inline JSAtom *
GetAtomFromBytecode(JSContext *cx, jsbytecode *pc, JSOp op, const JSCodeSpec *cs)
{
if (op == JSOP_LENGTH)
return cx->runtime->atomState.lengthAtom;
ptrdiff_t pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0;
JSAtom *atom;
GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
return atom;
}
JS_REQUIRES_STACK JSAtom *
js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
JSObject **objp, JSObject **pobjp,
JSPropCacheEntry **entryp)
JSPropCacheEntry *entry)
{
JSOp op;
const JSCodeSpec *cs;
ptrdiff_t pcoff;
JSAtom *atom;
JSObject *obj, *pobj, *tmp;
JSPropCacheEntry *entry;
uint32 vcap;
JS_ASSERT(uintN((cx->fp->imacpc ? cx->fp->imacpc : pc) - cx->fp->script->code)
< cx->fp->script->length);
op = js_GetOpcode(cx, cx->fp->script, pc);
cs = &js_CodeSpec[op];
if (op == JSOP_LENGTH) {
atom = cx->runtime->atomState.lengthAtom;
} else {
pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0;
GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
}
JSOp op = js_GetOpcode(cx, cx->fp->script, pc);
const JSCodeSpec *cs = &js_CodeSpec[op];
obj = *objp;
JS_ASSERT(OBJ_IS_NATIVE(obj));
entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_ATOM(atom, obj, NULL)];
*entryp = entry;
vcap = entry->vcap;
if (entry->kpc != (jsbytecode *) atom) {
PCMETER(JS_PROPERTY_CACHE(cx).idmisses++);
if (entry->kpc != pc) {
PCMETER(JS_PROPERTY_CACHE(cx).kpcmisses++);
JSAtom *atom = GetAtomFromBytecode(cx, pc, op, cs);
#ifdef DEBUG_notme
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",
@ -427,11 +410,15 @@ js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
return atom;
}
if (entry->kshape != (jsuword) obj) {
PCMETER(JS_PROPERTY_CACHE(cx).komisses++);
return atom;
if (entry->kshape != OBJ_SHAPE(obj)) {
PCMETER(JS_PROPERTY_CACHE(cx).kshmisses++);
return GetAtomFromBytecode(cx, pc, op, cs);
}
/*
* PROPERTY_CACHE_TEST handles only the direct and immediate-prototype hit
* cases, all others go here.
*/
pobj = obj;
if (JOF_MODE(cs->format) == JOF_NAME) {
@ -456,6 +443,7 @@ js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
if (JS_LOCK_OBJ_IF_SHAPE(cx, pobj, PCVCAP_SHAPE(vcap))) {
#ifdef DEBUG
JSAtom *atom = GetAtomFromBytecode(cx, pc, op, cs);
jsid id = ATOM_TO_JSID(atom);
CHECK_FOR_STRING_INDEX(id);
@ -467,7 +455,7 @@ js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
}
PCMETER(JS_PROPERTY_CACHE(cx).vcmisses++);
return atom;
return GetAtomFromBytecode(cx, pc, op, cs);
}
#ifdef DEBUG
@ -522,7 +510,6 @@ js_PurgePropertyCache(JSContext *cx, JSPropertyCache *cache)
P(noprotos);
P(longchains);
P(recycles);
P(pcrecycles);
P(tests);
P(pchits);
P(protopchits);
@ -535,8 +522,8 @@ js_PurgePropertyCache(JSContext *cx, JSPropertyCache *cache)
P(setpcmisses);
P(slotchanges);
P(setmisses);
P(idmisses);
P(komisses);
P(kpcmisses);
P(kshmisses);
P(vcmisses);
P(misses);
P(flushes);
@ -569,9 +556,8 @@ js_PurgePropertyCacheForScript(JSContext *cx, JSScript *script)
entry++) {
if (JS_UPTRDIFF(entry->kpc, script->code) < script->length) {
entry->kpc = NULL;
entry->kshape = 0;
#ifdef DEBUG
entry->vcap = entry->vword = 0;
entry->kshape = entry->vcap = entry->vword = 0;
#endif
}
}
@ -4810,7 +4796,7 @@ js_Interpret(JSContext *cx)
}
atom = js_FullTestPropertyCache(cx, regs.pc, &obj, &obj2,
&entry);
entry);
if (atom) {
PCMETER(cache->misses++);
PCMETER(cache->setmisses++);

View File

@ -217,9 +217,6 @@ typedef struct JSInlineFrame {
#define PROPERTY_CACHE_HASH_PC(pc,kshape) \
PROPERTY_CACHE_HASH(pc, kshape)
#define PROPERTY_CACHE_HASH_ATOM(atom,obj,pobj) \
PROPERTY_CACHE_HASH((jsuword)(atom) >> 2, OBJ_SHAPE(obj))
/*
* Property cache value capability macros.
*/
@ -282,8 +279,6 @@ typedef struct JSPropertyCache {
uint32 noprotos; /* resolve-returned non-proto pobj */
uint32 longchains; /* overlong scope and/or proto chain */
uint32 recycles; /* cache entries recycled by fills */
uint32 pcrecycles; /* pc-keyed entries recycled by atom-
keyed fills */
uint32 tests; /* cache probes */
uint32 pchits; /* fast-path polymorphic op hits */
uint32 protopchits; /* pchits hitting immediate prototype */
@ -297,8 +292,8 @@ typedef struct JSPropertyCache {
uint32 slotchanges; /* clasp->reserveSlots result variance-
induced slot changes */
uint32 setmisses; /* JSOP_SET{NAME,PROP} total misses */
uint32 idmisses; /* slow-path key id == atom misses */
uint32 komisses; /* slow-path key object misses */
uint32 kpcmisses; /* slow-path key pc misses */
uint32 kshmisses; /* slow-path key shape misses */
uint32 vcmisses; /* value capability misses */
uint32 misses; /* cache misses */
uint32 flushes; /* cache flushes */
@ -380,7 +375,6 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj,
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 && \
OBJ_IS_NATIVE(tmp_)) { \
@ -395,7 +389,7 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj,
break; \
} \
} \
atom = js_FullTestPropertyCache(cx, pc, &obj, &pobj, &entry); \
atom = js_FullTestPropertyCache(cx, pc, &obj, &pobj, entry); \
if (atom) \
PCMETER(cache_->misses++); \
} while (0)
@ -403,7 +397,7 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj,
extern JS_REQUIRES_STACK JSAtom *
js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
JSObject **objp, JSObject **pobjp,
JSPropCacheEntry **entryp);
JSPropCacheEntry *entry);
/* The property cache does not need a destructor. */
#define js_FinishPropertyCache(cache) ((void) 0)

View File

@ -110,8 +110,12 @@ js_GetMutableScope(JSContext *cx, JSObject *obj)
static void
InitMinimalScope(JSContext *cx, JSScope *scope)
{
js_LeaveTraceIfGlobalObject(cx, scope->object);
scope->shape = 0;
JSObject *obj = scope->object;
js_LeaveTraceIfGlobalObject(cx, obj);
JSObject *proto = OBJ_GET_PROTO(cx, obj);
scope->shape = (proto && OBJ_IS_NATIVE(proto)) ? OBJ_SHAPE(proto) : 0;
scope->hashShift = JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2;
scope->entryCount = scope->removedCount = 0;
scope->table = NULL;
@ -1018,7 +1022,7 @@ js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id,
uintN attrs, uintN flags, intN shortid)
{
JSScopeProperty **spp, *sprop, *overwriting, **spvec, **spp2, child;
uint32 size, splen, i;
uintN size, splen, i;
int change;
JSTempValueRooter tvr;
@ -1198,14 +1202,16 @@ js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id,
* sprop, while the former simply tests whether sprop->id
* is bound in scope.
*/
if (!SCOPE_GET_PROPERTY(scope, sprop->id))
continue;
if (SCOPE_GET_PROPERTY(scope, sprop->id)) {
JS_ASSERT(sprop != overwriting);
JS_ASSERT(i != 0);
spvec[--i] = sprop;
} while ((sprop = sprop->parent) != NULL);
JS_ASSERT(i == 0);
}
sprop = sprop->parent;
} while (i != 0);
JSObject *proto = OBJ_GET_PROTO(cx, scope->object);
if (proto && OBJ_IS_NATIVE(proto))
sprop = OBJ_SCOPE(proto)->lastProp;
/*
* Now loop forward through spvec, forking the property tree

View File

@ -7409,34 +7409,18 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2
JS_ASSERT(cx->requestDepth);
#endif
// Emit guard(s), common code for both hit and miss cases.
// Check for first-level cache hit and guard on kshape if possible.
// Otherwise guard on key object exact match.
if (PCVCAP_TAG(entry->vcap) <= 1) {
/*
* Guard on the shape of the directly accessed native object, unless it's
* the global object whose shape can't change on trace.
*/
if (aobj != globalObj) {
LIns* shape_ins = addName(lir->insLoad(LIR_ld, map_ins, offsetof(JSScope, shape)),
"shape");
guard(true, addName(lir->ins2i(LIR_eq, shape_ins, entry->kshape), "guard(kshape)(test_property_cache)"),
guard(true,
addName(lir->ins2i(LIR_eq, shape_ins, entry->kshape),
"guard(kshape)(test_property_cache)"),
BRANCH_EXIT);
}
} else {
#ifdef DEBUG
JSOp op = js_GetOpcode(cx, cx->fp->script, pc);
JSAtom *pcatom;
if (op == JSOP_LENGTH) {
pcatom = cx->runtime->atomState.lengthAtom;
} else {
ptrdiff_t pcoff = (JOF_TYPE(js_CodeSpec[op].format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0;
GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, pcatom);
}
JS_ASSERT(entry->kpc == (jsbytecode *) pcatom);
JS_ASSERT(entry->kshape == jsuword(aobj));
#endif
if (aobj != globalObj && !obj_ins->isconstp()) {
guard(true, addName(lir->ins2i(LIR_eq, obj_ins, entry->kshape), "guard(kobj)"),
BRANCH_EXIT);
}
}
// For any hit that goes up the scope and/or proto chains, we will need to
// guard on the shape of the object containing the property.