Bug 932982 - Trace type constraints and allow preserving jitcode in GCs without also marking all type information, r=billm,jandem. default tip
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 12 Dec 2013 08:42:24 -0800
changeset 160148 a29d7f5ddde63e437958b32305bd9896c210f750
parent 160147 6f419c7c2873aa5b1b4819dd3774de7da06907ba
push id1672
push userkvijayan@mozilla.com
push dateThu, 12 Dec 2013 20:08:00 +0000
reviewersbillm, jandem
bugs932982
milestone29.0a1
Bug 932982 - Trace type constraints and allow preserving jitcode in GCs without also marking all type information, r=billm,jandem.
js/public/MemoryMetrics.h
js/src/gc/Marking.cpp
js/src/gc/RootMarking.cpp
js/src/gc/Statistics.cpp
js/src/gc/Statistics.h
js/src/gc/Zone.cpp
js/src/gc/Zone.h
js/src/jit/CodeGenerator.cpp
js/src/jit/Ion.cpp
js/src/jit/Ion.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonCode.h
js/src/jit/MIR.cpp
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/vm/MemoryMetrics.cpp
js/xpconnect/src/XPCJSRuntime.cpp
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -388,17 +388,16 @@ struct CompartmentStats
     macro(Other,   NotLiveGCThing, shapesMallocHeapCompartmentTables) \
     macro(Other,   IsLiveGCThing,  scriptsGCHeap) \
     macro(Other,   NotLiveGCThing, scriptsMallocHeapData) \
     macro(Other,   NotLiveGCThing, baselineData) \
     macro(Other,   NotLiveGCThing, baselineStubsFallback) \
     macro(Other,   NotLiveGCThing, baselineStubsOptimized) \
     macro(Other,   NotLiveGCThing, ionData) \
     macro(Other,   NotLiveGCThing, typeInferenceTypeScripts) \
-    macro(Other,   NotLiveGCThing, typeInferencePendingArrays) \
     macro(Other,   NotLiveGCThing, typeInferenceAllocationSiteTables) \
     macro(Other,   NotLiveGCThing, typeInferenceArrayTypeTables) \
     macro(Other,   NotLiveGCThing, typeInferenceObjectTypeTables) \
     macro(Other,   NotLiveGCThing, compartmentObject) \
     macro(Other,   NotLiveGCThing, crossCompartmentWrappersTable) \
     macro(Other,   NotLiveGCThing, regexpCompartment) \
     macro(Other,   NotLiveGCThing, debuggeesSet)
 
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1117,24 +1117,21 @@ gc::MarkCycleCollectorChildren(JSTracer 
         MarkId(trc, &shape->propidRef(), "propid");
         shape = shape->previous();
     } while (shape);
 }
 
 static void
 ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type)
 {
-    /* Don't mark properties for singletons. They'll be purged by the GC. */
-    if (!type->singleton) {
-        unsigned count = type->getPropertyCount();
-        for (unsigned i = 0; i < count; i++) {
-            types::Property *prop = type->getProperty(i);
-            if (prop && JSID_IS_STRING(prop->id))
-                PushMarkStack(gcmarker, JSID_TO_STRING(prop->id));
-        }
+    unsigned count = type->getPropertyCount();
+    for (unsigned i = 0; i < count; i++) {
+        types::Property *prop = type->getProperty(i);
+        if (prop && JSID_IS_STRING(prop->id))
+            PushMarkStack(gcmarker, JSID_TO_STRING(prop->id));
     }
 
     if (TaggedProto(type->proto).isObject())
         PushMarkStack(gcmarker, type->proto);
 
     if (type->singleton && !type->lazy())
         PushMarkStack(gcmarker, type->singleton);
 
@@ -1345,59 +1342,30 @@ GCMarker::restoreValueArray(JSObject *ob
         }
     }
 
     JS_ASSERT(*vpp <= *endp);
     return true;
 }
 
 void
