Bug 941311 - Improve GGC pretenuring heuristics, r=terrence,jandem.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 21 Nov 2013 19:20:25 -0800
changeset 157347 5f093277a58681539a921d4391532c54c106944e
parent 157346 bed1dc9fbec44158e4c7ca809adf4d0cc495318c
child 157348 8b0cd6690de463c082543ad428e20f94385a4a5d
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersterrence, jandem
bugs941311
milestone28.0a1
Bug 941311 - Improve GGC pretenuring heuristics, r=terrence,jandem.
js/src/builtin/TestingFunctions.cpp
js/src/gc/Nursery.cpp
js/src/gc/Nursery.h
js/src/jit/CodeGenerator.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonMacroAssembler.cpp
js/src/jit/IonMacroAssembler.h
js/src/jit/MCallOptimize.cpp
js/src/jit/MIR.h
js/src/jit/VMFunctions.cpp
js/src/jscntxt.cpp
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsgcinlines.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/vm/Runtime-inl.h
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -246,17 +246,17 @@ static bool
 MinorGC(JSContext *cx, unsigned argc, jsval *vp)
 {
 #ifdef JSGC_GENERATIONAL
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.get(0) == BooleanValue(true))
         cx->runtime()->gcStoreBuffer.setAboutToOverflow();
 
