[INFER] Allocate typeset data and properties from arenas, occasionally purge observed types in compartments, bug 679329.
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 17 Aug 2011 06:48:14 -0700
changeset 77439 aa547ed80bba625a45c620c2757489c263637d32
parent 77438 427522c34b31ba8039e5a2304739986f4f9d42c8
child 77440 2d95fc517e57fa1cecab8564b3832f3353742a81
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs679329
milestone8.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
[INFER] Allocate typeset data and properties from arenas, occasionally purge observed types in compartments, bug 679329.
js/src/jsapi.cpp
js/src/jsarena.h
js/src/jscompartment.cpp
js/src/jsfriendapi.h
js/src/jsfun.cpp
js/src/jsgc.cpp
js/src/jsgcmark.cpp
js/src/jshashtable.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsinterp.cpp
js/src/jsopcode.tbl
js/src/jsscript.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/MethodJIT.cpp
js/src/xpconnect/src/xpcjsruntime.cpp
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4294,17 +4294,16 @@ JS_CloneFunctionObject(JSContext *cx, JS
                 return NULL;
             }
             obj = obj->getParent();
         }
 
         Value v;
         if (!obj->getProperty(cx, r.front().propid, &v))
             return NULL;
-        TypeScript::SetUpvar(cx, fun->script(), i, v);
         clone->getFlatClosureUpvars()[i] = v;
     }
 
     return clone;
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetFunctionObject(JSFunction *fun)
--- a/js/src/jsarena.h
+++ b/js/src/jsarena.h
@@ -293,13 +293,21 @@ ArenaAllocatedSize(const JSArenaPool &po
     const JSArena *a = &pool.first;
     while (a) {
         res += (a->limit - (jsuword)a);
         a = a->next;
     }
     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 */
 
 #endif /* jsarena_h___ */
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -131,17 +131,20 @@ bool
 JSCompartment::init(JSContext *cx)
 {
     for (unsigned i = 0; i < FINALIZE_LIMIT; i++)
         arenas[i].init();
 
     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())
         return false;
 
     if (!scriptFilenameTable.init())
         return false;
 
@@ -594,42 +597,60 @@ JSCompartment::sweep(JSContext *cx, uint
             }
         }
     }
 
 #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
          * enabled in the compartment.
          */
         if (types.inferenceEnabled) {
 #ifdef JS_METHODJIT
             mjit::ClearAllFrames(this);
 #endif
 
             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;
+                    }
+                }
             }
         }
 
         types.sweep(cx);
 
         for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
             JSScript *script = reinterpret_cast<JSScript *>(cursor);
             if (script->types)
                 script->types->analysis = NULL;
         }
 
         /* 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
          * to eval scripts has been removed.
          */
         js_DestroyScriptsToGC(cx, this);
     }
 
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -73,21 +73,21 @@ extern JS_FRIEND_API(size_t)
 JS_SetProtoCalled(JSContext *cx);
 
 extern JS_FRIEND_API(size_t)
 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)
 JS_GetTypeInferenceMemoryStats(JSContext *cx, JSCompartment *compartment,
                                TypeInferenceMemoryStats *stats);
 
 extern JS_FRIEND_API(void)
 JS_GetTypeInferenceObjectStats(/*TypeObject*/ void *object,
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -2638,20 +2638,18 @@ js_NewFlatClosure(JSContext *cx, JSFunct
     JSObject *closure = js_AllocFlatClosure(cx, fun, scopeChain);
     if (!closure || !fun->script()->bindings.hasUpvars())
         return closure;
 
     Value *upvars = closure->getFlatClosureUpvars();
     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;
 }
 
 JSFunction *
 js_DefineFunction(JSContext *cx, JSObject *obj, jsid id, Native native,
                   uintN nargs, uintN attrs)
 {
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -578,17 +578,17 @@ js_GCThingIsMarked(void *thing, uintN co
     return reinterpret_cast<Cell *>(thing)->isMarked(color);
 }
 
 /*
  * 1/8 life for JIT code. After this number of microseconds have passed, 1/8 of all
  * 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)
 {
     if (!rt->gcChunkSet.init(INITIAL_CHUNK_CAPACITY))
         return false;
 
     if (!rt->gcRootsHash.init(256))
--- a/js/src/jsgcmark.cpp
+++ b/js/src/jsgcmark.cpp
@@ -894,18 +894,21 @@ void
 js::types::TypeObject::trace(JSTracer *trc, bool weak)
 {
     /*
      * Only mark types if the Mark/Sweep GC is running; the bit won't be
      * cleared by the cycle collector. Also, don't mark for weak references
      * 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");
 #endif
 
     unsigned count = getPropertyCount();
     for (unsigned i = 0; i < count; i++) {
         Property *prop = getProperty(i);
--- a/js/src/jshashtable.h
+++ b/js/src/jshashtable.h
@@ -399,16 +399,21 @@ class HashTable : private AllocPolicy
     }
 
     ~HashTable()
     {
         if (table)
             destroyTable(*this, table, tableCapacity);
     }
 
+    size_t allocatedSize() const
+    {
+        return sizeof(Entry) * tableCapacity;
+    }
+
   private:
     static HashNumber hash1(HashNumber hash0, uint32 shift) {
         return hash0 >> shift;
     }
 
     static HashNumber hash2(HashNumber hash0, uint32 log2, uint32 shift) {
         return ((hash0 << log2) >> shift) | 1;
     }
@@ -1101,16 +1106,19 @@ class HashMap
     bool empty() const                                { return impl.empty(); }
 
     /*
      * If |generation()| is the same before and after a HashMap operation,
      * pointers into the table remain valid.
      */
     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 {
         return impl.lookup(l) != NULL;
     }
 
     /* Overwrite existing value with v. Return NULL on oom. */
     Entry *put(const Key &k, const Value &v) {
@@ -1300,16 +1308,19 @@ class HashSet
     bool empty() const                                { return impl.empty(); }
 
     /*
      * If |generation()| is the same before and after a HashSet operation,
      * pointers into the table remain valid.
      */
     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 {
         return impl.lookup(l) != NULL;
     }
 
     /* Overwrite existing value with v. Return NULL on oom. */
     const T *put(const T &t) {
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -351,17 +351,16 @@ TypeSet::make(JSContext *cx, const char 
     if (!res) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return NULL;
     }
 
     InferSpew(ISpewOps, "typeSet: %sT%p%s intermediate %s",
               InferSpewColor(res), res, InferSpewColorReset(),
               name);
-    res->setIntermediate();
 
     return res;
 }
 
 inline void
 TypeSet::add(JSContext *cx, TypeConstraint *constraint, bool callExisting)
 {
     if (!constraint) {
@@ -807,19 +806,17 @@ class TypeConstraintSubsetBarrier : publ
 {
 public:
     JSScript *script;
     jsbytecode *pc;
     TypeSet *target;
 
     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)
     {
         if (!target->hasType(type)) {
             script->analysis()->addTypeBarrier(cx, pc, target, type);
             return;
         }
 
@@ -1946,17 +1943,17 @@ types::UseNewType(JSContext *cx, JSScrip
 
     return false;
 }
 
 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;
     }
 
     memcpy(newArray, pendingArray, pendingCount * sizeof(PendingWork));
     cx->free_(pendingArray);
 
@@ -1987,19 +1984,20 @@ TypeCompartment::processPendingRecompile
 #endif /* JS_METHODJIT */
 
     cx->delete_(pending);
 }
 
 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;
     }
 }
 
 void
 TypeCompartment::nukeTypes(JSContext *cx)
 {
     JSCompartment *compartment = cx->compartment;
@@ -2618,27 +2616,23 @@ UpdatePropertyType(JSContext *cx, TypeSe
         types->addType(cx, type);
     }
 }
 
 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
          * which don't go through a barrier when read by the VM or jitcode.
          * We don't need to handle arrays or other JIT'ed non-natives as
          * these are not (yet) singletons.
          */
 
@@ -3091,33 +3085,31 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
 
             /*
              * The phi nodes at join points should all be unique, and every phi
              * node created should be in the phiValues list on some bytecode.
              */
             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);
             newv++;
         }
     }
 
     /*
      * Treat decomposed ops as no-ops, we will analyze the decomposed version
      * instead. (We do, however, need to look at introduced phi nodes).
      */
     if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
         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);
     }
 
     /* Add type constraints for the various opcodes. */
     switch (op) {
 
@@ -3343,28 +3335,22 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
       }
 
       case JSOP_SETNAME:
       case JSOP_SETCONST:
         cx->compartment->types.monitorBytecode(cx, script, offset);
         poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
 