-GCMarker::processMarkStackOther(SliceBudget &budget, uintptr_t tag, uintptr_t addr)
+GCMarker::processMarkStackOther(uintptr_t tag, uintptr_t addr)
 {
     if (tag == TypeTag) {
         ScanTypeObject(this, reinterpret_cast<types::TypeObject *>(addr));
     } else if (tag == SavedValueArrayTag) {
         JS_ASSERT(!(addr & CellMask));
         JSObject *obj = reinterpret_cast<JSObject *>(addr);
         HeapValue *vp, *end;
         if (restoreValueArray(obj, (void **)&vp, (void **)&end))
             pushValueArray(obj, vp, end);
         else
             pushObject(obj);
     } else if (tag == IonCodeTag) {
         MarkChildren(this, reinterpret_cast<jit::IonCode *>(addr));
-    } else if (tag == ArenaTag) {
-        ArenaHeader *aheader = reinterpret_cast<ArenaHeader *>(addr);
-        AllocKind thingKind = aheader->getAllocKind();
-        size_t thingSize = Arena::thingSize(thingKind);
-
-        for ( ; aheader; aheader = aheader->next) {
-            Arena *arena = aheader->getArena();
-            FreeSpan firstSpan(aheader->getFirstFreeSpan());
-            const FreeSpan *span = &firstSpan;
-
-            for (uintptr_t thing = arena->thingsStart(thingKind); ; thing += thingSize) {
-                JS_ASSERT(thing <= arena->thingsEnd());
-                if (thing == span->first) {
-                    if (!span->hasNext())
-                        break;
-                    thing = span->last;
-                    span = span->nextSpan();
-                } else {
-                    JSObject *object = reinterpret_cast<JSObject *>(thing);
-                    if (object->hasSingletonType() && object->markIfUnmarked(getMarkColor()))
-                        pushObject(object);
-                    budget.step();
-                }
-            }
-            if (budget.isOverBudget()) {
-                pushArenaList(aheader);
-                return;
-            }
-        }
     }
 }
 
 inline void
 GCMarker::processMarkStackTop(SliceBudget &budget)
 {
     /*
      * The function uses explicit goto and implements the scanning of the
@@ -1425,17 +1393,17 @@ GCMarker::processMarkStackTop(SliceBudge
     }
 
     if (tag == ObjectTag) {
         obj = reinterpret_cast<JSObject *>(addr);
         JS_COMPARTMENT_ASSERT(runtime, obj);
         goto scan_obj;
     }
 
-    processMarkStackOther(budget, tag, addr);
+    processMarkStackOther(tag, addr);
     return;
 
   scan_value_array:
     JS_ASSERT(vp <= end);
     while (vp != end) {
         const Value &v = *vp++;
         if (v.isString()) {
             JSString *str = v.toString();
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -721,21 +721,16 @@ js::gc::MarkRuntime(JSTracer *trc, bool 
 
     for (ContextIter acx(rt); !acx.done(); acx.next())
         acx->mark(trc);
 
     for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
         if (IS_GC_MARKING_TRACER(trc) && !zone->isCollecting())
             continue;
 
-        if (IS_GC_MARKING_TRACER(trc) && zone->isPreservingCode()) {
-            gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_MARK_TYPES);
-            zone->markTypes(trc);
-        }
-
         /* Do not discard scripts with counts while profiling. */
         if (rt->profilingScripts && !rt->isHeapMinorCollecting()) {
             for (CellIterUnderGC i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) {
                 JSScript *script = i.get<JSScript>();
                 if (script->hasScriptCounts()) {
                     MarkScriptRoot(trc, &script, "profilingScripts");
                     JS_ASSERT(script == i.get<JSScript>());
                 }
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -271,17 +271,16 @@ static const Phase PHASE_NO_PARENT = PHA
 
 static const PhaseInfo phases[] = {
     { PHASE_GC_BEGIN, "Begin Callback", PHASE_NO_PARENT },
     { PHASE_WAIT_BACKGROUND_THREAD, "Wait Background Thread", PHASE_NO_PARENT },
     { PHASE_MARK_DISCARD_CODE, "Mark Discard Code", PHASE_NO_PARENT },
     { PHASE_PURGE, "Purge", PHASE_NO_PARENT },
     { PHASE_MARK, "Mark", PHASE_NO_PARENT },
     { PHASE_MARK_ROOTS, "Mark Roots", PHASE_MARK },
-    { PHASE_MARK_TYPES, "Mark Types", PHASE_MARK_ROOTS },
     { PHASE_MARK_DELAYED, "Mark Delayed", PHASE_MARK },
     { PHASE_SWEEP, "Sweep", PHASE_NO_PARENT },
     { PHASE_SWEEP_MARK, "Mark During Sweeping", PHASE_SWEEP },
     { PHASE_SWEEP_MARK_TYPES, "Mark Types During Sweeping", PHASE_SWEEP_MARK },
     { PHASE_SWEEP_MARK_INCOMING_BLACK, "Mark Incoming Black Pointers", PHASE_SWEEP_MARK },
     { PHASE_SWEEP_MARK_WEAK, "Mark Weak", PHASE_SWEEP_MARK },
     { PHASE_SWEEP_MARK_INCOMING_GRAY, "Mark Incoming Gray Pointers", PHASE_SWEEP_MARK },
     { PHASE_SWEEP_MARK_GRAY, "Mark Gray", PHASE_SWEEP_MARK },
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -23,17 +23,16 @@ namespace gcstats {
 
 enum Phase {
     PHASE_GC_BEGIN,
     PHASE_WAIT_BACKGROUND_THREAD,
     PHASE_MARK_DISCARD_CODE,
     PHASE_PURGE,
     PHASE_MARK,
     PHASE_MARK_ROOTS,
-    PHASE_MARK_TYPES,
     PHASE_MARK_DELAYED,
     PHASE_SWEEP,
     PHASE_SWEEP_MARK,
     PHASE_SWEEP_MARK_TYPES,
     PHASE_SWEEP_MARK_INCOMING_BLACK,
     PHASE_SWEEP_MARK_WEAK,
     PHASE_SWEEP_MARK_INCOMING_GRAY,
     PHASE_SWEEP_MARK_GRAY,
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -76,45 +76,16 @@ Zone::setNeedsBarrier(bool needs, Should
     if (needs && runtimeFromMainThread()->isAtomsZone(this))
         JS_ASSERT(!runtimeFromMainThread()->exclusiveThreadsPresent());
 
     JS_ASSERT_IF(needs, canCollect());
     needsBarrier_ = needs;
 }
 
 void
-Zone::markTypes(JSTracer *trc)
-{
-    /*
-     * Mark all scripts, type objects and singleton JS objects in the
-     * compartment. These can be referred to directly by type sets, which we
-     * cannot modify while code which depends on these type sets is active.
-     */
-    JS_ASSERT(isPreservingCode());
-
-    for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
-        JSScript *script = i.get<JSScript>();
-        MarkScriptRoot(trc, &script, "mark_types_script");
-        JS_ASSERT(script == i.get<JSScript>());
-    }
-
-    for (size_t thingKind = FINALIZE_OBJECT0; thingKind < FINALIZE_OBJECT_LIMIT; thingKind++) {
-        ArenaHeader *aheader = allocator.arenas.getFirstArena(static_cast<AllocKind>(thingKind));
-        if (aheader)
-            trc->runtime->gcMarker.pushArenaList(aheader);
-    }
-
-    for (CellIterUnderGC i(this, FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
-        types::TypeObject *type = i.get<types::TypeObject>();
-        MarkTypeObjectRoot(trc, &type, "mark_types_scan");
-        JS_ASSERT(type == i.get<types::TypeObject>());
-    }
-}
-
-void
 Zone::resetGCMallocBytes()
 {
     gcMallocBytes = ptrdiff_t(gcMaxMallocBytes);
     gcMallocGCTriggered = false;
 }
 
 void
 Zone::setGCMaxMallocBytes(size_t value)
@@ -139,17 +110,17 @@ Zone::sweep(FreeOp *fop, bool releaseTyp
 {
     /*
      * Periodically release observed types for all scripts. This is safe to
      * do when there are no frames for the zone on the stack.
      */
     if (active)
         releaseTypes = false;
 
-    if (!isPreservingCode()) {
+    {
         gcstats::AutoPhase ap(fop->runtime()->gcStats, gcstats::PHASE_DISCARD_ANALYSIS);
         types.sweep(fop, releaseTypes);
     }
 
     if (!fop->runtime()->debuggerList.isEmpty())
         sweepBreakpoints(fop);
 
     active = false;
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -305,18 +305,16 @@ struct Zone : public JS::shadow::Zone,
 
     void *onOutOfMemory(void *p, size_t nbytes) {
         return runtimeFromMainThread()->onOutOfMemory(p, nbytes);
     }
     void reportAllocationOverflow() {
         js_ReportAllocationOverflow(nullptr);
     }
 
-    void markTypes(JSTracer *trc);
-
     js::types::TypeZone types;
 
     void sweep(js::FreeOp *fop, bool releaseTypes);
 
   private:
     void sweepBreakpoints(js::FreeOp *fop);
 };
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -5885,17 +5885,17 @@ CodeGenerator::link(JSContext *cx, types
       IonScript::New(cx, recompileInfo,
                      graph.totalSlotCount(), scriptFrameSize, snapshots_.size(),
                      bailouts_.length(), graph.numConstants(),
                      safepointIndices_.length(), osiIndices_.length(),
                      cacheList_.length(), runtimeData_.length(),
                      safepoints_.size(), callTargets.length(),
                      patchableBackedges_.length(), optimizationLevel);
     if (!ionScript) {
-        recompileInfo.compilerOutput(cx->compartment()->types)->invalidate();
+        recompileInfo.compilerOutput(cx->zone()->types)->invalidate();
         return false;
     }
 
     // Lock the runtime against operation callbacks during the link.
     // We don't want an operation callback to protect the code for the script
     // before it has been filled in, as we could segv before the runtime's
     // patchable backedges have been fully updated.
     JSRuntime::AutoLockForOperationCallback lock(cx->runtime());
@@ -5910,17 +5910,17 @@ CodeGenerator::link(JSContext *cx, types
     Linker linker(masm);
     IonCode *code = (executionMode == SequentialExecution)
                     ? linker.newCodeForIonScript(cx)
                     : linker.newCode<CanGC>(cx, JSC::ION_CODE);
     if (!code) {
         // Use js_free instead of IonScript::Destroy: the cache list and
         // backedge list are still uninitialized.
         js_free(ionScript);
-        recompileInfo.compilerOutput(cx->compartment()->types)->invalidate();
+        recompileInfo.compilerOutput(cx->zone()->types)->invalidate();
         return false;
     }
 
     ionScript->setMethod(code);
     ionScript->setSkipArgCheckEntryOffset(getSkipArgCheckEntryOffset());
 
     // If SPS is enabled, mark IonScript as having been instrumented with SPS
     if (sps_.enabled())
@@ -5931,17 +5931,17 @@ CodeGenerator::link(JSContext *cx, types
     if (HasIonScript(script, executionMode)) {
         JS_ASSERT(GetIonScript(script, executionMode)->isRecompiling());
         // Do a normal invalidate, except don't cancel offThread compilations,
         // since that will cancel this compilation too.
         if (!Invalidate(cx, script, SequentialExecution,
                         /* resetUses */ false, /* cancelOffThread*/ false))
         {
             js_free(ionScript);
-            recompileInfo.compilerOutput(cx->compartment()->types)->invalidate();
+            recompileInfo.compilerOutput(cx->zone()->types)->invalidate();
             return false;
         }
     }
 
     SetIonScript(script, executionMode, ionScript);
 
     // In parallel execution mode, when we first compile a script, we
     // don't know that its potential callees are compiled, so set a
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2503,17 +2503,17 @@ jit::InvalidateAll(FreeOp *fop, Zone *zo
             IonSpew(IonSpew_Invalidate, "Invalidating all frames for GC");
             InvalidateActivation(fop, iter.jitTop(), true);
         }
     }
 }
 
 
 void
-jit::Invalidate(types::TypeCompartment &types, FreeOp *fop,
+jit::Invalidate(types::TypeZone &types, FreeOp *fop,
                 const Vector<types::RecompileInfo> &invalid, bool resetUses,
                 bool cancelOffThread)
 {
     IonSpew(IonSpew_Invalidate, "Start invalidation.");
     AutoFlushCache afc ("Invalidate", fop->runtime()->jitRuntime());
 
     // Add an invalidation reference to all invalidated IonScripts to indicate
     // to the traversal which frames have been invalidated.
@@ -2586,17 +2586,17 @@ jit::Invalidate(types::TypeCompartment &
     // multiple times in the above loop.
     JS_ASSERT(!numInvalidations);
 }
 
 void
 jit::Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses,
                 bool cancelOffThread)
 {
-    jit::Invalidate(cx->compartment()->types, cx->runtime()->defaultFreeOp(), invalid, resetUses,
+    jit::Invalidate(cx->zone()->types, cx->runtime()->defaultFreeOp(), invalid, resetUses,
                     cancelOffThread);
 }
 
 bool
 jit::Invalidate(JSContext *cx, JSScript *script, ExecutionMode mode, bool resetUses,
                 bool cancelOffThread)
 {
     JS_ASSERT(script->hasIonScript());
@@ -2633,24 +2633,23 @@ FinishInvalidationOf(FreeOp *fop, JSScri
 {
     // In all cases, nullptr out script->ion or script->parallelIon to avoid
     // re-entry.
     if (parallel)
         script->setParallelIonScript(nullptr);
     else
         script->setIonScript(nullptr);
 
-    // If this script has Ion code on the stack, invalidation() will return
+    types::TypeZone &types = script->zone()->types;
+    ionScript->recompileInfo().compilerOutput(types)->invalidate();
+
+    // If this script has Ion code on the stack, invalidated() will return
     // true. In this case we have to wait until destroying it.
-    if (!ionScript->invalidated()) {
-        types::TypeCompartment &types = script->compartment()->types;
-        ionScript->recompileInfo().compilerOutput(types)->invalidate();
-
+    if (!ionScript->invalidated())
         jit::IonScript::Destroy(fop, ionScript);
-    }
 }
 
 void
 jit::FinishInvalidation(FreeOp *fop, JSScript *script)
 {
     if (script->hasIonScript())
         FinishInvalidationOf(fop, script, script->ionScript(), false);
 
@@ -2659,18 +2658,16 @@ jit::FinishInvalidation(FreeOp *fop, JSS
 }
 
 void
 jit::FinishDiscardJitCode(FreeOp *fop, JSCompartment *comp)
 {
     // Free optimized baseline stubs.
     if (comp->jitCompartment())
         comp->jitCompartment()->optimizedStubSpace()->free();
-
-    comp->types.clearCompilerOutputs(fop);
 }
 
 void
 jit::MarkValueFromIon(JSRuntime *rt, Value *vp)
 {
     gc::MarkValueUnbarriered(&rt->gcMarker, vp, "write barrier");
 }
 
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -122,17 +122,17 @@ struct EnterJitData;
 bool SetEnterJitData(JSContext *cx, EnterJitData &data, RunState &state, AutoValueVector &vals);
 
 IonExecStatus IonCannon(JSContext *cx, RunState &state);
 
 // Used to enter Ion from C++ natives like Array.map. Called from FastInvokeGuard.
 IonExecStatus FastInvoke(JSContext *cx, HandleFunction fun, CallArgs &args);
 
 // Walk the stack and invalidate active Ion frames for the invalid scripts.
-void Invalidate(types::TypeCompartment &types, FreeOp *fop,
+void Invalidate(types::TypeZone &types, FreeOp *fop,
                 const Vector<types::RecompileInfo> &invalid, bool resetUses = true,
                 bool cancelOffThread = true);
 void Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses = true,
                 bool cancelOffThread = true);
 bool Invalidate(JSContext *cx, JSScript *script, ExecutionMode mode, bool resetUses = true,
                 bool cancelOffThread = true);
 bool Invalidate(JSContext *cx, JSScript *script, bool resetUses = true,
                 bool cancelOffThread = true);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6316,17 +6316,17 @@ IonBuilder::getStaticName(JSObject *stat
     if (staticType->unknownProperties()) {
         *psucceeded = false;
         return true;
     }
 
     types::HeapTypeSetKey property = staticType->property(id);
     if (!property.maybeTypes() ||
         !property.maybeTypes()->definiteProperty() ||
-        property.configured(constraints(), staticType))
+        property.configured(constraints()))
     {
         // The property has been reconfigured as non-configurable, non-enumerable
         // or non-writable.
         *psucceeded = false;
         return true;
     }
 
     types::TemporaryTypeSet *types = bytecodeTypes(pc);
@@ -6406,17 +6406,17 @@ IonBuilder::setStaticName(JSObject *stat
 
     types::TypeObjectKey *staticType = types::TypeObjectKey::get(staticObject);
     if (staticType->unknownProperties())
         return jsop_setprop(name);
 
     types::HeapTypeSetKey property = staticType->property(id);
     if (!property.maybeTypes() ||
         !property.maybeTypes()->definiteProperty() ||
-        property.configured(constraints(), staticType))
+        property.configured(constraints()))
     {
         // The property has been reconfigured as non-configurable, non-enumerable
         // or non-writable.
         return jsop_setprop(name);
     }
 
     if (!TypeSetIncludes(property.maybeTypes(), value->type(), value->resultTypeSet()))
         return jsop_setprop(name);
@@ -7856,17 +7856,17 @@ IonBuilder::getDefiniteSlot(types::Tempo
     if (type->unknownProperties() || type->singleton())
         return false;
 
     jsid id = NameToId(name);
 
     *property = type->property(id);
     return property->maybeTypes() &&
            property->maybeTypes()->definiteProperty() &&
-           !property->configured(constraints(), type);
+           !property->configured(constraints());
 }
 
 bool
 IonBuilder::jsop_runonce()
 {
     MRunOncePrologue *ins = MRunOncePrologue::New(alloc());
     current->add(ins);
     return resumeAfter(ins);
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -526,16 +526,19 @@ struct IonScript
         JS_ASSERT(refcount_);
         refcount_--;
         if (!refcount_)
             Destroy(fop, this);
     }
     const types::RecompileInfo& recompileInfo() const {
         return recompileInfo_;
     }
+    types::RecompileInfo& recompileInfoRef() {
+        return recompileInfo_;
+    }
     OptimizationLevel optimizationLevel() const {
         return optimizationLevel_;
     }
     uint32_t incrOsrPcMismatchCounter() {
         return ++osrPcMismatchCounter_;
     }
     void resetOsrPcMismatchCounter() {
         osrPcMismatchCounter_ = 0;
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -3020,17 +3020,17 @@ jit::PropertyReadIsIdempotent(types::Com
     for (size_t i = 0; i < types->getObjectCount(); i++) {
         types::TypeObjectKey *object = types->getObject(i);
         if (object) {
             if (object->unknownProperties())
                 return false;
 
             // Check if the property has been reconfigured or is a getter.
             types::HeapTypeSetKey property = object->property(NameToId(name));
-            if (property.configured(constraints, object))
+            if (property.configured(constraints))
                 return false;
         }
     }
 
     return true;
 }
 
 bool
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -43,17 +43,16 @@ JSCompartment::JSCompartment(Zone *zone,
     principals(nullptr),
     isSystem(false),
     marked(true),
 #ifdef DEBUG
     firedOnNewGlobalObject(false),
 #endif
     global_(nullptr),
     enterCompartmentDepth(0),
-    lastCodeRelease(0),
     data(nullptr),
     objectMetadataCallback(nullptr),
     lastAnimationTime(0),
     regExps(runtime_),
     typeReprs(runtime_),
     globalWriteBarriered(false),
     propertyTree(thisForCtor()),
     gcIncomingGrayPointers(nullptr),
@@ -540,23 +539,16 @@ JSCompartment::sweep(FreeOp *fop, bool r
 
         if (debugScopes)
             debugScopes->sweep(rt);
 
         /* Finalize unreachable (key,value) pairs in all weak maps. */
         WeakMapBase::sweepCompartment(this);
     }
 
-    if (zone()->isPreservingCode()) {
-        gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_ANALYSIS);
-        types.sweepShapes(fop);
-    } else {
-        JS_ASSERT(!types.constrainedOutputs);
-    }
-
     NativeIterator *ni = enumerators->next();
     while (ni != enumerators) {
         JSObject *iterObj = ni->iterObj();
         NativeIterator *next = ni->next();
         if (gc::IsObjectAboutToBeFinalized(&iterObj))
             ni->unlink();
         ni = next;
     }
@@ -859,29 +851,28 @@ JSCompartment::clearTraps(FreeOp *fop)
         JSScript *script = i.get<JSScript>();
         if (script->compartment() == this && script->hasAnyBreakpointsOrStepMode())
             script->clearTraps(fop);
     }
 }
 
 void
 JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
-                                      size_t *tiPendingArrays,
                                       size_t *tiAllocationSiteTables,
                                       size_t *tiArrayTypeTables,
                                       size_t *tiObjectTypeTables,
                                       size_t *compartmentObject,
                                       size_t *shapesCompartmentTables,
                                       size_t *crossCompartmentWrappersArg,
                                       size_t *regexpCompartment,
                                       size_t *debuggeesSet,
                                       size_t *baselineStubsOptimized)
 {
     *compartmentObject += mallocSizeOf(this);
-    types.addSizeOfExcludingThis(mallocSizeOf, tiPendingArrays, tiAllocationSiteTables,
+    types.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables,
                                  tiArrayTypeTables, tiObjectTypeTables);
     *shapesCompartmentTables += baseShapes.sizeOfExcludingThis(mallocSizeOf)
                               + initialShapes.sizeOfExcludingThis(mallocSizeOf)
                               + newTypeObjects.sizeOfExcludingThis(mallocSizeOf)
                               + lazyTypeObjects.sizeOfExcludingThis(mallocSizeOf);
     *crossCompartmentWrappersArg += crossCompartmentWrappers.sizeOfExcludingThis(mallocSizeOf);
     *regexpCompartment += regExps.sizeOfExcludingThis(mallocSizeOf);
     *debuggeesSet += debuggees.sizeOfExcludingThis(mallocSizeOf);
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -182,18 +182,16 @@ struct JSCompartment
   public:
     /*
      * Moves all data from the allocator |workerAllocator|, which was
      * in use by a parallel worker, into the compartment's main
      * allocator.  This is used at the end of a parallel section.
      */
     void adoptWorkerAllocator(js::Allocator *workerAllocator);
 
-
-    int64_t                      lastCodeRelease;
     bool                         activeAnalysis;
 
     /* Type information about the scripts and objects in this compartment. */
     js::types::TypeCompartment   types;
 
     void                         *data;
 
   private:
@@ -216,17 +214,16 @@ struct JSCompartment
      *
      * This is used to avoid adding it to the store buffer on every write, which
      * can quickly fill the buffer and also cause performance problems.
      */
     bool                         globalWriteBarriered;
 
   public:
     void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
-                                size_t *tiPendingArrays,
                                 size_t *tiAllocationSiteTables,
                                 size_t *tiArrayTypeTables,
                                 size_t *tiObjectTypeTables,
                                 size_t *compartmentObject,
                                 size_t *shapesCompartmentTables,
                                 size_t *crossCompartmentWrappers,
                                 size_t *regexpCompartment,
                                 size_t *debuggeesSet,
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2849,23 +2849,19 @@ static bool
 ShouldPreserveJITCode(JSCompartment *comp, int64_t currentTime)
 {
     JSRuntime *rt = comp->runtimeFromMainThread();
     if (rt->gcShouldCleanUpEverything || !comp->zone()->types.inferenceEnabled)
         return false;
 
     if (rt->alwaysPreserveCode)
         return true;
-    if (comp->lastAnimationTime + PRMJ_USEC_PER_SEC >= currentTime &&
-        comp->lastCodeRelease + (PRMJ_USEC_PER_SEC * 300) >= currentTime)
-    {
+    if (comp->lastAnimationTime + PRMJ_USEC_PER_SEC >= currentTime)
         return true;
-    }
-
-    comp->lastCodeRelease = currentTime;
+
     return false;
 }
 
 #ifdef DEBUG
 struct CompartmentCheckTracer : public JSTracer
 {
     Cell *src;
     JSGCTraceKind srcKind;
@@ -3934,22 +3930,22 @@ BeginSweepingZoneGroup(JSRuntime *rt)
         for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
             gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_DISCARD_CODE);
             zone->discardJitCode(&fop);
         }
 
         bool releaseTypes = ReleaseObservedTypes(rt);
         for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
             gcstats::AutoSCC scc(rt->gcStats, rt->gcZoneGroupIndex);
-            c->sweep(&fop, releaseTypes);
+            c->sweep(&fop, releaseTypes && !c->zone()->isPreservingCode());
         }
 
         for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
             gcstats::AutoSCC scc(rt->gcStats, rt->gcZoneGroupIndex);
-            zone->sweep(&fop, releaseTypes);
+            zone->sweep(&fop, releaseTypes && !zone->isPreservingCode());
         }
     }
 
     /*
      * Queue all GC things in all zones for sweeping, either in the
      * foreground or on the background thread.
      *
      * Note that order is important here for the background case.
@@ -5288,20 +5284,16 @@ js::ReleaseAllJITCode(FreeOp *fop)
 
             /*
              * Discard baseline script if it's not marked as active. Note that
              * this also resets the active flag.
              */
             jit::FinishDiscardBaselineScript(fop, script);
         }
     }
-
-    /* Sweep now invalidated compiler outputs from each compartment. */
-    for (CompartmentsIter comp(fop->runtime(), SkipAtoms); !comp.done(); comp.next())
-        comp->types.clearCompilerOutputs(fop);
 #endif
 }
 
 /*
  * There are three possible PCCount profiling states:
  *
  * 1. None: Neither scripts nor the runtime have count information.
  * 2. Profile: Active scripts have count information, the runtime does not.
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1086,17 +1086,16 @@ struct GCMarker : public JSTracer {
      * the explicit tags to distinguish them when it cannot be deduced from
      * the context of push or pop operation.
      */
     enum StackTag {
         ValueArrayTag,
         ObjectTag,
         TypeTag,
         XmlTag,
-        ArenaTag,
         SavedValueArrayTag,
         IonCodeTag,
         LastTag = IonCodeTag
     };
 
     static const uintptr_t StackTagMask = 7;
 
     static void staticAsserts() {
@@ -1114,20 +1113,16 @@ struct GCMarker : public JSTracer {
     void start();
     void stop();
     void reset();
 
     void pushObject(ObjectImpl *obj) {
         pushTaggedPtr(ObjectTag, obj);
     }
 
-    void pushArenaList(gc::ArenaHeader *firstArena) {
-        pushTaggedPtr(ArenaTag, firstArena);
-    }
-
     void pushType(types::TypeObject *type) {
         pushTaggedPtr(TypeTag, type);
     }
 
     void pushIonCode(jit::IonCode *code) {
         pushTaggedPtr(IonCodeTag, code);
     }
 
@@ -1224,17 +1219,17 @@ struct GCMarker : public JSTracer {
 
     bool isMarkStackEmpty() {
         return stack.isEmpty();
     }
 
     bool restoreValueArray(JSObject *obj, void **vpp, void **endp);
     void saveValueRanges();
     inline void processMarkStackTop(SliceBudget &budget);
-    void processMarkStackOther(SliceBudget &budget, uintptr_t tag, uintptr_t addr);
+    void processMarkStackOther(uintptr_t tag, uintptr_t addr);
 
     void appendGrayRoot(void *thing, JSGCTraceKind kind);
 
     /* The color is only applied to objects and functions. */
     uint32_t color;
 
     mozilla::DebugOnly<bool> started;
 
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -238,24 +238,16 @@ types::TypeHasProperty(JSContext *cx, Ty
      */
     if (cx->typeInferenceEnabled() && !obj->unknownProperties() && !value.isUndefined()) {
         id = IdToTypeId(id);
 
         /* Watch for properties which inference does not monitor. */
         if (id == id___proto__(cx) || id == id_constructor(cx) || id == id_caller(cx))
             return true;
 
-        /*
-         * If we called in here while resolving a type constraint, we may be in the
-         * middle of resolving a standard class and the type sets will not be updated
-         * until the outer TypeSet::add finishes.
-         */
-        if (cx->compartment()->types.pendingCount)
-            return true;
-
         Type type = GetValueType(value);
 
         AutoEnterAnalysis enter(cx);
 
         /*
          * We don't track types for properties inherited from prototypes which
          * haven't yet been accessed during analysis of the inheriting object.
          * Don't do the property instantiation now.
@@ -737,30 +729,39 @@ class TypeCompilerConstraint : public Ty
     TypeCompilerConstraint<T>(RecompileInfo compilation, const T &data)
       : compilation(compilation), data(data)
     {}
 
     const char *kind() { return data.kind(); }
 
     void newType(JSContext *cx, TypeSet *source, Type type) {
         if (data.invalidateOnNewType(type))
-            cx->compartment()->types.addPendingRecompile(cx, compilation);
+            cx->zone()->types.addPendingRecompile(cx, compilation);
     }
 
     void newPropertyState(JSContext *cx, TypeSet *source) {
         if (data.invalidateOnNewPropertyState(source))
-            cx->compartment()->types.addPendingRecompile(cx, compilation);
+            cx->zone()->types.addPendingRecompile(cx, compilation);
     }
 
     void newObjectState(JSContext *cx, TypeObject *object) {
         // Note: Once the object has unknown properties, no more notifications
         // will be sent on changes to its state, so always invalidate any
         // associated compilations.
         if (object->unknownProperties() || data.invalidateOnNewObjectState(object))
-            cx->compartment()->types.addPendingRecompile(cx, compilation);
+            cx->zone()->types.addPendingRecompile(cx, compilation);
+    }
+
+    TypeConstraint *sweep(TypeZone &zone) {
+        if (data.shouldSweep() || compilation.shouldSweep(zone))
+            return nullptr;
+        TypeConstraint *res = zone.typeLifoAlloc.new_<TypeCompilerConstraint<T> >(compilation, data);
+        if (!res)
+            zone.setPendingNukeTypes();
+        return res;
     }
 };
 
 template <typename T>
 bool
 CompilerConstraintInstance<T>::generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo)
 {
     if (property.object()->unknownProperties())
@@ -903,46 +904,61 @@ class TypeConstraintFreezeStack : public
 
   public:
     TypeConstraintFreezeStack(JSScript *script)
         : script_(script)
     {}
 
     const char *kind() { return "freezeStack"; }
 
-    void newType(JSContext *cx, TypeSet *source, Type type)
-    {
+    void newType(JSContext *cx, TypeSet *source, Type type) {
         /*
          * Unlike TypeConstraintFreeze, triggering this constraint once does
          * not disable it on future changes to the type set.
          */
-        cx->compartment()->types.addPendingRecompile(cx, script_);
+        cx->zone()->types.addPendingRecompile(cx, script_);
+    }
+
+    TypeConstraint *sweep(TypeZone &zone) {
+        if (IsScriptAboutToBeFinalized(&script_))
+            return nullptr;
+        TypeConstraint *res = zone.typeLifoAlloc.new_<TypeConstraintFreezeStack>(script_);
+        if (!res)
+            zone.setPendingNukeTypes();
+        return res;
     }
 };
 
 } /* anonymous namespace */
 
 bool
 types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode executionMode,
                          CompilerConstraintList *constraints, RecompileInfo *precompileInfo)
 {
     if (constraints->failed())
         return false;
 
     CompilerOutput co(script, executionMode);
 
-    TypeCompartment &types = cx->compartment()->types;
-    if (!types.constrainedOutputs) {
-        types.constrainedOutputs = cx->new_< Vector<CompilerOutput> >(cx);
-        if (!types.constrainedOutputs)
+    TypeZone &types = cx->zone()->types;
+    if (!types.compilerOutputs) {
+        types.compilerOutputs = cx->new_< Vector<CompilerOutput> >(cx);
+        if (!types.compilerOutputs)
             return false;
     }
 
-    uint32_t index = types.constrainedOutputs->length();
-    if (!types.constrainedOutputs->append(co))
+#ifdef DEBUG
+    for (size_t i = 0; i < types.compilerOutputs->length(); i++) {
+        const CompilerOutput &co = (*types.compilerOutputs)[i];
+        JS_ASSERT_IF(co.isValid(), co.script() != script || co.mode() != executionMode);
+    }
+#endif
+
+    uint32_t index = types.compilerOutputs->length();
+    if (!types.compilerOutputs->append(co))
         return false;
 
     *precompileInfo = RecompileInfo(index);
 
     bool succeeded = true;
 
     for (size_t i = 0; i < constraints->length(); i++) {
         CompilerConstraint *constraint = constraints->get(i);
@@ -974,18 +990,18 @@ types::FinishCompilation(JSContext *cx, 
 
         size_t count = TypeScript::NumTypeSets(entry.script);
 
         StackTypeSet *array = entry.script->types->typeArray();
         for (size_t i = 0; i < count; i++)
             array[i].add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeStack>(entry.script), false);
     }
 
-    if (!succeeded || types.constrainedOutputs->back().pendingInvalidation()) {
-        types.constrainedOutputs->back().invalidate();
+    if (!succeeded || types.compilerOutputs->back().pendingInvalidation()) {
+        types.compilerOutputs->back().invalidate();
         script->resetUseCount();
         return false;
     }
 
     return true;
 }
 
 namespace {
@@ -1004,16 +1020,18 @@ class ConstraintDataFreeze
 
     bool constraintHolds(JSContext *cx,
                          const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
         return expected
                ? property.maybeTypes()->isSubset(expected)
                : property.maybeTypes()->empty();
     }
+
+    bool shouldSweep() { return false; }
 };
 
 } /* anonymous namespace */
 
 void
 HeapTypeSetKey::freeze(CompilerConstraintList *constraints)
 {
     LifoAlloc *alloc = constraints->alloc();
@@ -1190,16 +1208,18 @@ class ConstraintDataFreezeObjectFlags
         return object->hasAnyFlags(flags);
     }
 
     bool constraintHolds(JSContext *cx,
                          const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
         return !invalidateOnNewObjectState(property.object()->maybeType());
     }
+
+    bool shouldSweep() { return false; }
 };
 
 } /* anonymous namespace */
 
 bool
 TypeObjectKey::hasFlags(CompilerConstraintList *constraints, TypeObjectFlags flags)
 {
     JS_ASSERT(flags);
@@ -1284,16 +1304,18 @@ class ConstraintDataFreezeObjectForInlin
         return true;
     }
 
     bool constraintHolds(JSContext *cx,
                          const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
         return true;
     }
+
+    bool shouldSweep() { return false; }
 };
 
 // Constraint which triggers recompilation when the template object for a
 // type's new script changes.
 class ConstraintDataFreezeObjectForNewScriptTemplate
 {
     JSObject *templateObject;
 
@@ -1310,16 +1332,21 @@ class ConstraintDataFreezeObjectForNewSc
         return !object->hasNewScript() || object->newScript()->templateObject != templateObject;
     }
 
     bool constraintHolds(JSContext *cx,
                          const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
         return !invalidateOnNewObjectState(property.object()->maybeType());
     }
+
+    bool shouldSweep() {
+        // Note: |templateObject| is only used for equality testing.
+        return false;
+    }
 };
 
 // Constraint which triggers recompilation when the underlying data pointer for
 // a typed array changes.
 class ConstraintDataFreezeObjectForTypedArrayBuffer
 {
     void *viewData;
 
@@ -1336,16 +1363,21 @@ class ConstraintDataFreezeObjectForTyped
         return object->singleton->as<TypedArrayObject>().viewData() != viewData;
     }
 
     bool constraintHolds(JSContext *cx,
                          const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
         return !invalidateOnNewObjectState(property.object()->maybeType());
     }
+
+    bool shouldSweep() {
+        // Note: |viewData| is only used for equality testing.
+        return false;
+    }
 };
 
 } /* anonymous namespace */
 
 void
 TypeObjectKey::watchStateChangeForInlinedCall(CompilerConstraintList *constraints)
 {
     HeapTypeSetKey objectProperty = property(JSID_EMPTY);
@@ -1408,67 +1440,49 @@ ObjectStateChange(ExclusiveContext *cxAr
 static void
 CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun);
 
 namespace {
 
 class ConstraintDataFreezeConfiguredProperty
 {
   public:
-    TypeObjectKey *object;
-
-    ConstraintDataFreezeConfiguredProperty(TypeObjectKey *object)
-      : object(object)
+    ConstraintDataFreezeConfiguredProperty()
     {}
 
     const char *kind() { return "freezeConfiguredProperty"; }
 
     bool invalidateOnNewType(Type type) { return false; }
     bool invalidateOnNewPropertyState(TypeSet *property) {
         return property->configuredProperty();
     }
     bool invalidateOnNewObjectState(TypeObject *object) { return false; }
 
     bool constraintHolds(JSContext *cx,
                          const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
-        // Everywhere compiled code depends on definite properties associated
-        // with a type object's newScript, we need to make sure there are
-        // constraints in place which will mark those properties as configured
-        // should the definite properties be invalidated.
-        TypeObject *type = object->isSingleObject()
-                           ? object->asSingleObject()->type()
-                           : object->asTypeObject();
-        if (type->flags & OBJECT_FLAG_NEW_SCRIPT_REGENERATE) {
-            type->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
-            if (type->hasNewScript()) {
-                CheckNewScriptProperties(cx, type, type->newScript()->fun);
-            } else {
-                JS_ASSERT(type->flags & OBJECT_FLAG_ADDENDUM_CLEARED);
-                type->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
-            }
-        }
-
         return !property.maybeTypes()->configuredProperty();
     }
+
+    bool shouldSweep() { return false; }
 };
 
 } /* anonymous namespace */
 
 bool
-HeapTypeSetKey::configured(CompilerConstraintList *constraints, TypeObjectKey *type)
+HeapTypeSetKey::configured(CompilerConstraintList *constraints)
 {
     if (maybeTypes() && maybeTypes()->configuredProperty())
         return true;
 
     LifoAlloc *alloc = constraints->alloc();
 
     typedef CompilerConstraintInstance<ConstraintDataFreezeConfiguredProperty> T;
     constraints->add(alloc->new_<T>(alloc, *this,
-                                    ConstraintDataFreezeConfiguredProperty(type)));
+                                    ConstraintDataFreezeConfiguredProperty()));
     return false;
 }
 
 bool
 TemporaryTypeSet::filtersType(const TemporaryTypeSet *other, Type filteredType) const
 {
     if (other->unknown())
         return unknown();
@@ -1981,17 +1995,17 @@ PrototypeHasIndexedProperty(CompilerCons
 {
     do {
         TypeObjectKey *type = TypeObjectKey::get(obj);
         if (ClassCanHaveExtraProperties(type->clasp()))
             return true;
         if (type->unknownProperties())
             return true;
         HeapTypeSetKey index = type->property(JSID_VOID);
-        if (index.configured(constraints, type) || index.isOwnProperty(constraints))
+        if (index.configured(constraints) || index.isOwnProperty(constraints))
             return true;
         obj = obj->getProto();
     } while (obj);
 
     return false;
 }
 
 bool
@@ -2019,37 +2033,18 @@ types::TypeCanHaveExtraIndexedProperties
 
     JSObject *proto = types->getCommonPrototype();
     if (!proto)
         return true;
 
     return PrototypeHasIndexedProperty(constraints, proto);
 }
 
-bool
-TypeCompartment::growPendingArray(JSContext *cx)
-{
-    unsigned newCapacity = js::Max(unsigned(100), pendingCapacity * 2);
-    PendingWork *newArray = js_pod_calloc<PendingWork>(newCapacity);
-    if (!newArray) {
-        cx->compartment()->types.setPendingNukeTypes(cx);
-        return false;
-    }
-
-    PodCopy(newArray, pendingArray, pendingCount);
-    js_free(pendingArray);
-
-    pendingArray = newArray;
-    pendingCapacity = newCapacity;
-
-    return true;
-}
-
 void
-TypeCompartment::processPendingRecompiles(FreeOp *fop)
+TypeZone::processPendingRecompiles(FreeOp *fop)
 {
     if (!pendingRecompiles)
         return;
 
     /* Steal the list of scripts to recompile, else we will try to recursively recompile them. */
     Vector<RecompileInfo> *pending = pendingRecompiles;
     pendingRecompiles = nullptr;
 
@@ -2090,21 +2085,19 @@ TypeZone::nukeTypes(FreeOp *fop)
      * Because of the nature of constraint-based analysis (add constraints, and
      * iterate them until reaching a fixpoint), we can't undo an add of a type set,
      * and merely aborting the operation which triggered the add will not be
      * sufficient for correct behavior as we will be leaving the types in an
      * inconsistent state.
      */
     JS_ASSERT(pendingNukeTypes);
 
-    for (CompartmentsInZoneIter comp(zone()); !comp.done(); comp.next()) {
-        if (comp->types.pendingRecompiles) {
-            fop->free_(comp->types.pendingRecompiles);
-            comp->types.pendingRecompiles = nullptr;
-        }
+    if (pendingRecompiles) {
+        fop->free_(pendingRecompiles);
+        pendingRecompiles = nullptr;
     }
 
     inferenceEnabled = false;
 
 #ifdef JS_ION
     jit::InvalidateAll(fop, zone());
 
     /* Throw away all JIT code in the compartment, but leave everything else alone. */
@@ -2114,17 +2107,17 @@ TypeZone::nukeTypes(FreeOp *fop)
         jit::FinishInvalidation(fop, script);
     }
 #endif /* JS_ION */
 
     pendingNukeTypes = false;
 }
 
 void
-TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
+TypeZone::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
 {
     CompilerOutput *co = info.compilerOutput(cx);
     if (!co || !co->isValid() || co->pendingInvalidation())
         return;
 
     if (!pendingRecompiles) {
         pendingRecompiles = cx->new_< Vector<RecompileInfo> >(cx);
         if (!pendingRecompiles) {
@@ -2140,17 +2133,17 @@ TypeCompartment::addPendingRecompile(JSC
 
     InferSpew(ISpewOps, "addPendingRecompile: %p:%s:%d",
               co->script(), co->script()->filename(), co->script()->lineno());
 
     co->setPendingInvalidation();
 }
 
 void
-TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script)
+TypeZone::addPendingRecompile(JSContext *cx, JSScript *script)
 {
     JS_ASSERT(script);
 
 #ifdef JS_ION
     CancelOffThreadIonCompile(cx->compartment(), script);
 
     // Let the script warm up again before attempting another compile.
     if (jit::IsBaselineEnabled(cx))
@@ -3141,16 +3134,25 @@ class TypeConstraintClearDefiniteGetterS
          * non-writable, both of which are indicated by the source type set
          * being marked as configured.
          */
         if (!(object->flags & OBJECT_FLAG_ADDENDUM_CLEARED) && source->configuredProperty())
             object->clearAddendum(cx);
     }
 
     void newType(JSContext *cx, TypeSet *source, Type type) {}
+
+    TypeConstraint *sweep(TypeZone &zone) {
+        if (IsTypeObjectAboutToBeFinalized(&object))
+            return nullptr;
+        TypeConstraint *res = zone.typeLifoAlloc.new_<TypeConstraintClearDefiniteGetterSetter>(object);
+        if (!res)
+            zone.setPendingNukeTypes();
+        return res;
+    }
 };
 
 bool
 types::AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, TypeObject *type, jsid id)
 {
     /*
      * Ensure that if the properties named here could have a getter, setter or
      * a permanent property in any transitive prototype, the definite
@@ -3187,16 +3189,25 @@ class TypeConstraintClearDefiniteSingle 
 
     void newType(JSContext *cx, TypeSet *source, Type type) {
         if (object->flags & OBJECT_FLAG_ADDENDUM_CLEARED)
             return;
 
         if (source->baseFlags() || source->getObjectCount() > 1)
             object->clearAddendum(cx);
     }
+
+    TypeConstraint *sweep(TypeZone &zone) {
+        if (IsTypeObjectAboutToBeFinalized(&object))
+            return nullptr;
+        TypeConstraint *res = zone.typeLifoAlloc.new_<TypeConstraintClearDefiniteSingle>(object);
+        if (!res)
+            zone.setPendingNukeTypes();
+        return res;
+    }
 };
 
 void
 types::AddClearDefiniteFunctionUsesInScript(JSContext *cx, TypeObject *type,
                                             JSScript *script, JSScript *calleeScript)
 {
     // Look for any uses of the specified calleeScript in type sets for
     // |script|, and add constraints to ensure that if the type sets' contents
@@ -3936,18 +3947,29 @@ ConstraintTypeSet::sweep(Zone *zone)
     } else if (objectCount == 1) {
         TypeObjectKey *object = (TypeObjectKey *) objectSet;
         if (IsAboutToBeFinalized(object)) {
             objectSet = nullptr;
             setBaseObjectCount(0);
         }
     }
 
-    /* All constraints are wiped out on each GC. */
+    /*
+     * Type constraints only hold weak references. Copy constraints referring
+     * to data that is still live into the zone's new arena.
+     */
+    TypeConstraint *constraint = constraintList;
     constraintList = nullptr;
+    while (constraint) {
+        if (TypeConstraint *copy = constraint->sweep(zone->types)) {
+            copy->next = constraintList;
+            constraintList = copy;
+        }
+        constraint = constraint->next;
+    }
 }
 
 inline void
 TypeObject::clearProperties()
 {
     setBasePropertyCount(0);
     propertySet = nullptr;
 }
@@ -3957,28 +3979,16 @@ TypeObject::clearProperties()
  * 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.
  */
 inline void
 TypeObject::sweep(FreeOp *fop)
 {
-    if (singleton) {
-        JS_ASSERT(!hasNewScript());
-
-        /*
-         * All properties can be discarded. We will regenerate them as needed
-         * as code gets reanalyzed.
-         */
-        clearProperties();
-
-        return;
-    }
-
     if (!isMarked()) {
         if (addendum)
             fop->free_(addendum);
         return;
     }
 
     js::LifoAlloc &typeLifoAlloc = zone()->types.typeLifoAlloc;
 
@@ -3991,16 +4001,24 @@ TypeObject::sweep(FreeOp *fop)
         unsigned oldCapacity = HashSetCapacity(propertyCount);
         Property **oldArray = propertySet;
 
         clearProperties();
         propertyCount = 0;
         for (unsigned i = 0; i < oldCapacity; i++) {
             Property *prop = oldArray[i];
             if (prop) {
+                if (singleton && !prop->types.constraintList) {
+                    /*
+                     * Don't copy over properties of singleton objects which
+                     * don't have associated constraints. The contents of these
+                     * type sets will be regenerated as necessary.
+                     */
+                    continue;
+                }
                 Property *newProp = typeLifoAlloc.new_<Property>(*prop);
                 if (newProp) {
                     Property **pentry =
                         HashSetInsert<jsid,Property,Property>
                             (typeLifoAlloc, propertySet, propertyCount, prop->id);
                     if (pentry) {
                         *pentry = newProp;
                         newProp->types.sweep(zone());
@@ -4010,37 +4028,34 @@ TypeObject::sweep(FreeOp *fop)
                 } else {
                     zone()->types.setPendingNukeTypes();
                 }
             }
         }
         setBasePropertyCount(propertyCount);
     } else if (propertyCount == 1) {
         Property *prop = (Property *) propertySet;
-        Property *newProp = typeLifoAlloc.new_<Property>(*prop);
-        if (newProp) {
-            propertySet = (Property **) newProp;
-            newProp->types.sweep(zone());
+        if (singleton && !prop->types.constraintList) {
+            // Skip, as above.
+            clearProperties();
         } else {
-            zone()->types.setPendingNukeTypes();
+            Property *newProp = typeLifoAlloc.new_<Property>(*prop);
+            if (newProp) {
+                propertySet = (Property **) newProp;
+                newProp->types.sweep(zone());
+            } else {
+                zone()->types.setPendingNukeTypes();
+            }
         }
     }
 
     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 (hasNewScript())
-        flags |= OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
 }
 
 void
 TypeCompartment::sweep(FreeOp *fop)
 {
     /*
      * Iterate through the array/object type tables and remove all entries
      * referencing collected data. These tables only hold weak references.
@@ -4114,62 +4129,16 @@ TypeCompartment::sweep(FreeOp *fop)
             bool keyDying = IsScriptAboutToBeFinalized(&key.script);
             bool valDying = IsTypeObjectAboutToBeFinalized(e.front().value().unsafeGet());
             if (keyDying || valDying)
                 e.removeFront();
             else if (key.script != e.front().key().script)
                 e.rekeyFront(key);
         }
     }
-
-    /*
-     * 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)
-        fop->free_(pendingArray);
-
-    pendingArray = nullptr;
-    pendingCapacity = 0;
-}
-
-void
-TypeCompartment::sweepShapes(FreeOp *fop)
-{
-    /*
-     * Sweep any weak shape references that may be finalized even if a GC is
-     * preserving type information.
-     */
-    if (objectTypeTable) {
-        for (ObjectTypeTable::Enum e(*objectTypeTable); !e.empty(); e.popFront()) {
-            const ObjectTableKey &key = e.front().key();
-            ObjectTableEntry &entry = e.front().value();
-
-            if (IsShapeAboutToBeFinalized(entry.shape.unsafeGet())) {
-                fop->free_(key.properties);
-                fop->free_(entry.types);
-                e.removeFront();
-            }
-        }
-    }
-}
-
-void
-TypeCompartment::clearCompilerOutputs(FreeOp *fop)
-{
-    if (constrainedOutputs) {
-        fop->delete_(constrainedOutputs);
-        constrainedOutputs = nullptr;
-    }
-
-    if (pendingRecompiles) {
-        JS_ASSERT(pendingRecompiles->length() == 0);
-        fop->delete_(pendingRecompiles);
-        pendingRecompiles = nullptr;
-    }
 }
 
 void
 JSCompartment::sweepNewTypeObjectTable(TypeObjectWithNewScriptSet &table)
 {
     gcstats::AutoPhase ap(runtimeFromMainThread()->gcStats,
                           gcstats::PHASE_SWEEP_TABLES_TYPE_OBJECT);
 
@@ -4188,17 +4157,16 @@ JSCompartment::sweepNewTypeObjectTable(T
                 e.rekeyFront(lookup, entry);
             }
         }
     }
 }
 
 TypeCompartment::~TypeCompartment()
 {
-    js_free(pendingArray);
     js_delete(arrayTypeTable);
     js_delete(objectTypeTable);
     js_delete(allocationSiteTable);
 }
 
 /* static */ void
 TypeScript::Sweep(FreeOp *fop, JSScript *script)
 {
@@ -4207,52 +4175,36 @@ TypeScript::Sweep(FreeOp *fop, JSScript 
     JS_ASSERT(compartment->zone()->types.inferenceEnabled);
 
     unsigned num = NumTypeSets(script);
     StackTypeSet *typeArray = script->types->typeArray();
 
     /* Remove constraints and references to dead objects from the persistent type sets. */
     for (unsigned i = 0; i < num; i++)
         typeArray[i].sweep(compartment->zone());
-
-    /*
-     * Freeze constraints on stack type sets need to be regenerated the next
-     * time the script is analyzed.
-     */
-    script->clearHasFreezeConstraints();
 }
 
 void
 TypeScript::destroy()
 {
     js_free(this);
 }
 
 void
 Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t *typePool)
 {
     *typePool += types.typeLifoAlloc.sizeOfExcludingThis(mallocSizeOf);
 }
 
 void
 TypeCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
-                                        size_t *pendingArrays,
                                         size_t *allocationSiteTables,
                                         size_t *arrayTypeTables,
                                         size_t *objectTypeTables)
 {
-    /* Pending arrays are cleared on GC along with the analysis pool. */
-    *pendingArrays += mallocSizeOf(pendingArray);
-
-    /*
-     * TypeCompartment::pendingRecompiles is non-nullptr only while inference
-     * code is running.
-     */
-    JS_ASSERT(!pendingRecompiles);
-
     if (allocationSiteTable)
         *allocationSiteTables += allocationSiteTable->sizeOfIncludingThis(mallocSizeOf);
 
     if (arrayTypeTable)
         *arrayTypeTables += arrayTypeTable->sizeOfIncludingThis(mallocSizeOf);
 
     if (objectTypeTable) {
         *objectTypeTables += objectTypeTable->sizeOfIncludingThis(mallocSizeOf);
@@ -4284,55 +4236,84 @@ TypeObject::sizeOfExcludingThis(mozilla:
     }
 
     return mallocSizeOf(addendum);
 }
 
 TypeZone::TypeZone(Zone *zone)
   : zone_(zone),
     typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
+    compilerOutputs(nullptr),
+    pendingRecompiles(nullptr),
     pendingNukeTypes(false),
     inferenceEnabled(false)
 {
 }
 
 TypeZone::~TypeZone()
 {
+    js_delete(compilerOutputs);
+    js_delete(pendingRecompiles);
 }
 
 void
 TypeZone::sweep(FreeOp *fop, bool releaseTypes)
 {
     JS_ASSERT(zone()->isGCSweeping());
 
     JSRuntime *rt = fop->runtime();
 
     /*
      * Clear the analysis pool, but don't release its data yet. While
      * sweeping types any live data will be allocated into the pool.
      */
     LifoAlloc oldAlloc(typeLifoAlloc.defaultChunkSize());
     oldAlloc.steal(&typeLifoAlloc);
 
-    /*
-     * Sweep analysis information and everything depending on it from the
-     * compartment, including all remaining mjit code if inference is
-     * enabled in the compartment.
-     */
+    /* Sweep and find compressed indexes for each compiler output. */
+    size_t newCompilerOutputCount = 0;
+    if (compilerOutputs) {
+        for (size_t i = 0; i < compilerOutputs->length(); i++) {
+            CompilerOutput &output = (*compilerOutputs)[i];
+            if (output.isValid()) {
+                JSScript *script = output.script();
+                if (IsScriptAboutToBeFinalized(&script))
+                    output.invalidate();
+                else
+                    output.setSweepIndex(newCompilerOutputCount++);
+            }
+        }
+    }
+
     if (inferenceEnabled) {
         gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_TI);
 
         for (CellIterUnderGC i(zone(), FINALIZE_SCRIPT); !i.done(); i.next()) {
             JSScript *script = i.get<JSScript>();
             if (script->types) {
                 types::TypeScript::Sweep(fop, script);
 
                 if (releaseTypes) {
                     script->types->destroy();
                     script->types = nullptr;
+
+                    /*
+                     * Freeze constraints on stack type sets need to be
+                     * regenerated the next time the script is analyzed.
+                     */
+                    script->clearHasFreezeConstraints();
+
+                    JS_ASSERT(!script->hasIonScript());
+                    JS_ASSERT(!script->hasParallelIonScript());
+                } else {
+                    /* Update the recompile indexes in any IonScripts still on the script. */
+                    if (script->hasIonScript())
+                        script->ionScript()->recompileInfoRef().shouldSweep(*this);
+                    if (script->hasParallelIonScript())
+                        script->parallelIonScript()->recompileInfoRef().shouldSweep(*this);
                 }
             }
         }
     }
 
     {
         gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_SWEEP_TYPES);
 
@@ -4342,16 +4323,30 @@ TypeZone::sweep(FreeOp *fop, bool releas
             TypeObject *object = iter.get<TypeObject>();
             object->sweep(fop);
         }
 
         for (CompartmentsInZoneIter comp(zone()); !comp.done(); comp.next())
             comp->types.sweep(fop);
     }
 
