[INFER] Allocate typeset data and properties from arenas, occasionally purge observed types in compartments, bug 679329.

This commit is contained in:
Brian Hackett 2011-08-17 06:48:14 -07:00
parent 4857992503
commit f3b5be7a42
18 changed files with 320 additions and 233 deletions

View File

@ -4299,7 +4299,6 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
Value v;
if (!obj->getProperty(cx, r.front().propid, &v))
return NULL;
TypeScript::SetUpvar(cx, fun->script(), i, v);
clone->getFlatClosureUpvars()[i] = v;
}

View File

@ -298,6 +298,14 @@ ArenaAllocatedSize(const JSArenaPool &pool)
return res;
}
/* Move the contents of oldPool into newPool, and reset oldPool. */
inline void
MoveArenaPool(JSArenaPool *oldPool, JSArenaPool *newPool)
{
*newPool = *oldPool;
JS_InitArenaPool(oldPool, NULL, newPool->arenasize, newPool->mask + 1);
}
} /* namespace js */
#endif /* __cplusplus */

View File

@ -136,7 +136,10 @@ JSCompartment::init(JSContext *cx)
activeAnalysis = activeInference = false;
types.init(cx);
JS_InitArenaPool(&pool, "analysis", 4096, 8);
/* Duplicated from jscntxt.cpp. :XXX: bug 675150 fix hack. */
static const size_t ARENA_HEADER_SIZE_HACK = 40;
JS_InitArenaPool(&pool, "analysis", 4096 - ARENA_HEADER_SIZE_HACK, 8);
freeLists.init();
if (!crossCompartmentWrappers.init())
@ -598,6 +601,13 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
#endif
if (!activeAnalysis) {
/*
* Clear the analysis pool, but don't releas its data yet. While
* sweeping types any live data will be allocated into the pool.
*/
JSArenaPool oldPool;
MoveArenaPool(&pool, &oldPool);
/*
* Sweep analysis information and everything depending on it from the
* compartment, including all remaining mjit code if inference is
@ -610,8 +620,19 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
JSScript *script = reinterpret_cast<JSScript *>(cursor);
if (script->types)
if (script->types) {
types::TypeScript::Sweep(cx, script);
/*
* On each 1/8 lifetime, release observed types for all scripts.
* This is always safe to do when there are no frames for the
* compartment on the stack.
*/
if (discardScripts) {
script->types->destroy();
script->types = NULL;
}
}
}
}
@ -624,7 +645,7 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
}
/* Reset the analysis pool, releasing all analysis and intermediate type data. */
JS_FinishArenaPool(&pool);
JS_FinishArenaPool(&oldPool);
/*
* Destroy eval'ed scripts, now that any type inference information referring

View File

@ -78,11 +78,11 @@ JS_GetCustomIteratorCount(JSContext *cx);
/* Data for tracking analysis/inference memory usage. */
typedef struct TypeInferenceMemoryStats
{
int64 scriptMain;
int64 scriptSets;
int64 objectMain;
int64 objectSets;
int64 poolMain;
int64 scripts;
int64 objects;
int64 tables;
int64 temporary;
int64 emptyShapes;
} TypeInferenceMemoryStats;
extern JS_FRIEND_API(void)

View File

@ -2643,10 +2643,8 @@ js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen)
uintN level = fun->script()->staticLevel;
JSUpvarArray *uva = fun->script()->upvars();
for (uint32 i = 0, n = uva->length; i < n; i++) {
for (uint32 i = 0, n = uva->length; i < n; i++)
upvars[i] = GetUpvar(cx, level, uva->vector[i]);
TypeScript::SetUpvar(cx, fun->script(), i, upvars[i]);
}
return closure;
}

View File

@ -583,7 +583,7 @@ js_GCThingIsMarked(void *thing, uintN color = BLACK)
* JIT code is discarded in inactive compartments, regardless of how often that
* code runs.
*/
static const int64 JIT_SCRIPT_EIGHTH_LIFETIME = 120 * 1000 * 1000;
static const int64 JIT_SCRIPT_EIGHTH_LIFETIME = 60 * 1000 * 1000;
JSBool
js_InitGC(JSRuntime *rt, uint32 maxbytes)

View File

@ -899,8 +899,11 @@ js::types::TypeObject::trace(JSTracer *trc, bool weak)
* from singleton JS objects, as if there are no outstanding refs we will
* destroy the type object and revert the JS object to a lazy type.
*/
if (IS_GC_MARKING_TRACER(trc) && (!weak || !singleton))
if (IS_GC_MARKING_TRACER(trc) && (!weak || !singleton)) {
JS_ASSERT_IF(trc->context->runtime->gcCurrentCompartment,
compartment() == trc->context->runtime->gcCurrentCompartment);
markIfUnmarked(static_cast<GCMarker *>(trc)->getMarkColor());
}
#ifdef DEBUG
InlineMarkId(trc, name_, "type_name");

View File