-      case JSOP_GETXPROP: {
-        TypeSet *seen = script->analysis()->bytecodeTypes(pc);
+      case JSOP_GETXPROP:
+      case JSOP_GETFCSLOT:
+      case JSOP_CALLFCSLOT: {
+        TypeSet *seen = bytecodeTypes(pc);
         addTypeBarrier(cx, pc, seen, Type::UnknownType());
         seen->addSubset(cx, &pushed[0]);
-        break;
-      }
-
-      case JSOP_GETFCSLOT:
-      case JSOP_CALLFCSLOT: {
-        unsigned index = GET_UINT16(pc);
-        TypeSet *types = TypeScript::UpvarTypes(script, index);
-        types->addSubset(cx, &pushed[0]);
         if (op == JSOP_CALLFCSLOT) {
             pushed[1].addType(cx, Type::UndefinedType());
             pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType());
         }
         break;
       }
 
       case JSOP_GETARG:
@@ -4511,20 +4497,16 @@ ScriptAnalysis::printTypes(JSContext *cx
         TypeScript::ArgTypes(script, i)->print(cx);
     }
     for (unsigned i = 0; i < script->nfixed; i++) {
         if (!trackSlot(LocalSlot(script, i))) {
             printf("\n    local%u:", i);
             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++) {
         if (!maybeCode(offset))
             continue;
 
         jsbytecode *pc = script->code + offset;
         UntrapOpcode untrap(cx, script, pc);
@@ -4926,22 +4908,16 @@ JSScript::makeTypes(JSContext *cx)
                   i, id());
     }
     for (unsigned i = 0; i < nfixed; i++) {
         TypeSet *types = TypeScript::LocalTypes(this, i);
         InferSpew(ISpewOps, "typeSet: %sT%p%s local%u #%u",
                   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;
 }
 
 bool
 JSScript::makeAnalysis(JSContext *cx)
 {
@@ -5223,49 +5199,42 @@ JSObject::makeNewType(JSContext *cx, JSS
 
 /////////////////////////////////////////////////////////////////////
 // Tracing
 /////////////////////////////////////////////////////////////////////
 
 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)) {
             objectSet = NULL;
             setBaseObjectCount(0);
         }
     }
 