+    if (compilerOutputs) {
+        size_t sweepIndex = 0;
+        for (size_t i = 0; i < compilerOutputs->length(); i++) {
+            CompilerOutput output = (*compilerOutputs)[i];
+            if (output.isValid()) {
+                JS_ASSERT(sweepIndex == output.sweepIndex());
+                output.setSweepIndex(0);
+                (*compilerOutputs)[sweepIndex++] = output;
+            }
+        }
+        JS_ASSERT(sweepIndex == newCompilerOutputCount);
+        JS_ALWAYS_TRUE(compilerOutputs->resize(newCompilerOutputCount));
+    }
+
     {
         gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_CLEAR_SCRIPT_ANALYSIS);
         for (CellIterUnderGC i(zone(), FINALIZE_SCRIPT); !i.done(); i.next()) {
             JSScript *script = i.get<JSScript>();
             script->clearAnalysis();
         }
     }
 
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -172,17 +172,17 @@ namespace jit {
 }
 
 namespace analyze {
     class ScriptAnalysis;
 }
 
 namespace types {
 
-class TypeCompartment;
+class TypeZone;
 class TypeSet;
 class TypeObjectKey;
 
 /*
  * Information about a single concrete type. We pack this into a single word,
  * where small values are particular primitive or other singleton types, and
  * larger values are either specific JS objects or type objects.
  */
@@ -273,38 +273,20 @@ class Type
 inline Type GetValueType(const Value &val);
 
 /*
  * Type inference memory management overview.
  *
  * Type information about the values observed within scripts and about the
  * contents of the heap is accumulated as the program executes. Compilation
  * accumulates constraints relating type information on the heap with the
- * compilations that should be invalidated when those types change. This data
- * is periodically cleared to reduce memory usage.
- *
- * GCs may clear both analysis information and jitcode. Sometimes GCs will
- * preserve all information and code, and will not collect any scripts, type
- * objects or singleton JS objects.
- *
- * The following data is cleared by all non-preserving GCs:
- *
- * - The ScriptAnalysis for each analyzed script and data from each analysis
- *   pass performed.
- *
- * - Property type sets for singleton JS objects.
- *
- * - Type constraints and dead references in all type sets.
- *
- * The following data is occasionally cleared by non-preserving GCs:
- *
- * - TypeScripts and their type sets are occasionally destroyed, per a timer.
- *
- * - When a JSScript or TypeObject is swept, type information for its contents
- *   is destroyed.
+ * compilations that should be invalidated when those types change. Type
+ * information and constraints are allocated in the zone's typeLifoAlloc,
+ * and on GC all data referring to live things is copied into a new allocator.
+ * Thus, type set and constraints only hold weak references.
  */
 
 /*
  * A constraint which listens to additions to a type set and propagates those
  * changes to other type sets.
  */
 class TypeConstraint
 {
@@ -329,16 +311,22 @@ public:
     virtual void newPropertyState(JSContext *cx, TypeSet *source) {}
 
     /*
      * For constraints attached to the JSID_EMPTY type set on an object,
      * indicate a change in one of the object's dynamic property flags or other
      * state.
      */
     virtual void newObjectState(JSContext *cx, TypeObject *object) {}
+
+    /*
+     * If the data this constraint refers to is still live, copy it into the
+     * zone's new allocator. Type constraints only hold weak references.
+     */
+    virtual TypeConstraint *sweep(TypeZone &zone) = 0;
 };
 
 /* Flags and other state stored in TypeSet::flags */
 enum {
     TYPE_FLAG_UNDEFINED =  0x1,
     TYPE_FLAG_NULL      =  0x2,
     TYPE_FLAG_BOOLEAN   =  0x4,
     TYPE_FLAG_INT32     =  0x8,
@@ -390,23 +378,16 @@ typedef uint32_t TypeFlags;
 enum {
     /* Whether this type object is associated with some allocation site. */
     OBJECT_FLAG_FROM_ALLOCATION_SITE  = 0x1,
 
     /* If set, addendum information should not be installed on this object. */
     OBJECT_FLAG_ADDENDUM_CLEARED      = 0x2,
 
     /*
-     * If set, type constraints covering the correctness of the newScript
-     * definite properties need to be regenerated before compiling any jitcode
-     * which depends on this information.
-     */
-    OBJECT_FLAG_NEW_SCRIPT_REGENERATE = 0x4,
-
-    /*
      * Whether we have ensured all type sets in the compartment contain
      * ANYOBJECT instead of this object.
      */
     OBJECT_FLAG_SETS_MARKED_UNKNOWN   = 0x8,
 
     /* Mask/shift for the number of properties in propertySet */
     OBJECT_FLAG_PROPERTY_COUNT_MASK   = 0xfff0,
     OBJECT_FLAG_PROPERTY_COUNT_SHIFT  = 4,
@@ -864,22 +845,16 @@ struct TypeTypedObject : public TypeObje
  * We can recover the type information for the object from examining it,
  * so don't normally track the possible types of its properties as it is
  * updated. Property type sets for the object are only constructed when an
  * analyzed script attaches constraints to it: the script is querying that
  * property off the object or another which delegates to it, and the analysis
  * information is sensitive to changes in the property's type. Future changes
  * to the property (whether those uncovered by analysis or those occurring
  * in the VM) will treat these properties like those of any other type object.
- *
- * When a GC occurs, we wipe out all analysis information for all the
- * compartment's scripts, so can destroy all properties on singleton type
- * objects at the same time. If there is no reference on the stack to the
- * type object itself, the type object is also destroyed, and the JS object
- * reverts to having a lazy type.
  */
 
 /* Type information about an object accessed by a script. */
 struct TypeObject : gc::BarrieredCell<TypeObject>
 {
     /* Class shared by objects using this type. */
     const Class *clasp;
 
@@ -1311,17 +1286,17 @@ class HeapTypeSetKey
     TypeObjectKey *object() const { return object_; }
     jsid id() const { return id_; }
     HeapTypeSet *maybeTypes() const { return maybeTypes_; }
 
     bool instantiate(JSContext *cx);
 
     void freeze(CompilerConstraintList *constraints);
     JSValueType knownTypeTag(CompilerConstraintList *constraints);
-    bool configured(CompilerConstraintList *constraints, TypeObjectKey *type);
+    bool configured(CompilerConstraintList *constraints);
     bool isOwnProperty(CompilerConstraintList *constraints);
     bool knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other);
     JSObject *singleton(CompilerConstraintList *constraints);
     bool needsBarrier(CompilerConstraintList *constraints);
 };
 
 /*
  * Information about the result of the compilation of a script.  This structure
@@ -1334,16 +1309,20 @@ class CompilerOutput
     // If this compilation has not been invalidated, the associated script and
     // kind of compilation being performed.
     JSScript *script_;
     ExecutionMode mode_ : 2;
 
     // Whether this compilation is about to be invalidated.
     bool pendingInvalidation_ : 1;
 
+    // During sweeping, the list of compiler outputs is compacted and invalidated
+    // outputs are removed. This gives the new index for a valid compiler output.
+    uint32_t sweepIndex_ : 29;
+
   public:
     CompilerOutput()
       : script_(nullptr), mode_(SequentialExecution), pendingInvalidation_(false)
     {}
 
     CompilerOutput(JSScript *script, ExecutionMode mode)
       : script_(script), mode_(mode), pendingInvalidation_(false)
     {}
@@ -1361,65 +1340,52 @@ class CompilerOutput
     }
 
     void setPendingInvalidation() {
         pendingInvalidation_ = true;
     }
     bool pendingInvalidation() {
         return pendingInvalidation_;
     }
+
+    void setSweepIndex(uint32_t index) {
+        if (index >= 1 << 29)
+            MOZ_CRASH();
+        sweepIndex_ = index;
+    }
+    uint32_t sweepIndex() {
+        return sweepIndex_;
+    }
 };
 
 class RecompileInfo
 {
     uint32_t outputIndex;
 
   public:
     RecompileInfo(uint32_t outputIndex = uint32_t(-1))
       : outputIndex(outputIndex)
     {}
 
     bool operator == (const RecompileInfo &o) const {
         return outputIndex == o.outputIndex;
     }
-    CompilerOutput *compilerOutput(TypeCompartment &types) const;
+    CompilerOutput *compilerOutput(TypeZone &types) const;
     CompilerOutput *compilerOutput(JSContext *cx) const;
+    bool shouldSweep(TypeZone &types);
 };
 
 /* Type information for a compartment. */
 struct TypeCompartment
 {
     /* Constraint solving worklist structures. */
 
-    /*
-     * Worklist of types which need to be propagated to constraints. We use a
-     * worklist to avoid blowing the native stack.
-     */
-    struct PendingWork
-    {
-        TypeConstraint *constraint;
-        ConstraintTypeSet *source;
-        Type type;
-    };
-    PendingWork *pendingArray;
-    unsigned pendingCount;
-    unsigned pendingCapacity;
-
-    /* Whether we are currently resolving the pending worklist. */
-    bool resolving;
-
     /* Number of scripts in this compartment. */
     unsigned scriptCount;
 
-    /* Valid & Invalid script referenced by type constraints. */
-    Vector<CompilerOutput> *constrainedOutputs;
-
-    /* Pending recompilations to perform before execution of JIT code can resume. */
-    Vector<RecompileInfo> *pendingRecompiles;
-
     /* Table for referencing types of objects keyed to an allocation site. */
     AllocationSiteTable *allocationSiteTable;
 
     /* Tables for determining types of singleton/JSON objects. */
 
     ArrayTypeTable *arrayTypeTable;
     ObjectTypeTable *objectTypeTable;
 
@@ -1433,75 +1399,67 @@ struct TypeCompartment
 
     JSObject *newTypedObject(JSContext *cx, IdValuePair *properties, size_t nproperties);
 
     TypeCompartment();
     ~TypeCompartment();
 
     inline JSCompartment *compartment();
 
-    /* Add a type to register with a list of constraints. */
-    inline void addPending(JSContext *cx, TypeConstraint *constraint,
-                           ConstraintTypeSet *source, Type type);
-    bool growPendingArray(JSContext *cx);
-
-    /* Resolve pending type registrations, excluding delayed ones. */
-    inline void resolvePending(JSContext *cx);
-
     /* Prints results of this compartment if spew is enabled or force is set. */
     void print(JSContext *cx, bool force);
 
     /*
      * Make a function or non-function object associated with an optional
      * script. The 'key' parameter here may be an array, typed array, function
      * or JSProto_Object to indicate a type whose class is unknown (not just
      * js_ObjectClass).
      */
     TypeObject *newTypeObject(ExclusiveContext *cx, const Class *clasp, Handle<TaggedProto> proto,
                               bool unknown = false);
 
     /* Get or make an object for an allocation site, and add to the allocation site table. */
     TypeObject *addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey key);
 
-    void processPendingRecompiles(FreeOp *fop);
-
     /* Mark all types as needing destruction once inference has 'finished'. */
     void setPendingNukeTypes(ExclusiveContext *cx);
 
-    /* Mark a script as needing recompilation once inference has finished. */
-    void addPendingRecompile(JSContext *cx, const RecompileInfo &info);
-    void addPendingRecompile(JSContext *cx, JSScript *script);
-
     /* Mark any type set containing obj as having a generic object type. */
     void markSetsUnknown(JSContext *cx, TypeObject *obj);
 
     void sweep(FreeOp *fop);
-    void sweepShapes(FreeOp *fop);
-    void clearCompilerOutputs(FreeOp *fop);
-
     void finalizeObjects();
 
     void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
-                                size_t *pendingArrays,
                                 size_t *allocationSiteTables,
                                 size_t *arrayTypeTables,
                                 size_t *objectTypeTables);
 };
 
 void FixRestArgumentsType(ExclusiveContext *cxArg, JSObject *obj);
 
 struct TypeZone
 {
     JS::Zone                     *zone_;
 
     /* Pool for type information in this zone. */
     static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 8 * 1024;
     js::LifoAlloc                typeLifoAlloc;
 
     /*
+     * All Ion compilations that have occured in this zone, for indexing via
+     * RecompileInfo. This includes both valid and invalid compilations, though
+     * invalidated compilations are swept on GC.
+     */
+    Vector<CompilerOutput> *compilerOutputs;
+
+    /* Pending recompilations to perform before execution of JIT code can resume. */
+    Vector<RecompileInfo> *pendingRecompiles;
+
+    /*
      * Bit set if all current types must be marked as unknown, and all scripts
      * recompiled. Caused by OOM failure within inference operations.
      */
     bool                         pendingNukeTypes;
 
     /* Whether type inference is enabled in this compartment. */
     bool                         inferenceEnabled;
 
@@ -1511,16 +1469,22 @@ struct TypeZone
 
     JS::Zone *zone() const { return zone_; }
 
     void sweep(FreeOp *fop, bool releaseTypes);
 
     /* Mark all types as needing destruction once inference has 'finished'. */
     void setPendingNukeTypes();
 
+    /* Mark a script as needing recompilation once inference has finished. */
+    void addPendingRecompile(JSContext *cx, const RecompileInfo &info);
+    void addPendingRecompile(JSContext *cx, JSScript *script);
+
+    void processPendingRecompiles(FreeOp *fop);
+
     void nukeTypes(FreeOp *fop);
 };
 
 enum SpewChannel {
     ISpewOps,      /* ops: New constraints and types. */
     ISpewResult,   /* result: Final type sets. */
     SPEW_COUNT
 };
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -99,27 +99,39 @@ CompilerOutput::ion() const
     jit::IonScript *ion = jit::GetIonScript(script(), mode());
     JS_ASSERT(ion != ION_COMPILING_SCRIPT);
     return ion;
 #endif
     MOZ_ASSUME_UNREACHABLE("Invalid kind of CompilerOutput");
 }
 
 inline CompilerOutput*
