Bug 720753 - hoist NewObjectCache from JSCompartment into JSRuntime (r=bhackett)

This commit is contained in:
Luke Wagner 2012-05-03 09:12:47 +02:00
parent facff78a38
commit 4d418a1913
11 changed files with 199 additions and 194 deletions

View File

@ -3789,7 +3789,7 @@ NewArray(JSContext *cx, uint32_t length, JSObject *proto)
GlobalObject *parent = GetCurrentGlobal(cx);
NewObjectCache &cache = cx->compartment->newObjectCache;
NewObjectCache &cache = cx->runtime->newObjectCache;
NewObjectCache::EntryIndex entry = -1;
if (cache.lookupGlobal(&ArrayClass, parent, kind, &entry)) {

View File

@ -190,6 +190,84 @@ struct ConservativeGCData
}
};
/*
* Cache for speeding up repetitive creation of objects in the VM.
* When an object is created which matches the criteria in the 'key' section
* below, an entry is filled with the resulting object.
*/
class NewObjectCache
{
/* Statically asserted to be equal to sizeof(JSObject_Slots16) */
static const unsigned MAX_OBJ_SIZE = 4 * sizeof(void*) + 16 * sizeof(Value);
static inline void staticAsserts();
struct Entry
{
/* Class of the constructed object. */
Class *clasp;
/*
* Key with one of three possible values:
*
* - Global for the object. The object must have a standard class for
* which the global's prototype can be determined, and the object's
* parent will be the global.
*
* - Prototype for the object (cannot be global). The object's parent
* will be the prototype's parent.
*
* - Type for the object. The object's parent will be the type's
* prototype's parent.
*/
gc::Cell *key;
/* Allocation kind for the constructed object. */
gc::AllocKind kind;
/* Number of bytes to copy from the template object. */
uint32_t nbytes;
/*
* Template object to copy from, with the initial values of fields,
* fixed slots (undefined) and private data (NULL).
*/
char templateObject[MAX_OBJ_SIZE];
};
Entry entries[41]; // TODO: reconsider size
public:
typedef int EntryIndex;
NewObjectCache() { PodZero(this); }
void purge() { PodZero(this); }
/*
* Get the entry index for the given lookup, return whether there was a hit
* on an existing entry.
*/
inline bool lookupProto(Class *clasp, JSObject *proto, gc::AllocKind kind, EntryIndex *pentry);
inline bool lookupGlobal(Class *clasp, js::GlobalObject *global, gc::AllocKind kind, EntryIndex *pentry);
inline bool lookupType(Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, EntryIndex *pentry);
/* Return a new object from a cache hit produced by a lookup method. */
inline JSObject *newObjectFromHit(JSContext *cx, EntryIndex entry);
/* Fill an entry after a cache miss. */
inline void fillProto(EntryIndex entry, Class *clasp, JSObject *proto, gc::AllocKind kind, JSObject *obj);
inline void fillGlobal(EntryIndex entry, Class *clasp, js::GlobalObject *global, gc::AllocKind kind, JSObject *obj);
inline void fillType(EntryIndex entry, Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, JSObject *obj);
/* Invalidate any entries which might produce an object with shape/proto. */
void invalidateEntriesForShape(JSContext *cx, Shape *shape, JSObject *proto);
private:
inline bool lookup(Class *clasp, gc::Cell *key, gc::AllocKind kind, EntryIndex *pentry);
inline void fill(EntryIndex entry, Class *clasp, gc::Cell *key, gc::AllocKind kind, JSObject *obj);
static inline void copyCachedToObject(JSObject *dst, JSObject *src);
};
/*
* A FreeOp can do one thing: free memory. For convenience, it has delete_
* convenience methods that also call destructors.
@ -626,6 +704,7 @@ struct JSRuntime : js::RuntimeFriendFields
js::GSNCache gsnCache;
js::PropertyCache propertyCache;
js::NewObjectCache newObjectCache;
/* State used by jsdtoa.cpp. */
DtoaState *dtoaState;

View File