@@ -5284,100 +5253,133 @@ JSObject::revertLazyType()
     JS_ASSERT_IF(type_->proto, type_->proto->newType);
     flags |= LAZY_TYPE;
     type_ = (type_->proto) ? type_->proto->newType : &emptyTypeObject;
 }
 
 inline void
 TypeObject::clearProperties()
 {
-    JS_ASSERT(singleton);
     setBasePropertyCount(0);
     propertySet = NULL;
 }
 
 /*
  * Before sweeping the arenas themselves, scan all type objects in a
  * compartment to fixup weak references: property type sets referencing dead
  * JS and type objects, and singleton JS objects whose type is not referenced
  * 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;
-
-    if (object->singleton) {
-        JS_ASSERT(!object->emptyShapes);
-        JS_ASSERT(!object->newScript);
+    contribution = 0;
+
+    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();
-
-        if (!object->isMarked()) {
+        clearProperties();
+
+        if (!isMarked()) {
             /*
              * Singleton objects do not hold strong references on their types.
              * When removing the type, however, we need to fixup the singleton
              * so that it has a lazy type again. The generic 'new' type for the
              * 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);
-
-        unsigned count = object->getPropertyCount();
-        for (unsigned i = 0; i < count; i++) {
-            Property *prop = object->getProperty(i);
-            if (prop) {
-                prop->types.clearObjects();
-                Foreground::delete_(prop);
+    if (!isMarked()) {
+        if (emptyShapes)
+            Foreground::free_(emptyShapes);
+        if (newScript)
+            Foreground::free_(newScript);
+        return;
+    }
+
+    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;
-    }
-
-    /* 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());
+        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);
+        }
+    }
+
+    if (basePropertyCount() <= SET_ARRAY_SIZE) {
+        for (unsigned i = 0; i < basePropertyCount(); i++)
+            JS_ASSERT(propertySet[i]);
     }
 
     /*
      * The GC will clear out the constraints ensuring the correctness of the
      * 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
 SweepTypeObjects(JSContext *cx, JSCompartment *compartment)
 {
     JS_ASSERT(!emptyTypeObject.emptyShapes);
     JS_ASSERT(!emptyTypeObject.newScript);
 
@@ -5392,17 +5394,17 @@ SweepTypeObjects(JSContext *cx, JSCompar
         for (uintptr_t thing = arena->thingsStart(thingSize); ; thing += thingSize) {
             JS_ASSERT(thing <= arena->thingsEnd());
             if (thing == span->first) {
                 if (!span->hasNext())
                     break;
                 thing = span->last;
                 span = span->nextSpan();
             } else {
-                SweepTypeObject(cx, reinterpret_cast<TypeObject *>(thing));
+                reinterpret_cast<TypeObject *>(thing)->sweep(cx);
             }
         }
     }
 }
 
 void
 TypeCompartment::sweep(JSContext *cx)
 {
@@ -5465,16 +5467,26 @@ TypeCompartment::sweep(JSContext *cx)
         for (AllocationSiteTable::Enum e(*allocationSiteTable); !e.empty(); e.popFront()) {
             const AllocationSiteKey &key = e.front().key;
             TypeObject *object = e.front().value;
 
             if (key.uncached || key.script->isAboutToBeFinalized(cx) || !object->isMarked())
                 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()
 {
     if (pendingArray)
         Foreground::free_(pendingArray);
 
     if (arrayTypeTable)
@@ -5532,84 +5544,141 @@ TypeScript::Sweep(JSContext *cx, JSScrip
 void
 TypeScript::destroy()
 {
     while (dynamicList) {
         TypeResult *next = dynamicList->next;
         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)
 {
     if (!script->types)
         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) {
         JSScript *script = reinterpret_cast<JSScript *>(cursor);
         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;
 }
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -173,18 +173,23 @@ inline Type GetValueType(JSContext *cx, 
  *
  * Inference constructs a global web of constraints relating the contents of
  * type sets particular to various scripts and type objects within a
  * compartment. This data can consume a significant amount of memory, and to
  * avoid this building up we try to clear it with some regularity. On each GC
  * 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.
  */
 
 /*
  * A constraint which listens to additions to a type set and propagates those
  * changes to other type sets.
  */
 class TypeConstraint
 {
@@ -242,43 +247,40 @@ enum {
         TYPE_FLAG_OBJECT_COUNT_MASK >> TYPE_FLAG_OBJECT_COUNT_SHIFT,
 
     /* Whether the contents of this type set are totally unknown. */
     TYPE_FLAG_UNKNOWN             = 0x00010000,
 
     /* 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,
     TYPE_FLAG_DEFINITE_SHIFT      = 24
 };
 typedef uint32 TypeFlags;
 
 /* Flags and other state stored in TypeObject::flags */
@@ -365,17 +367,17 @@ class TypeSet
 
     TypeSet()
         : flags(0), objectSet(NULL), constraintList(NULL)
     {}
 
     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);
 
     TypeFlags baseFlags() { return flags & TYPE_FLAG_BASE_MASK; }
     bool unknown() { return !!(flags & TYPE_FLAG_UNKNOWN); }
     bool unknownObject() { return !!(flags & (TYPE_FLAG_UNKNOWN | TYPE_FLAG_ANYOBJECT)); }
 
@@ -407,18 +409,16 @@ class TypeSet
      * in the hash case (see SET_ARRAY_SIZE in jsinferinlines.h), and getObject
      * may return NULL.
      */
     inline unsigned getObjectCount();
     inline TypeObjectKey *getObject(unsigned i);
     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)
             flags |= TYPE_FLAG_CONFIGURED_PROPERTY;
     }
     void setDefinite(unsigned slot) {
         JS_ASSERT(slot <= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT));
         flags |= TYPE_FLAG_DEFINITE_PROPERTY | (slot << TYPE_FLAG_DEFINITE_SHIFT);