-RecompileInfo::compilerOutput(TypeCompartment &types) const
+RecompileInfo::compilerOutput(TypeZone &types) const
 {
-    if (!types.constrainedOutputs || outputIndex >= types.constrainedOutputs->length())
+    if (!types.compilerOutputs || outputIndex >= types.compilerOutputs->length())
         return nullptr;
-    return &(*types.constrainedOutputs)[outputIndex];
+    return &(*types.compilerOutputs)[outputIndex];
 }
 
 inline CompilerOutput*
 RecompileInfo::compilerOutput(JSContext *cx) const
 {
-    return compilerOutput(cx->compartment()->types);
+    return compilerOutput(cx->zone()->types);
+}
+
+inline bool
+RecompileInfo::shouldSweep(TypeZone &types)
+{
+    CompilerOutput *output = compilerOutput(types);
+    if (!output || !output->isValid())
+        return true;
+
+    // Update this info for the output's new index in the zone's compiler outputs.
+    outputIndex = output->sweepIndex();
+    return false;
 }
 
 /////////////////////////////////////////////////////////////////////
 // Types
 /////////////////////////////////////////////////////////////////////
 
 /* static */ inline Type
 Type::ObjectType(JSObject *obj)
@@ -283,24 +295,23 @@ struct AutoEnterAnalysis
     ~AutoEnterAnalysis()
     {
         compartment->activeAnalysis = oldActiveAnalysis;
 
         /*
          * If there are no more type inference activations on the stack,
          * process any triggered recompilations. Note that we should not be
          * invoking any scripted code while type inference is running.
-         * :TODO: assert this.
          */
         if (!compartment->activeAnalysis) {
-            TypeCompartment *types = &compartment->types;
-            if (compartment->zone()->types.pendingNukeTypes)
-                compartment->zone()->types.nukeTypes(freeOp);
-            else if (types->pendingRecompiles)
-                types->processPendingRecompiles(freeOp);
+            TypeZone &types = compartment->zone()->types;
+            if (types.pendingNukeTypes)
+                types.nukeTypes(freeOp);
+            else if (types.pendingRecompiles)
+                types.processPendingRecompiles(freeOp);
         }
     }
 
   private:
     void init(FreeOp *fop, JSCompartment *comp) {
         freeOp = fop;
         compartment = comp;
         oldActiveAnalysis = compartment->activeAnalysis;
@@ -847,60 +858,16 @@ TypeScript::SetArgument(JSContext *cx, J
 /////////////////////////////////////////////////////////////////////
 
 inline JSCompartment *
 TypeCompartment::compartment()
 {
     return (JSCompartment *)((char *)this - offsetof(JSCompartment, types));
 }
 
-inline void
-TypeCompartment::addPending(JSContext *cx, TypeConstraint *constraint,
-                            ConstraintTypeSet *source, Type type)
-{
-    JS_ASSERT(this == &cx->compartment()->types);
-    JS_ASSERT(!cx->runtime()->isHeapBusy());
-
-    InferSpew(ISpewOps, "pending: %sC%p%s %s",
-              InferSpewColor(constraint), constraint, InferSpewColorReset(),
-              TypeString(type));
-
-    if ((pendingCount == pendingCapacity) && !growPendingArray(cx))
-        return;
-
-    PendingWork &pending = pendingArray[pendingCount++];
-    pending.constraint = constraint;
-    pending.source = source;
-    pending.type = type;
-}
-
-inline void
-TypeCompartment::resolvePending(JSContext *cx)
-{
-    JS_ASSERT(this == &cx->compartment()->types);
-
-    if (resolving) {
-        /* There is an active call further up resolving the worklist. */
-        return;
-    }
-
-    resolving = true;
-
-    /* Handle all pending type registrations. */
-    while (pendingCount) {
-        const PendingWork &pending = pendingArray[--pendingCount];
-        InferSpew(ISpewOps, "resolve: %sC%p%s %s",
-                  InferSpewColor(pending.constraint), pending.constraint,
-                  InferSpewColorReset(), TypeString(pending.type));
-        pending.constraint->newType(cx, pending.source, pending.type);
-    }
-
-    resolving = false;
-}
-
 /////////////////////////////////////////////////////////////////////
 // TypeSet
 /////////////////////////////////////////////////////////////////////
 
 /*
  * The sets of objects and scripts in a type set grow monotonically, are usually
  * empty, almost always small, and sometimes big.  For empty or singleton sets,
  * the pointer refers directly to the value.  For sets fitting into SET_ARRAY_SIZE,
@@ -1214,20 +1181,19 @@ ConstraintTypeSet::addType(ExclusiveCont
     InferSpew(ISpewOps, "addType: %sT%p%s %s",
               InferSpewColor(this), this, InferSpewColorReset(),
               TypeString(type));
 
     /* Propagate the type to all constraints. */
     if (JSContext *cx = cxArg->maybeJSContext()) {
         TypeConstraint *constraint = constraintList;
         while (constraint) {
-            cx->compartment()->types.addPending(cx, constraint, this, type);
+            constraint->newType(cx, this, type);
             constraint = constraint->next;
         }
-        cx->compartment()->types.resolvePending(cx);
     } else {
         JS_ASSERT(!constraintList);
     }
 }
 
 inline void
 HeapTypeSet::setConfiguredProperty(ExclusiveContext *cxArg)
 {
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -210,17 +210,16 @@ StatsCompartmentCallback(JSRuntime *rt, 
     MOZ_ALWAYS_TRUE(rtStats->compartmentStatsVector.growBy(1));
     CompartmentStats &cStats = rtStats->compartmentStatsVector.back();
     rtStats->initExtraCompartmentStats(compartment, &cStats);
 
     compartment->compartmentStats = &cStats;
 
     // Measure the compartment object itself, and things hanging off it.
     compartment->addSizeOfIncludingThis(rtStats->mallocSizeOf_,
-                                        &cStats.typeInferencePendingArrays,
                                         &cStats.typeInferenceAllocationSiteTables,
                                         &cStats.typeInferenceArrayTypeTables,
                                         &cStats.typeInferenceObjectTypeTables,
                                         &cStats.compartmentObject,
                                         &cStats.shapesMallocHeapCompartmentTables,
                                         &cStats.crossCompartmentWrappersTable,
                                         &cStats.regexpCompartment,
                                         &cStats.debuggeesSet,
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -2117,20 +2117,16 @@ ReportCompartmentStats(const JS::Compart
                    cStats.ionData,
                    "Memory used by the IonMonkey JIT for compilation data: "
                    "IonScripts.");
 
     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/type-scripts"),
                    cStats.typeInferenceTypeScripts,
                    "Memory used by type sets associated with scripts.");
 
-    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/pending-arrays"),
-                   cStats.typeInferencePendingArrays,
-                   "Memory used for solving constraints during type inference.");
-
     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/allocation-site-tables"),
                    cStats.typeInferenceAllocationSiteTables,
                    "Memory indexing type objects associated with allocation sites.");
 
     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/array-type-tables"),
                    cStats.typeInferenceArrayTypeTables,
                    "Memory indexing type objects associated with array literals.");