-    MinorGC(cx->runtime(), gcreason::API);
+    MinorGC(cx, gcreason::API);
 #endif
     return true;
 }
 
 static const struct ParamPair {
     const char      *name;
     JSGCParamKey    param;
 } paramMap[] = {
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -350,47 +350,55 @@ js::Nursery::forwardBufferPointer(HeapSl
      * isInside will still be true, even if this zero-size allocation abuts the
      * end of the allocable area. Thus, it is always safe to read the first
      * word of |old| here.
      */
     *pSlotsElems = *reinterpret_cast<HeapSlot **>(old);
     JS_ASSERT(!isInside(*pSlotsElems));
 }
 
-static void
-MaybeInvalidateScriptUsedWithNew(JSRuntime *rt, types::TypeObject *type)
+namespace {
+
+// Structure for counting how many times objects of a particular type have been
+// tenured during a minor collection.
+struct TenureCount
 {
-    types::TypeNewScript *newScript = type->newScript();
-    if (!newScript)
-        return;
+    types::TypeObject *type;
+    int count;
+};
+
+} // anonymous namespace
 
-    JSScript *script = newScript->fun->nonLazyScript();
-    if (script && script->hasIonScript()) {
-        for (ContextIter cx(rt); !cx.done(); cx.next())
-            jit::Invalidate(cx, script);
+// Keep rough track of how many times we tenure objects of particular types
+// during minor collections, using a fixed size hash for efficiency at the cost
+// of potential collisions.
+struct Nursery::TenureCountCache
+{
+    TenureCount entries[16];
+
+    TenureCountCache() { PodZero(this); }
+
+    TenureCount &findEntry(types::TypeObject *type) {
+        return entries[PointerHasher<types::TypeObject *, 3>::hash(type) % ArrayLength(entries)];
     }
-}
+};
 
 void
-js::Nursery::collectToFixedPoint(MinorCollectionTracer *trc)
+js::Nursery::collectToFixedPoint(MinorCollectionTracer *trc, TenureCountCache &tenureCounts)
 {
     for (RelocationOverlay *p = trc->head; p; p = p->next()) {
         JSObject *obj = static_cast<JSObject*>(p->forwardingAddress());
         traceObject(trc, obj);
 
-        /*
-         * Increment tenure count and recompile the script for pre-tenuring if
-         * long-lived. Attempt to distinguish between tenuring because the
-         * object is long lived and tenuring while the nursery is still
-         * smaller than the working set size.
-         */
-        if (isFullyGrown() && !obj->hasLazyType() && obj->type()->hasNewScript() &&
-            obj->type()->incrementTenureCount())
-        {
-            MaybeInvalidateScriptUsedWithNew(trc->runtime, obj->type());
+        TenureCount &entry = tenureCounts.findEntry(obj->type());
+        if (entry.type == obj->type()) {
+            entry.count++;
+        } else if (!entry.type) {
+            entry.type = obj->type();
+            entry.count = 1;
         }
     }
 }
 
 JS_ALWAYS_INLINE void
 js::Nursery::traceObject(MinorCollectionTracer *trc, JSObject *obj)
 {
     const Class *clasp = obj->getClass();
@@ -575,17 +583,17 @@ ShouldMoveToTenured(MinorCollectionTrace
 js::Nursery::MinorGCCallback(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
 {
     MinorCollectionTracer *trc = static_cast<MinorCollectionTracer *>(jstrc);
     if (ShouldMoveToTenured(trc, thingp))
         *thingp = trc->nursery->moveToTenured(trc, static_cast<JSObject *>(*thingp));
 }
 
 void
-js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason)
+js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason, TypeObjectList *pretenureTypes)
 {
     JS_AbortIfWrongThread(rt);
 
     if (rt->mainThread.suppressGC)
         return;
 
     if (!isEnabled())
         return;
@@ -609,25 +617,38 @@ js::Nursery::collect(JSRuntime *rt, JS::
     rt->newObjectCache.clearNurseryObjects(rt);
 
     /*
      * Most of the work is done here. This loop iterates over objects that have
      * been moved to the major heap. If these objects have any outgoing pointers
      * to the nursery, then those nursery objects get moved as well, until no
      * objects are left to move. That is, we iterate to a fixed point.
      */
-    collectToFixedPoint(&trc);
+    TenureCountCache tenureCounts;
+    collectToFixedPoint(&trc, tenureCounts);
 
     /* Resize the nursery. */
     double promotionRate = trc.tenuredSize / double(allocationEnd() - start());
     if (promotionRate > 0.05)
         growAllocableSpace();
     else if (promotionRate < 0.01)
         shrinkAllocableSpace();
 
+    // If we are promoting the nursery, or exhausted the store buffer with
+    // pointers to nursery things, which will force a collection well before
+    // the nursery is full, look for object types that are getting promoted
+    // excessively and try to pretenure them.
+    if (pretenureTypes && (promotionRate > 0.8 || reason == JS::gcreason::FULL_STORE_BUFFER)) {
+        for (size_t i = 0; i < ArrayLength(tenureCounts.entries); i++) {
+            const TenureCount &entry = tenureCounts.entries[i];
+            if (entry.count >= 3000)
+                pretenureTypes->append(entry.type); // ignore alloc failure
+        }
+    }
+
     /* Sweep. */
     sweep(rt);
     rt->gcStoreBuffer.clear();
 
     /*
      * We ignore gcMaxBytes when allocating for minor collection. However, if we
      * overflowed, we disable the nursery. The next time we allocate, we'll fail
      * because gcBytes >= gcMaxBytes.
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -14,31 +14,36 @@
 #include "jspubtd.h"
 
 #include "ds/BitArray.h"
 #include "gc/Heap.h"
 #include "js/GCAPI.h"
 #include "js/HashTable.h"
 #include "js/HeapAPI.h"
 #include "js/Value.h"
+#include "js/Vector.h"
 
 namespace JS {
 struct Zone;
 }
 
 namespace js {
 
 class ObjectElements;
 class HeapSlot;
 
 namespace gc {
 class Cell;
 class MinorCollectionTracer;
 } /* namespace gc */
 
+namespace types {
+struct TypeObject;
+}
+
 namespace jit {
 class CodeGenerator;
 class MacroAssembler;
 class ICStubCompiler;
 class BaselineCompiler;
 }
 
 class Nursery
@@ -90,18 +95,23 @@ class Nursery
                                        uint32_t oldCount, uint32_t newCount);
 
     /* Free a slots array. */
     void freeSlots(JSContext *cx, HeapSlot *slots);
 
     /* Add a slots to our tracking list if it is out-of-line. */
     void notifyInitialSlots(gc::Cell *cell, HeapSlot *slots);
 
-    /* Do a minor collection. */
-    void collect(JSRuntime *rt, JS::gcreason::Reason reason);
+    typedef Vector<types::TypeObject *, 0, SystemAllocPolicy> TypeObjectList;
+
+    /*
+     * Do a minor collection, optionally specifying a list to store types which
+     * should be pretenured afterwards.
+     */
+    void collect(JSRuntime *rt, JS::gcreason::Reason reason, TypeObjectList *pretenureTypes);
 
     /*
      * Check if the thing at |*ref| in the Nursery has been forwarded. If so,
      * sets |*ref| to the new location of the object and returns true. Otherwise
      * returns false and leaves |*ref| unset.
      */
     template <typename T>
     JS_ALWAYS_INLINE bool getForwardedPointer(T **ref);
@@ -214,21 +224,23 @@ class Nursery
     JSRuntime *runtime() const { return runtime_; }
 
     /* Allocates and registers external slots with the nursery. */
     HeapSlot *allocateHugeSlots(JSContext *cx, size_t nslots);
 
     /* Allocates a new GC thing from the tenured generation during minor GC. */
     void *allocateFromTenured(JS::Zone *zone, gc::AllocKind thingKind);
 
+    struct TenureCountCache;
+
     /*
      * Move the object at |src| in the Nursery to an already-allocated cell
      * |dst| in Tenured.
      */
-    void collectToFixedPoint(gc::MinorCollectionTracer *trc);
+    void collectToFixedPoint(gc::MinorCollectionTracer *trc, TenureCountCache &tenureCounts);
     JS_ALWAYS_INLINE void traceObject(gc::MinorCollectionTracer *trc, JSObject *src);
     JS_ALWAYS_INLINE void markSlots(gc::MinorCollectionTracer *trc, HeapSlot *vp, uint32_t nslots);
     JS_ALWAYS_INLINE void markSlots(gc::MinorCollectionTracer *trc, HeapSlot *vp, HeapSlot *end);
     JS_ALWAYS_INLINE void markSlot(gc::MinorCollectionTracer *trc, HeapSlot *slotp);
     void *moveToTenured(gc::MinorCollectionTracer *trc, JSObject *src);
     size_t moveObjectToTenured(JSObject *dst, JSObject *src, gc::AllocKind dstKind);
     size_t moveElementsToTenured(JSObject *dst, JSObject *src, gc::AllocKind dstKind);
     size_t moveSlotsToTenured(JSObject *dst, JSObject *src, gc::AllocKind dstKind);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -795,17 +795,17 @@ CodeGenerator::visitLambda(LLambda *lir)
 
     OutOfLineCode *ool = oolCallVM(LambdaInfo, lir, (ArgList(), ImmGCPtr(info.fun), scopeChain),
                                    StoreRegisterTo(output));
     if (!ool)
         return false;
 
     JS_ASSERT(!info.singletonType);
 
-    masm.newGCThing(output, info.fun, ool->entry());
+    masm.newGCThing(output, info.fun, ool->entry(), gc::DefaultHeap);
     masm.initGCThing(output, info.fun);
 
     emitLambdaInit(output, scopeChain, info);
 
     masm.bind(ool->rejoin());
     return true;
 }
 
@@ -3074,17 +3074,17 @@ CodeGenerator::visitNewParallelArray(LNe
 {
     Register objReg = ToRegister(lir->output());
     JSObject *templateObject = lir->mir()->templateObject();
 
     OutOfLineNewParallelArray *ool = new OutOfLineNewParallelArray(lir);
     if (!addOutOfLineCode(ool))
         return false;
 
-    masm.newGCThing(objReg, templateObject, ool->entry());
+    masm.newGCThing(objReg, templateObject, ool->entry(), gc::DefaultHeap);
     masm.initGCThing(objReg, templateObject);
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
 CodeGenerator::visitOutOfLineNewParallelArray(OutOfLineNewParallelArray *ool)
@@ -3107,17 +3107,17 @@ CodeGenerator::visitNewArray(LNewArray *
 
     if (lir->mir()->shouldUseVM())
         return visitNewArrayCallVM(lir);
 
     OutOfLineNewArray *ool = new OutOfLineNewArray(lir);
     if (!addOutOfLineCode(ool))
         return false;
 
-    masm.newGCThing(objReg, templateObject, ool->entry());
+    masm.newGCThing(objReg, templateObject, ool->entry(), lir->mir()->initialHeap());
     masm.initGCThing(objReg, templateObject);
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
 CodeGenerator::visitOutOfLineNewArray(OutOfLineNewArray *ool)
@@ -3193,17 +3193,17 @@ CodeGenerator::visitNewObject(LNewObject
 
     if (lir->mir()->shouldUseVM())
         return visitNewObjectVMCall(lir);
 
     OutOfLineNewObject *ool = new OutOfLineNewObject(lir);
     if (!addOutOfLineCode(ool))
         return false;
 
-    masm.newGCThing(objReg, templateObject, ool->entry());
+    masm.newGCThing(objReg, templateObject, ool->entry(), lir->mir()->initialHeap());
     masm.initGCThing(objReg, templateObject);
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
 CodeGenerator::visitOutOfLineNewObject(OutOfLineNewObject *ool)
@@ -3227,17 +3227,17 @@ CodeGenerator::visitNewDeclEnvObject(LNe
 
     // If we have a template object, we can inline call object creation.
     OutOfLineCode *ool = oolCallVM(NewDeclEnvObjectInfo, lir,
                                    (ArgList(), ImmGCPtr(info.fun()), Imm32(gc::DefaultHeap)),
                                    StoreRegisterTo(obj));
     if (!ool)
         return false;
 
-    masm.newGCThing(obj, templateObj, ool->entry());
+    masm.newGCThing(obj, templateObj, ool->entry(), gc::DefaultHeap);
     masm.initGCThing(obj, templateObj);
     masm.bind(ool->rejoin());
     return true;
 }
 
 typedef JSObject *(*NewCallObjectFn)(JSContext *, HandleScript, HandleShape,
                                      HandleTypeObject, HeapSlot *);
 static const VMFunction NewCallObjectInfo =
@@ -3269,17 +3269,17 @@ CodeGenerator::visitNewCallObject(LNewCa
     }
     if (!ool)
         return false;
 
     if (lir->mir()->needsSingletonType()) {
         // Objects can only be given singleton types in VM calls.
         masm.jump(ool->entry());
     } else {
-        masm.newGCThing(obj, templateObj, ool->entry());
+        masm.newGCThing(obj, templateObj, ool->entry(), gc::DefaultHeap);
         masm.initGCThing(obj, templateObj);
 
         if (lir->slots()->isRegister())
             masm.storePtr(ToRegister(lir->slots()), Address(obj, JSObject::offsetOfSlots()));
     }
 
     masm.bind(ool->rejoin());
     return true;
@@ -3359,17 +3359,17 @@ CodeGenerator::visitNewStringObject(LNew
 
     StringObject *templateObj = lir->mir()->templateObj();
 
     OutOfLineCode *ool = oolCallVM(NewStringObjectInfo, lir, (ArgList(), input),
                                    StoreRegisterTo(output));
     if (!ool)
         return false;
 
-    masm.newGCThing(output, templateObj, ool->entry());
+    masm.newGCThing(output, templateObj, ool->entry(), gc::DefaultHeap);
     masm.initGCThing(output, templateObj);
 
     masm.loadStringLength(input, temp);
 
     masm.storeValue(JSVAL_TYPE_STRING, input, Address(output, StringObject::offsetOfPrimitiveValue()));
     masm.storeValue(JSVAL_TYPE_INT32, temp, Address(output, StringObject::offsetOfLength()));
 
     masm.bind(ool->rejoin());
@@ -3581,27 +3581,27 @@ static const VMFunction NewGCThingInfo =
     FunctionInfo<NewGCThingFn>(js::jit::NewGCThing);
 
 bool
 CodeGenerator::visitCreateThisWithTemplate(LCreateThisWithTemplate *lir)
 {
     JSObject *templateObject = lir->mir()->templateObject();
     gc::AllocKind allocKind = templateObject->tenuredGetAllocKind();
     int thingSize = (int)gc::Arena::thingSize(allocKind);
-    gc::InitialHeap initialHeap = templateObject->type()->initialHeapForJITAlloc();
+    gc::InitialHeap initialHeap = lir->mir()->initialHeap();
     Register objReg = ToRegister(lir->output());
 
     OutOfLineCode *ool = oolCallVM(NewGCThingInfo, lir,
                                    (ArgList(), Imm32(allocKind), Imm32(thingSize), Imm32(initialHeap)),
                                    StoreRegisterTo(objReg));
     if (!ool)
         return false;
 
     // Allocate. If the FreeList is empty, call to VM, which may GC.
-    masm.newGCThing(objReg, templateObject, ool->entry());
+    masm.newGCThing(objReg, templateObject, ool->entry(), lir->mir()->initialHeap());
 
     // Initialize based on the templateObject.
     masm.bind(ool->rejoin());
     masm.initGCThing(objReg, templateObject);
 
     return true;
 }
 
@@ -5290,17 +5290,17 @@ CodeGenerator::visitArrayConcat(LArrayCo
     masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
 
     masm.loadPtr(Address(rhs, JSObject::offsetOfElements()), temp1);
     masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
     masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
 
     // Try to allocate an object.
     JSObject *templateObj = lir->mir()->templateObj();
-    masm.newGCThing(temp1, templateObj, &fail);
+    masm.newGCThing(temp1, templateObj, &fail, lir->mir()->initialHeap());
     masm.initGCThing(temp1, templateObj);
     masm.jump(&call);
     {
         masm.bind(&fail);
         masm.movePtr(ImmPtr(nullptr), temp1);
     }
     masm.bind(&call);
 
@@ -5662,17 +5662,17 @@ CodeGenerator::visitRest(LRest *lir)
     Register numActuals = ToRegister(lir->numActuals());
     Register temp0 = ToRegister(lir->getTemp(0));
     Register temp1 = ToRegister(lir->getTemp(1));
     Register temp2 = ToRegister(lir->getTemp(2));
     unsigned numFormals = lir->mir()->numFormals();
     JSObject *templateObject = lir->mir()->templateObject();
 
     Label joinAlloc, failAlloc;
-    masm.newGCThing(temp2, templateObject, &failAlloc);
+    masm.newGCThing(temp2, templateObject, &failAlloc, gc::DefaultHeap);
     masm.initGCThing(temp2, templateObject);
     masm.jump(&joinAlloc);
     {
         masm.bind(&failAlloc);
         masm.movePtr(ImmPtr(nullptr), temp2);
     }
     masm.bind(&joinAlloc);
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -4635,24 +4635,19 @@ IonBuilder::createCallObject(MDefinition
         slots = MConstant::New(alloc(), NullValue());
     }
     current->add(slots);
 
     // Allocate the actual object. It is important that no intervening
     // instructions could potentially bailout, thus leaking the dynamic slots
     // pointer. Run-once scripts need a singleton type, so always do a VM call
     // in such cases.
-    MInstruction *callObj = MNewCallObject::New(alloc(), templateObj, script()->treatAsRunOnce, slots);
+    MNewCallObject *callObj = MNewCallObject::New(alloc(), templateObj, script()->treatAsRunOnce, slots);
     current->add(callObj);
 
-    // Insert a post barrier to protect the following writes if we allocated
-    // the new call object directly into tenured storage.
-    if (templateObj->type()->isLongLivedForJITAlloc())
-        current->add(MPostWriteBarrier::New(alloc(), callObj));
-
     // Initialize the object's reserved slots. No post barrier is needed here,
     // for the same reason as in createDeclEnvObject.
     current->add(MStoreFixedSlot::New(alloc(), callObj, CallObject::enclosingScopeSlot(), scope));
     current->add(MStoreFixedSlot::New(alloc(), callObj, CallObject::calleeSlot(), callee));
 
     // Initialize argument slots.
     for (AliasedFormalIter i(script()); i; i++) {
         unsigned slot = i.scopeSlot();
@@ -4745,17 +4740,19 @@ IonBuilder::createThisScriptedSingleton(
         JS_ASSERT(templateObject->type() == templateType);
 
         // Trigger recompilation if the templateObject changes.
         types::TypeObjectKey::get(templateType)->watchStateChangeForNewScriptTemplate(constraints());
     }
 
     // Generate an inline path to create a new |this| object with
     // the given singleton prototype.
-    MCreateThisWithTemplate *createThis = MCreateThisWithTemplate::New(alloc(), templateObject);
+    MCreateThisWithTemplate *createThis =
+        MCreateThisWithTemplate::New(alloc(), templateObject,
+                                     templateObject->type()->initialHeap(constraints()));
     current->add(createThis);
 
     return createThis;
 }
 
 MDefinition *
 IonBuilder::createThis(JSFunction *target, MDefinition *callee)
 {
@@ -5440,17 +5437,19 @@ IonBuilder::jsop_newarray(uint32_t count
         return abort("New array has unknown properties");
     }
 
     types::TemporaryTypeSet::DoubleConversion conversion =
         bytecodeTypes(pc)->convertDoubleElements(constraints());
     if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles)
         templateObject->setShouldConvertDoubleElements();
 
-    MNewArray *ins = MNewArray::New(alloc(), count, templateObject, MNewArray::NewArray_Allocating);
+    MNewArray *ins = MNewArray::New(alloc(), count, templateObject,
+                                    templateObject->type()->initialHeap(constraints()),
+                                    MNewArray::NewArray_Allocating);
 
     current->add(ins);
     current->push(ins);
 
     return true;
 }
 
 bool
@@ -5460,16 +5459,19 @@ IonBuilder::jsop_newobject()
     JS_ASSERT(script()->compileAndGo);
 
     JSObject *templateObject = inspector->getTemplateObject(pc);
     if (!templateObject)
         return abort("No template object for NEWOBJECT");
 
     JS_ASSERT(templateObject->is<JSObject>());
     MNewObject *ins = MNewObject::New(alloc(), templateObject,
+                                      templateObject->hasSingletonType()
+                                      ? gc::TenuredHeap
+                                      : templateObject->type()->initialHeap(constraints()),
                                       /* templateObjectIsClassPrototype = */ false);
 
     current->add(ins);
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
@@ -7770,17 +7772,19 @@ IonBuilder::jsop_rest()
         return true;
     }
 
     // We know the exact number of arguments the callee pushed.
     unsigned numActuals = inlineCallInfo_->argv().length();
     unsigned numFormals = info().nargs() - 1;
     unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0;
 
-    MNewArray *array = MNewArray::New(alloc(), numRest, templateObject, MNewArray::NewArray_Allocating);
+    MNewArray *array = MNewArray::New(alloc(), numRest, templateObject,
+                                      templateObject->type()->initialHeap(constraints()),
+                                      MNewArray::NewArray_Allocating);
     current->add(array);
 
     if (numActuals <= numFormals) {
         current->push(array);
         return true;
     }
 
     MElements *elements = MElements::New(alloc(), array);
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -680,22 +680,22 @@ MacroAssembler::newGCThing(const Registe
     branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(zone->addressOfFreeListLast(allocKind)), result, fail);
 
     addPtr(Imm32(thingSize), result);
     storePtr(result, AbsoluteAddress(zone->addressOfFreeListFirst(allocKind)));
     subPtr(Imm32(thingSize), result);
 }
 
 void
-MacroAssembler::newGCThing(const Register &result, JSObject *templateObject, Label *fail)
+MacroAssembler::newGCThing(const Register &result, JSObject *templateObject,
+                           Label *fail, gc::InitialHeap initialHeap)
 {
     gc::AllocKind allocKind = templateObject->tenuredGetAllocKind();
     JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
 
-    gc::InitialHeap initialHeap = templateObject->type()->initialHeapForJITAlloc();
     newGCThing(result, allocKind, fail, initialHeap);
 }
 
 void
 MacroAssembler::newGCString(const Register &result, Label *fail)
 {
     newGCThing(result, js::gc::FINALIZE_STRING, fail);
 }
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -775,17 +775,18 @@ class MacroAssembler : public MacroAssem
     // Emit type case branch on tag matching if the type tag in the definition
     // might actually be that type.
     void branchEqualTypeIfNeeded(MIRType type, MDefinition *maybeDef, const Register &tag,
                                  Label *label);
 
     // Inline allocation.
     void newGCThing(const Register &result, gc::AllocKind allocKind, Label *fail,
                     gc::InitialHeap initialHeap = gc::DefaultHeap);
-    void newGCThing(const Register &result, JSObject *templateObject, Label *fail);
+    void newGCThing(const Register &result, JSObject *templateObject, Label *fail,
+                    gc::InitialHeap initialHeap);
     void newGCString(const Register &result, Label *fail);
     void newGCShortString(const Register &result, Label *fail);
 
     void newGCThingPar(const Register &result, const Register &slice,
                        const Register &tempReg1, const Register &tempReg2,
                        gc::AllocKind allocKind, Label *fail);
     void newGCThingPar(const Register &result, const Register &slice,
                        const Register &tempReg1, const Register &tempReg2,
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -246,17 +246,19 @@ IonBuilder::inlineArray(CallInfo &callIn
 
     callInfo.unwrapArgs();
 
     types::TemporaryTypeSet::DoubleConversion conversion =
         getInlineReturnTypeSet()->convertDoubleElements(constraints());
     if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles)
         templateObject->setShouldConvertDoubleElements();
 
-    MNewArray *ins = MNewArray::New(alloc(), initLength, templateObject, allocating);
+    MNewArray *ins = MNewArray::New(alloc(), initLength, templateObject,
+                                    templateObject->type()->initialHeap(constraints()),
+                                    allocating);
     current->add(ins);
     current->push(ins);
 
     if (callInfo.argc() >= 2) {
         // Get the elements vector.
         MElements *elements = MElements::New(alloc(), ins);
         current->add(elements);
 
@@ -493,17 +495,18 @@ IonBuilder::inlineArrayConcat(CallInfo &
     // Inline the call.
     JSObject *templateObj = inspector->getTemplateObjectForNative(pc, js::array_concat);
     if (!templateObj || templateObj->type() != baseThisType)
         return InliningStatus_NotInlined;
     JS_ASSERT(templateObj->is<ArrayObject>());
 
     callInfo.unwrapArgs();
 
-    MArrayConcat *ins = MArrayConcat::New(alloc(), callInfo.thisArg(), callInfo.getArg(0), templateObj);
+    MArrayConcat *ins = MArrayConcat::New(alloc(), callInfo.thisArg(), callInfo.getArg(0), templateObj,
+                                          templateObj->type()->initialHeap(constraints()));
     current->add(ins);
     current->push(ins);
 
     if (!resumeAfter(ins))
         return InliningStatus_Error;
     return InliningStatus_Inlined;
 }
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -1403,45 +1403,52 @@ class MNewArray : public MNullaryInstruc
         NewArray_Unallocating
     };
 
   private:
     // Number of space to allocate for the array.
     uint32_t count_;
     // Template for the created object.
     CompilerRootObject templateObject_;
+    gc::InitialHeap initialHeap_;
     // Allocate space at initialization or not
     AllocatingBehaviour allocating_;
 
-    MNewArray(uint32_t count, JSObject *templateObject, AllocatingBehaviour allocating)
+    MNewArray(uint32_t count, JSObject *templateObject, gc::InitialHeap initialHeap,
+              AllocatingBehaviour allocating)
       : count_(count),
         templateObject_(templateObject),
+        initialHeap_(initialHeap),
         allocating_(allocating)
     {
         setResultType(MIRType_Object);
         setResultTypeSet(MakeSingletonTypeSet(templateObject));
     }
 
   public:
     INSTRUCTION_HEADER(NewArray)
 
     static MNewArray *New(TempAllocator &alloc, uint32_t count, JSObject *templateObject,
-                          AllocatingBehaviour allocating)
-    {
-        return new(alloc) MNewArray(count, templateObject, allocating);
+                          gc::InitialHeap initialHeap, AllocatingBehaviour allocating)
+    {
+        return new(alloc) MNewArray(count, templateObject, initialHeap, allocating);
     }
 
     uint32_t count() const {
         return count_;
     }
 
     JSObject *templateObject() const {
         return templateObject_;
     }
 
+    gc::InitialHeap initialHeap() const {
+        return initialHeap_;
+    }
+
     bool isAllocating() const {
         return allocating_ == NewArray_Allocating;
     }
 
     // Returns true if the code generator should call through to the
     // VM rather than the fast path.
     bool shouldUseVM() const;
 
@@ -1454,47 +1461,54 @@ class MNewArray : public MNullaryInstruc
     virtual AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
 class MNewObject : public MNullaryInstruction
 {
     CompilerRootObject templateObject_;
+    gc::InitialHeap initialHeap_;
     bool templateObjectIsClassPrototype_;
 
-    MNewObject(JSObject *templateObject, bool templateObjectIsClassPrototype)
+    MNewObject(JSObject *templateObject, gc::InitialHeap initialHeap,
+               bool templateObjectIsClassPrototype)
       : templateObject_(templateObject),
+        initialHeap_(initialHeap),
         templateObjectIsClassPrototype_(templateObjectIsClassPrototype)
     {
         JS_ASSERT_IF(templateObjectIsClassPrototype, !shouldUseVM());
         setResultType(MIRType_Object);
         setResultTypeSet(MakeSingletonTypeSet(templateObject));
     }
 
   public:
     INSTRUCTION_HEADER(NewObject)
 
-    static MNewObject *New(TempAllocator &alloc, JSObject *templateObject,
+    static MNewObject *New(TempAllocator &alloc, JSObject *templateObject, gc::InitialHeap initialHeap,
                            bool templateObjectIsClassPrototype)
     {
-        return new(alloc) MNewObject(templateObject, templateObjectIsClassPrototype);
+        return new(alloc) MNewObject(templateObject, initialHeap, templateObjectIsClassPrototype);
     }
 
     // Returns true if the code generator should call through to the
     // VM rather than the fast path.
     bool shouldUseVM() const;
 
     bool templateObjectIsClassPrototype() const {
         return templateObjectIsClassPrototype_;
     }
 
     JSObject *templateObject() const {
         return templateObject_;
     }
+
+    gc::InitialHeap initialHeap() const {
+        return initialHeap_;
+    }
 };
 
 // Could be allocating either a new array or a new object.
 class MNewPar : public MUnaryInstruction
 {
     CompilerRootObject templateObject_;
 
   public:
@@ -2468,34 +2482,42 @@ class MAssertRange
 
 // Caller-side allocation of |this| for |new|:
 // Given a templateobject, construct |this| for JSOP_NEW
 class MCreateThisWithTemplate
   : public MNullaryInstruction
 {
     // Template for |this|, provided by TI
     CompilerRootObject templateObject_;
-
-    MCreateThisWithTemplate(JSObject *templateObject)
-      : templateObject_(templateObject)
+    gc::InitialHeap initialHeap_;
+
+    MCreateThisWithTemplate(JSObject *templateObject, gc::InitialHeap initialHeap)
+      : templateObject_(templateObject),
+        initialHeap_(initialHeap)
     {
         setResultType(MIRType_Object);
         setResultTypeSet(MakeSingletonTypeSet(templateObject));
     }
 
   public:
     INSTRUCTION_HEADER(CreateThisWithTemplate);
-    static MCreateThisWithTemplate *New(TempAllocator &alloc, JSObject *templateObject)
-    {
-        return new(alloc) MCreateThisWithTemplate(templateObject);
-    }
+    static MCreateThisWithTemplate *New(TempAllocator &alloc, JSObject *templateObject,
+                                        gc::InitialHeap initialHeap)
+    {
+        return new(alloc) MCreateThisWithTemplate(templateObject, initialHeap);
+    }
+
     JSObject *templateObject() const {
         return templateObject_;
     }
 
+    gc::InitialHeap initialHeap() const {
+        return initialHeap_;
+    }
+
     // Although creation of |this| modifies global state, it is safely repeatable.
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
 // Caller-side allocation of |this| for |new|:
 // Given a prototype operand, construct |this| for JSOP_NEW.
@@ -5798,37 +5820,44 @@ class MArrayPush
 };
 
 // Array.prototype.concat on two dense arrays.
 class MArrayConcat
   : public MBinaryInstruction,
     public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >
 {
     CompilerRootObject templateObj_;
-
-    MArrayConcat(MDefinition *lhs, MDefinition *rhs, JSObject *templateObj)
+    gc::InitialHeap initialHeap_;
+
+    MArrayConcat(MDefinition *lhs, MDefinition *rhs, JSObject *templateObj, gc::InitialHeap initialHeap)
       : MBinaryInstruction(lhs, rhs),
-        templateObj_(templateObj)
+        templateObj_(templateObj),
+        initialHeap_(initialHeap)
     {
         setResultType(MIRType_Object);
         setResultTypeSet(MakeSingletonTypeSet(templateObj));
     }
 
   public:
     INSTRUCTION_HEADER(ArrayConcat)
 
     static MArrayConcat *New(TempAllocator &alloc, MDefinition *lhs, MDefinition *rhs,
-                             JSObject *templateObj)
-    {
-        return new(alloc) MArrayConcat(lhs, rhs, templateObj);
+                             JSObject *templateObj, gc::InitialHeap initialHeap)
+    {
+        return new(alloc) MArrayConcat(lhs, rhs, templateObj, initialHeap);
     }
 
     JSObject *templateObj() const {
         return templateObj_;
     }
+
+    gc::InitialHeap initialHeap() const {
+        return initialHeap_;
+    }
+
     TypePolicy *typePolicy() {
         return this;
     }
     AliasSet getAliasSet() const {
         return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
     }
     bool possiblyCalls() const {
         return true;
@@ -8500,33 +8529,23 @@ class MPostWriteBarrier
     bool hasValue_;
 
     MPostWriteBarrier(MDefinition *obj, MDefinition *value)
       : MBinaryInstruction(obj, value), hasValue_(true)
     {
         setGuard();
     }
 
-    MPostWriteBarrier(MDefinition *obj)
-      : MBinaryInstruction(obj, nullptr), hasValue_(false)
-    {
-        setGuard();
-    }
-
   public:
     INSTRUCTION_HEADER(PostWriteBarrier)
 
     static MPostWriteBarrier *New(TempAllocator &alloc, MDefinition *obj, MDefinition *value) {
         return new(alloc) MPostWriteBarrier(obj, value);
     }
 
-    static MPostWriteBarrier *New(TempAllocator &alloc, MDefinition *obj) {
-        return new(alloc) MPostWriteBarrier(obj);
-    }
-
     TypePolicy *typePolicy() {
         return this;
     }
 
     MDefinition *object() const {
         return getOperand(0);
     }
 
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -292,17 +292,17 @@ NewInitParallelArray(JSContext *cx, Hand
     return obj;
 }
 
 JSObject*
 NewInitArray(JSContext *cx, uint32_t count, types::TypeObject *typeArg)
 {
     RootedTypeObject type(cx, typeArg);
     NewObjectKind newKind = !type ? SingletonObject : GenericObject;
-    if (type && type->isLongLivedForJITAlloc())
+    if (type && type->shouldPreTenure())
         newKind = TenuredObject;
     RootedObject obj(cx, NewDenseAllocatedArray(cx, count, nullptr, newKind));
     if (!obj)
         return nullptr;
 
     if (!type)
         types::TypeScript::Monitor(cx, ObjectValue(*obj));
     else
@@ -310,17 +310,17 @@ NewInitArray(JSContext *cx, uint32_t cou
 
     return obj;
 }
 
 JSObject*
 NewInitObject(JSContext *cx, HandleObject templateObject)
 {
     NewObjectKind newKind = templateObject->hasSingletonType() ? SingletonObject : GenericObject;
-    if (!templateObject->hasLazyType() && templateObject->type()->isLongLivedForJITAlloc())
+    if (!templateObject->hasLazyType() && templateObject->type()->shouldPreTenure())
         newKind = TenuredObject;
     RootedObject obj(cx, CopyInitializerObject(cx, templateObject, newKind));
 
     if (!obj)
         return nullptr;
 
     if (templateObject->hasSingletonType())
         types::TypeScript::Monitor(cx, ObjectValue(*obj));
@@ -331,17 +331,17 @@ NewInitObject(JSContext *cx, HandleObjec
 }
 
 JSObject *
 NewInitObjectWithClassPrototype(JSContext *cx, HandleObject templateObject)
 {
     JS_ASSERT(!templateObject->hasSingletonType());
     JS_ASSERT(!templateObject->hasLazyType());
 
-    NewObjectKind newKind = templateObject->type()->isLongLivedForJITAlloc()
+    NewObjectKind newKind = templateObject->type()->shouldPreTenure()
                             ? TenuredObject
                             : GenericObject;
     JSObject *obj = NewObjectWithGivenProto(cx,
                                             templateObject->getClass(),
                                             templateObject->getProto(),
                                             cx->global(),
                                             newKind);
     if (!obj)
@@ -799,17 +799,17 @@ InitRestParameter(JSContext *cx, uint32_
                 return nullptr;
             arrRes->setDenseInitializedLength(length);
             arrRes->initDenseElements(0, rest, length);
             arrRes->setLengthInt32(length);
         }
         return arrRes;
     }
 
-    NewObjectKind newKind = templateObj->type()->isLongLivedForJITAlloc()
+    NewObjectKind newKind = templateObj->type()->shouldPreTenure()
                             ? TenuredObject
                             : GenericObject;
     ArrayObject *arrRes = NewDenseCopiedArray(cx, length, rest, nullptr, newKind);
     if (arrRes)
         arrRes->setType(templateObj->type());
     return arrRes;
 }
 
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1012,17 +1012,17 @@ js_InvokeOperationCallback(JSContext *cx
     /* IonMonkey sets its stack limit to UINTPTR_MAX to trigger operaton callbacks. */
     rt->resetIonStackLimit();
 
     if (rt->gcIsNeeded)
         GCSlice(rt, GC_NORMAL, rt->gcTriggerReason);
 
 #ifdef JSGC_GENERATIONAL
     if (rt->gcStoreBuffer.isAboutToOverflow())
-        MinorGC(rt, JS::gcreason::FULL_STORE_BUFFER);
+        MinorGC(cx, JS::gcreason::FULL_STORE_BUFFER);
 #endif
 
 #ifdef JS_ION
     /*
      * A worker thread may have set the callback after finishing an Ion
      * compilation.
      */
     jit::AttachFinishedCompilations(cx);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -4867,17 +4867,37 @@ void
 js::MinorGC(JSRuntime *rt, JS::gcreason::Reason reason)
 {
 #ifdef JSGC_GENERATIONAL
 #if JS_TRACE_LOGGING
     AutoTraceLog logger(TraceLogging::defaultLogger(),
                         TraceLogging::MINOR_GC_START,
                         TraceLogging::MINOR_GC_STOP);
 #endif
-    rt->gcNursery.collect(rt, reason);
+    rt->gcNursery.collect(rt, reason, nullptr);
+#endif
+}
+
+void
+js::MinorGC(JSContext *cx, JS::gcreason::Reason reason)
+{
+    // Alternate to the runtime-taking form above which allows marking type
+    // objects as needing pretenuring.
+#ifdef JSGC_GENERATIONAL
+#if JS_TRACE_LOGGING
+    AutoTraceLog logger(TraceLogging::defaultLogger(),
+                        TraceLogging::MINOR_GC_START,
+                        TraceLogging::MINOR_GC_STOP);
+#endif
+    Nursery::TypeObjectList pretenureTypes;
+    cx->runtime()->gcNursery.collect(cx->runtime(), reason, &pretenureTypes);
+    for (size_t i = 0; i < pretenureTypes.length(); i++) {
+        if (pretenureTypes[i]->canPreTenure())
+            pretenureTypes[i]->setShouldPreTenure(cx);
+    }
 #endif
 }
 
 void
 js::gc::FinishBackgroundFinalize(JSRuntime *rt)
 {
     rt->gcHelperThread.waitBackgroundSweepEnd();
 }
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -727,16 +727,19 @@ extern void
 GCDebugSlice(JSRuntime *rt, bool limit, int64_t objCount);
 
 extern void
 PrepareForDebugGC(JSRuntime *rt);
 
 extern void
 MinorGC(JSRuntime *rt, JS::gcreason::Reason reason);
 
+extern void
+MinorGC(JSContext *cx, JS::gcreason::Reason reason);
+
 #ifdef JS_GC_ZEAL
 extern void
 SetGCZeal(JSRuntime *rt, uint8_t zeal, uint32_t frequency);
 #endif
 
 /* Functions for managing cross compartment gray pointers. */
 
 extern void
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -374,17 +374,17 @@ TryNewNurseryGCThing(ThreadSafeContext *
 
     JS_ASSERT(!IsAtomsCompartment(cx->compartment()));
     JSRuntime *rt = cx->runtime();
     Nursery &nursery = rt->gcNursery;
     T *t = static_cast<T *>(nursery.allocate(thingSize));
     if (t)
         return t;
     if (allowGC && !rt->mainThread.suppressGC) {
-        MinorGC(rt, JS::gcreason::OUT_OF_NURSERY);
+        MinorGC(cx, JS::gcreason::OUT_OF_NURSERY);
 
         /* Exceeding gcMaxBytes while tenuring can disable the Nursery. */
         if (nursery.isEnabled()) {
             t = static_cast<T *>(nursery.allocate(thingSize));
             JS_ASSERT(t);
             return t;
         }
     }
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -1233,16 +1233,35 @@ TemporaryTypeSet::hasObjectFlags(Compile
         TypeObjectKey *object = getObject(i);
         if (object && object->hasFlags(constraints, flags))
             return true;
     }
 
     return false;
 }
 
+gc::InitialHeap
+TypeObject::initialHeap(CompilerConstraintList *constraints)
+{
+    // If this object is not required to be pretenured but could be in the
+    // future, add a constraint to trigger recompilation if the requirement
+    // changes.
+
+    if (shouldPreTenure())
+        return gc::TenuredHeap;
+
+    if (!canPreTenure())
+        return gc::DefaultHeap;
+
+    HeapTypeSetKey objectProperty = TypeObjectKey::get(this)->property(JSID_EMPTY);
+    constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> >(objectProperty, ConstraintDataFreezeObjectFlags(OBJECT_FLAG_PRE_TENURE)));
+
+    return gc::DefaultHeap;
+}
+
 namespace {
 
 // Constraint which triggers recompilation on any type change in an inlined
 // script. The freeze constraints added to stack type sets will only directly
 // invalidate the script containing those stack type sets. To invalidate code
 // for scripts into which the base script was inlined, ObjectStateChange is used.
 class ConstraintDataFreezeObjectForInlinedCall
 {
@@ -1425,31 +1444,16 @@ HeapTypeSetKey::configured(CompilerConst
     if (maybeTypes() && maybeTypes()->configuredProperty())
         return true;
 
     constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeConfiguredProperty> >(*this, ConstraintDataFreezeConfiguredProperty(type)));
     return false;
 }
 
 bool
-TypeObject::incrementTenureCount()
-{
-    uint32_t count = tenureCount();
-    JS_ASSERT(count <= OBJECT_FLAG_TENURE_COUNT_LIMIT);
-
-    if (count >= OBJECT_FLAG_TENURE_COUNT_LIMIT)
-        return false;
-
-    flags = (flags & ~OBJECT_FLAG_TENURE_COUNT_MASK)
-          | ((count + 1) << OBJECT_FLAG_TENURE_COUNT_SHIFT);
-
-    return count >= MaxJITAllocTenures;
-}
-
-bool
 TemporaryTypeSet::filtersType(const TemporaryTypeSet *other, Type filteredType) const
 {
     if (other->unknown())
         return unknown();
 
     for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) {
         Type type = Type::PrimitiveType(TypeFlagPrimitive(flag));
         if (type != filteredType && other->hasType(type) && !hasType(type))
@@ -1826,16 +1830,17 @@ TypeCompartment::addAllocationSiteTypeOb
             return nullptr;
 
         Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
         res = newTypeObject(cx, GetClassForProtoKey(key.kind), tagged);
         if (!res) {
             cx->compartment()->types.setPendingNukeTypes(cx);
             return nullptr;
         }
+        res->flags |= OBJECT_FLAG_FROM_ALLOCATION_SITE;
         key.script = keyScript;
     }
 
     if (JSOp(*pc) == JSOP_NEWOBJECT) {
         /*
          * This object is always constructed the same way and will not be
          * observed by other code before all properties have been added. Mark
          * all the properties as definite properties of the object.
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -383,19 +383,18 @@ enum {
      */
     TYPE_FLAG_DEFINITE_MASK       = 0xfffe0000,
     TYPE_FLAG_DEFINITE_SHIFT      = 17
 };
 typedef uint32_t TypeFlags;
 
 /* Flags and other state stored in TypeObject::flags */
 enum {
-    /*
-     * UNUSED FLAG                    = 0x1,
-     */
+    /* 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.
@@ -421,50 +420,42 @@ enum {
     OBJECT_FLAG_NON_PACKED            = 0x00020000,
 
     /*
      * Whether any objects this represents may be arrays whose length does not
      * fit in an int32.
      */
     OBJECT_FLAG_LENGTH_OVERFLOW       = 0x00040000,
 
-    /*
-     * UNUSED FLAG                    = 0x00080000,
-     */
-
     /* Whether any objects have been iterated over. */
-    OBJECT_FLAG_ITERATED              = 0x00100000,
+    OBJECT_FLAG_ITERATED              = 0x00080000,
 
     /* For a global object, whether flags were set on the RegExpStatics. */
-    OBJECT_FLAG_REGEXP_FLAGS_SET      = 0x00200000,
-
-    /*
-     * UNUSED FLAG                    = 0x00400000,
-     */
+    OBJECT_FLAG_REGEXP_FLAGS_SET      = 0x00100000,
 
     /*
      * For the function on a run-once script, whether the function has actually
      * run multiple times.
      */
-    OBJECT_FLAG_RUNONCE_INVALIDATED   = 0x00800000,
+    OBJECT_FLAG_RUNONCE_INVALIDATED   = 0x00200000,
+
+    /*
+     * Whether objects with this type should be allocated directly in the
+     * tenured heap.
+     */
+    OBJECT_FLAG_PRE_TENURE            = 0x00400000,
 
     /* Flags which indicate dynamic properties of represented objects. */
-    OBJECT_FLAG_DYNAMIC_MASK          = 0x00ff0000,
-
-    /* Mask/shift for tenuring count. */
-    OBJECT_FLAG_TENURE_COUNT_MASK     = 0x7f000000,
-    OBJECT_FLAG_TENURE_COUNT_SHIFT    = 24,
-    OBJECT_FLAG_TENURE_COUNT_LIMIT    =
-        OBJECT_FLAG_TENURE_COUNT_MASK >> OBJECT_FLAG_TENURE_COUNT_SHIFT,
+    OBJECT_FLAG_DYNAMIC_MASK          = 0x007f0000,
 
     /*
      * Whether all properties of this object are considered unknown.
      * If set, all flags in DYNAMIC_MASK will also be set.
      */
-    OBJECT_FLAG_UNKNOWN_PROPERTIES    = 0x80000000,
+    OBJECT_FLAG_UNKNOWN_PROPERTIES    = 0x00800000,
 
     /* Mask for objects created with unknown properties. */
     OBJECT_FLAG_UNKNOWN_MASK =
         OBJECT_FLAG_DYNAMIC_MASK
       | OBJECT_FLAG_UNKNOWN_PROPERTIES
       | OBJECT_FLAG_SETS_MARKED_UNKNOWN
 };
 typedef uint32_t TypeObjectFlags;
@@ -986,61 +977,49 @@ struct TypeObject : gc::BarrieredCell<Ty
     }
 
     bool unknownProperties() {
         JS_ASSERT_IF(flags & OBJECT_FLAG_UNKNOWN_PROPERTIES,
                      hasAllFlags(OBJECT_FLAG_DYNAMIC_MASK));
         return !!(flags & OBJECT_FLAG_UNKNOWN_PROPERTIES);
     }
 
+    bool shouldPreTenure() {
+        return hasAnyFlags(OBJECT_FLAG_PRE_TENURE) && !unknownProperties();
+    }
+
+    gc::InitialHeap initialHeap(CompilerConstraintList *constraints);
+
+    bool canPreTenure() {
+        // Only types associated with particular allocation sites or 'new'
+        // scripts can be marked as needing pretenuring. Other types can be
+        // used for different purposes across the compartment and can't use
+        // this bit reliably.
+        if (unknownProperties())
+            return false;
+        return (flags & OBJECT_FLAG_FROM_ALLOCATION_SITE) || hasNewScript();
+    }
+
+    void setShouldPreTenure(ExclusiveContext *cx) {
+        JS_ASSERT(canPreTenure());
+        setFlags(cx, OBJECT_FLAG_PRE_TENURE);
+    }
+
     /*
      * Get or create a property of this object. Only call this for properties which
      * a script accesses explicitly.
      */
     inline HeapTypeSet *getProperty(ExclusiveContext *cx, jsid id);
 
     /* Get a property only if it already exists. */
     inline HeapTypeSet *maybeGetProperty(jsid id);
 
     inline unsigned getPropertyCount();
     inline Property *getProperty(unsigned i);
 
-    /* Tenure counter management. */
-
-    /*
-     * When an object allocation site generates objects that are long lived
-     * enough to frequently be tenured during minor collections, we mark the
-     * site as long lived and allocate directly into the tenured generation.
-     */
-    const static uint32_t MaxJITAllocTenures = OBJECT_FLAG_TENURE_COUNT_LIMIT - 2;
-
-    /*
-     * NewObjectCache is used when we take a stub for allocation. It is used
-     * more rarely, but still in hot paths, so pre-tenure with fewer uses.
-     */
-    const static uint32_t MaxCachedAllocTenures = 64;
-
-    /* Returns true if the allocating script should be recompiled. */
-    bool incrementTenureCount();
-    uint32_t tenureCount() const {
-        return (flags & OBJECT_FLAG_TENURE_COUNT_MASK) >> OBJECT_FLAG_TENURE_COUNT_SHIFT;
-    }
-
-    bool isLongLivedForCachedAlloc() const {
-        return tenureCount() >= MaxCachedAllocTenures;
-    }
-
-    bool isLongLivedForJITAlloc() const {
-        return tenureCount() >= MaxJITAllocTenures;
-    }
-
-    gc::InitialHeap initialHeapForJITAlloc() const {
-        return isLongLivedForJITAlloc() ? gc::TenuredHeap : gc::DefaultHeap;
-    }
-
     /* Helpers */
 
     bool addProperty(ExclusiveContext *cx, jsid id, Property **pprop);
     bool addDefiniteProperties(ExclusiveContext *cx, JSObject *obj);
     bool matchDefiniteProperties(HandleObject obj);
     void addPrototype(JSContext *cx, TypeObject *proto);
     void addPropertyType(ExclusiveContext *cx, jsid id, Type type);
     void addPropertyType(ExclusiveContext *cx, jsid id, const Value &value);
--- a/js/src/vm/Runtime-inl.h
+++ b/js/src/vm/Runtime-inl.h
@@ -42,17 +42,17 @@ NewObjectCache::newObjectFromHit(JSConte
 {
     // The new object cache does not account for metadata attached via callbacks.
     JS_ASSERT(!cx->compartment()->hasObjectMetadataCallback());
 
     JS_ASSERT(unsigned(entry_) < mozilla::ArrayLength(entries));
     Entry *entry = &entries[entry_];
 
     JSObject *templateObj = reinterpret_cast<JSObject *>(&entry->templateObject);
-    if (templateObj->type()->isLongLivedForCachedAlloc())
+    if (templateObj->type()->shouldPreTenure())
         heap = gc::TenuredHeap;
 
     JSObject *obj = js_NewGCObject<NoGC>(cx, entry->kind, heap);
     if (obj) {
         copyCachedToObject(obj, templateObj, entry->kind);
         probes::CreateObject(cx, obj);
         return obj;
     }