@ -45,14 +45,126 @@
#include "jscompartment.h"
#include "jsfriendapi.h"
#include "jsinterp.h"
#include "jsprobes.h"
#include "jsxml.h"
#include "jsgc.h"
#include "frontend/ParseMaps.h"
#include "vm/RegExpObject.h"
#include "jsgcinlines.h"
namespace js {
inline void
NewObjectCache::staticAsserts()
{
JS_STATIC_ASSERT(NewObjectCache::MAX_OBJ_SIZE == sizeof(JSObject_Slots16));
JS_STATIC_ASSERT(gc::FINALIZE_OBJECT_LAST == gc::FINALIZE_OBJECT16_BACKGROUND);
}
inline bool
NewObjectCache::lookup(Class *clasp, gc::Cell *key, gc::AllocKind kind, EntryIndex *pentry)
{
uintptr_t hash = (uintptr_t(clasp) ^ uintptr_t(key)) + kind;
*pentry = hash % js::ArrayLength(entries);
Entry *entry = &entries[*pentry];
/* N.B. Lookups with the same clasp/key but different kinds map to different entries. */
return (entry->clasp == clasp && entry->key == key);
}
inline bool
NewObjectCache::lookupProto(Class *clasp, JSObject *proto, gc::AllocKind kind, EntryIndex *pentry)
{
JS_ASSERT(!proto->isGlobal());
return lookup(clasp, proto, kind, pentry);
}
inline bool
NewObjectCache::lookupGlobal(Class *clasp, js::GlobalObject *global, gc::AllocKind kind, EntryIndex *pentry)
{
return lookup(clasp, global, kind, pentry);
}
inline bool
NewObjectCache::lookupType(Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, EntryIndex *pentry)
{
return lookup(clasp, type, kind, pentry);
}
inline void
NewObjectCache::fill(EntryIndex entry_, Class *clasp, gc::Cell *key, gc::AllocKind kind, JSObject *obj)
{
JS_ASSERT(unsigned(entry_) < ArrayLength(entries));
Entry *entry = &entries[entry_];
JS_ASSERT(!obj->hasDynamicSlots() && !obj->hasDynamicElements());
entry->clasp = clasp;
entry->key = key;
entry->kind = kind;
entry->nbytes = obj->sizeOfThis();
js_memcpy(&entry->templateObject, obj, entry->nbytes);
}
inline void
NewObjectCache::fillProto(EntryIndex entry, Class *clasp, JSObject *proto, gc::AllocKind kind, JSObject *obj)
{
JS_ASSERT(!proto->isGlobal());
JS_ASSERT(obj->getProto() == proto);
return fill(entry, clasp, proto, kind, obj);
}
inline void
NewObjectCache::fillGlobal(EntryIndex entry, Class *clasp, js::GlobalObject *global, gc::AllocKind kind, JSObject *obj)
{
//JS_ASSERT(global == obj->getGlobal());
return fill(entry, clasp, global, kind, obj);
}
inline void
NewObjectCache::fillType(EntryIndex entry, Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, JSObject *obj)
{
JS_ASSERT(obj->type() == type);
return fill(entry, clasp, type, kind, obj);
}
inline JSObject *
NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entry_)
{
JS_ASSERT(unsigned(entry_) < ArrayLength(entries));
Entry *entry = &entries[entry_];
JSObject *obj = js_TryNewGCObject(cx, entry->kind);
if (obj) {
copyCachedToObject(obj, reinterpret_cast<JSObject *>(&entry->templateObject));
Probes::createObject(cx, obj);
return obj;
}
/* Copy the entry to the stack first in case it is purged by a GC. */
size_t nbytes = entry->nbytes;
char stackObject[sizeof(JSObject_Slots16)];
JS_ASSERT(nbytes <= sizeof(stackObject));
js_memcpy(&stackObject, &entry->templateObject, nbytes);
JSObject *baseobj = (JSObject *) stackObject;
RootShape shapeRoot(cx, (Shape **) baseobj->addressOfShape());
RootTypeObject typeRoot(cx, (types::TypeObject **) baseobj->addressOfType());
obj = js_NewGCObject(cx, entry->kind);
if (obj) {
copyCachedToObject(obj, baseobj);
Probes::createObject(cx, obj);
return obj;
}
return NULL;
}
struct PreserveRegsGuard
{
PreserveRegsGuard(JSContext *cx, FrameRegs &regs)

View File

@ -115,8 +115,6 @@ JSCompartment::init(JSContext *cx)
activeAnalysis = activeInference = false;
types.init(cx);
newObjectCache.reset();
if (!crossCompartmentWrappers.init())
return false;
@ -471,8 +469,6 @@ JSCompartment::sweep(FreeOp *fop, bool releaseTypes)
if (emptyTypeObject && IsAboutToBeFinalized(emptyTypeObject))
emptyTypeObject = NULL;
newObjectCache.reset();
sweepBreakpoints(fop);
{

View File

@ -283,9 +283,6 @@ struct JSCompartment
js::types::TypeObject *getLazyType(JSContext *cx, JSObject *proto);
/* Cache to speed up object creation. */
js::NewObjectCache newObjectCache;
/*
* Keeps track of the total number of malloc bytes connected to a
* compartment's GC things. This counter should be used in preference to

View File

@ -2887,10 +2887,10 @@ PurgeRuntime(JSTracer *trc)
}
rt->tempLifoAlloc.freeUnused();
rt->gsnCache.purge();
/* FIXME: bug 506341 */
rt->gsnCache.purge();
rt->propertyCache.purge(rt);
rt->newObjectCache.purge();
for (ContextIter acx(rt); !acx.done(); acx.next())
acx->purge();

View File

@ -2771,7 +2771,7 @@ js::NewObjectWithGivenProto(JSContext *cx, js::Class *clasp, JSObject *proto, JS
if (CanBeFinalizedInBackground(kind, clasp))
kind = GetBackgroundAllocKind(kind);
NewObjectCache &cache = cx->compartment->newObjectCache;
NewObjectCache &cache = cx->runtime->newObjectCache;
NewObjectCache::EntryIndex entry = -1;
if (proto && (!parent || parent == proto->getParent()) && !proto->isGlobal()) {
@ -2827,7 +2827,7 @@ js::NewObjectWithClassProto(JSContext *cx, js::Class *clasp, JSObject *proto, JS
*/
JSProtoKey protoKey = GetClassProtoKey(clasp);
NewObjectCache &cache = cx->compartment->newObjectCache;
NewObjectCache &cache = cx->runtime->newObjectCache;
NewObjectCache::EntryIndex entry = -1;
if (parent->isGlobal() && protoKey != JSProto_Null) {
@ -2863,7 +2863,7 @@ js::NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, gc
if (CanBeFinalizedInBackground(kind, &ObjectClass))
kind = GetBackgroundAllocKind(kind);
NewObjectCache &cache = cx->compartment->newObjectCache;
NewObjectCache &cache = cx->runtime->newObjectCache;
NewObjectCache::EntryIndex entry = -1;
if (parent == type->proto->getParent()) {

View File

@ -1079,83 +1079,6 @@ IsStandardClassResolved(JSObject *obj, js::Class *clasp);
void
MarkStandardClassInitializedNoProto(JSObject *obj, js::Class *clasp);
/*
* Cache for speeding up repetitive creation of objects in the VM.
* When an object is created which matches the criteria in the 'key' section
* below, an entry is filled with the resulting object.
*/
class NewObjectCache
{
struct Entry
{
/* Class of the constructed object. */
Class *clasp;
/*
* Key with one of three possible values:
*
* - Global for the object. The object must have a standard class for
* which the global's prototype can be determined, and the object's
* parent will be the global.
*
* - Prototype for the object (cannot be global). The object's parent
* will be the prototype's parent.
*
* - Type for the object. The object's parent will be the type's
* prototype's parent.
*/
gc::Cell *key;
/* Allocation kind for the constructed object. */
gc::AllocKind kind;
/* Number of bytes to copy from the template object. */
uint32_t nbytes;
/*
* Template object to copy from, with the initial values of fields,
* fixed slots (undefined) and private data (NULL).
*/
JSObject_Slots16 templateObject;
};
Entry entries[41];
void staticAsserts() {
JS_STATIC_ASSERT(gc::FINALIZE_OBJECT_LAST == gc::FINALIZE_OBJECT16_BACKGROUND);
}
public:
typedef int EntryIndex;
void reset() { PodZero(this); }
/*
* Get the entry index for the given lookup, return whether there was a hit
* on an existing entry.
*/
inline bool lookupProto(Class *clasp, JSObject *proto, gc::AllocKind kind, EntryIndex *pentry);
inline bool lookupGlobal(Class *clasp, js::GlobalObject *global, gc::AllocKind kind, EntryIndex *pentry);
inline bool lookupType(Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, EntryIndex *pentry);
/* Return a new object from a cache hit produced by a lookup method. */
inline JSObject *newObjectFromHit(JSContext *cx, EntryIndex entry);
/* Fill an entry after a cache miss. */
inline void fillProto(EntryIndex entry, Class *clasp, JSObject *proto, gc::AllocKind kind, JSObject *obj);
inline void fillGlobal(EntryIndex entry, Class *clasp, js::GlobalObject *global, gc::AllocKind kind, JSObject *obj);
inline void fillType(EntryIndex entry, Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, JSObject *obj);
/* Invalidate any entries which might produce an object with shape/proto. */
void invalidateEntriesForShape(JSContext *cx, Shape *shape, JSObject *proto);
private:
inline bool lookup(Class *clasp, gc::Cell *key, gc::AllocKind kind, EntryIndex *pentry);
inline void fill(EntryIndex entry, Class *clasp, gc::Cell *key, gc::AllocKind kind, JSObject *obj);
static inline void copyCachedToObject(JSObject *dst, JSObject *src);
};
} /* namespace js */
/*

View File

@ -1331,75 +1331,6 @@ class AutoPropertyDescriptorRooter : private AutoGCRooter, public PropertyDescri
SkipRoot skip;
};
inline bool
NewObjectCache::lookup(Class *clasp, gc::Cell *key, gc::AllocKind kind, EntryIndex *pentry)
{
uintptr_t hash = (uintptr_t(clasp) ^ uintptr_t(key)) + kind;
*pentry = hash % js::ArrayLength(entries);
Entry *entry = &entries[*pentry];
/* N.B. Lookups with the same clasp/key but different kinds map to different entries. */
return (entry->clasp == clasp && entry->key == key);
}
inline bool
NewObjectCache::lookupProto(Class *clasp, JSObject *proto, gc::AllocKind kind, EntryIndex *pentry)
{
JS_ASSERT(!proto->isGlobal());
return lookup(clasp, proto, kind, pentry);
}
inline bool
NewObjectCache::lookupGlobal(Class *clasp, js::GlobalObject *global, gc::AllocKind kind, EntryIndex *pentry)
{
return lookup(clasp, global, kind, pentry);
}
inline bool
NewObjectCache::lookupType(Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, EntryIndex *pentry)
{
return lookup(clasp, type, kind, pentry);
}
inline void
NewObjectCache::fill(EntryIndex entry_, Class *clasp, gc::Cell *key, gc::AllocKind kind, JSObject *obj)
{
JS_ASSERT(unsigned(entry_) < ArrayLength(entries));
Entry *entry = &entries[entry_];
JS_ASSERT(!obj->hasDynamicSlots() && !obj->hasDynamicElements());
entry->clasp = clasp;
entry->key = key;
entry->kind = kind;
entry->nbytes = obj->sizeOfThis();
js_memcpy(&entry->templateObject, obj, entry->nbytes);
}
inline void
NewObjectCache::fillProto(EntryIndex entry, Class *clasp, JSObject *proto, gc::AllocKind kind, JSObject *obj)
{
JS_ASSERT(!proto->isGlobal());
JS_ASSERT(obj->getProto() == proto);
return fill(entry, clasp, proto, kind, obj);
}
inline void
NewObjectCache::fillGlobal(EntryIndex entry, Class *clasp, js::GlobalObject *global, gc::AllocKind kind, JSObject *obj)
{
//JS_ASSERT(global == obj->getGlobal());
return fill(entry, clasp, global, kind, obj);
}
inline void
NewObjectCache::fillType(EntryIndex entry, Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, JSObject *obj)
{
JS_ASSERT(obj->type() == type);
return fill(entry, clasp, type, kind, obj);
}
inline void
NewObjectCache::copyCachedToObject(JSObject *dst, JSObject *src)
{
@ -1410,39 +1341,6 @@ NewObjectCache::copyCachedToObject(JSObject *dst, JSObject *src)
#endif
}
inline JSObject *
NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entry_)
{
JS_ASSERT(unsigned(entry_) < ArrayLength(entries));
Entry *entry = &entries[entry_];
JSObject *obj = js_TryNewGCObject(cx, entry->kind);
if (obj) {
copyCachedToObject(obj, &entry->templateObject);
Probes::createObject(cx, obj);
return obj;
}
/* Copy the entry to the stack first in case it is purged by a GC. */
size_t nbytes = entry->nbytes;
char stackObject[sizeof(JSObject_Slots16)];
JS_ASSERT(nbytes <= sizeof(stackObject));
js_memcpy(&stackObject, &entry->templateObject, nbytes);
JSObject *baseobj = (JSObject *) stackObject;
RootShape shapeRoot(cx, (Shape **) baseobj->addressOfShape());
RootTypeObject typeRoot(cx, (types::TypeObject **) baseobj->addressOfType());
obj = js_NewGCObject(cx, entry->kind);
if (obj) {
copyCachedToObject(obj, baseobj);
Probes::createObject(cx, obj);
return obj;
}
return NULL;
}
static inline bool
CanBeFinalizedInBackground(gc::AllocKind kind, Class *clasp)
{

View File

@ -1397,7 +1397,7 @@ EmptyShape::insertInitialShape(JSContext *cx, Shape *shape, JSObject *proto)
* the appropriate properties if found. Clearing the cache entry avoids
* this duplicate regeneration.
*/
cx->compartment->newObjectCache.invalidateEntriesForShape(cx, shape, proto);
cx->runtime->newObjectCache.invalidateEntriesForShape(cx, shape, proto);
}
void

View File

@ -359,7 +359,7 @@ GlobalObject::clear(JSContext *cx)
* Reset the new object cache in the compartment, which assumes that
* prototypes cached on the global object are immutable.
*/
cx->compartment->newObjectCache.reset();
cx->runtime->newObjectCache.purge();
#ifdef JS_METHODJIT
/*