Avoid needless prototype-shape purges (454035, r=igor).

This commit is contained in:
Brendan Eich 2008-09-09 09:57:10 -07:00
parent 6fe1722879
commit c1ea855a9c
8 changed files with 72 additions and 74 deletions

View File

@ -1170,9 +1170,9 @@ js_MakeArraySlow(JSContext *cx, JSObject *obj)
? INT_TO_JSVAL(length)
: JSVAL_VOID;
/* Make sure we preserve any flags borrowing bits in JSSLOT_CLASS. */
obj->fslots[JSSLOT_CLASS] ^= (jsval) &js_ArrayClass;
obj->fslots[JSSLOT_CLASS] |= (jsval) &js_SlowArrayClass;
/* Make sure we preserve any flags borrowing bits in classword. */
obj->classword ^= (jsuword) &js_ArrayClass;
obj->classword |= (jsuword) &js_SlowArrayClass;
/* Swap in our new map. */
oldmap = obj->map;

View File

@ -498,12 +498,12 @@ js_FastNewArray(JSContext* cx, JSObject* proto)
if (!obj)
return NULL;
JSClass* clasp = &js_ArrayClass;
obj->classword = jsuword(clasp);
obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);
obj->fslots[JSSLOT_PARENT] = proto->fslots[JSSLOT_PARENT];
JSClass* clasp = &js_ArrayClass;
obj->fslots[JSSLOT_CLASS] = PRIVATE_TO_JSVAL(clasp);
obj->fslots[JSSLOT_ARRAY_LENGTH] = 0;
obj->fslots[JSSLOT_ARRAY_COUNT] = 0;
for (unsigned i = JSSLOT_ARRAY_COUNT + 1; i != JS_INITIAL_NSLOTS; ++i)
@ -543,9 +543,9 @@ js_FastNewObject(JSContext* cx, JSObject* ctor)
JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
JSObject* proto = JSVAL_TO_OBJECT(v);
obj->classword = jsuword(clasp);
obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);
obj->fslots[JSSLOT_PARENT] = ctor->fslots[JSSLOT_PARENT];
obj->fslots[JSSLOT_CLASS] = PRIVATE_TO_JSVAL(clasp);
for (unsigned i = JSSLOT_PRIVATE; i != JS_INITIAL_NSLOTS; ++i)
obj->fslots[i] = JSVAL_VOID;

View File