@@ -441,18 +441,18 @@ class TypeSet
     void addArith(JSContext *cx, TypeSet *target, TypeSet *other = NULL);
     void addTransformThis(JSContext *cx, JSScript *script, TypeSet *target);
     void addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, Type type);
     void addFilterPrimitives(JSContext *cx, TypeSet *target, bool onlyNullVoid);
     void addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target);
     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);
 
     /*
      * Methods for JIT compilation. If a script is currently being compiled
      * (see AutoEnterCompilation) these will add constraints ensuring that if
      * the return value change in the future due to new type information, the
      * currently compiled script will be marked for recompilation.
@@ -493,19 +493,16 @@ class TypeSet
      * Get the typed array type of all objects in this set. Returns
      * TypedArray::TYPE_MAX if the set contains different array types.
      */
     int getTypedArrayType(JSContext *cx);
 
     /* 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:
     inline uint32 baseObjectCount() const;
     inline void setBaseObjectCount(uint32 count);
 };
 
 /*
@@ -603,16 +600,20 @@ struct Property
 
     /* Possible types for this property, including types inherited from prototypes. */
     TypeSet types;
 
     Property(jsid id)
         : 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; }
 };
 
 /*
  * Information attached to a TypeObject if it is always constructed using 'new'
  * on a particular script. This is used to manage state related to the definite
  * properties on the type object: these definite properties depend on type
@@ -828,16 +829,19 @@ struct TypeObject : gc::Cell
     void markUnknown(JSContext *cx);
     void clearNewScript(JSContext *cx);
     void getFromPrototypes(JSContext *cx, jsid id, TypeSet *types, bool force = false);
 
     void print(JSContext *cx);
     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
      * object pending deletion is released when weak references are sweeped
      * from all the compartment's type objects.
      */
     void finalize(JSContext *cx) {}
 