@ -404,6 +404,11 @@ class HashTable : private AllocPolicy
destroyTable(*this, table, tableCapacity);
}
size_t allocatedSize() const
{
return sizeof(Entry) * tableCapacity;
}
private:
static HashNumber hash1(HashNumber hash0, uint32 shift) {
return hash0 >> shift;
@ -1106,6 +1111,9 @@ class HashMap
*/
unsigned generation() const { return impl.generation(); }
/* Number of bytes of heap data allocated by this table. */
size_t allocatedSize() const { return impl.allocatedSize(); }
/* Shorthand operations: */
bool has(const Lookup &l) const {
@ -1305,6 +1313,9 @@ class HashSet
*/
unsigned generation() const { return impl.generation(); }
/* Number of bytes of heap data allocated by this table. */
size_t allocatedSize() const { return impl.allocatedSize(); }
/* Shorthand operations: */
bool has(const Lookup &l) const {

View File

@ -356,7 +356,6 @@ TypeSet::make(JSContext *cx, const char *name)
InferSpew(ISpewOps, "typeSet: %sT%p%s intermediate %s",
InferSpewColor(res), res, InferSpewColorReset(),
name);
res->setIntermediate();
return res;
}
@ -812,9 +811,7 @@ public:
TypeConstraintSubsetBarrier(JSScript *script, jsbytecode *pc, TypeSet *target)
: TypeConstraint("subsetBarrier"), script(script), pc(pc), target(target)
{
JS_ASSERT(!target->intermediate());
}
{}
void newType(JSContext *cx, TypeSet *source, Type type)
{
@ -1951,7 +1948,7 @@ void
TypeCompartment::growPendingArray(JSContext *cx)
{
unsigned newCapacity = js::Max(unsigned(100), pendingCapacity * 2);
PendingWork *newArray = (PendingWork *) js::OffTheBooks::calloc_(newCapacity * sizeof(PendingWork));
PendingWork *newArray = (PendingWork *) OffTheBooks::calloc_(newCapacity * sizeof(PendingWork));
if (!newArray) {
cx->compartment->types.setPendingNukeTypes(cx);
return;
@ -1992,9 +1989,10 @@ TypeCompartment::processPendingRecompiles(JSContext *cx)
void
TypeCompartment::setPendingNukeTypes(JSContext *cx)
{
JS_ASSERT(cx->compartment->activeInference);
JS_ASSERT(compartment()->activeInference);
if (!pendingNukeTypes) {
js_ReportOutOfMemory(cx);
if (cx->compartment)
js_ReportOutOfMemory(cx);
pendingNukeTypes = true;
}
}
@ -2623,17 +2621,13 @@ bool
TypeObject::addProperty(JSContext *cx, jsid id, Property **pprop)
{
JS_ASSERT(!*pprop);
Property *base = singleton
? ArenaNew<Property>(cx->compartment->pool, id)
: cx->new_<Property>(id);
Property *base = ArenaNew<Property>(cx->compartment->pool, id);
if (!base) {
cx->compartment->types.setPendingNukeTypes(cx);
return false;
}
if (singleton) {
base->types.setIntermediate();
/*
* Fill the property in with any type the object already has in an
* own property. We are only interested in plain native properties
@ -3096,7 +3090,6 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
if (!state.phiNodes.append(newv->value.phiNode()))
return false;
TypeSet &types = newv->value.phiNode()->types;
types.setIntermediate();
InferSpew(ISpewOps, "typeSet: %sT%p%s phi #%u:%05u:%u",
InferSpewColor(&types), &types, InferSpewColorReset(),
script->id(), offset, newv->slot);
@ -3112,7 +3105,6 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
return true;
for (unsigned i = 0; i < defCount; i++) {
pushed[i].setIntermediate();
InferSpew(ISpewOps, "typeSet: %sT%p%s pushed%u #%u:%05u",
InferSpewColor(&pushed[i]), &pushed[i], InferSpewColorReset(),
i, script->id(), offset);
@ -3348,18 +3340,12 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
break;
case JSOP_GETXPROP: {
TypeSet *seen = script->analysis()->bytecodeTypes(pc);
addTypeBarrier(cx, pc, seen, Type::UnknownType());
seen->addSubset(cx, &pushed[0]);
break;
}
case JSOP_GETXPROP:
case JSOP_GETFCSLOT:
case JSOP_CALLFCSLOT: {
unsigned index = GET_UINT16(pc);
TypeSet *types = TypeScript::UpvarTypes(script, index);
types->addSubset(cx, &pushed[0]);
TypeSet *seen = bytecodeTypes(pc);
addTypeBarrier(cx, pc, seen, Type::UnknownType());
seen->addSubset(cx, &pushed[0]);
if (op == JSOP_CALLFCSLOT) {
pushed[1].addType(cx, Type::UndefinedType());
pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType());
@ -4516,10 +4502,6 @@ ScriptAnalysis::printTypes(JSContext *cx)
TypeScript::LocalTypes(script, i)->print(cx);
}
}
for (unsigned i = 0; i < script->bindings.countUpvars(); i++) {
printf("\n upvar%u:", i);
TypeScript::UpvarTypes(script, i)->print(cx);
}
printf("\n");
for (unsigned offset = 0; offset < script->length; offset++) {
@ -4931,12 +4913,6 @@ JSScript::makeTypes(JSContext *cx)
InferSpewColor(types), types, InferSpewColorReset(),
i, id());
}
for (unsigned i = 0; i < bindings.countUpvars(); i++) {
TypeSet *types = TypeScript::UpvarTypes(this, i);
InferSpew(ISpewOps, "typeSet: %sT%p%s upvar%u #%u",
InferSpewColor(types), types, InferSpewColorReset(),
i, id());
}
#endif
return true;
@ -5228,39 +5204,32 @@ JSObject::makeNewType(JSContext *cx, JSScript *newScript, bool unknown)
void
TypeSet::sweep(JSContext *cx, JSCompartment *compartment)
{
JS_ASSERT(!intermediate());
uint32 objectCount = baseObjectCount();
/*
* Purge references to type objects that are no longer live. Type sets hold
* only weak references. For type sets containing more than one object,
* live entries in the object hash need to be copied to the compartment's
* new arena.
*/
unsigned objectCount = baseObjectCount();
if (objectCount >= 2) {
bool removed = false;
unsigned objectCapacity = HashSetCapacity(objectCount);
for (unsigned i = 0; i < objectCapacity; i++) {
TypeObjectKey *object = objectSet[i];
if (object && IsAboutToBeFinalized(cx, object)) {
objectSet[i] = NULL;
removed = true;
unsigned oldCapacity = HashSetCapacity(objectCount);
TypeObjectKey **oldArray = objectSet;
clearObjects();
objectCount = 0;
for (unsigned i = 0; i < oldCapacity; i++) {
TypeObjectKey *object = oldArray[i];
if (object && !IsAboutToBeFinalized(cx, object)) {
TypeObjectKey **pentry =
HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
(compartment, objectSet, objectCount, object);
if (pentry)
*pentry = object;
else
compartment->types.setPendingNukeTypes(cx);
}
}
if (removed) {
/* Reconstruct the type set to re-resolve hash collisions. */
TypeObjectKey **oldArray = objectSet;
objectSet = NULL;
objectCount = 0;
for (unsigned i = 0; i < objectCapacity; i++) {
TypeObjectKey *object = oldArray[i];
if (object) {
TypeObjectKey **pentry =
HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
(cx, objectSet, objectCount, object, false);
if (pentry)
*pentry = object;
else
compartment->types.setPendingNukeTypes(cx);
}
}
setBaseObjectCount(objectCount);
cx->free_(oldArray);
}
setBaseObjectCount(objectCount);
} else if (objectCount == 1) {
TypeObjectKey *object = (TypeObjectKey *) objectSet;
if (IsAboutToBeFinalized(cx, object)) {
@ -5289,7 +5258,6 @@ JSObject::revertLazyType()
inline void
TypeObject::clearProperties()
{
JS_ASSERT(singleton);
setBasePropertyCount(0);
propertySet = NULL;
}
@ -5301,27 +5269,26 @@ TypeObject::clearProperties()
* elsewhere. This also releases memory associated with dead type objects,
* so that type objects do not need later finalization.
*/
static inline void
SweepTypeObject(JSContext *cx, TypeObject *object)
inline void
TypeObject::sweep(JSContext *cx)
{
/*
* We may be regenerating existing type sets containing this object,
* so reset contributions on each GC to avoid tripping the limit.
*/
object->contribution = 0;
contribution = 0;
if (object->singleton) {
JS_ASSERT(!object->emptyShapes);
JS_ASSERT(!object->newScript);
if (singleton) {
JS_ASSERT(!emptyShapes);
JS_ASSERT(!newScript);
/*
* All properties on the object are allocated from the analysis pool,
* and can be discarded. We will regenerate them as needed as code gets
* reanalyzed.
* All properties can be discarded. We will regenerate them as needed
* as code gets reanalyzed.
*/
object->clearProperties();
clearProperties();
if (!object->isMarked()) {
if (!isMarked()) {
/*
* Singleton objects do not hold strong references on their types.
* When removing the type, however, we need to fixup the singleton
@ -5329,41 +5296,76 @@ SweepTypeObject(JSContext *cx, TypeObject *object)
* proto must be live, since the type's prototype and its 'new'
* type are both strong references.
*/
JS_ASSERT_IF(object->singleton->isMarked() && object->proto,
object->proto->isMarked() && object->proto->newType->isMarked());
object->singleton->revertLazyType();
JS_ASSERT_IF(singleton->isMarked() && proto,
proto->isMarked() && proto->newType->isMarked());
singleton->revertLazyType();
}
return;
}
if (!object->isMarked()) {
if (object->emptyShapes)
Foreground::free_(object->emptyShapes);
if (!isMarked()) {
if (emptyShapes)
Foreground::free_(emptyShapes);
if (newScript)
Foreground::free_(newScript);
return;
}
unsigned count = object->getPropertyCount();
for (unsigned i = 0; i < count; i++) {
Property *prop = object->getProperty(i);
if (prop) {
prop->types.clearObjects();
Foreground::delete_(prop);
JSCompartment *compartment = this->compartment();
/*
* Properties were allocated from the old arena, and need to be copied over
* to the new one. Don't hang onto properties without the OWN_PROPERTY
* flag; these were never directly assigned, and get any possible values
* from the object's prototype.
*/
unsigned propertyCount = basePropertyCount();
if (propertyCount >= 2) {
unsigned oldCapacity = HashSetCapacity(propertyCount);
Property **oldArray = propertySet;
clearProperties();
propertyCount = 0;
for (unsigned i = 0; i < oldCapacity; i++) {
Property *prop = oldArray[i];
if (prop && prop->types.isOwnProperty(false)) {
Property *newProp = ArenaNew<Property>(compartment->pool, *prop);
if (newProp) {
Property **pentry =
HashSetInsert<jsid,Property,Property>
(compartment, propertySet, propertyCount, prop->id);
if (pentry) {
*pentry = newProp;
newProp->types.sweep(cx, compartment);
} else {
compartment->types.setPendingNukeTypes(cx);
}
} else {
compartment->types.setPendingNukeTypes(cx);
}
}
}
if (count >= 2)
Foreground::free_(object->propertySet);
if (object->newScript)
Foreground::free_(object->newScript);
return;
setBasePropertyCount(propertyCount);
} else if (propertyCount == 1) {
Property *prop = (Property *) propertySet;
if (prop->types.isOwnProperty(false)) {
Property *newProp = ArenaNew<Property>(compartment->pool, *prop);
if (newProp) {
propertySet = (Property **) newProp;
newProp->types.sweep(cx, compartment);
} else {
compartment->types.setPendingNukeTypes(cx);
}
} else {
propertySet = NULL;
setBasePropertyCount(0);
}
}
/* Sweep type sets for all properties of the object. */
unsigned count = object->getPropertyCount();
for (unsigned i = 0; i < count; i++) {
Property *prop = object->getProperty(i);
if (prop)
prop->types.sweep(cx, object->compartment());
if (basePropertyCount() <= SET_ARRAY_SIZE) {
for (unsigned i = 0; i < basePropertyCount(); i++)
JS_ASSERT(propertySet[i]);
}
/*
@ -5371,8 +5373,8 @@ SweepTypeObject(JSContext *cx, TypeObject *object)
* newScript information, these constraints will need to be regenerated
* the next time we compile code which depends on this info.
*/
if (object->newScript)
object->flags |= OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
if (newScript)
flags |= OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
}
void
@ -5397,7 +5399,7 @@ SweepTypeObjects(JSContext *cx, JSCompartment *compartment)
thing = span->last;
span = span->nextSpan();
} else {
SweepTypeObject(cx, reinterpret_cast<TypeObject *>(thing));
reinterpret_cast<TypeObject *>(thing)->sweep(cx);
}
}
}
@ -5470,6 +5472,16 @@ TypeCompartment::sweep(JSContext *cx)
e.removeFront();
}
}
/*
* The pending array is reset on GC, it can grow large (75+ KB) and is easy
* to reallocate if the compartment becomes active again.
*/
if (pendingArray)
cx->free_(pendingArray);
pendingArray = NULL;
pendingCapacity = 0;
}
TypeCompartment::~TypeCompartment()
@ -5537,17 +5549,39 @@ TypeScript::destroy()
Foreground::delete_(dynamicList);
dynamicList = next;
}
Foreground::free_(this);
}
size_t
inline size_t
TypeSet::dynamicSize()
{
/* Get the amount of memory allocated from the analysis pool for this set. */
uint32 count = baseObjectCount();
if (count >= 2)
return HashSetCapacity(count) * sizeof(TypeObject *);
return 0;
}
inline size_t
TypeObject::dynamicSize()
{
size_t bytes = 0;
uint32 count = basePropertyCount();
if (count >= 2)
bytes += HashSetCapacity(count) * sizeof(TypeObject *);
count = getPropertyCount();
for (unsigned i = 0; i < count; i++) {
Property *prop = getProperty(i);
if (prop)
bytes += sizeof(Property) + prop->types.dynamicSize();
}
return bytes;
}
static void
GetScriptMemoryStats(JSScript *script, TypeInferenceMemoryStats *stats)
{
@ -5555,28 +5589,41 @@ GetScriptMemoryStats(JSScript *script, TypeInferenceMemoryStats *stats)
return;
if (!script->compartment->types.inferenceEnabled) {
stats->scriptMain += sizeof(TypeScript);
stats->scripts += sizeof(TypeScript);
return;
}
unsigned count = TypeScript::NumTypeSets(script);
stats->scriptMain += sizeof(TypeScript) + count * sizeof(TypeSet);
TypeSet *typeArray = script->types->typeArray();
for (unsigned i = 0; i < count; i++)
stats->scriptSets += typeArray[i].dynamicSize();
stats->scripts += sizeof(TypeScript) + count * sizeof(TypeSet);
TypeResult *result = script->types->dynamicList;
while (result) {
stats->scriptMain += sizeof(TypeResult);
stats->scripts += sizeof(TypeResult);
result = result->next;
}
TypeSet *typeArray = script->types->typeArray();
for (unsigned i = 0; i < count; i++) {
size_t bytes = typeArray[i].dynamicSize();
stats->scripts += bytes;
stats->temporary -= bytes;
}
}
JS_FRIEND_API(void)
JS_GetTypeInferenceMemoryStats(JSContext *cx, JSCompartment *compartment,
TypeInferenceMemoryStats *stats)
{
/*
* Note: not all data in the pool is temporary, and some will survive GCs
* by being copied to the replacement pool. This memory will be counted too
* and deducted from the amount of temporary data.
*/
stats->temporary += ArenaAllocatedSize(compartment->pool);
/* Pending arrays are cleared on GC along with the analysis pool. */
stats->temporary += sizeof(TypeCompartment::PendingWork) * compartment->types.pendingCapacity;
for (JSCList *cursor = compartment->scripts.next;
cursor != &compartment->scripts;
cursor = cursor->next) {
@ -5584,32 +5631,54 @@ JS_GetTypeInferenceMemoryStats(JSContext *cx, JSCompartment *compartment,
GetScriptMemoryStats(script, stats);
}
stats->poolMain += ArenaAllocatedSize(compartment->pool);
if (compartment->types.allocationSiteTable)
stats->tables += compartment->types.allocationSiteTable->allocatedSize();
if (compartment->types.arrayTypeTable)
stats->tables += compartment->types.arrayTypeTable->allocatedSize();
if (compartment->types.objectTypeTable) {
stats->tables += compartment->types.objectTypeTable->allocatedSize();
for (ObjectTypeTable::Enum e(*compartment->types.objectTypeTable);
!e.empty();
e.popFront()) {
const ObjectTableKey &key = e.front().key;
stats->tables += key.nslots * (sizeof(jsid) + sizeof(Type));
}
}
}
JS_FRIEND_API(void)
JS_GetTypeInferenceObjectStats(void *object_, TypeInferenceMemoryStats *stats)
{
TypeObject *object = (TypeObject *) object_;
stats->objectMain += sizeof(TypeObject);
stats->objects += sizeof(TypeObject);
if (object->singleton) {
/*
* Properties and TypeSet data for singletons are allocated in the
* compartment's analysis pool.
* Properties and associated type sets for singletons are cleared on
* every GC. The type object is normally destroyed too, but we don't
* charge this to 'temporary' as this is not for GC heap values.
*/
JS_ASSERT(!object->newScript && !object->emptyShapes);
return;
}
uint32 count = object->getPropertyCount();
if (count >= 2)
stats->objectMain += count * sizeof(Property *);
for (unsigned i = 0; i < count; i++) {
Property *prop = object->getProperty(i);
if (prop) {
stats->objectMain += sizeof(Property);
stats->objectSets += prop->types.dynamicSize();
if (object->newScript) {
size_t length = 0;
for (TypeNewScript::Initializer *init = object->newScript->initializerList;; init++) {
length++;
if (init->kind == TypeNewScript::Initializer::DONE)
break;
}
stats->objects += sizeof(TypeNewScript) + (length * sizeof(TypeNewScript::Initializer));
}
if (object->emptyShapes)
stats->emptyShapes += sizeof(EmptyShape*) * gc::FINALIZE_FUNCTION_AND_OBJECT_LAST;
size_t bytes = object->dynamicSize();
stats->objects += bytes;
stats->temporary -= bytes;
}

View File

@ -178,8 +178,13 @@ inline Type GetValueType(JSContext *cx, const Value &val);
* which occurs while we are not actively working with inference or other
* analysis information, we clear out all generated constraints, all type sets
* describing stack types within scripts, and (normally) all data describing
* type objects describing particular JS objects (see the lazy type objects
* overview below). JIT code depends on this data and is cleared as well.
* type objects for particular JS objects (see the lazy type objects overview
* below). JIT code depends on this data and is cleared as well.
*
* All this data is allocated into compartment->pool. Some type inference data
* lives across GCs: type sets for scripts and non-singleton type objects, and
* propeties for such type objects. This data is also allocated into
* compartment->pool, but everything still live is copied to a new arena on GC.
*/
/*
@ -247,33 +252,30 @@ enum {
/* Mask of normal type flags on a type set. */
TYPE_FLAG_BASE_MASK = 0x000100ff,
/* Flag for type sets which are cleared on GC. */
TYPE_FLAG_INTERMEDIATE_SET = 0x00020000,
/* Flags for type sets which are on object properties. */
/*
* Whether there are subset constraints propagating the possible types
* for this property inherited from the object's prototypes. Reset on GC.
*/
TYPE_FLAG_PROPAGATED_PROPERTY = 0x00040000,
TYPE_FLAG_PROPAGATED_PROPERTY = 0x00020000,
/* Whether this property has ever been directly written. */
TYPE_FLAG_OWN_PROPERTY = 0x00080000,
TYPE_FLAG_OWN_PROPERTY = 0x00040000,
/*
* Whether the property has ever been deleted or reconfigured to behave
* differently from a normal native property (e.g. made non-writable or
* given a scripted getter or setter).
*/
TYPE_FLAG_CONFIGURED_PROPERTY = 0x00100000,
TYPE_FLAG_CONFIGURED_PROPERTY = 0x00080000,
/*
* Whether the property is definitely in a particular inline slot on all
* objects from which it has not been deleted or reconfigured. Implies
* OWN_PROPERTY and unlike OWN/CONFIGURED property, this cannot change.
*/
TYPE_FLAG_DEFINITE_PROPERTY = 0x00200000,
TYPE_FLAG_DEFINITE_PROPERTY = 0x00100000,
/* If the property is definite, mask and shift storing the slot. */
TYPE_FLAG_DEFINITE_MASK = 0x0f000000,
@ -370,7 +372,7 @@ class TypeSet
void print(JSContext *cx);
inline void sweep(JSContext *cx, JSCompartment *compartment);
size_t dynamicSize();
inline size_t dynamicSize();
/* Whether this set contains a specific type. */
inline bool hasType(Type type);
@ -412,8 +414,6 @@ class TypeSet
inline JSObject *getSingleObject(unsigned i);
inline TypeObject *getTypeObject(unsigned i);
bool intermediate() { return !!(flags & TYPE_FLAG_INTERMEDIATE_SET); }
void setIntermediate() { JS_ASSERT(!flags); flags = TYPE_FLAG_INTERMEDIATE_SET; }
void setOwnProperty(bool configurable) {
flags |= TYPE_FLAG_OWN_PROPERTY;
if (configurable)
@ -446,8 +446,8 @@ class TypeSet
void addLazyArguments(JSContext *cx, TypeSet *target);
/*
* Make an intermediate type set with the specified debugging name,
* not embedded in another structure.
* Make an type set with the specified debugging name, not embedded in
* another structure.
*/
static TypeSet *make(JSContext *cx, const char *name);
@ -498,9 +498,6 @@ class TypeSet
/* Get the single value which can appear in this type set, otherwise NULL. */
JSObject *getSingleton(JSContext *cx, bool freeze = true);
static bool
SweepTypeSet(JSContext *cx, JSCompartment *compartment, TypeSet *types);
inline void clearObjects();
private:
@ -608,6 +605,10 @@ struct Property
: id(id)
{}
Property(const Property &o)
: id(o.id), types(o.types)
{}
static uint32 keyBits(jsid id) { return (uint32) JSID_BITS(id); }
static jsid getKey(Property *p) { return p->id; }
};
@ -833,6 +834,9 @@ struct TypeObject : gc::Cell
void trace(JSTracer *trc, bool weak = false);
inline void clearProperties();
inline void sweep(JSContext *cx);
inline size_t dynamicSize();
/*
* Type objects don't have explicit finalizers. Memory owned by a type
@ -905,7 +909,6 @@ struct TypeScript
static inline TypeSet *ThisTypes(JSScript *script);
static inline TypeSet *ArgTypes(JSScript *script, unsigned i);
static inline TypeSet *LocalTypes(JSScript *script, unsigned i);
static inline TypeSet *UpvarTypes(JSScript *script, unsigned i);
/* Follows slot layout in jsanalyze.h, can get this/arg/local type sets. */
static inline TypeSet *SlotTypes(JSScript *script, unsigned slot);
@ -950,7 +953,6 @@ struct TypeScript
static inline void SetLocal(JSContext *cx, JSScript *script, unsigned local, const js::Value &value);
static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type);
static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value);
static inline void SetUpvar(JSContext *cx, JSScript *script, unsigned upvar, const js::Value &value);
static void Sweep(JSContext *cx, JSScript *script);
void destroy();

View File

@ -455,7 +455,7 @@ UseNewTypeAtEntry(JSContext *cx, StackFrame *fp)
/* static */ inline unsigned
TypeScript::NumTypeSets(JSScript *script)
{
return script->nTypeSets + analyze::TotalSlots(script) + script->bindings.countUpvars();
return script->nTypeSets + analyze::TotalSlots(script);
}
/* static */ inline TypeSet *
@ -490,13 +490,6 @@ TypeScript::LocalTypes(JSScript *script, unsigned i)
return script->types->typeArray() + script->nTypeSets + js::analyze::LocalSlot(script, i);
}
/* static */ inline TypeSet *
TypeScript::UpvarTypes(JSScript *script, unsigned i)
{
JS_ASSERT(i < script->bindings.countUpvars());
return script->types->typeArray() + script->nTypeSets + js::analyze::TotalSlots(script) + i;
}
/* static */ inline TypeSet *
TypeScript::SlotTypes(JSScript *script, unsigned slot)
{
@ -681,21 +674,6 @@ TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js:
}
}
/* static */ inline void
TypeScript::SetUpvar(JSContext *cx, JSScript *script, unsigned upvar, const js::Value &value)
{
if (!cx->typeInferenceEnabled() || !script->ensureHasTypes(cx))
return;
Type type = GetValueType(cx, value);
if (!UpvarTypes(script, upvar)->hasType(type)) {
AutoEnterTypeInference enter(cx);
InferSpew(ISpewOps, "externalType: setUpvar #%u %u: %s",
script->id(), upvar, TypeString(type));
UpvarTypes(script, upvar)->addType(cx, type);
}
}
/////////////////////////////////////////////////////////////////////
// TypeCompartment
/////////////////////////////////////////////////////////////////////
@ -796,7 +774,7 @@ HashKey(T v)
*/
template <class T, class U, class KEY>
static U **
HashSetInsertTry(JSContext *cx, U **&values, unsigned &count, T key, bool pool)
HashSetInsertTry(JSCompartment *compartment, U **&values, unsigned &count, T key)
{
unsigned capacity = HashSetCapacity(count);
unsigned insertpos = HashKey<T,KEY>(key) & (capacity - 1);
@ -820,13 +798,9 @@ HashSetInsertTry(JSContext *cx, U **&values, unsigned &count, T key, bool pool)
return &values[insertpos];
}
U **newValues = pool
? ArenaArray<U*>(cx->compartment->pool, newCapacity)
: (U **) js::OffTheBooks::malloc_(newCapacity * sizeof(U*));
if (!newValues) {
cx->compartment->types.setPendingNukeTypes(cx);
U **newValues = ArenaArray<U*>(compartment->pool, newCapacity);
if (!newValues)
return NULL;
}
PodZero(newValues, newCapacity);
for (unsigned i = 0; i < capacity; i++) {
@ -838,8 +812,6 @@ HashSetInsertTry(JSContext *cx, U **&values, unsigned &count, T key, bool pool)
}
}
if (values && !pool)
Foreground::free_(values);
values = newValues;
insertpos = HashKey<T,KEY>(key) & (newCapacity - 1);
@ -854,7 +826,7 @@ HashSetInsertTry(JSContext *cx, U **&values, unsigned &count, T key, bool pool)
*/
template <class T, class U, class KEY>
static inline U **
HashSetInsert(JSContext *cx, U **&values, unsigned &count, T key, bool pool)
HashSetInsert(JSCompartment *compartment, U **&values, unsigned &count, T key)
{
if (count == 0) {
JS_ASSERT(values == NULL);
@ -867,12 +839,9 @@ HashSetInsert(JSContext *cx, U **&values, unsigned &count, T key, bool pool)
if (KEY::getKey(oldData) == key)
return (U **) &values;
values = pool
? ArenaArray<U*>(cx->compartment->pool, SET_ARRAY_SIZE)
: (U **) js::OffTheBooks::malloc_(SET_ARRAY_SIZE * sizeof(U*));
values = ArenaArray<U*>(compartment->pool, SET_ARRAY_SIZE);
if (!values) {
values = (U **) oldData;
cx->compartment->types.setPendingNukeTypes(cx);
return NULL;
}
PodZero(values, SET_ARRAY_SIZE);
@ -894,7 +863,7 @@ HashSetInsert(JSContext *cx, U **&values, unsigned &count, T key, bool pool)
}
}
return HashSetInsertTry<T,U,KEY>(cx, values, count, key, pool);
return HashSetInsertTry<T,U,KEY>(compartment, values, count, key);
}
/* Lookup an entry in a hash set, return NULL if it does not exist. */
@ -964,8 +933,6 @@ TypeSet::setBaseObjectCount(uint32 count)
inline void
TypeSet::clearObjects()
{
if (baseObjectCount() >= 2 && !intermediate())
Foreground::free_(objectSet);
setBaseObjectCount(0);
objectSet = NULL;
}
@ -999,8 +966,12 @@ TypeSet::addType(JSContext *cx, Type type)
uint32 objectCount = baseObjectCount();
TypeObjectKey *object = type.objectKey();
TypeObjectKey **pentry = HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
(cx, objectSet, objectCount, object, intermediate());
if (!pentry || *pentry)
(cx->compartment, objectSet, objectCount, object);
if (!pentry) {
cx->compartment->types.setPendingNukeTypes(cx);
return;
}
if (*pentry)
return;
*pentry = object;
@ -1168,9 +1139,11 @@ TypeObject::getProperty(JSContext *cx, jsid id, bool assign)
uint32 propertyCount = basePropertyCount();
Property **pprop = HashSetInsert<jsid,Property,Property>
(cx, propertySet, propertyCount, id, singleton != NULL);
if (!pprop)
(cx->compartment, propertySet, propertyCount, id);
if (!pprop) {
cx->compartment->types.setPendingNukeTypes(cx);
return NULL;
}
if (!*pprop) {
setBasePropertyCount(propertyCount);

View File

@ -4694,6 +4694,7 @@ BEGIN_CASE(JSOP_CALLFCSLOT)
JS_ASSERT(index < obj->getFunctionPrivate()->script()->bindings.countUpvars());
PUSH_COPY(obj->getFlatClosureUpvar(index));
TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
if (op == JSOP_CALLFCSLOT)
PUSH_UNDEFINED();
}

View File

@ -370,8 +370,8 @@ OPDEF(JSOP_FINALLY, 135,"finally", NULL, 1, 0, 2, 0, JOF_BYTE)
* in the function's u.i.script->upvars() array. The CALL variant computes the
* callee and this-object in preparation for a JSOP_CALL.
*/
OPDEF(JSOP_GETFCSLOT, 136,"getfcslot", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME)
OPDEF(JSOP_CALLFCSLOT, 137,"callfcslot", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_GETFCSLOT, 136,"getfcslot", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME|JOF_TYPESET)
OPDEF(JSOP_CALLFCSLOT, 137,"callfcslot", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_TYPESET|JOF_CALLOP)
/*
* Bytecodes that avoid making an arguments object in most cases:

View File

@ -1419,10 +1419,8 @@ DestroyScript(JSContext *cx, JSScript *script, JSObject *owner, uint32 caller)
PurgeScriptFragments(script->compartment->traceMonitor(), script);
#endif
if (script->types) {
if (script->types)
script->types->destroy();
Foreground::free_(script->types);
}
#ifdef JS_METHODJIT
mjit::ReleaseScriptCode(cx, script);

View File

@ -1330,6 +1330,7 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
#endif
JS_ASSERT(size_t(cursor - (uint8*)jit) == dataSize);
JS_ASSERT(jit->scriptDataSize() == dataSize);
/* Link fast and slow paths together. */
stubcc.fixCrossJumps(result, masm.size(), masm.size() + stubcc.size());
@ -2511,8 +2512,11 @@ mjit::Compiler::generateMethod()
Address upvarAddress(reg, JSObject::getFlatClosureUpvarsOffset());
masm.loadPrivate(upvarAddress, reg);
// push ((Value *) reg)[index]
frame.freeReg(reg);
frame.push(Address(reg, index * sizeof(Value)), knownPushedType(0));
BarrierState barrier = pushAddressMaybeBarrier(Address(reg, index * sizeof(Value)),
knownPushedType(0), true);
finishBarrier(barrier, REJOIN_GETTER, 0);
if (op == JSOP_CALLFCSLOT)
frame.push(UndefinedValue());
}

View File

@ -1487,6 +1487,7 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM
case JSOP_NAME:
case JSOP_GETGNAME:
case JSOP_GETGLOBAL:
case JSOP_GETFCSLOT:
case JSOP_GETPROP:
case JSOP_GETXPROP:
case JSOP_LENGTH:
@ -1501,7 +1502,8 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM
break;
case JSOP_CALLGLOBAL:
/* |this| is always undefined for CALLGLOBAL. */
case JSOP_CALLFCSLOT:
/* |this| is always undefined for CALLGLOBAL/CALLFCSLOT. */
nextsp[-1].setUndefined();
f.regs.pc = nextpc;
break;

View File

@ -1153,6 +1153,9 @@ mjit::JITScript::scriptDataSize()
{
return sizeof(JITScript) +
sizeof(NativeMapEntry) * nNmapPairs +
sizeof(InlineFrame) * nInlineFrames +
sizeof(CallSite) * nCallSites +
sizeof(JSObject *) * nRootedObjects +
#if defined JS_MONOIC
sizeof(ic::GetGlobalNameIC) * nGetGlobalNames +
sizeof(ic::SetGlobalNameIC) * nSetGlobalNames +
@ -1165,7 +1168,7 @@ mjit::JITScript::scriptDataSize()
sizeof(ic::GetElementIC) * nGetElems +
sizeof(ic::SetElementIC) * nSetElems +
#endif
sizeof(CallSite) * nCallSites;
0;
}
void

View File

@ -1764,6 +1764,13 @@ ReportCompartmentStats(const CompartmentStats &stats,
"accesses fast.",
callback, closure);
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
"object-empty-shapes"),
nsIMemoryReporter::KIND_HEAP,
stats.typeInferenceMemory.emptyShapes,
"Arrays attached to prototype JS objects managing shape information.",
callback, closure);
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
"scripts"),
nsIMemoryReporter::KIND_HEAP, stats.scripts,
@ -1814,44 +1821,32 @@ ReportCompartmentStats(const CompartmentStats &stats,
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
"type-inference/script-main"),
nsIMemoryReporter::KIND_HEAP,
stats.typeInferenceMemory.scriptMain,
stats.typeInferenceMemory.scripts,
"Memory used during type inference to store type sets of variables "
"and dynamically observed types.",
callback, closure);
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
"type-inference/script-typesets"),
nsIMemoryReporter::KIND_HEAP,
stats.typeInferenceMemory.scriptSets,
"Memory used during type inference to hold the contents of type "
"sets associated with scripts.",
callback, closure);
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
"type-inference/object-main"),
nsIMemoryReporter::KIND_HEAP,
stats.typeInferenceMemory.objectMain,
JS_GC_HEAP_KIND,
stats.typeInferenceMemory.objects,
"Memory used during type inference to store types and possible "
"property types of JS objects.",
callback, closure);
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
"type-inference/object-typesets"),
"type-inference/tables"),
nsIMemoryReporter::KIND_HEAP,
stats.typeInferenceMemory.objectSets,
"Memory used during type inference to hold the contents of type "
"sets associated with objects.",
stats.typeInferenceMemory.tables,
"Memory used during type inference for compartment-wide tables.",
callback, closure);
/*
* This is in a different category from the rest of type inference
* data as this can be large but is volatile and cleared on GC.
*/
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
"type-inference-pools"),
"type-inference-temporary"),
nsIMemoryReporter::KIND_HEAP,
stats.typeInferenceMemory.poolMain,
"Memory used during type inference to hold transient analysis information.",
stats.typeInferenceMemory.temporary,
"Memory used during type inference to hold transient analysis "
"information. Cleared on GC.",
callback, closure);
}