@ -331,8 +331,7 @@ js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
PCMETER(JS_PROPERTY_CACHE(cx).idmisses++);
#ifdef DEBUG_notme
entry = &JS_PROPERTY_CACHE(cx)
.table[PROPERTY_CACHE_HASH_PC(pc, OBJ_SCOPE(obj)->shape)];
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",
@ -342,10 +341,10 @@ js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
pc - cx->fp->script->code,
entry->kpc - cx->fp->script->code,
entry->kshape,
OBJ_SCOPE(obj)->shape);
OBJ_SHAPE(obj));
js_Disassemble1(cx, cx->fp->script, pc,
PTRDIFF(pc, cx->fp->script->code, jsbytecode),
JS_FALSE, stderr);
PTRDIFF(pc, cx->fp->script->code, jsbytecode),
JS_FALSE, stderr);
#endif
return atom;
@ -383,7 +382,7 @@ js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
--vcap;
}
if (PCVCAP_SHAPE(vcap) == OBJ_SCOPE(pobj)->shape) {
if (PCVCAP_SHAPE(vcap) == OBJ_SHAPE(pobj)) {
#ifdef DEBUG
jsid id = ATOM_TO_JSID(atom);
@ -4419,7 +4418,7 @@ js_Interpret(JSContext *cx)
atom = NULL;
if (JS_LIKELY(obj->map->ops->setProperty == js_SetProperty)) {
JSPropertyCache *cache = &JS_PROPERTY_CACHE(cx);
uint32 kshape = OBJ_SCOPE(obj)->shape;
uint32 kshape = OBJ_SHAPE(obj);
/*
* Open-code JS_PROPERTY_CACHE_TEST, specializing for two

View File

@ -163,7 +163,7 @@ typedef struct JSInlineFrame {
PROPERTY_CACHE_HASH(pc, kshape)
#define PROPERTY_CACHE_HASH_ATOM(atom,obj,pobj) \
PROPERTY_CACHE_HASH((jsuword)(atom) >> 2, OBJ_SCOPE(obj)->shape)
PROPERTY_CACHE_HASH((jsuword)(atom) >> 2, OBJ_SHAPE(obj))
/*
* Property cache value capability macros.
@ -277,8 +277,8 @@ typedef struct JSPropertyCache {
/*
* Fill property cache entry for key cx->fp->pc, optimized value word computed
* from obj and sprop, and entry capability forged from OBJ_SCOPE(obj)->shape,
* scopeIndex, and protoIndex.
* from obj and sprop, and entry capability forged from 24-bit OBJ_SHAPE(obj),
* 4-bit scopeIndex, and 4-bit protoIndex.
*/
extern void
js_FillPropertyCache(JSContext *cx, JSObject *obj, jsuword kshape,
@ -305,8 +305,7 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj, jsuword kshape,
#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_SCOPE(obj)->shape); \
uint32 kshape_ = (JS_ASSERT(OBJ_IS_NATIVE(obj)), OBJ_SHAPE(obj)); \
entry = &cache_->table[PROPERTY_CACHE_HASH_PC(pc, kshape_)]; \
PCMETER(cache_->tests++); \
JS_ASSERT(&obj != &pobj); \
@ -322,7 +321,7 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj, jsuword kshape,
pobj = tmp_; \
JS_LOCK_OBJ(cx, pobj); \
} \
if (PCVCAP_SHAPE(entry->vcap) == OBJ_SCOPE(pobj)->shape) { \
if (PCVCAP_SHAPE(entry->vcap) == OBJ_SHAPE(pobj)) { \
PCMETER(cache_->pchits++); \
PCMETER(!PCVCAP_TAG(entry->vcap) || cache_->protopchits++); \
pobj = OBJ_SCOPE(pobj)->object; \

View File

@ -2565,18 +2565,19 @@ js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto,
obj->map = NULL;
obj->dslots = NULL;
/*
* Set the class slot with the initial value of the system and delegate
* flags set to false.
*/
JS_ASSERT(((jsuword) clasp & 3) == 0);
obj->classword = jsuword(clasp);
JS_ASSERT(!STOBJ_IS_DELEGATE(obj));
JS_ASSERT(!STOBJ_IS_SYSTEM(obj));
/* Set the proto and parent properties. */
STOBJ_SET_PROTO(obj, proto);
STOBJ_SET_PARENT(obj, parent);
/*
* Set the class slot with the initial value of the system flag set to
* false.
*/
JS_ASSERT(((jsuword) clasp & 3) == 0);
STOBJ_SET_SLOT(obj, JSSLOT_CLASS, PRIVATE_TO_JSVAL(clasp));
JS_ASSERT(!STOBJ_IS_SYSTEM(obj));
/* Initialize the remaining fixed slots. */
for (i = JSSLOT_PRIVATE; i != JS_INITIAL_NSLOTS; ++i)
obj->fslots[i] = JSVAL_VOID;
@ -3031,6 +3032,9 @@ PurgeProtoChain(JSContext *cx, JSObject *obj, jsid id)
static void
PurgeScopeChain(JSContext *cx, JSObject *obj, jsid id)
{
if (!OBJ_IS_DELEGATE(cx, obj))
return;
PurgeProtoChain(cx, OBJ_GET_PROTO(cx, obj), id);
while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL) {
if (PurgeProtoChain(cx, obj, id))
@ -3496,13 +3500,13 @@ js_FindPropertyHelper(JSContext *cx, jsid id, JSObject **objp,
JSPropCacheEntry **entryp)
{
JSObject *obj, *pobj, *lastobj;
uint32 type;
uint32 shape;
int scopeIndex, protoIndex;
JSProperty *prop;
JSScopeProperty *sprop;
obj = cx->fp->scopeChain;
type = OBJ_SCOPE(obj)->shape;
shape = OBJ_SHAPE(obj);
for (scopeIndex = 0; ; scopeIndex++) {
if (obj->map->ops->lookupProperty == js_LookupProperty) {
protoIndex =
@ -3517,7 +3521,7 @@ js_FindPropertyHelper(JSContext *cx, jsid id, JSObject **objp,
if (entryp) {
if (protoIndex >= 0 && OBJ_IS_NATIVE(pobj)) {
sprop = (JSScopeProperty *) prop;
js_FillPropertyCache(cx, cx->fp->scopeChain, type,
js_FillPropertyCache(cx, cx->fp->scopeChain, shape,
scopeIndex, protoIndex, pobj, sprop,
entryp);
} else {
@ -3692,7 +3696,7 @@ JSBool
js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
JSPropCacheEntry **entryp)
{
uint32 type;
uint32 shape;
int protoIndex;
JSObject *obj2;
JSProperty *prop;
@ -3702,7 +3706,7 @@ js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
CHECK_FOR_STRING_INDEX(id);
JS_COUNT_OPERATION(cx, JSOW_GET_PROPERTY);
type = OBJ_SCOPE(obj)->shape;
shape = OBJ_SHAPE(obj);
protoIndex = js_LookupPropertyWithFlags(cx, obj, id, 0, &obj2, &prop);
if (protoIndex < 0)
return JS_FALSE;
@ -3772,10 +3776,8 @@ js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
if (!js_NativeGet(cx, obj, obj2, sprop, vp))
return JS_FALSE;
if (entryp) {
js_FillPropertyCache(cx, obj, type, 0, protoIndex, obj2, sprop,
entryp);
}
if (entryp)
js_FillPropertyCache(cx, obj, shape, 0, protoIndex, obj2, sprop, entryp);
JS_UNLOCK_OBJ(cx, obj2);
return JS_TRUE;
}
@ -3790,7 +3792,7 @@ JSBool
js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
JSPropCacheEntry **entryp)
{
uint32 type;
uint32 shape;
int protoIndex;
JSObject *pobj;
JSProperty *prop;
@ -3805,7 +3807,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
CHECK_FOR_STRING_INDEX(id);
JS_COUNT_OPERATION(cx, JSOW_SET_PROPERTY);
type = OBJ_SCOPE(obj)->shape;
shape = OBJ_SHAPE(obj);
protoIndex = js_LookupPropertyWithFlags(cx, obj, id, 0, &pobj, &prop);
if (protoIndex < 0)
return JS_FALSE;
@ -3873,16 +3875,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
* NB: Thanks to the immutable, garbage-collected property tree
* maintained by jsscope.c in cx->runtime, we needn't worry about
* sprop going away behind our back after we've unlocked scope.
*
* But if we are shadowing (not sharing) the proto-property, then
* we need to regenerate the property cache shape id for scope, in
* case the cache contains the old type in an entry value that was
* filled by a get on obj that delegated up the prototype chain to
* pobj. Once we've shadowed the proto-property, that cache entry
* must not be hit.
*/
if (!(attrs & JSPROP_SHARED))
SCOPE_MAKE_UNIQUE_SHAPE(cx, scope);
JS_UNLOCK_SCOPE(cx, scope);
/*
@ -3984,7 +3977,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
if (entryp) {
if (!(attrs & JSPROP_SHARED))
js_FillPropertyCache(cx, obj, type, 0, 0, obj, sprop, entryp);
js_FillPropertyCache(cx, obj, shape, 0, 0, obj, sprop, entryp);
else
PCMETER(JS_PROPERTY_CACHE(cx).nofills++);
}

View File

@ -120,7 +120,7 @@ struct JSObjectMap {
} \
JS_END_MACRO
#define JS_INITIAL_NSLOTS 6
#define JS_INITIAL_NSLOTS 5
/*
* When JSObject.dslots is not null, JSObject.dslots[-1] records the number of
@ -128,17 +128,17 @@ struct JSObjectMap {
*/
struct JSObject {
JSObjectMap *map;
jsuword classword;
jsval fslots[JS_INITIAL_NSLOTS];
jsval *dslots; /* dynamically allocated slots */
};
#define JSSLOT_PROTO 0
#define JSSLOT_PARENT 1
#define JSSLOT_CLASS 2
#define JSSLOT_PRIVATE 3
#define JSSLOT_PRIVATE 2
#define JSSLOT_START(clasp) (((clasp)->flags & JSCLASS_HAS_PRIVATE) \
? JSSLOT_PRIVATE + 1 \
: JSSLOT_CLASS + 1)
: JSSLOT_PARENT + 1)
#define JSSLOT_FREE(clasp) (JSSLOT_START(clasp) \
+ JSCLASS_RESERVED_SLOTS(clasp))
@ -167,26 +167,29 @@ struct JSObject {
#define STOBJ_GET_PROTO(obj) \
JSVAL_TO_OBJECT((obj)->fslots[JSSLOT_PROTO])
#define STOBJ_SET_PROTO(obj,proto) \
((obj)->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto))
(void)((!(proto) || STOBJ_SET_DELEGATE(proto)), \
(obj)->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto))
#define STOBJ_CLEAR_PROTO(obj) \
((obj)->fslots[JSSLOT_PROTO] = JSVAL_NULL)
#define STOBJ_GET_PARENT(obj) \
JSVAL_TO_OBJECT((obj)->fslots[JSSLOT_PARENT])
#define STOBJ_SET_PARENT(obj,parent) \
((obj)->fslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent))
(void)((!(parent) || STOBJ_SET_DELEGATE(parent)), \
(obj)->fslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent))
#define STOBJ_CLEAR_PARENT(obj) \
((obj)->fslots[JSSLOT_PARENT] = JSVAL_NULL)
/*
* We use JSSLOT_CLASS to store both JSClass* and the system flag as an int-
* tagged value (see jsapi.h for details) with the system flag stored in the
* second lowest bit.
* We use JSObject.classword to store both JSClass* and the delegate and system
* flags in the two least significant bits. We do *not* synchronize updates of
* obj->classword -- API clients must take care.
*/
#define STOBJ_GET_CLASS(obj) ((JSClass *)((obj)->fslots[JSSLOT_CLASS] & ~3))
#define STOBJ_IS_SYSTEM(obj) (((obj)->fslots[JSSLOT_CLASS] & 2) != 0)
#define STOBJ_SET_SYSTEM(obj) ((void)((obj)->fslots[JSSLOT_CLASS] |= 2))
#define STOBJ_GET_CLASS(obj) ((JSClass *)((obj)->classword & ~3))
#define STOBJ_IS_DELEGATE(obj) (((obj)->classword & 1) != 0)
#define STOBJ_SET_DELEGATE(obj) ((obj)->classword |= 1)
#define STOBJ_IS_SYSTEM(obj) (((obj)->classword & 2) != 0)
#define STOBJ_SET_SYSTEM(obj) ((obj)->classword |= 2)
#define STOBJ_GET_PRIVATE(obj) \
(JS_ASSERT(JSVAL_IS_INT(STOBJ_GET_SLOT(obj, JSSLOT_PRIVATE))), \
@ -227,7 +230,7 @@ struct JSObject {
(OBJ_CHECK_SLOT(obj, JSSLOT_PARENT), STOBJ_SET_PARENT(obj, parent))
#define LOCKED_OBJ_GET_CLASS(obj) \
(OBJ_CHECK_SLOT(obj, JSSLOT_CLASS), STOBJ_GET_CLASS(obj))
STOBJ_GET_CLASS(obj)
#define LOCKED_OBJ_GET_PRIVATE(obj) \
(OBJ_CHECK_SLOT(obj, JSSLOT_PRIVATE), STOBJ_GET_PRIVATE(obj))
@ -276,7 +279,10 @@ struct JSObject {
#endif /* !JS_THREADSAFE */
/* Thread-safe proto, parent, and class access macros. */
/* Thread-safe delegate, proto, parent, and class access macros. */
#define OBJ_IS_DELEGATE(cx,obj) STOBJ_IS_DELEGATE(obj)
#define OBJ_SET_DELEGATE(cx,obj) STOBJ_SET_DELEGATE(obj)
#define OBJ_GET_PROTO(cx,obj) STOBJ_GET_PROTO(obj)
#define OBJ_SET_PROTO(cx,obj,proto) STOBJ_SET_PROTO(obj, proto)
#define OBJ_CLEAR_PROTO(cx,obj) STOBJ_CLEAR_PROTO(obj)
@ -286,7 +292,7 @@ struct JSObject {
#define OBJ_CLEAR_PARENT(cx,obj) STOBJ_CLEAR_PARENT(obj)
/*
* Class is invariant and comes from the fixed JSSLOT_CLASS. Thus no locking
* Class is invariant and comes from the fixed clasp member. Thus no locking
* is necessary to read it. Same for the private slot.
*/
#define OBJ_GET_CLASS(cx,obj) STOBJ_GET_CLASS(obj)

View File

@ -218,6 +218,7 @@ JS_STATIC_ASSERT(offsetof(JSScope, title) == sizeof(JSObjectMap));
#define JS_IS_SCOPE_LOCKED(cx, scope) JS_IS_TITLE_LOCKED(cx, &(scope)->title)
#define OBJ_SCOPE(obj) ((JSScope *)(obj)->map)
#define OBJ_SHAPE(obj) (OBJ_SCOPE(obj)->shape)
#define SCOPE_MAKE_UNIQUE_SHAPE(cx,scope) \
((scope)->shape = js_GenerateShape((cx), JS_FALSE))

View File

@ -1360,7 +1360,7 @@ TraceRecorder::lazilyImportGlobalSlot(unsigned slot)
unsigned index = traceMonitor->globalSlots->length();
/* If this the first global we are adding, remember the shape of the global object. */
if (index == 0)
traceMonitor->globalShape = OBJ_SCOPE(JS_GetGlobalForObject(cx, cx->fp->scopeChain))->shape;
traceMonitor->globalShape = OBJ_SHAPE(JS_GetGlobalForObject(cx, cx->fp->scopeChain));
/* Add the slot to the list of interned global slots. */
traceMonitor->globalSlots->add(slot);
uint8 type = getCoercedType(*vp);
@ -2062,7 +2062,7 @@ bool
js_RecordTree(JSContext* cx, JSTraceMonitor* tm, Fragment* f)
{
/* Make sure the global type map didn't change on us. */
uint32 globalShape = OBJ_SCOPE(JS_GetGlobalForObject(cx, cx->fp->scopeChain))->shape;
uint32 globalShape = OBJ_SHAPE(JS_GetGlobalForObject(cx, cx->fp->scopeChain));
if (tm->globalShape != globalShape) {
debug_only_v(printf("Global shape mismatch (%u vs. %u) in RecordTree, flushing cache.\n",
globalShape, tm->globalShape);)
@ -2287,11 +2287,11 @@ js_ExecuteTree(JSContext* cx, Fragment** treep, uintN& inlineCallCount,
the global type map must remain applicable at all times (we expect absolute type
stability for globals). */
if (ngslots &&
(OBJ_SCOPE(globalObj)->shape != tm->globalShape ||
(OBJ_SHAPE(globalObj) != tm->globalShape ||
!BuildNativeGlobalFrame(cx, ngslots, gslots, tm->globalTypeMap->data(), global))) {
AUDIT(globalShapeMismatchAtEntry);
debug_only_v(printf("Global shape mismatch (%u vs. %u), flushing cache.\n",
OBJ_SCOPE(globalObj)->shape, tm->globalShape);)
OBJ_SHAPE(globalObj), tm->globalShape);)
const void* ip = f->ip;
js_FlushJITCache(cx);
*treep = tm->fragmento->newLoop(ip);
@ -2704,7 +2704,7 @@ js_FlushJITCache(JSContext* cx)
}
memset(&tm->fcache, 0, sizeof(tm->fcache));
if (cx->fp) {
tm->globalShape = OBJ_SCOPE(JS_GetGlobalForObject(cx, cx->fp->scopeChain))->shape;
tm->globalShape = OBJ_SHAPE(JS_GetGlobalForObject(cx, cx->fp->scopeChain));
tm->globalSlots->clear();
tm->globalTypeMap->clear();
}
@ -3359,7 +3359,7 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2
ABORT_TRACE("failed to lookup property");
if (prop) {
js_FillPropertyCache(cx, aobj, OBJ_SCOPE(aobj)->shape, 0, protoIndex, obj2,
js_FillPropertyCache(cx, aobj, OBJ_SHAPE(aobj), 0, protoIndex, obj2,
(JSScopeProperty*) prop, &entry);
}
}
@ -3429,7 +3429,7 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2
if (PCVCAP_TAG(entry->vcap) >= 1) {
jsuword vcap = entry->vcap;
uint32 vshape = PCVCAP_SHAPE(vcap);
JS_ASSERT(OBJ_SCOPE(obj2)->shape == vshape);
JS_ASSERT(OBJ_SHAPE(obj2) == vshape);
LIns* obj2_ins = INS_CONSTPTR(obj2);
map_ins = lir->insLoad(LIR_ldp, obj2_ins, (int)offsetof(JSObject, map));
@ -3640,7 +3640,7 @@ TraceRecorder::guardClass(JSObject* obj, LIns* obj_ins, JSClass* clasp)
if (STOBJ_GET_CLASS(obj) != clasp)
return false;
LIns* class_ins = stobj_get_fslot(obj_ins, JSSLOT_CLASS);
LIns* class_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, classword));
class_ins = lir->ins2(LIR_piand, class_ins, lir->insImm(~3));
char namebuf[32];
@ -4398,7 +4398,7 @@ TraceRecorder::record_JSOP_SETPROP()
LIns* obj_ins = get(&l);
JSPropertyCache* cache = &JS_PROPERTY_CACHE(cx);
uint32 kshape = OBJ_SCOPE(obj)->shape;
uint32 kshape = OBJ_SHAPE(obj);
jsbytecode* pc = cx->fp->regs->pc;
JSPropCacheEntry* entry = &cache->table[PROPERTY_CACHE_HASH_PC(pc, kshape)];