@@ -900,17 +904,16 @@ struct TypeScript
 
     /* Dynamic types generated at points within this script. */
     TypeResult *dynamicList;
 
     static inline TypeSet *ReturnTypes(JSScript *script);
     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);
 
 #ifdef DEBUG
     /* Check that correct types were inferred for the values pushed by this bytecode. */
     static void CheckBytecode(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value *sp);
 #endif
@@ -945,17 +948,16 @@ struct TypeScript
 
     /* Add a type for a variable in a script. */
     static inline void SetThis(JSContext *cx, JSScript *script, Type type);
     static inline void SetThis(JSContext *cx, JSScript *script, const js::Value &value);
     static inline void SetLocal(JSContext *cx, JSScript *script, unsigned local, Type type);
     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();
 };
 
 struct ArrayTableKey;
 typedef HashMap<ArrayTableKey,TypeObject*,ArrayTableKey,SystemAllocPolicy> ArrayTypeTable;
 
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -450,17 +450,17 @@ UseNewTypeAtEntry(JSContext *cx, StackFr
 
 /////////////////////////////////////////////////////////////////////
 // Script interface functions
 /////////////////////////////////////////////////////////////////////
 
 /* 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 *
 TypeScript::ReturnTypes(JSScript *script)
 {
     return script->types->typeArray() + script->nTypeSets + js::analyze::CalleeSlot();
 }
 
@@ -486,23 +486,16 @@ TypeScript::ArgTypes(JSScript *script, u
 /* static */ inline TypeSet *
 TypeScript::LocalTypes(JSScript *script, unsigned i)
 {
     JS_ASSERT(i < script->nfixed);
     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)
 {
     JS_ASSERT(slot < js::analyze::TotalSlots(script));
     return script->types->typeArray() + script->nTypeSets + slot;
 }
 
 /* static */ inline TypeObject *
 TypeScript::StandardType(JSContext *cx, JSScript *script, JSProtoKey key)
@@ -676,31 +669,16 @@ TypeScript::SetArgument(JSContext *cx, J
 TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value)
 {
     if (cx->typeInferenceEnabled()) {
         Type type = GetValueType(cx, value);
         SetArgument(cx, script, arg, type);
     }
 }
 
-/* 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
 /////////////////////////////////////////////////////////////////////
 
 inline JSCompartment *
 TypeCompartment::compartment()
 {
     return (JSCompartment *)((char *)this - offsetof(JSCompartment, types));
@@ -791,17 +769,17 @@ HashKey(T v)
 }
 
 /*
  * Insert space for an element into the specified set and grow its capacity if needed.
  * returned value is an existing or new entry (NULL if new).
  */
 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);
 
     /* Whether we are converting from a fixed array to hashtable. */
     bool converting = (count == SET_ARRAY_SIZE);
 
     if (!converting) {
@@ -815,69 +793,60 @@ HashSetInsertTry(JSContext *cx, U **&val
     count++;
     unsigned newCapacity = HashSetCapacity(count);
 
     if (newCapacity == capacity) {
         JS_ASSERT(!converting);
         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++) {
         if (values[i]) {
             unsigned pos = HashKey<T,KEY>(KEY::getKey(values[i])) & (newCapacity - 1);
             while (newValues[pos] != NULL)
                 pos = (pos + 1) & (newCapacity - 1);
             newValues[pos] = values[i];
         }
     }
 
-    if (values && !pool)
-        Foreground::free_(values);
     values = newValues;
 
     insertpos = HashKey<T,KEY>(key) & (newCapacity - 1);
     while (values[insertpos] != NULL)
         insertpos = (insertpos + 1) & (newCapacity - 1);
     return &values[insertpos];
 }
 
 /*
  * Insert an element into the specified set if it is not already there, returning
  * an entry which is NULL if the element was not there.
  */
 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);
         count++;
         return (U **) &values;
     }
 
     if (count == 1) {
         U *oldData = (U*) values;
         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);
         count++;
 
         values[0] = oldData;
         return &values[1];
     }
@@ -889,17 +858,17 @@ HashSetInsert(JSContext *cx, U **&values
         }
 
         if (count < SET_ARRAY_SIZE) {
             count++;
             return &values[count - 1];
         }
     }
 
-    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. */
 template <class T, class U, class KEY>
 static inline U *
 HashSetLookup(U **values, unsigned count, T key)
 {
     if (count == 0)
@@ -959,18 +928,16 @@ TypeSet::setBaseObjectCount(uint32 count
     JS_ASSERT(count <= TYPE_FLAG_OBJECT_COUNT_LIMIT);
     flags = (flags & ~TYPE_FLAG_OBJECT_COUNT_MASK)
           | (count << TYPE_FLAG_OBJECT_COUNT_SHIFT);
 }
 
 inline void
 TypeSet::clearObjects()
 {
-    if (baseObjectCount() >= 2 && !intermediate())
-        Foreground::free_(objectSet);
     setBaseObjectCount(0);
     objectSet = NULL;
 }
 
 inline void
 TypeSet::addType(JSContext *cx, Type type)
 {
     JS_ASSERT(cx->compartment->activeInference);
@@ -994,18 +961,22 @@ TypeSet::addType(JSContext *cx, Type typ
     } else {
         if (flags & TYPE_FLAG_ANYOBJECT)
             return;
         if (type.isAnyObject())
             goto unknownObject;
         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;
 
         setBaseObjectCount(objectCount);
 
         if (objectCount == TYPE_FLAG_OBJECT_COUNT_LIMIT)
             goto unknownObject;
 
@@ -1163,19 +1134,21 @@ TypeObject::getProperty(JSContext *cx, j
 {
     JS_ASSERT(cx->compartment->activeInference);
     JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
     JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == MakeTypeId(cx, id));
     JS_ASSERT(!unknownProperties());
 
     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);
         if (!addProperty(cx, id, pprop))
             return NULL;
         if (propertyCount == OBJECT_FLAG_PROPERTY_COUNT_LIMIT) {
             markUnknown(cx);
             TypeSet *types = TypeSet::make(cx, "propertyOverflow");
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -4689,16 +4689,17 @@ BEGIN_CASE(JSOP_GETFCSLOT)
 BEGIN_CASE(JSOP_CALLFCSLOT)
 {
     JS_ASSERT(regs.fp()->isNonEvalFunctionFrame());
     uintN index = GET_UINT16(regs.pc);
     JSObject *obj = &argv[-2].toObject();
 
     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();
 }
 END_CASE(JSOP_GETFCSLOT)
 
 BEGIN_CASE(JSOP_GETGLOBAL)
 BEGIN_CASE(JSOP_CALLGLOBAL)
 {
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -365,18 +365,18 @@ OPDEF(JSOP_TRY,         134,"try",      
 OPDEF(JSOP_FINALLY,     135,"finally",    NULL,       1,  0,  2,  0,  JOF_BYTE)
 
 /*
  * Get a slot from a flat closure function object that contains a snapshot of
  * the closure-invariant upvar values. The immediate operand indexes the upvar
  * 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:
  * JSOP_ARGSUB gets arguments[i] from fp->argv, iff i is in [0, fp->argc-1].
  * JSOP_ARGCNT returns fp->argc.
  */
 OPDEF(JSOP_ARGSUB,      138,"argsub",     NULL,       3,  0,  1, 18,  JOF_QARG |JOF_NAME)
 OPDEF(JSOP_ARGCNT,      139,"argcnt",     NULL,       1,  0,  1, 18,  JOF_BYTE)
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1414,20 +1414,18 @@ DestroyScript(JSContext *cx, JSScript *s
 #endif
     }
 
 #ifdef JS_TRACER
     if (script->compartment->hasTraceMonitor())
         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);
 #endif
 
     JS_REMOVE_LINK(&script->links);
 
     script->pcCounters.destroy(cx);
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -1325,16 +1325,17 @@ mjit::Compiler::finishThisUp(JITScript *
                 jitPics[i].u.get.typeCheckOffset = distance;
             }
         }
         stubCode.patch(pics[i].paramAddr, &jitPics[i]);
     }
 #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());
 
     size_t doubleOffset = masm.size() + stubcc.size();
     double *inlineDoubles = (double *) (result + doubleOffset);
     double *oolDoubles = (double*) (result + doubleOffset +
                                     masm.numDoubles() * sizeof(double));
@@ -2506,18 +2507,21 @@ mjit::Compiler::generateMethod()
             frame.pushCallee();
             RegisterID reg = frame.copyDataIntoReg(frame.peek(-1));
             frame.pop();
 
             // obj->getFlatClosureUpvars()
             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());
           }
           END_CASE(JSOP_CALLFCSLOT)
 
           BEGIN_CASE(JSOP_ARGSUB)
           {
             prepareStubCall(Uses(0));
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -1482,31 +1482,33 @@ js_InternalInterpret(void *returnData, v
         /*
          * Match the PC to figure out whether this property fetch is part of a
          * fused opcode which needs to be finished.
          */
         switch (op) {
           case JSOP_NAME:
           case JSOP_GETGNAME:
           case JSOP_GETGLOBAL:
+          case JSOP_GETFCSLOT:
           case JSOP_GETPROP:
           case JSOP_GETXPROP:
           case JSOP_LENGTH:
             /* Non-fused opcode, state is already correct for the next op. */
             f.regs.pc = nextpc;
             break;
 
           case JSOP_CALLGNAME:
             if (!ComputeImplicitThis(cx, &fp->scopeChain(), nextsp[-2], &nextsp[-1]))
                 return js_InternalThrow(f);
             f.regs.pc = nextpc;
             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;
 
           case JSOP_CALLPROP: {
             /*
              * CALLPROP is compiled in terms of GETPROP for known strings.
              * In such cases the top two entries are in place, but are swapped.
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -1148,29 +1148,32 @@ JSScript::jitDataSize()
 }
 
 /* Please keep in sync with Compiler::finishThisUp! */
 size_t
 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 +
         sizeof(ic::CallICInfo) * nCallICs +
         sizeof(ic::EqualityICInfo) * nEqualityICs +
         sizeof(ic::TraceICInfo) * nTraceICs +
 #endif
 #if defined JS_POLYIC
         sizeof(ic::PICInfo) * nPICs +
         sizeof(ic::GetElementIC) * nGetElems +
         sizeof(ic::SetElementIC) * nSetElems +
 #endif
-        sizeof(CallSite) * nCallSites;
+        0;
 }
 
 void
 mjit::ReleaseScriptCode(JSContext *cx, JSScript *script, bool normal)
 {
     // NB: The recompiler may call ReleaseScriptCode, in which case it
     // will get called again when the script is destroyed, so we
     // must protect against calling ReleaseScriptCode twice.
--- a/js/src/xpconnect/src/xpcjsruntime.cpp
+++ b/js/src/xpconnect/src/xpcjsruntime.cpp
@@ -1760,16 +1760,23 @@ ReportCompartmentStats(const Compartment
                                               "property-tables"),
                        nsIMemoryReporter::KIND_HEAP, stats.propertyTables,
     "Memory allocated for the compartment's property tables.  A property "
     "table is an internal data structure that makes JavaScript property "
     "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,
     "Memory allocated for the compartment's JSScripts.  A JSScript is created "
     "for each user-defined function in a script.  One is also created for "
     "the top-level code in a script.  Each JSScript includes byte-code and "
     "various other things.",
                        callback, closure);
 
@@ -1809,54 +1816,42 @@ ReportCompartmentStats(const Compartment
     "Memory used by the trace JIT and held in reserve for the compartment's "
     "VMAllocators in case of OOM.",
                        callback, closure);
 #endif
 
     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);
 }
 
 void
 ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix,
                      nsIMemoryMultiReporterCallback *callback,
                      nsISupports *closure)
 {