Allow purging analysis-temporary while retaining jitcode, bug 778724. r=luke
☠☠ backed out by 4c0af103f0f8 ☠ ☠
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 22 Aug 2012 06:27:18 -0600
changeset 105063 d62929fa43251d582362225df3dcc9033b8c19be
parent 105062 5f0e24568cbb5801171fd94807f526f195f7f0dd
child 105064 4c0af103f0f84d44088302d82087f6f2a6c3de3d
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewersluke
bugs778724
milestone17.0a1
Allow purging analysis-temporary while retaining jitcode, bug 778724. r=luke
dom/base/nsJSEnvironment.cpp
js/src/builtin/ParallelArray.cpp
js/src/builtin/TestingFunctions.cpp
js/src/ds/LifoAlloc.h
js/src/frontend/BytecodeEmitter.cpp
js/src/jsanalyze.cpp
js/src/jsanalyze.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.h
js/src/jscntxtinlines.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsscript.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/FastBuiltins.cpp
js/src/methodjit/FastOps.cpp
js/src/methodjit/FrameState.cpp
js/src/methodjit/FrameState.h
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/LoopState.cpp
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/MethodJIT.h
js/src/methodjit/PolyIC.cpp
js/src/methodjit/PolyIC.h
modules/libpref/src/init/all.js
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -142,16 +142,17 @@ static nsITimer *sFullGCTimer;
 static nsITimer *sInterSliceGCTimer;
 
 static PRTime sLastCCEndTime;
 
 static bool sCCLockedOut;
 static PRTime sCCLockedOutTime;
 
 static js::GCSliceCallback sPrevGCSliceCallback;
+static js::AnalysisPurgeCallback sPrevAnalysisPurgeCallback;
 
 // The number of currently pending document loads. This count isn't
 // guaranteed to always reflect reality and can't easily as we don't
 // have an easy place to know when a load ends or is interrupted in
 // all cases. This counter also gets reset if we end up GC'ing while
 // we're waiting for a slow page to load. IOW, this count may be 0
 // even when there are pending loads.
 static PRUint32 sPendingLoadCount;
@@ -194,16 +195,27 @@ static PRTime sMaxChromeScriptRunTime;
 static nsIScriptSecurityManager *sSecurityManager;
 
 // nsMemoryPressureObserver observes the memory-pressure notifications
 // and forces a garbage collection and cycle collection when it happens, if
 // the appropriate pref is set.
 
 static bool sGCOnMemoryPressure;
 
+static PRTime
+GetCollectionTimeDelta()
+{
+  PRTime now = PR_Now();
+  if (sFirstCollectionTime) {
+    return now - sFirstCollectionTime;
+  }
+  sFirstCollectionTime = now;
+  return 0;
+}
+
 class nsMemoryPressureObserver MOZ_FINAL : public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 };
 
 NS_IMPL_ISUPPORTS1(nsMemoryPressureObserver, nsIObserver)
@@ -3112,22 +3124,17 @@ nsJSContext::CycleCollectNow(nsICycleCol
     PRUint32 timeBetween = (PRUint32)(start - sLastCCEndTime) / PR_USEC_PER_SEC;
     Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_TIME_BETWEEN, timeBetween);
   }
   sLastCCEndTime = now;
 
   Telemetry::Accumulate(Telemetry::FORGET_SKIPPABLE_MAX,
                         sMaxForgetSkippableTime / PR_USEC_PER_MSEC);
 
-  PRTime delta = 0;
-  if (sFirstCollectionTime) {
-    delta = now - sFirstCollectionTime;
-  } else {
-    sFirstCollectionTime = now;
-  }
+  PRTime delta = GetCollectionTimeDelta();
 
   PRUint32 cleanups = sForgetSkippableBeforeCC ? sForgetSkippableBeforeCC : 1;
   PRUint32 minForgetSkippableTime = (sMinForgetSkippableTime == PR_UINT32_MAX)
     ? 0 : sMinForgetSkippableTime;
 
   if (sPostGCEventsToConsole) {
     nsCString mergeMsg;
     if (mergingCC) {
@@ -3503,40 +3510,34 @@ NotifyGCEndRunnable::Run()
 }
 
 static void
 DOMGCSliceCallback(JSRuntime *aRt, js::GCProgress aProgress, const js::GCDescription &aDesc)
 {
   NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread");
 
   if (aProgress == js::GC_CYCLE_END) {
-    PRTime now = PR_Now();
-    PRTime delta = 0;
-    if (sFirstCollectionTime) {
-      delta = now - sFirstCollectionTime;
-    } else {
-      sFirstCollectionTime = now;
-    }
+    PRTime delta = GetCollectionTimeDelta();
 
     if (sPostGCEventsToConsole) {
       NS_NAMED_LITERAL_STRING(kFmt, "GC(T+%.1f) ");
       nsString prefix, gcstats;
       gcstats.Adopt(aDesc.formatMessage(aRt));
       prefix.Adopt(nsTextFormatter::smprintf(kFmt.get(),
                                              double(delta) / PR_USEC_PER_SEC));
       nsString msg = prefix + gcstats;
       nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
       if (cs) {
         cs->LogStringMessage(msg.get());
       }
     }
 
     if (sPostGCEventsToConsole || sPostGCEventsToObserver) {
       nsString json;
-      json.Adopt(aDesc.formatJSON(aRt, now));
+      json.Adopt(aDesc.formatJSON(aRt, PR_Now()));
       nsRefPtr<NotifyGCEndRunnable> notify = new NotifyGCEndRunnable(json);
       NS_DispatchToMainThread(notify);
     }
   }
 
   // Prevent cycle collections and shrinking during incremental GC.
   if (aProgress == js::GC_CYCLE_BEGIN) {
     sCCLockedOut = true;
@@ -3583,16 +3584,42 @@ DOMGCSliceCallback(JSRuntime *aRt, js::G
       nsJSContext::PokeShrinkGCBuffers();
     }
   }
 
   if (sPrevGCSliceCallback)
     (*sPrevGCSliceCallback)(aRt, aProgress, aDesc);
 }
 
+static void
+DOMAnalysisPurgeCallback(JSRuntime *aRt, JSFlatString *aDesc)
+{
+  NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread");
+
+  PRTime delta = GetCollectionTimeDelta();
+
+  if (sPostGCEventsToConsole) {
+    NS_NAMED_LITERAL_STRING(kFmt, "Analysis Purge (T+%.1f) ");
+    nsString prefix;
+    prefix.Adopt(nsTextFormatter::smprintf(kFmt.get(),
+                                           double(delta) / PR_USEC_PER_SEC));
+
+    nsDependentJSString stats(aDesc);
+    nsString msg = prefix + stats;
+
+    nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+    if (cs) {
+      cs->LogStringMessage(msg.get());
+    }
+  }
+
+  if (sPrevAnalysisPurgeCallback)
+    (*sPrevAnalysisPurgeCallback)(aRt, aDesc);
+}
+
 // Script object mananagement - note duplicate implementation
 // in nsJSRuntime below...
 nsresult
 nsJSContext::HoldScriptObject(void* aScriptObject)
 {
     NS_ASSERTION(sIsInitialized, "runtime not initialized");
     if (! nsJSRuntime::sRuntime) {
         NS_NOTREACHED("couldn't add GC root - no runtime");
@@ -3999,16 +4026,17 @@ nsJSRuntime::Init()
   // JS_CompileFunction*). In practice, this means content scripts and event
   // handlers.
   JS_SetSourceHook(sRuntime, SourceHook);
 
   // Let's make sure that our main thread is the same as the xpcom main thread.
   NS_ASSERTION(NS_IsMainThread(), "bad");
 
   sPrevGCSliceCallback = js::SetGCSliceCallback(sRuntime, DOMGCSliceCallback);
+  sPrevAnalysisPurgeCallback = js::SetAnalysisPurgeCallback(sRuntime, DOMAnalysisPurgeCallback);
 
   // Set up the structured clone callbacks.
   static JSStructuredCloneCallbacks cloneCallbacks = {
     NS_DOMReadStructuredClone,
     NS_DOMWriteStructuredClone,
     NS_DOMStructuredCloneError
   };
   JS_SetStructuredCloneCallbacks(sRuntime, &cloneCallbacks);
@@ -4093,16 +4121,22 @@ nsJSRuntime::Init()
   SetMemoryGCPrefChangedCallback("javascript.options.mem.gc_high_frequency_low_limit_mb",
                                  (void *)JSGC_HIGH_FREQUENCY_LOW_LIMIT);
 
   Preferences::RegisterCallback(SetMemoryGCPrefChangedCallback,
                                 "javascript.options.mem.gc_high_frequency_high_limit_mb");
   SetMemoryGCPrefChangedCallback("javascript.options.mem.gc_high_frequency_high_limit_mb",
                                  (void *)JSGC_HIGH_FREQUENCY_HIGH_LIMIT);
 
+  Preferences::RegisterCallback(SetMemoryGCPrefChangedCallback,
+                                "javascript.options.mem.analysis_purge_mb",
+                                (void *)JSGC_ANALYSIS_PURGE_TRIGGER);
+  SetMemoryGCPrefChangedCallback("javascript.options.mem.analysis_purge_mb",
+                                 (void *)JSGC_ANALYSIS_PURGE_TRIGGER);
+
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (!obs)
     return NS_ERROR_FAILURE;
 
   Preferences::AddBoolVarCache(&sGCOnMemoryPressure,
                                "javascript.options.gc_on_memory_pressure",
                                true);
 
--- a/js/src/builtin/ParallelArray.cpp
+++ b/js/src/builtin/ParallelArray.cpp
@@ -958,18 +958,18 @@ ParallelArrayObject::create(JSContext *c
         return false;
 
     // Propagate element types.
     if (cx->typeInferenceEnabled()) {
         AutoEnterTypeInference enter(cx);
         TypeObject *bufferType = buffer->getType(cx);
         TypeObject *resultType = result->getType(cx);
         if (!bufferType->unknownProperties() && !resultType->unknownProperties()) {
-            TypeSet *bufferIndexTypes = bufferType->getProperty(cx, JSID_VOID, false);
-            TypeSet *resultIndexTypes = resultType->getProperty(cx, JSID_VOID, true);
+            HeapTypeSet *bufferIndexTypes = bufferType->getProperty(cx, JSID_VOID, false);
+            HeapTypeSet *resultIndexTypes = resultType->getProperty(cx, JSID_VOID, true);
             bufferIndexTypes->addSubset(cx, resultIndexTypes);
         }
     }
 
     // Store the dimension vector into a dense array for better GC / layout.
     RootedObject dimArray(cx, NewDenseArrayWithType(cx, dims.length()));
     if (!dimArray)
         return false;
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -801,16 +801,17 @@ static JSFunctionSpecWithHelp TestingFun
 "    5: Verify pre write barriers between paints\n"
 "    6: Verify stack rooting (ignoring XML and Reflect)\n"
 "    7: Verify stack rooting (all roots)\n"
 "    8: Incremental GC in two slices: 1) mark roots 2) finish collection\n"
 "    9: Incremental GC in two slices: 1) mark all 2) new marking and finish\n"
 "   10: Incremental GC in multiple slices\n"
 "   11: Verify post write barriers between instructions\n"
 "   12: Verify post write barriers between paints\n"
+"   13: Purge analysis state when memory is allocated\n"
 "  Period specifies that collection happens every n allocations.\n"),
 
     JS_FN_HELP("schedulegc", ScheduleGC, 1, 0,
 "schedulegc(num | obj)",
 "  If num is given, schedule a GC after num allocations.\n"
 "  If obj is given, schedule a GC of obj's compartment."),
 
     JS_FN_HELP("selectforgc", SelectForGC, 0, 0,
--- a/js/src/ds/LifoAlloc.h
+++ b/js/src/ds/LifoAlloc.h
@@ -258,22 +258,31 @@ class LifoAlloc
                 break;
             JS_ASSERT(container != latest);
             container = container->next();
         }
         latest = container;
         latest->release(mark);
     }
 
+    void releaseAll() {
+        JS_ASSERT(!markCount);
+        latest = first;
+        if (latest)
+            latest->resetBump();
+    }
+
     /* Get the total "used" (occupied bytes) count for the arena chunks. */
     size_t used() const {
         size_t accum = 0;
         BumpChunk *it = first;
         while (it) {
             accum += it->used();
+            if (it == latest)
+                break;
             it = it->next();
         }
         return accum;
     }
 
     /* Get the total size of the arena chunks (including unused space). */
     size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const {
         size_t accum = 0;
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2637,17 +2637,21 @@ frontend::EmitFunctionScript(JSContext *
         bce->switchToProlog();
         if (Emit1(cx, bce, JSOP_GENERATOR) < 0)
             return false;
         bce->switchToMain();
     }
 
     if (!EmitTree(cx, bce, body))
         return false;
-        
+
+    /*
+     * Always end the script with a JSOP_STOP. Some other parts of the codebase
+     * depend on this opcode, e.g. js_InternalInterpret.
+     */
     if (Emit1(cx, bce, JSOP_STOP) < 0)
         return false;
 
     if (!JSScript::fullyInitFromEmitter(cx, bce->script, bce))
         return false;
 
 
     /* Mark functions which will only be executed once as singletons. */
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -42,17 +42,17 @@ inline bool
 ScriptAnalysis::addJump(JSContext *cx, unsigned offset,
                         unsigned *currentOffset, unsigned *forwardJump, unsigned *forwardLoop,
                         unsigned stackDepth)
 {
     JS_ASSERT(offset < script->length);
 
     Bytecode *&code = codeArray[offset];
     if (!code) {
-        code = cx->typeLifoAlloc().new_<Bytecode>();
+        code = cx->analysisLifoAlloc().new_<Bytecode>();
         if (!code) {
             setOOM(cx);
             return false;
         }
         code->stackDepth = stackDepth;
     }
     JS_ASSERT(code->stackDepth == stackDepth);
 
@@ -91,23 +91,23 @@ ScriptAnalysis::addJump(JSContext *cx, u
     return true;
 }
 
 void
 ScriptAnalysis::analyzeBytecode(JSContext *cx)
 {
     JS_ASSERT(cx->compartment->activeAnalysis);
     JS_ASSERT(!ranBytecode());
-    LifoAlloc &tla = cx->typeLifoAlloc();
+    LifoAlloc &alloc = cx->analysisLifoAlloc();
 
     numSlots = TotalSlots(script);
 
     unsigned length = script->length;
-    codeArray = tla.newArray<Bytecode*>(length);
-    escapedSlots = tla.newArray<bool>(numSlots);
+    codeArray = alloc.newArray<Bytecode*>(length);
+    escapedSlots = alloc.newArray<bool>(numSlots);
 
     if (!codeArray || !escapedSlots) {
         setOOM(cx);
         return;
     }
 
     PodZero(codeArray, length);
 
@@ -168,17 +168,17 @@ ScriptAnalysis::analyzeBytecode(JSContex
 
     /*
      * If we are in the middle of a try block, the offset of the highest
      * catch/finally/enditer.
      */
     unsigned forwardCatch = 0;
 
     /* Fill in stack depth and definitions at initial bytecode. */
-    Bytecode *startcode = tla.new_<Bytecode>();
+    Bytecode *startcode = alloc.new_<Bytecode>();
     if (!startcode) {
         setOOM(cx);
         return;
     }
 
     startcode->stackDepth = 0;
     codeArray[0] = startcode;
 
@@ -265,20 +265,20 @@ ScriptAnalysis::analyzeBytecode(JSContex
          * Assign an observed type set to each reachable JOF_TYPESET opcode.
          * This may be less than the number of type sets in the script if some
          * are unreachable, and may be greater in case the number of type sets
          * overflows a uint16. In the latter case a single type set will be
          * used for the observed types of all ops after the overflow.
          */
         if ((js_CodeSpec[op].format & JOF_TYPESET) && cx->typeInferenceEnabled()) {
             if (nTypeSets < script->nTypeSets) {
-                code->observedTypes = &typeArray[nTypeSets++];
+                code->observedTypes = typeArray[nTypeSets++].toStackTypeSet();
             } else {
                 JS_ASSERT(nTypeSets == UINT16_MAX);
-                code->observedTypes = &typeArray[nTypeSets - 1];
+                code->observedTypes = typeArray[nTypeSets - 1].toStackTypeSet();
             }
         }
 
         switch (op) {
 
           case JSOP_RETURN:
           case JSOP_STOP:
             numReturnSites_++;
@@ -455,16 +455,24 @@ ScriptAnalysis::analyzeBytecode(JSContex
           case JSOP_INCARG:
           case JSOP_DECARG:
           case JSOP_ARGINC:
           case JSOP_ARGDEC:
             modifiesArguments_ = true;
             isInlineable = false;
             break;
 
+          case JSOP_GETPROP:
+          case JSOP_CALLPROP:
+          case JSOP_LENGTH:
+          case JSOP_GETELEM:
+          case JSOP_CALLELEM:
+            numPropertyReads_++;
+            break;
+
           /* Additional opcodes which can be compiled but which can't be inlined. */
           case JSOP_ARGUMENTS:
           case JSOP_THROW:
           case JSOP_EXCEPTION:
           case JSOP_LAMBDA:
           case JSOP_DEBUGGER:
           case JSOP_FUNCALL:
           case JSOP_FUNAPPLY:
@@ -504,21 +512,16 @@ ScriptAnalysis::analyzeBytecode(JSContex
           case JSOP_BITNOT:
           case JSOP_NEG:
           case JSOP_POS:
           case JSOP_DELPROP:
           case JSOP_DELELEM:
           case JSOP_TYPEOF:
           case JSOP_TYPEOFEXPR:
           case JSOP_VOID:
-          case JSOP_GETPROP:
-          case JSOP_CALLPROP:
-          case JSOP_LENGTH:
-          case JSOP_GETELEM:
-          case JSOP_CALLELEM:
           case JSOP_TOID:
           case JSOP_SETELEM:
           case JSOP_IMPLICITTHIS:
           case JSOP_DOUBLE:
           case JSOP_STRING:
           case JSOP_ZERO:
           case JSOP_ONE:
           case JSOP_NULL:
@@ -597,17 +600,17 @@ ScriptAnalysis::analyzeBytecode(JSContex
 
         /* Handle any fallthrough from this opcode. */
         if (!BytecodeNoFallThrough(op)) {
             JS_ASSERT(successorOffset < script->length);
 
             Bytecode *&nextcode = codeArray[successorOffset];
 
             if (!nextcode) {
-                nextcode = tla.new_<Bytecode>();
+                nextcode = alloc.new_<Bytecode>();
                 if (!nextcode) {
                     setOOM(cx);
                     return;
                 }
                 nextcode->stackDepth = stackDepth;
             }
             JS_ASSERT(nextcode->stackDepth == stackDepth);
 
@@ -629,16 +632,35 @@ ScriptAnalysis::analyzeBytecode(JSContex
 
     /*
      * Always ensure that a script's arguments usage has been analyzed before
      * entering the script. This allows the functionPrologue to ensure that
      * arguments are always created eagerly which simplifies interp logic.
      */
     if (!script->analyzedArgsUsage())
         analyzeSSA(cx);
+
+    /*
+     * If the script has JIT information (we are reanalyzing the script after
+     * a purge), add safepoints for the targets of any cross chunk edges in
+     * the script. These safepoints are normally added when the JITScript is
+     * constructed, but will have been lost during the purge.
+     */
+#ifdef JS_METHODJIT
+    mjit::JITScript *jit = NULL;
+    for (int constructing = 0; constructing <= 1 && !jit; constructing++) {
+        for (int barriers = 0; barriers <= 1 && !jit; barriers++)
+            jit = script->getJIT((bool) constructing, (bool) barriers);
+    }
+    if (jit) {
+        mjit::CrossChunkEdge *edges = jit->edges();
+        for (size_t i = 0; i < jit->nedges; i++)
+            getCode(edges[i].target).safePoint = true;
+    }
+#endif
 }
 
 /////////////////////////////////////////////////////////////////////
 // Lifetime Analysis
 /////////////////////////////////////////////////////////////////////
 
 void
 ScriptAnalysis::analyzeLifetimes(JSContext *cx)
@@ -646,19 +668,19 @@ ScriptAnalysis::analyzeLifetimes(JSConte
     JS_ASSERT(cx->compartment->activeAnalysis && !ranLifetimes() && !failed());
 
     if (!ranBytecode()) {
         analyzeBytecode(cx);
         if (failed())
             return;
     }
 
-    LifoAlloc &tla = cx->typeLifoAlloc();
+    LifoAlloc &alloc = cx->analysisLifoAlloc();
 
-    lifetimes = tla.newArray<LifetimeVariable>(numSlots);
+    lifetimes = alloc.newArray<LifetimeVariable>(numSlots);
     if (!lifetimes) {
         setOOM(cx);
         return;
     }
     PodZero(lifetimes, numSlots);
 
     /*
      * Variables which are currently dead. On forward branches to locations
@@ -770,17 +792,17 @@ ScriptAnalysis::analyzeLifetimes(JSConte
             break;
           }
 
           case JSOP_LOOKUPSWITCH:
           case JSOP_TABLESWITCH:
             /* Restore all saved variables. :FIXME: maybe do this precisely. */
             for (unsigned i = 0; i < savedCount; i++) {
                 LifetimeVariable &var = *saved[i];
-                var.lifetime = tla.new_<Lifetime>(offset, var.savedEnd, var.saved);
+                var.lifetime = alloc.new_<Lifetime>(offset, var.savedEnd, var.saved);
                 if (!var.lifetime) {
                     cx->free_(saved);
                     setOOM(cx);
                     return;
                 }
                 var.saved = NULL;
                 saved[i--] = saved[--savedCount];
             }
@@ -837,17 +859,17 @@ ScriptAnalysis::analyzeLifetimes(JSConte
                 /*
                  * If we already have a loop, it is an outer loop and we
                  * need to prune the last block in the loop --- we do not
                  * track 'continue' statements for outer loops.
                  */
                 if (loop && loop->entry > loop->lastBlock)
                     loop->lastBlock = loop->entry;
 
-                LoopAnalysis *nloop = tla.new_<LoopAnalysis>();
+                LoopAnalysis *nloop = alloc.new_<LoopAnalysis>();
                 if (!nloop) {
                     cx->free_(saved);
                     setOOM(cx);
                     return;
                 }
                 PodZero(nloop);
 
                 if (loop)
@@ -887,17 +909,17 @@ ScriptAnalysis::analyzeLifetimes(JSConte
                 for (unsigned i = 0; i < savedCount; i++) {
                     LifetimeVariable &var = *saved[i];
                     JS_ASSERT(!var.lifetime && var.saved);
                     if (var.live(targetOffset)) {
                         /*
                          * Jumping to a place where this variable is live. Make a new
                          * lifetime segment for the variable.
                          */
-                        var.lifetime = tla.new_<Lifetime>(offset, var.savedEnd, var.saved);
+                        var.lifetime = alloc.new_<Lifetime>(offset, var.savedEnd, var.saved);
                         if (!var.lifetime) {
                             cx->free_(saved);
                             setOOM(cx);
                             return;
                         }
                         var.saved = NULL;
                         saved[i--] = saved[--savedCount];
                     } else if (loop && !var.savedEnd) {
@@ -951,32 +973,32 @@ ScriptAnalysis::addVariable(JSContext *c
             for (unsigned i = 0; i < savedCount; i++) {
                 if (saved[i] == &var) {
                     JS_ASSERT(savedCount);
                     saved[i--] = saved[--savedCount];
                     break;
                 }
             }
         }
-        var.lifetime = cx->typeLifoAlloc().new_<Lifetime>(offset, var.savedEnd, var.saved);
+        var.lifetime = cx->analysisLifoAlloc().new_<Lifetime>(offset, var.savedEnd, var.saved);
         if (!var.lifetime) {
             setOOM(cx);
             return;
         }
         var.saved = NULL;
     }
 }
 
 inline void
 ScriptAnalysis::killVariable(JSContext *cx, LifetimeVariable &var, unsigned offset,
                              LifetimeVariable **&saved, unsigned &savedCount)
 {
     if (!var.lifetime) {
         /* Make a point lifetime indicating the write. */
-        Lifetime *lifetime = cx->typeLifoAlloc().new_<Lifetime>(offset, var.savedEnd, var.saved);
+        Lifetime *lifetime = cx->analysisLifoAlloc().new_<Lifetime>(offset, var.savedEnd, var.saved);
         if (!lifetime) {
             setOOM(cx);
             return;
         }
         if (!var.saved)
             saved[savedCount++] = &var;
         var.saved = lifetime;
         var.saved->write = true;
@@ -997,17 +1019,17 @@ ScriptAnalysis::killVariable(JSContext *
 
     if (var.ensured) {
         /*
          * The variable is live even before the write, due to an enclosing try
          * block. We need to split the lifetime to indicate there was a write.
          * We set the new interval's savedEnd to 0, since it will always be
          * adjacent to the old interval, so it never needs to be extended.
          */
-        var.lifetime = cx->typeLifoAlloc().new_<Lifetime>(start, 0, var.lifetime);
+        var.lifetime = cx->analysisLifoAlloc().new_<Lifetime>(start, 0, var.lifetime);
         if (!var.lifetime) {
             setOOM(cx);
             return;
         }
         var.lifetime->end = offset;
     } else {
         var.saved = var.lifetime;
         var.savedEnd = 0;
@@ -1086,17 +1108,17 @@ ScriptAnalysis::extendVariable(JSContext
             if (segment->end >= end) {
                 /* Variable known to be live after the loop finishes. */
                 break;
             }
             savedEnd = end;
         }
         JS_ASSERT(savedEnd <= end);
         if (savedEnd > segment->end) {
-            Lifetime *tail = cx->typeLifoAlloc().new_<Lifetime>(savedEnd, 0, segment->next);
+            Lifetime *tail = cx->analysisLifoAlloc().new_<Lifetime>(savedEnd, 0, segment->next);
             if (!tail) {
                 setOOM(cx);
                 return;
             }
             tail->start = segment->end;
             tail->loopTail = true;
 
             /*
@@ -1163,17 +1185,17 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
     JS_ASSERT(cx->compartment->activeAnalysis && !ranSSA() && !failed());
 
     if (!ranLifetimes()) {
         analyzeLifetimes(cx);
         if (failed())
             return;
     }
 
-    LifoAlloc &tla = cx->typeLifoAlloc();
+    LifoAlloc &alloc = cx->analysisLifoAlloc();
     unsigned maxDepth = script->nslots - script->nfixed;
 
     /*
      * Current value of each variable and stack value. Empty for missing or
      * untracked entries, i.e. escaping locals and arguments.
      */
     SSAValueInfo *values = (SSAValueInfo *)
         cx->calloc_((numSlots + maxDepth) * sizeof(SSAValueInfo));
@@ -1347,17 +1369,17 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
 
         unsigned nuses = GetUseCount(script, offset);
         unsigned ndefs = GetDefCount(script, offset);
         JS_ASSERT(stackDepth >= nuses);
 
         unsigned xuses = ExtendedUse(pc) ? nuses + 1 : nuses;
 
         if (xuses) {
-            code->poppedValues = tla.newArray<SSAValue>(xuses);
+            code->poppedValues = alloc.newArray<SSAValue>(xuses);
             if (!code->poppedValues) {
                 setOOM(cx);
                 return;
             }
             for (unsigned i = 0; i < nuses; i++) {
                 SSAValue &v = stack[stackDepth - 1 - i].v;
                 code->poppedValues[i] = v;
                 v.clear();
@@ -1370,17 +1392,17 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
                 uint32_t slot = GetBytecodeSlot(script, pc);
                 if (trackSlot(slot))
                     code->poppedValues[nuses] = values[slot].v;
                 else
                     code->poppedValues[nuses].clear();
             }
 
             if (xuses) {
-                SSAUseChain *useChains = tla.newArray<SSAUseChain>(xuses);
+                SSAUseChain *useChains = alloc.newArray<SSAUseChain>(xuses);
                 if (!useChains) {
                     setOOM(cx);
                     return;
                 }
                 PodZero(useChains, xuses);
                 for (unsigned i = 0; i < xuses; i++) {
                     const SSAValue &v = code->poppedValues[i];
                     if (trackUseChain(v)) {
@@ -1397,17 +1419,17 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
 
         stackDepth -= nuses;
 
         for (unsigned i = 0; i < ndefs; i++)
             stack[stackDepth + i].v.initPushed(offset, i);
 
         unsigned xdefs = ExtendedDef(pc) ? ndefs + 1 : ndefs;
         if (xdefs) {
-            code->pushedUses = tla.newArray<SSAUseChain *>(xdefs);
+            code->pushedUses = alloc.newArray<SSAUseChain *>(xdefs);
             if (!code->pushedUses) {
                 setOOM(cx);
                 return;
             }
             PodZero(code->pushedUses, xdefs);
         }
 
         stackDepth += ndefs;
@@ -1578,18 +1600,18 @@ PhiNodeCapacity(unsigned length)
     unsigned log2;
     JS_FLOOR_LOG2(log2, length - 1);
     return 1 << (log2 + 1);
 }
 
 bool
 ScriptAnalysis::makePhi(JSContext *cx, uint32_t slot, uint32_t offset, SSAValue *pv)
 {
-    SSAPhiNode *node = cx->typeLifoAlloc().new_<SSAPhiNode>();
-    SSAValue *options = cx->typeLifoAlloc().newArray<SSAValue>(PhiNodeCapacity(0));
+    SSAPhiNode *node = cx->analysisLifoAlloc().new_<SSAPhiNode>();
+    SSAValue *options = cx->analysisLifoAlloc().newArray<SSAValue>(PhiNodeCapacity(0));
     if (!node || !options) {
         setOOM(cx);
         return false;
     }
     node->slot = slot;
     node->options = options;
     pv->initPhi(offset, node);
     return true;
@@ -1611,17 +1633,17 @@ ScriptAnalysis::insertPhi(JSContext *cx,
             if (v == node->options[i])
                 return;
         }
     }
 
     if (trackUseChain(v)) {
         SSAUseChain *&uses = useChain(v);
 
-        SSAUseChain *use = cx->typeLifoAlloc().new_<SSAUseChain>();
+        SSAUseChain *use = cx->analysisLifoAlloc().new_<SSAUseChain>();
         if (!use) {
             setOOM(cx);
             return;
         }
 
         use->popped = false;
         use->offset = phi.phiOffset();
         use->u.phi = node;
@@ -1630,17 +1652,17 @@ ScriptAnalysis::insertPhi(JSContext *cx,
     }
 
     if (node->length < PhiNodeCapacity(node->length)) {
         node->options[node->length++] = v;
         return;
     }
 
     SSAValue *newOptions =
-        cx->typeLifoAlloc().newArray<SSAValue>(PhiNodeCapacity(node->length + 1));
+        cx->analysisLifoAlloc().newArray<SSAValue>(PhiNodeCapacity(node->length + 1));
     if (!newOptions) {
         setOOM(cx);
         return;
     }
 
     PodCopy(newOptions, node->options, node->length);
     node->options = newOptions;
     node->options[node->length++] = v;
@@ -1830,17 +1852,17 @@ ScriptAnalysis::freezeNewValues(JSContex
     code.pendingValues = NULL;
 
     unsigned count = pending->length();
     if (count == 0) {
         cx->delete_(pending);
         return;
     }
 
-    code.newValues = cx->typeLifoAlloc().newArray<SlotValue>(count + 1);
+    code.newValues = cx->analysisLifoAlloc().newArray<SlotValue>(count + 1);
     if (!code.newValues) {
         setOOM(cx);
         return;
     }
 
     for (unsigned i = 0; i < count; i++)
         code.newValues[i] = (*pending)[i];
     code.newValues[count].slot = 0;
@@ -2009,17 +2031,17 @@ CrossScriptSSA::foldValue(const CrossSSA
           case JSOP_TOID: {
             /*
              * TOID acts as identity for integers, so to get better precision
              * we should propagate its popped values forward if it acted as
              * identity.
              */
             ScriptAnalysis *analysis = frame.script->analysis();
             SSAValue toidv = analysis->poppedValue(pc, 0);
-            if (analysis->getValueTypes(toidv)->getKnownTypeTag(cx) == JSVAL_TYPE_INT32)
+            if (analysis->getValueTypes(toidv)->getKnownTypeTag() == JSVAL_TYPE_INT32)
                 return foldValue(CrossSSAValue(cv.frame, toidv));
             break;
           }
 
           default:;
         }
     }
 
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -117,17 +117,17 @@ class Bytecode
 
     /* Stack depth before this opcode. */
     uint32_t stackDepth;
 
   private:
 
     union {
         /* If this is a JOF_TYPESET opcode, index into the observed types for the op. */
-        types::TypeSet *observedTypes;
+        types::StackTypeSet *observedTypes;
 
         /* If this is a loop head (TRACE or NOTRACE), information about the loop. */
         LoopAnalysis *loop;
     };
 
     /* --------- Lifetime analysis --------- */
 
     /* Any allocation computed downstream for this bytecode. */
@@ -159,17 +159,17 @@ class Bytecode
          * for variables and entries live at the head of the loop.
          */
         Vector<SlotValue> *pendingValues;
     };
 
     /* --------- Type inference --------- */
 
     /* Types for all values pushed by this bytecode. */
-    types::TypeSet *pushedTypes;
+    types::StackTypeSet *pushedTypes;
 
     /* Any type barriers in place at this bytecode. */
     types::TypeBarrier *typeBarriers;
 };
 
 static inline unsigned
 GetDefCount(JSScript *script, unsigned offset)
 {
@@ -734,17 +734,17 @@ class SSAValue
 /*
  * Mutable component of a phi node, with the possible values of the phi
  * and the possible types of the node as determined by type inference.
  * When phi nodes are copied around, any updates to the original will
  * be seen by all copies made.
  */
 struct SSAPhiNode
 {
-    types::TypeSet types;
+    types::StackTypeSet types;
     uint32_t slot;
     uint32_t length;
     SSAValue *options;
     SSAUseChain *uses;
     SSAPhiNode() { PodZero(this); }
 };
 
 inline uint32_t
@@ -804,16 +804,17 @@ class ScriptAnalysis
 {
     friend class Bytecode;
 
     JSScript *script;
 
     Bytecode **codeArray;
 
     uint32_t numSlots;
+    uint32_t numPropertyReads_;
 
     bool outOfMemory;
     bool hadFailure;
 
     bool *escapedSlots;
 
     /* Which analyses have been performed. */
     bool ranBytecode_;
@@ -867,16 +868,19 @@ class ScriptAnalysis
     /* Analyze the effect of invoking 'new' on script. */
     void analyzeTypesNew(JSContext *cx);
 
     bool OOM() { return outOfMemory; }
     bool failed() { return hadFailure; }
     bool inlineable(uint32_t argc) { return isInlineable && argc == script->function()->nargs; }
     bool jaegerCompileable() { return isJaegerCompileable; }
 
+    /* Number of property read opcodes in the script. */
+    uint32_t numPropertyReads() const { return numPropertyReads_; }
+
     /* Whether there are POPV/SETRVAL bytecodes which can write to the frame's rval. */
     bool usesReturnValue() const { return usesReturnValue_; }
 
     /* Whether there are NAME bytecodes which can access the frame's scope chain. */
     bool usesScopeChain() const { return usesScopeChain_; }
 
     bool usesThisValue() const { return usesThisValue_; }
     bool hasFunctionCalls() const { return hasFunctionCalls_; }
@@ -920,17 +924,17 @@ class ScriptAnalysis
         return JSOp(*next) == JSOP_POP && !jumpTarget(next);
     }
 
     bool incrementInitialValueObserved(jsbytecode *pc) {
         const JSCodeSpec *cs = &js_CodeSpec[*pc];
         return (cs->format & JOF_POST) && !popGuaranteed(pc);
     }
 
-    types::TypeSet *bytecodeTypes(const jsbytecode *pc) {
+    types::StackTypeSet *bytecodeTypes(const jsbytecode *pc) {
         JS_ASSERT(js_CodeSpec[*pc].format & JOF_TYPESET);
         return getCode(pc).observedTypes;
     }
 
     const SSAValue &poppedValue(uint32_t offset, uint32_t which) {
         JS_ASSERT(offset < script->length);
         JS_ASSERT(which < GetUseCount(script, offset) +
                   (ExtendedUse(script->code + offset) ? 1 : 0));
@@ -941,25 +945,25 @@ class ScriptAnalysis
     }
 
     const SlotValue *newValues(uint32_t offset) {
         JS_ASSERT(offset < script->length);
         return getCode(offset).newValues;
     }
     const SlotValue *newValues(const jsbytecode *pc) { return newValues(pc - script->code); }
 
-    types::TypeSet *pushedTypes(uint32_t offset, uint32_t which = 0) {
+    types::StackTypeSet *pushedTypes(uint32_t offset, uint32_t which = 0) {
         JS_ASSERT(offset < script->length);
         JS_ASSERT(which < GetDefCount(script, offset) +
                   (ExtendedDef(script->code + offset) ? 1 : 0));
-        types::TypeSet *array = getCode(offset).pushedTypes;
+        types::StackTypeSet *array = getCode(offset).pushedTypes;
         JS_ASSERT(array);
         return array + which;
     }
-    types::TypeSet *pushedTypes(const jsbytecode *pc, uint32_t which) {
+    types::StackTypeSet *pushedTypes(const jsbytecode *pc, uint32_t which) {
         return pushedTypes(pc - script->code, which);
     }
 
     bool hasPushedTypes(const jsbytecode *pc) { return getCode(pc).pushedTypes != NULL; }
 
     types::TypeBarrier *typeBarriers(JSContext *cx, uint32_t offset) {
         if (getCode(offset).typeBarriers)
             pruneTypeBarriers(cx, offset);
@@ -983,17 +987,17 @@ class ScriptAnalysis
      */
     void breakTypeBarriers(JSContext *cx, uint32_t offset, bool all);
 
     /* Break all type barriers used in computing v. */
     void breakTypeBarriersSSA(JSContext *cx, const SSAValue &v);
 
     inline void addPushedType(JSContext *cx, uint32_t offset, uint32_t which, types::Type type);
 
-    types::TypeSet *getValueTypes(const SSAValue &v) {
+    types::StackTypeSet *getValueTypes(const SSAValue &v) {
         switch (v.kind()) {
           case SSAValue::PUSHED:
             return pushedTypes(v.pushedOffset(), v.pushedIndex());
           case SSAValue::VAR:
             JS_ASSERT(!slotEscapes(v.varSlot()));
             if (v.varInitial()) {
                 return types::TypeScript::SlotTypes(script, v.varSlot());
             } else {
@@ -1009,20 +1013,20 @@ class ScriptAnalysis
             return &v.phiNode()->types;
           default:
             /* Cannot compute types for empty SSA values. */
             JS_NOT_REACHED("Bad SSA value");
             return NULL;
         }
     }
 
-    types::TypeSet *poppedTypes(uint32_t offset, uint32_t which) {
+    types::StackTypeSet *poppedTypes(uint32_t offset, uint32_t which) {
         return getValueTypes(poppedValue(offset, which));
     }
-    types::TypeSet *poppedTypes(const jsbytecode *pc, uint32_t which) {
+    types::StackTypeSet *poppedTypes(const jsbytecode *pc, uint32_t which) {
         return getValueTypes(poppedValue(pc, which));
     }
 
     /* Whether an arithmetic operation is operating on integers, with an integer result. */
     bool integerOperation(JSContext *cx, jsbytecode *pc);
 
     bool trackUseChain(const SSAValue &v) {
         JS_ASSERT_IF(v.kind() == SSAValue::VAR, trackSlot(v.varSlot()));
@@ -1152,19 +1156,22 @@ class ScriptAnalysis
     void mergeAllExceptionTargets(JSContext *cx, SSAValueInfo *values,
                                   const Vector<uint32_t> &exceptionTargets);
     void freezeNewValues(JSContext *cx, uint32_t offset);
 
     struct TypeInferenceState {
         Vector<SSAPhiNode *> phiNodes;
         bool hasGetSet;
         bool hasHole;
-        types::TypeSet *forTypes;
+        types::StackTypeSet *forTypes;
+        bool hasPropertyReadTypes;
+        uint32_t propertyReadIndex;
         TypeInferenceState(JSContext *cx)
-            : phiNodes(cx), hasGetSet(false), hasHole(false), forTypes(NULL)
+            : phiNodes(cx), hasGetSet(false), hasHole(false), forTypes(NULL),
+              hasPropertyReadTypes(false), propertyReadIndex(0)
         {}
     };
 
     /* Type inference helpers */
     bool analyzeTypesBytecode(JSContext *cx, unsigned offset, TypeInferenceState &state);
 
     typedef Vector<SSAValue, 16> SeenVector;
     bool needsArgsObj(JSContext *cx, SeenVector &seen, const SSAValue &v);
@@ -1264,17 +1271,17 @@ class CrossScriptSSA
         if (index == OUTER_FRAME)
             return 0;
         size_t res = outerFrame.script->length;
         for (unsigned i = 0; i < index; i++)
             res += inlineFrames[i].script->length;
         return res;
     }
 
-    types::TypeSet *getValueTypes(const CrossSSAValue &cv) {
+    types::StackTypeSet *getValueTypes(const CrossSSAValue &cv) {
         return getFrame(cv.frame).script->analysis()->getValueTypes(cv.v);
     }
 
     bool addInlineFrame(JSScript *script, uint32_t depth, uint32_t parent, jsbytecode *parentpc)
     {
         uint32_t index = inlineFrames.length();
         return inlineFrames.append(Frame(index, script, depth, parent, parentpc));
     }
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -802,16 +802,18 @@ JSRuntime::JSRuntime()
     gcNextScheduled(0),
     gcDeterministicOnly(false),
     gcIncrementalLimit(0),
 #endif
     gcValidate(true),
     gcCallback(NULL),
     gcSliceCallback(NULL),
     gcFinalizeCallback(NULL),
+    analysisPurgeCallback(NULL),
+    analysisPurgeTriggerBytes(0),
     gcMallocBytes(0),
     gcBlackRootsTraceOp(NULL),
     gcBlackRootsData(NULL),
     gcGrayRootsTraceOp(NULL),
     gcGrayRootsData(NULL),
     autoGCRooters(NULL),
     scriptAndCountsVector(NULL),
     NaNValue(UndefinedValue()),
@@ -3057,16 +3059,19 @@ JS_SetGCParameter(JSRuntime *rt, JSGCPar
         rt->gcLowFrequencyHeapGrowth = value / 100.0;
         break;
       case JSGC_DYNAMIC_HEAP_GROWTH:
         rt->gcDynamicHeapGrowth = value;
         break;
       case JSGC_DYNAMIC_MARK_SLICE:
         rt->gcDynamicMarkSlice = value;
         break;
+      case JSGC_ANALYSIS_PURGE_TRIGGER:
+        rt->analysisPurgeTriggerBytes = value * 1024 * 1024;
+        break;
       default:
         JS_ASSERT(key == JSGC_MODE);
         rt->gcMode = JSGCMode(value);
         JS_ASSERT(rt->gcMode == JSGC_MODE_GLOBAL ||
                   rt->gcMode == JSGC_MODE_COMPARTMENT ||
                   rt->gcMode == JSGC_MODE_INCREMENTAL);
         return;
     }
@@ -3103,16 +3108,18 @@ JS_GetGCParameter(JSRuntime *rt, JSGCPar
       case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN:
         return uint32_t(rt->gcHighFrequencyHeapGrowthMin * 100);
       case JSGC_LOW_FREQUENCY_HEAP_GROWTH:
         return uint32_t(rt->gcLowFrequencyHeapGrowth * 100);
       case JSGC_DYNAMIC_HEAP_GROWTH:
         return rt->gcDynamicHeapGrowth;
       case JSGC_DYNAMIC_MARK_SLICE:
         return rt->gcDynamicMarkSlice;
+      case JSGC_ANALYSIS_PURGE_TRIGGER:
+        return rt->analysisPurgeTriggerBytes / 1024 / 1024;
       default:
         JS_ASSERT(key == JSGC_NUMBER);
         return uint32_t(rt->gcNumber);
     }
 }
 
 JS_PUBLIC_API(void)
 JS_SetGCParameterForThread(JSContext *cx, JSGCParamKey key, uint32_t value)
@@ -7096,17 +7103,18 @@ JS_SetGCZeal(JSContext *cx, uint8_t zeal
                    "  4: Verify pre write barriers between instructions\n"
                    "  5: Verify pre write barriers between paints\n"
                    "  6: Verify stack rooting (ignoring XML and Reflect)\n"
                    "  7: Verify stack rooting (all roots)\n"
                    "  8: Incremental GC in two slices: 1) mark roots 2) finish collection\n"
                    "  9: Incremental GC in two slices: 1) mark all 2) new marking and finish\n"
                    " 10: Incremental GC in multiple slices\n"
                    " 11: Verify post write barriers between instructions\n"
-                   " 12: Verify post write barriers between paints\n");
+                   " 12: Verify post write barriers between paints\n"
+                   " 13: Purge analysis state every F allocations (default: 100)\n");
         }
         const char *p = strchr(env, ',');
         zeal = atoi(env);
         frequency = p ? atoi(p + 1) : JS_DEFAULT_ZEAL_FREQ;
     }
 
     JSRuntime *rt = cx->runtime;
 
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4091,17 +4091,20 @@ typedef enum JSGCParamKey {
 
     /*
      * If false, the heap growth factor is fixed at 3. If true, it is determined
      * based on whether GCs are high- or low- frequency.
      */
     JSGC_DYNAMIC_HEAP_GROWTH = 17,
 
     /* If true, high-frequency GCs will use a longer mark slice. */
-    JSGC_DYNAMIC_MARK_SLICE = 18
+    JSGC_DYNAMIC_MARK_SLICE = 18,
+
+    /* Number of megabytes of analysis data to allocate before purging. */
+    JSGC_ANALYSIS_PURGE_TRIGGER = 19
 } JSGCParamKey;
 
 typedef enum JSGCMode {
     /* Perform only global GCs. */
     JSGC_MODE_GLOBAL = 0,
 
     /* Perform per-compartment GCs until too much garbage has accumulated. */
     JSGC_MODE_COMPARTMENT = 1,
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -682,16 +682,17 @@ struct JSRuntime : js::RuntimeFriendFiel
 
     js::Vector<JSObject *, 0, js::SystemAllocPolicy> gcSelectedForMarking;
 
     int gcZeal() { return gcZeal_; }
 
     bool needZealousGC() {
         if (gcNextScheduled > 0 && --gcNextScheduled == 0) {
             if (gcZeal() == js::gc::ZealAllocValue ||
+                gcZeal() == js::gc::ZealPurgeAnalysisValue ||
                 (gcZeal() >= js::gc::ZealIncrementalRootsThenFinish &&
                  gcZeal() <= js::gc::ZealIncrementalMultipleSlices))
             {
                 gcNextScheduled = gcZealFrequency;
             }
             return true;
         }
         return false;
@@ -702,16 +703,19 @@ struct JSRuntime : js::RuntimeFriendFiel
 #endif
 
     bool                gcValidate;
 
     JSGCCallback        gcCallback;
     js::GCSliceCallback gcSliceCallback;
     JSFinalizeCallback  gcFinalizeCallback;
 
+    js::AnalysisPurgeCallback analysisPurgeCallback;
+    uint64_t            analysisPurgeTriggerBytes;
+
   private:
     /*
      * Malloc counter to measure memory pressure for GC scheduling. It runs
      * from gcMaxMallocBytes down to zero.
      */
     volatile ptrdiff_t  gcMallocBytes;
 
   public:
@@ -1287,16 +1291,17 @@ struct JSContext : js::ContextFriendFiel
         return !!(runOptions & ropt);
     }
 
     bool hasStrictOption() const { return hasRunOption(JSOPTION_STRICT); }
     bool hasWErrorOption() const { return hasRunOption(JSOPTION_WERROR); }
     bool hasAtLineOption() const { return hasRunOption(JSOPTION_ATLINE); }
 
     js::LifoAlloc &tempLifoAlloc() { return runtime->tempLifoAlloc; }
+    inline js::LifoAlloc &analysisLifoAlloc();
     inline js::LifoAlloc &typeLifoAlloc();
 
     inline js::PropertyTree &propertyTree();
 
 #ifdef JS_THREADSAFE
     unsigned            outstandingRequests;/* number of JS_BeginRequest calls
                                                without the corresponding
                                                JS_EndRequest. */
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -544,16 +544,21 @@ JSContext::setCompileOptions(unsigned ne
     JS_ASSERT((newcopts & JSCOMPILEOPTION_MASK) == newcopts);
     if (JS_LIKELY(getCompileOptions() == newcopts))
         return;
     JSVersion version = findVersion();
     JSVersion newVersion = js::OptionFlagsToVersion(newcopts, version);
     maybeOverrideVersion(newVersion);
 }
 
+inline js::LifoAlloc &
+JSContext::analysisLifoAlloc()
+{
+    return compartment->analysisLifoAlloc;
+}
 
 inline js::LifoAlloc &
 JSContext::typeLifoAlloc()
 {
     return compartment->typeLifoAlloc;
 }
 
 inline void
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -50,17 +50,18 @@ JSCompartment::JSCompartment(JSRuntime *
     gcPreserveCode(false),
     gcBytes(0),
     gcTriggerBytes(0),
     gcHeapGrowthFactor(3.0),
     gcNextCompartment(NULL),
     hold(false),
     isSystemCompartment(false),
     lastCodeRelease(0),
-    typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
+    analysisLifoAlloc(LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
+    typeLifoAlloc(LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     data(NULL),
     active(false),
     lastAnimationTime(0),
     regExps(rt),
     propertyTree(thisForCtor()),
     emptyTypeObject(NULL),
     gcMallocAndFreeBytes(0),
     gcTriggerMallocAndFreeBytes(0),
@@ -574,21 +575,23 @@ JSCompartment::sweep(FreeOp *fop, bool r
             types.sweep(fop);
         }
 
         {
             gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_CLEAR_SCRIPT_ANALYSIS);
             for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
                 JSScript *script = i.get<JSScript>();
                 script->clearAnalysis();
+                script->clearPropertyReadTypes();
             }
         }
 
         {
             gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_FREE_TI_ARENA);
+            rt->freeLifoAlloc.transferFrom(&analysisLifoAlloc);
             rt->freeLifoAlloc.transferFrom(&oldAlloc);
         }
     }
 
     active = false;
 }
 
 /*
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -243,23 +243,21 @@ struct JSCompartment
     double                       gcHeapGrowthFactor;
     JSCompartment                *gcNextCompartment;
 
     bool                         hold;
     bool                         isSystemCompartment;
 
     int64_t                      lastCodeRelease;
 
-    /*
-     * Pool for analysis and intermediate type information in this compartment.
-     * Cleared on every GC, unless the GC happens during analysis (indicated
-     * by activeAnalysis, which is implied by activeInference).
-     */
-    static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 128 * 1024;
+    /* Pools for analysis and type information in this compartment. */
+    static const size_t LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 128 * 1024;
+    js::LifoAlloc                analysisLifoAlloc;
     js::LifoAlloc                typeLifoAlloc;
+
     bool                         activeAnalysis;
     bool                         activeInference;
 
     /* Type information about the scripts and objects in this compartment. */
     js::types::TypeCompartment   types;
 
     void                         *data;
     bool                         active;  // GC flag, whether there are active frames
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -793,16 +793,24 @@ GCDescription::formatMessage(JSRuntime *
 }
 
 jschar *
 GCDescription::formatJSON(JSRuntime *rt, uint64_t timestamp) const
 {
     return rt->gcStats.formatJSON(timestamp);
 }
 
+JS_FRIEND_API(AnalysisPurgeCallback)
+SetAnalysisPurgeCallback(JSRuntime *rt, AnalysisPurgeCallback callback)
+{
+    AnalysisPurgeCallback old = rt->analysisPurgeCallback;
+    rt->analysisPurgeCallback = callback;
+    return old;
+}
+
 JS_FRIEND_API(void)
 NotifyDidPaint(JSRuntime *rt)
 {
     if (rt->gcZeal() == gc::ZealFrameVerifierPreValue) {
         gc::VerifyBarriers(rt, gc::PreBarrierVerifier);
         return;
     }
 
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -794,24 +794,29 @@ struct JS_FRIEND_API(GCDescription) {
 };
 
 typedef void
 (* GCSliceCallback)(JSRuntime *rt, GCProgress progress, const GCDescription &desc);
 
 extern JS_FRIEND_API(GCSliceCallback)
 SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback);
 
+typedef void
+(* AnalysisPurgeCallback)(JSRuntime *rt, JSFlatString *desc);
+
+extern JS_FRIEND_API(AnalysisPurgeCallback)
+SetAnalysisPurgeCallback(JSRuntime *rt, AnalysisPurgeCallback callback);
+
 /* Was the most recent GC run incrementally? */
 extern JS_FRIEND_API(bool)
 WasIncrementalGC(JSRuntime *rt);
 
 typedef JSBool
 (* DOMInstanceClassMatchesProto)(JSHandleObject protoObject, uint32_t protoID,
                                  uint32_t depth);
-
 struct JSDOMCallbacks {
     DOMInstanceClassMatchesProto instanceClassMatchesProto;
 };
 typedef struct JSDOMCallbacks DOMCallbacks;
 
 extern JS_FRIEND_API(void)
 SetDOMCallbacks(JSRuntime *rt, const DOMCallbacks *callbacks);
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -4788,16 +4788,19 @@ RunDebugGC(JSContext *cx)
          * For multi-slice zeal, reset the slice size when we get to the sweep
          * phase.
          */
         if (type == ZealIncrementalMultipleSlices &&
             initialState == MARK && rt->gcIncrementalState == SWEEP)
         {
             rt->gcIncrementalLimit = rt->gcZealFrequency / 2;
         }
+    } else if (type == ZealPurgeAnalysisValue) {
+        if (!cx->compartment->activeAnalysis)
+            cx->compartment->types.maybePurgeAnalysis(cx, /* force = */ true);
     } else {
         Collect(rt, false, SliceBudget::Unlimited, GC_NORMAL, gcreason::DEBUG_GC);
     }
 
 #endif
 }
 
 void
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1160,16 +1160,17 @@ const int ZealVerifierPreValue = 4;
 const int ZealFrameVerifierPreValue = 5;
 const int ZealStackRootingSafeValue = 6;
 const int ZealStackRootingValue = 7;
 const int ZealIncrementalRootsThenFinish = 8;
 const int ZealIncrementalMarkAllThenFinish = 9;
 const int ZealIncrementalMultipleSlices = 10;
 const int ZealVerifierPostValue = 11;
 const int ZealFrameVerifierPostValue = 12;
+const int ZealPurgeAnalysisValue = 13;
 
 enum VerifierType {
     PreBarrierVerifier,
     PostBarrierVerifier
 };
 
 #ifdef JS_GC_ZEAL
 
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -325,32 +325,44 @@ types::TypeFailure(JSContext *cx, const 
     MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
     MOZ_CRASH();
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeSet
 /////////////////////////////////////////////////////////////////////
 
-TypeSet *
-TypeSet::make(JSContext *cx, const char *name)
-{
-    JS_ASSERT(cx->compartment->activeInference);
-
-    TypeSet *res = cx->typeLifoAlloc().new_<TypeSet>();
-    if (!res) {
-        cx->compartment->types.setPendingNukeTypes(cx);
-        return NULL;
-    }
-
-    InferSpew(ISpewOps, "typeSet: %sT%p%s intermediate %s",
-              InferSpewColor(res), res, InferSpewColorReset(),
-              name);
-
-    return res;
+inline void
+TypeSet::addTypesToConstraint(JSContext *cx, TypeConstraint *constraint)
+{
+    /* If any type is possible, there's no need to worry about specifics. */
+    if (flags & TYPE_FLAG_UNKNOWN) {
+        constraint->newType(cx, this, Type::UnknownType());
+    } else {
+        /* Enqueue type set members stored as bits. */
+        for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) {
+            if (flags & flag) {
+                Type type = Type::PrimitiveType(TypeFlagPrimitive(flag));
+                constraint->newType(cx, this, type);
+            }
+        }
+
+        /* If any object is possible, skip specifics. */
+        if (flags & TYPE_FLAG_ANYOBJECT) {
+            constraint->newType(cx, this, Type::AnyObjectType());
+        } else {
+            /* Enqueue specific object types. */
+            unsigned count = getObjectCount();
+            for (unsigned i = 0; i < count; i++) {
+                TypeObjectKey *object = getObject(i);
+                if (object)
+                    constraint->newType(cx, this, Type::ObjectType(object));
+            }
+        }
+    }
 }
 
 inline void
 TypeSet::add(JSContext *cx, TypeConstraint *constraint, bool callExisting)
 {
     if (!constraint) {
         /* OOM failure while constructing the constraint. */
         cx->compartment->types.setPendingNukeTypes(cx);
@@ -363,58 +375,29 @@ TypeSet::add(JSContext *cx, TypeConstrai
               InferSpewColor(this), this, InferSpewColorReset(),
               InferSpewColor(constraint), constraint, InferSpewColorReset(),
               constraint->kind());
 
     JS_ASSERT(constraint->next == NULL);
     constraint->next = constraintList;
     constraintList = constraint;
 
-    if (!callExisting)
-        return;
-
-    /* If any type is possible, there's no need to worry about specifics. */
-    if (flags & TYPE_FLAG_UNKNOWN) {
-        cx->compartment->types.addPending(cx, constraint, this, Type::UnknownType());
-    } else {
-        /* Enqueue type set members stored as bits. */
-        for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) {
-            if (flags & flag) {
-                Type type = Type::PrimitiveType(TypeFlagPrimitive(flag));
-                cx->compartment->types.addPending(cx, constraint, this, type);
-            }
-        }
-
-        /* If any object is possible, skip specifics. */
-        if (flags & TYPE_FLAG_ANYOBJECT) {
-            cx->compartment->types.addPending(cx, constraint, this, Type::AnyObjectType());
-        } else {
-            /* Enqueue specific object types. */
-            unsigned count = getObjectCount();
-            for (unsigned i = 0; i < count; i++) {
-                TypeObjectKey *object = getObject(i);
-                if (object)
-                    cx->compartment->types.addPending(cx, constraint, this,
-                                                      Type::ObjectType(object));
-            }
-        }
-    }
-
-    cx->compartment->types.resolvePending(cx);
+    if (callExisting)
+        addTypesToConstraint(cx, constraint);
 }
 
 void
 TypeSet::print(JSContext *cx)
 {
     if (flags & TYPE_FLAG_OWN_PROPERTY)
         printf(" [own]");
     if (flags & TYPE_FLAG_CONFIGURED_PROPERTY)
         printf(" [configured]");
 
-    if (isDefiniteProperty())
+    if (definiteProperty())
         printf(" [definite:%d]", definiteSlot());
 
     if (baseFlags() == 0 && !baseObjectCount()) {
         printf(" missing");
         return;
     }
 
     if (flags & TYPE_FLAG_UNKNOWN)
@@ -445,331 +428,357 @@ TypeSet::print(JSContext *cx)
         for (unsigned i = 0; i < count; i++) {
             TypeObjectKey *object = getObject(i);
             if (object)
                 printf(" %s", TypeString(Type::ObjectType(object)));
         }
     }
 }
 
-bool
-TypeSet::propertyNeedsBarrier(JSContext *cx, jsid id)
-{
-    id = MakeTypeId(cx, id);
-
-    if (unknownObject())
-        return true;
-
-    for (unsigned i = 0; i < getObjectCount(); i++) {
-        if (getSingleObject(i))
-            return true;
-
-        if (types::TypeObject *otype = getTypeObject(i)) {
-            if (otype->unknownProperties())
-                return true;
-
-            if (types::TypeSet *propTypes = otype->maybeGetProperty(cx, id)) {
-                if (propTypes->needsBarrier(cx))
-                    return true;
-            }
-        }
-    }
-
-    addFreeze(cx);
-    return false;
+StackTypeSet *
+StackTypeSet::make(JSContext *cx, const char *name)
+{
+    JS_ASSERT(cx->compartment->activeInference);
+
+    StackTypeSet *res = cx->analysisLifoAlloc().new_<StackTypeSet>();
+    if (!res) {
+        cx->compartment->types.setPendingNukeTypes(cx);
+        return NULL;
+    }
+
+    InferSpew(ISpewOps, "typeSet: %sT%p%s intermediate %s",
+              InferSpewColor(res), res, InferSpewColorReset(),
+              name);
+    res->setPurged();
+
+    return res;
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeSet constraints
 /////////////////////////////////////////////////////////////////////
 
 /* Standard subset constraint, propagate all types from one set to another. */
 class TypeConstraintSubset : public TypeConstraint
 {
 public:
     TypeSet *target;
 
     TypeConstraintSubset(TypeSet *target)
-        : TypeConstraint("subset"), target(target)
+        : target(target)
     {
         JS_ASSERT(target);
     }
 
+    const char *kind() { return "subset"; }
+
     void newType(JSContext *cx, TypeSet *source, Type type)
     {
         /* Basic subset constraint, move all types to the target. */
         target->addType(cx, type);
     }
 };
 
 void
-TypeSet::addSubset(JSContext *cx, TypeSet *target)
-{
+StackTypeSet::addSubset(JSContext *cx, TypeSet *target)
+{
+    add(cx, cx->analysisLifoAlloc().new_<TypeConstraintSubset>(target));
+}
+
+void
+HeapTypeSet::addSubset(JSContext *cx, TypeSet *target)
+{
+    JS_ASSERT(!target->purged());
     add(cx, cx->typeLifoAlloc().new_<TypeConstraintSubset>(target));
 }
 
+enum PropertyAccessKind {
+    PROPERTY_WRITE,
+    PROPERTY_READ,
+    PROPERTY_READ_EXISTING
+};
+
 /* Constraints for reads/writes on object properties. */
+template <PropertyAccessKind access>
 class TypeConstraintProp : public TypeConstraint
 {
 public:
     JSScript *script;
     jsbytecode *pc;
 
     /*
      * If assign is true, the target is used to update a property of the object.
      * If assign is false, the target is assigned the value of the property.
      */
-    bool assign;
-    TypeSet *target;
+    StackTypeSet *target;
 
     /* Property being accessed. */
     jsid id;
 
-    TypeConstraintProp(JSScript *script, jsbytecode *pc,
-                       TypeSet *target, jsid id, bool assign)
-        : TypeConstraint("prop"), script(script), pc(pc),
-          assign(assign), target(target), id(id)
+    TypeConstraintProp(JSScript *script, jsbytecode *pc, StackTypeSet *target, jsid id)
+        : script(script), pc(pc), target(target), id(id)
     {
         JS_ASSERT(script && pc && target);
     }
 
+    const char *kind() { return "prop"; }
+
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
+typedef TypeConstraintProp<PROPERTY_WRITE> TypeConstraintSetProperty;
+typedef TypeConstraintProp<PROPERTY_READ>  TypeConstraintGetProperty;
+typedef TypeConstraintProp<PROPERTY_READ_EXISTING> TypeConstraintGetPropertyExisting;
+
 void
-TypeSet::addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
-                        TypeSet *target, jsid id)
-{
-    add(cx, cx->typeLifoAlloc().new_<TypeConstraintProp>(script, pc, target, id, false));
+StackTypeSet::addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
+                             StackTypeSet *target, jsid id)
+{
+    /*
+     * GetProperty constraints are normally used with property read input type
+     * sets, except for array_pop/array_shift special casing.
+     */
+    JS_ASSERT(js_CodeSpec[*pc].format & JOF_INVOKE);
+
+    add(cx, cx->analysisLifoAlloc().new_<TypeConstraintGetProperty>(script, pc, target, id));
 }
 
 void
-TypeSet::addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
-                        TypeSet *target, jsid id)
-{
-    add(cx, cx->typeLifoAlloc().new_<TypeConstraintProp>(script, pc, target, id, true));
+StackTypeSet::addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
+                             StackTypeSet *target, jsid id)
+{
+    add(cx, cx->analysisLifoAlloc().new_<TypeConstraintSetProperty>(script, pc, target, id));
+}
+
+void
+HeapTypeSet::addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
+                            StackTypeSet *target, jsid id)
+{
+    JS_ASSERT(!target->purged());
+    add(cx, cx->typeLifoAlloc().new_<TypeConstraintGetProperty>(script, pc, target, id));
 }
 
 /*
  * Constraints for updating the 'this' types of callees on CALLPROP/CALLELEM.
  * These are derived from the types on the properties themselves, rather than
  * those pushed in the 'this' slot at the call site, which allows us to retain
  * correlations between the type of the 'this' object and the associated
  * callee scripts at polymorphic call sites.
  */
+template <PropertyAccessKind access>
 class TypeConstraintCallProp : public TypeConstraint
 {
 public:
     JSScript *script;
     jsbytecode *callpc;
 
     /* Property being accessed. */
     jsid id;
 
     TypeConstraintCallProp(JSScript *script, jsbytecode *callpc, jsid id)
-        : TypeConstraint("callprop"), script(script), callpc(callpc), id(id)
+        : script(script), callpc(callpc), id(id)
     {
         JS_ASSERT(script && callpc);
     }
 
+    const char *kind() { return "callprop"; }
+
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
+typedef TypeConstraintCallProp<PROPERTY_READ> TypeConstraintCallProperty;
+typedef TypeConstraintCallProp<PROPERTY_READ_EXISTING> TypeConstraintCallPropertyExisting;
+
 void
-TypeSet::addCallProperty(JSContext *cx, JSScript *script, jsbytecode *pc, jsid id)
+HeapTypeSet::addCallProperty(JSContext *cx, JSScript *script, jsbytecode *pc, jsid id)
 {
     /*
      * For calls which will go through JSOP_NEW, don't add any constraints to
      * modify the 'this' types of callees. The initial 'this' value will be
      * outright ignored.
      */
     jsbytecode *callpc = script->analysis()->getCallPC(pc);
     if (JSOp(*callpc) == JSOP_NEW)
         return;
 
-    add(cx, cx->typeLifoAlloc().new_<TypeConstraintCallProp>(script, callpc, id));
+    add(cx, cx->typeLifoAlloc().new_<TypeConstraintCallProperty>(script, callpc, id));
 }
 
 /*
  * Constraints for generating 'set' property constraints on a SETELEM only if
  * the element type may be a number. For SETELEM we only account for integer
  * indexes, and if the element cannot be an integer (e.g. it must be a string)
  * then we lose precision by treating it like one.
  */
 class TypeConstraintSetElement : public TypeConstraint
 {
 public:
     JSScript *script;
     jsbytecode *pc;
 
-    TypeSet *objectTypes;
-    TypeSet *valueTypes;
+    StackTypeSet *objectTypes;
+    StackTypeSet *valueTypes;
 
     TypeConstraintSetElement(JSScript *script, jsbytecode *pc,
-                             TypeSet *objectTypes, TypeSet *valueTypes)
-        : TypeConstraint("setelement"), script(script), pc(pc),
+                             StackTypeSet *objectTypes, StackTypeSet *valueTypes)
+        : script(script), pc(pc),
           objectTypes(objectTypes), valueTypes(valueTypes)
     {
         JS_ASSERT(script && pc);
     }
 
+    const char *kind() { return "setelement"; }
+
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
-TypeSet::addSetElement(JSContext *cx, JSScript *script, jsbytecode *pc,
-                       TypeSet *objectTypes, TypeSet *valueTypes)
-{
-    add(cx, cx->typeLifoAlloc().new_<TypeConstraintSetElement>(script, pc, objectTypes,
-                                                               valueTypes));
+StackTypeSet::addSetElement(JSContext *cx, JSScript *script, jsbytecode *pc,
+                            StackTypeSet *objectTypes, StackTypeSet *valueTypes)
+{
+    add(cx, cx->analysisLifoAlloc().new_<TypeConstraintSetElement>(script, pc, objectTypes,
+                                                                   valueTypes));
 }
 
 /*
  * Constraints for watching call edges as they are discovered and invoking native
  * function handlers, adding constraints for arguments, receiver objects and the
  * return value, and updating script foundOffsets.
  */
 class TypeConstraintCall : public TypeConstraint
 {
 public:
     /* Call site being tracked. */
     TypeCallsite *callsite;
 
     TypeConstraintCall(TypeCallsite *callsite)
-        : TypeConstraint("call"), callsite(callsite)
+        : callsite(callsite)
     {}
 
+    const char *kind() { return "call"; }
+
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
-TypeSet::addCall(JSContext *cx, TypeCallsite *site)
-{
-    add(cx, cx->typeLifoAlloc().new_<TypeConstraintCall>(site));
+StackTypeSet::addCall(JSContext *cx, TypeCallsite *site)
+{
+    add(cx, cx->analysisLifoAlloc().new_<TypeConstraintCall>(site));
 }
 
 /* Constraints for arithmetic operations. */
 class TypeConstraintArith : public TypeConstraint
 {
 public:
     JSScript *script;
     jsbytecode *pc;
 
     /* Type set receiving the result of the arithmetic. */
     TypeSet *target;
 
     /* For addition operations, the other operand. */
     TypeSet *other;
 
     TypeConstraintArith(JSScript *script, jsbytecode *pc, TypeSet *target, TypeSet *other)
-        : TypeConstraint("arith"), script(script), pc(pc), target(target), other(other)
+        : script(script), pc(pc), target(target), other(other)
     {
         JS_ASSERT(target);
     }
 
+    const char *kind() { return "arith"; }
+
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
-TypeSet::addArith(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target, TypeSet *other)
-{
-    add(cx, cx->typeLifoAlloc().new_<TypeConstraintArith>(script, pc, target, other));
+StackTypeSet::addArith(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target, TypeSet *other)
+{
+    add(cx, cx->analysisLifoAlloc().new_<TypeConstraintArith>(script, pc, target, other));
 }
 
 /* Subset constraint which transforms primitive values into appropriate objects. */
 class TypeConstraintTransformThis : public TypeConstraint
 {
 public:
     JSScript *script;
     TypeSet *target;
 
     TypeConstraintTransformThis(JSScript *script, TypeSet *target)
-        : TypeConstraint("transformthis"), script(script), target(target)
+        : script(script), target(target)
     {}
 
+    const char *kind() { return "transformthis"; }
+
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
-TypeSet::addTransformThis(JSContext *cx, JSScript *script, TypeSet *target)
-{
-    add(cx, cx->typeLifoAlloc().new_<TypeConstraintTransformThis>(script, target));
+StackTypeSet::addTransformThis(JSContext *cx, JSScript *script, TypeSet *target)
+{
+    add(cx, cx->analysisLifoAlloc().new_<TypeConstraintTransformThis>(script, target));
 }
 
 /*
  * Constraint which adds a particular type to the 'this' types of all
  * discovered scripted functions.
  */
 class TypeConstraintPropagateThis : public TypeConstraint
 {
 public:
     JSScript *script;
     jsbytecode *callpc;
     Type type;
-    TypeSet *types;
-
-    TypeConstraintPropagateThis(JSScript *script, jsbytecode *callpc, Type type, TypeSet *types)
-        : TypeConstraint("propagatethis"), script(script), callpc(callpc), type(type), types(types)
+    StackTypeSet *types;
+
+    TypeConstraintPropagateThis(JSScript *script, jsbytecode *callpc, Type type, StackTypeSet *types)
+        : script(script), callpc(callpc), type(type), types(types)
     {}
 
+    const char *kind() { return "propagatethis"; }
+
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
-TypeSet::addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, Type type, TypeSet *types)
+StackTypeSet::addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc,
+                               Type type, StackTypeSet *types)
 {
     /* Don't add constraints when the call will be 'new' (see addCallProperty). */
     jsbytecode *callpc = script->analysis()->getCallPC(pc);
     if (JSOp(*callpc) == JSOP_NEW)
         return;
 
-    add(cx, cx->typeLifoAlloc().new_<TypeConstraintPropagateThis>(script, callpc, type, types));
+    add(cx, cx->analysisLifoAlloc().new_<TypeConstraintPropagateThis>(script, callpc, type, types));
 }
 
 /* Subset constraint which filters out primitive types. */
 class TypeConstraintFilterPrimitive : public TypeConstraint
 {
 public:
     TypeSet *target;
-    TypeSet::FilterKind filter;
-
-    TypeConstraintFilterPrimitive(TypeSet *target, TypeSet::FilterKind filter)
-        : TypeConstraint("filter"), target(target), filter(filter)
+
+    TypeConstraintFilterPrimitive(TypeSet *target)
+        : target(target)
     {}
 
+    const char *kind() { return "filter"; }
+
     void newType(JSContext *cx, TypeSet *source, Type type)
     {
-        switch (filter) {
-          case TypeSet::FILTER_ALL_PRIMITIVES:
-            if (type.isPrimitive())
-                return;
-            break;
-
-          case TypeSet::FILTER_NULL_VOID:
-            if (type.isPrimitive(JSVAL_TYPE_NULL) || type.isPrimitive(JSVAL_TYPE_UNDEFINED))
-                return;
-            break;
-
-          case TypeSet::FILTER_VOID:
-            if (type.isPrimitive(JSVAL_TYPE_UNDEFINED))
-                return;
-            break;
-
-          default:
-            JS_NOT_REACHED("Bad filter");
-        }
+        if (type.isPrimitive())
+            return;
 
         target->addType(cx, type);
     }
 };
 
 void
-TypeSet::addFilterPrimitives(JSContext *cx, TypeSet *target, FilterKind filter)
-{
-    add(cx, cx->typeLifoAlloc().new_<TypeConstraintFilterPrimitive>(target, filter));
+HeapTypeSet::addFilterPrimitives(JSContext *cx, TypeSet *target)
+{
+    add(cx, cx->typeLifoAlloc().new_<TypeConstraintFilterPrimitive>(target));
 }
 
 /* If id is a normal slotful 'own' property of an object, get its shape. */
 static inline Shape *
 GetSingletonShape(JSContext *cx, JSObject *obj, jsid id)
 {
     if (!obj->isNative())
         return NULL;
@@ -877,29 +886,41 @@ void ScriptAnalysis::breakTypeBarriersSS
 class TypeConstraintSubsetBarrier : public TypeConstraint
 {
 public:
     JSScript *script;
     jsbytecode *pc;
     TypeSet *target;
 
     TypeConstraintSubsetBarrier(JSScript *script, jsbytecode *pc, TypeSet *target)
-        : TypeConstraint("subsetBarrier"), script(script), pc(pc), target(target)
+        : script(script), pc(pc), target(target)
     {}
 
+    const char *kind() { return "subsetBarrier"; }
+
     void newType(JSContext *cx, TypeSet *source, Type type)
     {
-        if (!target->hasType(type))
+        if (!target->hasType(type)) {
+            if (!script->ensureRanAnalysis(cx))
+                return;
             script->analysis()->addTypeBarrier(cx, pc, target, type);
+        }
     }
 };
 
 void
-TypeSet::addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target)
-{
+StackTypeSet::addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target)
+{
+    add(cx, cx->analysisLifoAlloc().new_<TypeConstraintSubsetBarrier>(script, pc, target));
+}
+
+void
+HeapTypeSet::addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target)
+{
+    JS_ASSERT(!target->purged());
     add(cx, cx->typeLifoAlloc().new_<TypeConstraintSubsetBarrier>(script, pc, target));
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeConstraint
 /////////////////////////////////////////////////////////////////////
 
 /* Get the object to use for a property access on type. */
@@ -959,104 +980,186 @@ MarkPropertyAccessUnknown(JSContext *cx,
 {
     if (UsePropertyTypeBarrier(pc))
         script->analysis()->addTypeBarrier(cx, pc, target, Type::UnknownType());
     else
         target->addType(cx, Type::UnknownType());
 }
 
 /*
+ * Get a value for reading id from obj or its prototypes according to the
+ * current VM state, returning the unknown type on failure or an undefined
+ * property.
+ */
+static inline Type
+GetSingletonPropertyType(JSContext *cx, JSObject *objArg, jsid id)
+{
+    JS_ASSERT(id == MakeTypeId(cx, id));
+
+    RootedObject obj(cx, objArg);
+
+    if (JSID_IS_VOID(id))
+        return Type::UnknownType();
+
+    if (obj->isTypedArray()) {
+        if (id == id_length(cx))
+            return Type::Int32Type();
+        obj = obj->getProto();
+    }
+
+    while (obj) {
+        if (!obj->isNative())
+            return Type::UnknownType();
+
+        Value v;
+        if (HasDataProperty(cx, obj, id, &v)) {
+            if (v.isUndefined())
+                return Type::UnknownType();
+            return GetValueType(cx, v);
+        }
+
+        obj = obj->getProto();
+    }
+
+    return Type::UnknownType();
+}
+
+/*
  * Handle a property access on a specific object. All property accesses go through
  * here, whether via x.f, x[f], or global name accesses.
  */
+template <PropertyAccessKind access>
 static inline void
-PropertyAccess(JSContext *cx, JSScript *script_, jsbytecode *pc, TypeObject *object_,
-               bool assign, TypeSet *target, jsid id)
-{
-    RootedScript script(cx, script_);
-    Rooted<TypeObject*> object(cx, object_);
-
+PropertyAccess(JSContext *cx, JSScript *script, jsbytecode *pc, TypeObject *object,
+               StackTypeSet *target, jsid id)
+{
     /* Reads from objects with unknown properties are unknown, writes to such objects are ignored. */
     if (object->unknownProperties()) {
-        if (!assign)
+        if (access != PROPERTY_WRITE)
             MarkPropertyAccessUnknown(cx, script, pc, target);
         return;
     }
 
+    /*
+     * Short circuit indexed accesses on objects which are definitely typed
+     * arrays. Inference only covers the behavior of indexed accesses when
+     * getting integer properties, and the types for these are known ahead of
+     * time for typed arrays. Propagate the possible element types of the array
+     * to sites reading from it.
+     */
+    if (object->singleton && object->singleton->isTypedArray() && JSID_IS_VOID(id)) {
+        if (access != PROPERTY_WRITE) {
+            int arrayKind = object->proto->getClass() - TypedArray::protoClasses;
+            JS_ASSERT(arrayKind >= 0 && arrayKind < TypedArray::TYPE_MAX);
+
+            bool maybeDouble = (arrayKind == TypedArray::TYPE_UINT32 ||
+                                arrayKind == TypedArray::TYPE_FLOAT32 ||
+                                arrayKind == TypedArray::TYPE_FLOAT64);
+            target->addType(cx, maybeDouble ? Type::DoubleType() : Type::Int32Type());
+        }
+        return;
+    }
+
+    /*
+     * Try to resolve reads from the VM state ahead of time, e.g. for reads
+     * of defined global variables or from the prototype of the object. This
+     * reduces the need to monitor cold code as it first executes.
+     *
+     * This is speculating that the type of a defined property in a singleton
+     * object or prototype will not change between analysis and execution.
+     */
+    if (access != PROPERTY_WRITE) {
+        if (JSObject *singleton = object->singleton ? object->singleton : object->proto) {
+            Type type = GetSingletonPropertyType(cx, singleton, id);
+            if (!type.isUnknown())
+                target->addType(cx, type);
+        }
+    }
+
     /* Capture the effects of a standard property access. */
-    TypeSet *types = object->getProperty(cx, id, assign);
+    HeapTypeSet *types = object->getProperty(cx, id, access == PROPERTY_WRITE);
     if (!types)
         return;
-    if (assign) {
+    if (access == PROPERTY_WRITE) {
         target->addSubset(cx, types);
     } else {
+        JS_ASSERT_IF(script->hasAnalysis(),
+                     target == script->analysis()->bytecodeTypes(pc));
         if (!types->hasPropagatedProperty())
             object->getFromPrototypes(cx, id, types);
         if (UsePropertyTypeBarrier(pc)) {
-            types->addSubsetBarrier(cx, script, pc, target);
+            if (access == PROPERTY_READ) {
+                types->addSubsetBarrier(cx, script, pc, target);
+            } else {
+                TypeConstraintSubsetBarrier constraint(script, pc, target);
+                types->addTypesToConstraint(cx, &constraint);
+            }
             if (object->singleton && !JSID_IS_VOID(id)) {
                 /*
                  * Add a singleton type barrier on the object if it has an
                  * 'own' property which is currently undefined. We'll be able
                  * to remove the barrier after the property becomes defined,
                  * even if no undefined value is ever observed at pc.
                  */
                 Shape *shape = GetSingletonShape(cx, object->singleton, id);
                 if (shape && object->singleton->nativeGetSlot(shape->slot()).isUndefined())
                     script->analysis()->addSingletonTypeBarrier(cx, pc, target, object->singleton, id);
             }
         } else {
+            JS_ASSERT(access == PROPERTY_READ);
             types->addSubset(cx, target);
         }
     }
 }
 
 /* Whether the JSObject/TypeObject referent of an access on type cannot be determined. */
 static inline bool
 UnknownPropertyAccess(JSScript *script, Type type)
 {
     return type.isUnknown()
         || type.isAnyObject()
         || (!type.isObject() && !script->hasGlobal());
 }
 
+template <PropertyAccessKind access>
 void
-TypeConstraintProp::newType(JSContext *cx, TypeSet *source, Type type)
+TypeConstraintProp<access>::newType(JSContext *cx, TypeSet *source, Type type)
 {
     if (UnknownPropertyAccess(script, type)) {
         /*
          * Access on an unknown object. Reads produce an unknown result, writes
          * need to be monitored.
          */
-        if (assign)
+        if (access == PROPERTY_WRITE)
             cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
         else
             MarkPropertyAccessUnknown(cx, script, pc, target);
         return;
     }
 
     if (type.isPrimitive(JSVAL_TYPE_MAGIC)) {
         /* Ignore cases which will be accounted for by the followEscapingArguments analysis. */
-        if (assign || (id != JSID_VOID && id != id_length(cx)))
+        if (access == PROPERTY_WRITE || (id != JSID_VOID && id != id_length(cx)))
             return;
 
         if (id == JSID_VOID)
             MarkPropertyAccessUnknown(cx, script, pc, target);
         else
             target->addType(cx, Type::Int32Type());
         return;
     }
 
     TypeObject *object = GetPropertyObject(cx, script, type);
     if (object)
-        PropertyAccess(cx, script, pc, object, assign, target, id);
-}
-
+        PropertyAccess<access>(cx, script, pc, object, target, id);
+}
+
+template <PropertyAccessKind access>
 void
-TypeConstraintCallProp::newType(JSContext *cx, TypeSet *source, Type type)
+TypeConstraintCallProp<access>::newType(JSContext *cx, TypeSet *source, Type type)
 {
     /*
      * For CALLPROP, we need to update not just the pushed types but also the
      * 'this' types of possible callees. If we can't figure out that set of
      * callees, monitor the call to make sure discovered callees get their
      * 'this' types updated.
      */
 
@@ -1071,18 +1174,23 @@ TypeConstraintCallProp::newType(JSContex
             cx->compartment->types.monitorBytecode(cx, script, callpc - script->code);
         } else {
             TypeSet *types = object->getProperty(cx, id, false);
             if (!types)
                 return;
             if (!types->hasPropagatedProperty())
                 object->getFromPrototypes(cx, id, types);
             /* Bypass addPropagateThis, we already have the callpc. */
-            types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintPropagateThis>(
-                            script, callpc, type, (TypeSet *) NULL));
+            if (access == PROPERTY_READ) {
+                types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintPropagateThis>(
+                                script, callpc, type, (StackTypeSet *) NULL));
+            } else {
+                TypeConstraintPropagateThis constraint(script, callpc, type, NULL);
+                types->addTypesToConstraint(cx, &constraint);
+            }
         }
     }
 }
 
 void
 TypeConstraintSetElement::newType(JSContext *cx, TypeSet *source, Type type)
 {
     if (type.isUnknown() ||
@@ -1093,16 +1201,19 @@ TypeConstraintSetElement::newType(JSCont
 }
 
 void
 TypeConstraintCall::newType(JSContext *cx, TypeSet *source, Type type)
 {
     JSScript *script = callsite->script;
     jsbytecode *pc = callsite->pc;
 
+    JS_ASSERT_IF(script->hasAnalysis(),
+                 callsite->returnTypes == script->analysis()->bytecodeTypes(pc));
+
     if (type.isUnknown() || type.isAnyObject()) {
         /* Monitor calls on unknown functions. */
         cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
         return;
     }
 
     JSFunction *callee = NULL;
 
@@ -1146,18 +1257,18 @@ TypeConstraintCall::newType(JSContext *c
                 TypeObject *res = TypeScript::InitObject(cx, script, pc, JSProto_Array);
                 if (!res)
                     return;
 
                 callsite->returnTypes->addType(cx, Type::ObjectType(res));
 
                 if (callsite->argumentCount >= 2) {
                     for (unsigned i = 0; i < callsite->argumentCount; i++) {
-                        PropertyAccess(cx, script, pc, res, true,
-                                       callsite->argumentTypes[i], JSID_VOID);
+                        PropertyAccess<PROPERTY_WRITE>(cx, script, pc, res,
+                                                       callsite->argumentTypes[i], JSID_VOID);
                     }
                 }
             }
 
             return;
         }
 
         callee = obj->toFunction();
@@ -1172,40 +1283,39 @@ TypeConstraintCall::newType(JSContext *c
 
     if (!callee->script()->ensureHasTypes(cx))
         return;
 
     unsigned nargs = callee->nargs;
 
     /* Add bindings for the arguments of the call. */
     for (unsigned i = 0; i < callsite->argumentCount && i < nargs; i++) {
-        TypeSet *argTypes = callsite->argumentTypes[i];
-        TypeSet *types = TypeScript::ArgTypes(callee->script(), i);
+        StackTypeSet *argTypes = callsite->argumentTypes[i];
+        StackTypeSet *types = TypeScript::ArgTypes(callee->script(), i);
         argTypes->addSubsetBarrier(cx, script, pc, types);
     }
 
     /* Add void type for any formals in the callee not supplied at the call site. */
     for (unsigned i = callsite->argumentCount; i < nargs; i++) {
         TypeSet *types = TypeScript::ArgTypes(callee->script(), i);
         types->addType(cx, Type::UndefinedType());
     }
 
-    TypeSet *thisTypes = TypeScript::ThisTypes(callee->script());
-    TypeSet *returnTypes = TypeScript::ReturnTypes(callee->script());
+    StackTypeSet *thisTypes = TypeScript::ThisTypes(callee->script());
+    HeapTypeSet *returnTypes = TypeScript::ReturnTypes(callee->script());
 
     if (callsite->isNew) {
         /*
          * If the script does not return a value then the pushed value is the
          * new object (typical case). Note that we don't model construction of
          * the new value, which is done dynamically; we don't keep track of the
          * possible 'new' types for a given prototype type object.
          */
-        thisTypes->addSubset(cx, callsite->returnTypes);
-        returnTypes->addFilterPrimitives(cx, callsite->returnTypes,
-                                         TypeSet::FILTER_ALL_PRIMITIVES);
+        thisTypes->addSubset(cx, returnTypes);
+        returnTypes->addFilterPrimitives(cx, callsite->returnTypes);
     } else {
         /*
          * Add a binding for the return value of the call. We don't add a
          * binding for the receiver object, as this is done with PropagateThis
          * constraints added by the original JSOP_CALL* op. The type sets we
          * manipulate here have lost any correlations between particular types
          * in the 'this' and 'callee' sets, which we want to maintain for
          * polymorphic JSOP_CALLPROP invocations.
@@ -1359,71 +1469,38 @@ class TypeConstraintFreeze : public Type
 {
 public:
     RecompileInfo info;
 
     /* Whether a new type has already been added, triggering recompilation. */
     bool typeAdded;
 
     TypeConstraintFreeze(RecompileInfo info)
-        : TypeConstraint("freeze"), info(info), typeAdded(false)
+        : info(info), typeAdded(false)
     {}
 
+    const char *kind() { return "freeze"; }
+
     void newType(JSContext *cx, TypeSet *source, Type type)
     {
         if (typeAdded)
             return;
 
         typeAdded = true;
         cx->compartment->types.addPendingRecompile(cx, info);
     }
 };
 
 void
-TypeSet::addFreeze(JSContext *cx)
+HeapTypeSet::addFreeze(JSContext *cx)
 {
     add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreeze>(
                 cx->compartment->types.compiledInfo), false);
 }
 
-/*
- * Constraint which triggers recompilation of a script if a possible new JSValueType
- * tag is realized for a type set.
- */
-class TypeConstraintFreezeTypeTag : public TypeConstraint
-{
-public:
-    RecompileInfo info;
-
-    /*
-     * Whether the type tag has been marked unknown due to a type change which
-     * occurred after this constraint was generated (and which triggered recompilation).
-     */
-    bool typeUnknown;
-
-    TypeConstraintFreezeTypeTag(RecompileInfo info)
-        : TypeConstraint("freezeTypeTag"), info(info), typeUnknown(false)
-    {}
-
-    void newType(JSContext *cx, TypeSet *source, Type type)
-    {
-        if (typeUnknown)
-            return;
-
-        if (!type.isUnknown() && !type.isAnyObject() && type.isObject()) {
-            /* Ignore new objects when the type set already has other objects. */
-            if (source->getObjectCount() >= 2)
-                return;
-        }
-
-        typeUnknown = true;
-        cx->compartment->types.addPendingRecompile(cx, info);
-    }
-};
-
 static inline JSValueType
 GetValueTypeFromTypeFlags(TypeFlags flags)
 {
     switch (flags) {
       case TYPE_FLAG_UNDEFINED:
         return JSVAL_TYPE_UNDEFINED;
       case TYPE_FLAG_NULL:
         return JSVAL_TYPE_NULL;
@@ -1440,174 +1517,115 @@ GetValueTypeFromTypeFlags(TypeFlags flag
       case TYPE_FLAG_ANYOBJECT:
         return JSVAL_TYPE_OBJECT;
       default:
         return JSVAL_TYPE_UNKNOWN;
     }
 }
 
 JSValueType
-TypeSet::getKnownTypeTag(JSContext *cx)
+StackTypeSet::getKnownTypeTag()
 {
     TypeFlags flags = baseFlags();
     JSValueType type;
 
     if (baseObjectCount())
         type = flags ? JSVAL_TYPE_UNKNOWN : JSVAL_TYPE_OBJECT;
     else
         type = GetValueTypeFromTypeFlags(flags);
 
     /*
      * If the type set is totally empty then it will be treated as unknown,
      * but we still need to record the dependency as adding a new type can give
      * it a definite type tag. This is not needed if there are enough types
      * that the exact tag is unknown, as it will stay unknown as more types are
      * added to the set.
      */
-    bool empty = flags == 0 && baseObjectCount() == 0;
+    DebugOnly<bool> empty = flags == 0 && baseObjectCount() == 0;
     JS_ASSERT_IF(empty, type == JSVAL_TYPE_UNKNOWN);
 
-    if (cx->compartment->types.compiledInfo.compilerOutput(cx)->script &&
-        (empty || type != JSVAL_TYPE_UNKNOWN))
-    {
-        add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeTypeTag>(
-                  cx->compartment->types.compiledInfo), false);
-    }
-
     return type;
 }
 
 /* Constraint which triggers recompilation if an object acquires particular flags. */
 class TypeConstraintFreezeObjectFlags : public TypeConstraint
 {
 public:
     RecompileInfo info;
 
     /* Flags we are watching for on this object. */
     TypeObjectFlags flags;
 
     /* Whether the object has already been marked as having one of the flags. */
-    bool *pmarked;
-    bool localMarked;
-
-    TypeConstraintFreezeObjectFlags(RecompileInfo info, TypeObjectFlags flags, bool *pmarked)
-        : TypeConstraint("freezeObjectFlags"), info(info), flags(flags),
-          pmarked(pmarked), localMarked(false)
-    {}
+    bool marked;
 
     TypeConstraintFreezeObjectFlags(RecompileInfo info, TypeObjectFlags flags)
-        : TypeConstraint("freezeObjectFlags"), info(info), flags(flags),
-          pmarked(&localMarked), localMarked(false)
+        : info(info), flags(flags),
+          marked(false)
     {}
 
+    const char *kind() { return "freezeObjectFlags"; }
+
     void newType(JSContext *cx, TypeSet *source, Type type) {}
 
     void newObjectState(JSContext *cx, TypeObject *object, bool force)
     {
-        if (object->hasAnyFlags(flags) && !*pmarked) {
-            *pmarked = true;
-            cx->compartment->types.addPendingRecompile(cx, info);
-        } else if (force) {
+        if (!marked && (object->hasAnyFlags(flags) || (!flags && force))) {
+            marked = true;
             cx->compartment->types.addPendingRecompile(cx, info);
         }
     }
 };
 
-/*
- * Constraint which triggers recompilation if any object in a type set acquire
- * particular flags.
- */
-class TypeConstraintFreezeObjectFlagsSet : public TypeConstraint
-{
-public:
-    RecompileInfo info;
-
-    TypeObjectFlags flags;
-    bool marked;
-
-    TypeConstraintFreezeObjectFlagsSet(RecompileInfo info, TypeObjectFlags flags)
-        : TypeConstraint("freezeObjectKindSet"), info(info), flags(flags), marked(false)
-    {}
-
-    void newType(JSContext *cx, TypeSet *source, Type type)
-    {
-        if (marked) {
-            /* Despecialized the kind we were interested in due to recompilation. */
-            return;
-        }
-
-        if (type.isUnknown() || type.isAnyObject()) {
-            /* Fallthrough and recompile. */
-        } else if (type.isObject()) {
-            TypeObject *object = type.isSingleObject()
-                ? type.singleObject()->getType(cx)
-                : type.typeObject();
-            if (!object->hasAnyFlags(flags)) {
-                /*
-                 * Add a constraint on the the object to pick up changes in the
-                 * object's properties.
-                 */
-                TypeSet *types = object->getProperty(cx, JSID_EMPTY, false);
-                if (!types)
-                    return;
-                types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>(
-                                  info, flags, &marked), false);
-                return;
-            }
-        } else {
-            return;
-        }
-
-        marked = true;
-        cx->compartment->types.addPendingRecompile(cx, info);
-    }
-};
-
 bool
-TypeSet::hasObjectFlags(JSContext *cx, TypeObjectFlags flags)
+StackTypeSet::hasObjectFlags(JSContext *cx, TypeObjectFlags flags)
 {
     if (unknownObject())
         return true;
 
     /*
      * Treat type sets containing no objects as having all object flags,
      * to spare callers from having to check this.
      */
     if (baseObjectCount() == 0)
         return true;
 
     unsigned count = getObjectCount();
     for (unsigned i = 0; i < count; i++) {
         TypeObject *object = getTypeObject(i);
         if (!object) {
             JSObject *obj = getSingleObject(i);
-            if (obj)
-                object = obj->getType(cx);
+            if (!obj)
+                continue;
+            object = obj->getType(cx);
         }
-        if (object && object->hasAnyFlags(flags))
+        if (object->hasAnyFlags(flags))
             return true;
-    }
-
-    /*
-     * Watch for new objects of different kind, and re-traverse existing types
-     * in this set to add any needed FreezeArray constraints.
-     */
-    add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlagsSet>(
-                 cx->compartment->types.compiledInfo, flags));
+
+        /*
+         * Add a constraint on the the object to pick up changes in the
+         * object's properties.
+         */
+        TypeSet *types = object->getProperty(cx, JSID_EMPTY, false);
+        if (!types)
+            return true;
+        types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>(
+                          cx->compartment->types.compiledInfo, flags), false);
+    }
 
     return false;
 }
 
 bool
-TypeSet::HasObjectFlags(JSContext *cx, TypeObject *object, TypeObjectFlags flags)
+HeapTypeSet::HasObjectFlags(JSContext *cx, TypeObject *object, TypeObjectFlags flags)
 {
     if (object->hasAnyFlags(flags))
         return true;
 
-    TypeSet *types = object->getProperty(cx, JSID_EMPTY, false);
+    HeapTypeSet *types = object->getProperty(cx, JSID_EMPTY, false);
     if (!types)
         return true;
     types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>(
                       cx->compartment->types.compiledInfo, flags), false);
     return false;
 }
 
 static inline void
@@ -1628,20 +1646,20 @@ ObjectStateChange(JSContext *cx, TypeObj
         while (constraint) {
             constraint->newObjectState(cx, object, force);
             constraint = constraint->next;
         }
     }
 }
 
 void
-TypeSet::WatchObjectStateChange(JSContext *cx, TypeObject *obj)
+HeapTypeSet::WatchObjectStateChange(JSContext *cx, TypeObject *obj)
 {
     JS_ASSERT(!obj->unknownProperties());
-    TypeSet *types = obj->getProperty(cx, JSID_EMPTY, false);
+    HeapTypeSet *types = obj->getProperty(cx, JSID_EMPTY, false);
     if (!types)
         return;
 
     /*
      * Use a constraint which triggers recompilation when markStateChange is
      * called, which will set 'force' to true.
      */
     types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>(
@@ -1653,38 +1671,39 @@ class TypeConstraintFreezeOwnProperty : 
 {
 public:
     RecompileInfo info;
 
     bool updated;
     bool configurable;
 
     TypeConstraintFreezeOwnProperty(RecompileInfo info, bool configurable)
-        : TypeConstraint("freezeOwnProperty"),
-          info(info), updated(false), configurable(configurable)
+        : info(info), updated(false), configurable(configurable)
     {}
 
+    const char *kind() { return "freezeOwnProperty"; }
+
     void newType(JSContext *cx, TypeSet *source, Type type) {}
 
     void newPropertyState(JSContext *cx, TypeSet *source)
     {
         if (updated)
             return;
-        if (source->isOwnProperty(configurable)) {
+        if (source->ownProperty(configurable)) {
             updated = true;
             cx->compartment->types.addPendingRecompile(cx, info);
         }
     }
 };
 
 static void
 CheckNewScriptProperties(JSContext *cx, HandleTypeObject type, JSFunction *fun);
 
 bool
-TypeSet::isOwnProperty(JSContext *cx, TypeObject *object, bool configurable)
+HeapTypeSet::isOwnProperty(JSContext *cx, TypeObject *object, bool configurable)
 {
     /*
      * 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.
      */
     if (object->flags & OBJECT_FLAG_NEW_SCRIPT_REGENERATE) {
@@ -1692,39 +1711,41 @@ TypeSet::isOwnProperty(JSContext *cx, Ty
             Rooted<TypeObject*> typeObj(cx, object);
             CheckNewScriptProperties(cx, typeObj, object->newScript->fun);
         } else {
             JS_ASSERT(object->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED);
             object->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
         }
     }
 
-    if (isOwnProperty(configurable))
+    if (ownProperty(configurable))
         return true;
 
     add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeOwnProperty>(
                                                       cx->compartment->types.compiledInfo,
                                                       configurable), false);
     return false;
 }
 
 bool
-TypeSet::knownNonEmpty(JSContext *cx)
+HeapTypeSet::knownNonEmpty(JSContext *cx)
 {
     if (baseFlags() != 0 || baseObjectCount() != 0)
         return true;
 
     addFreeze(cx);
 
     return false;
 }
 
 bool
-TypeSet::knownSubset(JSContext *cx, TypeSet *other)
-{
+HeapTypeSet::knownSubset(JSContext *cx, TypeSet *other)
+{
+    JS_ASSERT(!other->constraintsPurged());
+
     if ((baseFlags() & other->baseFlags()) != baseFlags())
         return false;
 
     if (unknownObject()) {
         JS_ASSERT(other->unknownObject());
     } else {
         for (unsigned i = 0; i < getObjectCount(); i++) {
             TypeObjectKey *obj = getObject(i);
@@ -1736,17 +1757,17 @@ TypeSet::knownSubset(JSContext *cx, Type
     }
 
     addFreeze(cx);
 
     return true;
 }
 
 int
-TypeSet::getTypedArrayType(JSContext *cx)
+StackTypeSet::getTypedArrayType()
 {
     int arrayType = TypedArray::TYPE_MAX;
     unsigned count = getObjectCount();
 
     for (unsigned i = 0; i < count; i++) {
         JSObject *proto = NULL;
         if (JSObject *object = getSingleObject(i)) {
             proto = object->getProto();
@@ -1772,51 +1793,177 @@ TypeSet::getTypedArrayType(JSContext *cx
 
     /*
      * Assume the caller checked that OBJECT_FLAG_NON_TYPED_ARRAY is not set.
      * This means the set contains at least one object because sets with no
      * objects have all object flags.
      */
     JS_ASSERT(arrayType != TypedArray::TYPE_MAX);
 
-    /* Recompile when another typed array is added to this set. */
-    addFreeze(cx);
-
     return arrayType;
 }
 
 JSObject *
-TypeSet::getSingleton(JSContext *cx, bool freeze)
+StackTypeSet::getSingleton()
+{
+    if (baseFlags() != 0 || baseObjectCount() != 1)
+        return NULL;
+
+    return getSingleObject(0);
+}
+
+JSObject *
+HeapTypeSet::getSingleton(JSContext *cx)
 {
     if (baseFlags() != 0 || baseObjectCount() != 1)
         return NULL;
 
     JSObject *obj = getSingleObject(0);
-    if (!obj)
-        return NULL;
-
-    if (freeze) {
-        add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreeze>(
-                                               cx->compartment->types.compiledInfo), false);
-    }
+
+    if (obj)
+        addFreeze(cx);
 
     return obj;
 }
 
 bool
-TypeSet::needsBarrier(JSContext *cx)
+HeapTypeSet::needsBarrier(JSContext *cx)
 {
     bool result = unknownObject()
                || getObjectCount() > 0
                || hasAnyFlag(TYPE_FLAG_STRING);
     if (!result)
         addFreeze(cx);
     return result;
 }
 
+bool
+StackTypeSet::propertyNeedsBarrier(JSContext *cx, jsid id)
+{
+    id = MakeTypeId(cx, id);
+
+    if (unknownObject())
+        return true;
+
+    for (unsigned i = 0; i < getObjectCount(); i++) {
+        if (getSingleObject(i))
+            return true;
+
+        if (types::TypeObject *otype = getTypeObject(i)) {
+            if (otype->unknownProperties())
+                return true;
+
+            if (types::HeapTypeSet *propTypes = otype->maybeGetProperty(cx, id)) {
+                if (propTypes->needsBarrier(cx))
+                    return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+enum RecompileKind {
+    RECOMPILE_CHECK_MONITORED,
+    RECOMPILE_CHECK_BARRIERS,
+    RECOMPILE_NONE
+};
+
+/*
+ * Whether all jitcode for a given pc was compiled with monitoring or barriers.
+ * If we reanalyze the script after generating jitcode, new monitoring and
+ * barriers will be added which may be duplicating information available when
+ * the script was originally compiled, and which should not invalidate that
+ * compilation.
+ */
+static inline bool
+JITCodeHasCheck(JSScript *script, jsbytecode *pc, RecompileKind kind)
+{
+    if (kind == RECOMPILE_NONE)
+        return false;
+
+#ifdef JS_METHODJIT
+    for (int constructing = 0; constructing <= 1; constructing++) {
+        for (int barriers = 0; barriers <= 1; barriers++) {
+            mjit::JITScript *jit = script->getJIT((bool) constructing, (bool) barriers);
+            if (!jit)
+                continue;
+            mjit::JITChunk *chunk = jit->chunk(pc);
+            if (!chunk)
+                continue;
+            bool found = false;
+            uint32_t count = (kind == RECOMPILE_CHECK_MONITORED)
+                             ? chunk->nMonitoredBytecodes
+                             : chunk->nTypeBarrierBytecodes;
+            uint32_t *bytecodes = (kind == RECOMPILE_CHECK_MONITORED)
+                                  ? chunk->monitoredBytecodes()
+                                  : chunk->typeBarrierBytecodes();
+            for (size_t i = 0; i < count; i++) {
+                if (bytecodes[i] == uint32_t(pc - script->code))
+                    found = true;
+            }
+            if (!found)
+                return false;
+        }
+    }
+#endif
+
+    return true;
+}
+
+/*
+ * Force recompilation of any jitcode for script at pc, or of any other script
+ * which this script was inlined into.
+ */
+static inline void
+AddPendingRecompile(JSContext *cx, JSScript *script, jsbytecode *pc,
+                    RecompileKind kind = RECOMPILE_NONE)
+{
+    /*
+     * Trigger recompilation of the script itself, if code was not previously
+     * compiled with the specified information.
+     */
+    if (!JITCodeHasCheck(script, pc, kind))
+        cx->compartment->types.addPendingRecompile(cx, script, pc);
+
+    /*
+     * When one script is inlined into another the caller listens to state
+     * changes on the callee's script, so trigger these to force recompilation
+     * of any such callers.
+     */
+    if (script->function() && !script->function()->hasLazyType())
+        ObjectStateChange(cx, script->function()->type(), false, true);
+}
+
+/*
+ * As for TypeConstraintFreeze, but describes an implicit freeze constraint
+ * added for stack types within a script. Applies to all compilations of the
+ * script, not just a single one.
+ */
+class TypeConstraintFreezeStack : public TypeConstraint
+{
+public:
+    JSScript *script;
+
+    TypeConstraintFreezeStack(JSScript *script)
+        : script(script)
+    {}
+
+    const char *kind() { return "freezeStack"; }
+
+    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.
+         */
+        AddPendingRecompile(cx, script, NULL);
+    }
+};
+
 /////////////////////////////////////////////////////////////////////
 // TypeCompartment
 /////////////////////////////////////////////////////////////////////
 
 void
 TypeCompartment::init(JSContext *cx)
 {
     PodZero(this);
@@ -1982,17 +2129,17 @@ types::ArrayPrototypeHasIndexedProperty(
     JSObject *proto = script->global().getOrCreateArrayPrototype(cx);
     if (!proto)
         return true;
 
     do {
         TypeObject *type = proto->getType(cx);
         if (type->unknownProperties())
             return true;
-        TypeSet *indexTypes = type->getProperty(cx, JSID_VOID, false);
+        HeapTypeSet *indexTypes = type->getProperty(cx, JSID_VOID, false);
         if (!indexTypes || indexTypes->isOwnProperty(cx, type, true) || indexTypes->knownNonEmpty(cx))
             return true;
         proto = proto->getProto();
     } while (proto);
 
     return false;
 }
 
@@ -2156,58 +2303,63 @@ TypeCompartment::addPendingRecompile(JSC
 
 #ifdef JS_METHODJIT
     for (int constructing = 0; constructing <= 1; constructing++) {
         for (int barriers = 0; barriers <= 1; barriers++) {
             mjit::JITScript *jit = script->getJIT((bool) constructing, (bool) barriers);
             if (!jit)
                 continue;
 
-            unsigned int chunkIndex = jit->chunkIndex(pc);
-            mjit::JITChunk *chunk = jit->chunkDescriptor(chunkIndex).chunk;
-            if (!chunk)
-                continue;
-
-            addPendingRecompile(cx, chunk->recompileInfo);
+            if (pc) {
+                unsigned int chunkIndex = jit->chunkIndex(pc);
+                mjit::JITChunk *chunk = jit->chunkDescriptor(chunkIndex).chunk;
+                if (chunk)
+                    addPendingRecompile(cx, chunk->recompileInfo);
+            } else {
+                for (size_t chunkIndex = 0; chunkIndex < jit->nchunks; chunkIndex++) {
+                    mjit::JITChunk *chunk = jit->chunkDescriptor(chunkIndex).chunk;
+                    if (chunk)
+                        addPendingRecompile(cx, chunk->recompileInfo);
+                }
+            }
         }
     }
 #endif
 }
 
 void
 TypeCompartment::monitorBytecode(JSContext *cx, JSScript *script, uint32_t offset,
                                  bool returnOnly)
 {
+    if (!script->ensureRanInference(cx))
+        return;
+
     ScriptAnalysis *analysis = script->analysis();
-    JS_ASSERT(analysis->ranInference());
-
     jsbytecode *pc = script->code + offset;
 
     JS_ASSERT_IF(returnOnly, js_CodeSpec[*pc].format & JOF_INVOKE);
 
     Bytecode &code = analysis->getCode(pc);
 
     if (returnOnly ? code.monitoredTypesReturn : code.monitoredTypes)
         return;
 
     InferSpew(ISpewOps, "addMonitorNeeded:%s #%u:%05u",
               returnOnly ? " returnOnly" : "", script->id(), offset);
 
     /* Dynamically monitor this call to keep track of its result types. */
     if (js_CodeSpec[*pc].format & JOF_INVOKE)
         code.monitoredTypesReturn = true;
 
-    if (!returnOnly)
-        code.monitoredTypes = true;
-
-    cx->compartment->types.addPendingRecompile(cx, script, pc);
-
-    /* Trigger recompilation of any inline callers. */
-    if (script->function() && !script->function()->hasLazyType())
-        ObjectStateChange(cx, script->function()->type(), false, true);
+    if (returnOnly)
+        return;
+
+    code.monitoredTypes = true;
+
+    AddPendingRecompile(cx, script, pc, RECOMPILE_CHECK_MONITORED);
 }
 
 void
 TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target)
 {
     JS_ASSERT(this == &cx->compartment->types);
     JS_ASSERT(!(target->flags & OBJECT_FLAG_SETS_MARKED_UNKNOWN));
     JS_ASSERT(!target->singleton);
@@ -2287,37 +2439,33 @@ ScriptAnalysis::addTypeBarrier(JSContext
 
     if (!code.typeBarriers) {
         /*
          * Adding type barriers at a bytecode which did not have them before
          * will trigger recompilation. If there were already type barriers,
          * however, do not trigger recompilation (the script will be recompiled
          * if any of the barriers is ever violated).
          */
-        cx->compartment->types.addPendingRecompile(cx, script, const_cast<jsbytecode*>(pc));
-
-        /* Trigger recompilation of any inline callers. */
-        if (script->function() && !script->function()->hasLazyType())
-            ObjectStateChange(cx, script->function()->type(), false, true);
+        AddPendingRecompile(cx, script, const_cast<jsbytecode*>(pc), RECOMPILE_CHECK_BARRIERS);
     }
 
     /* Ignore duplicate barriers. */
     TypeBarrier *barrier = code.typeBarriers;
     while (barrier) {
         if (barrier->target == target && barrier->type == type && !barrier->singleton)
             return;
         barrier = barrier->next;
     }
 
     InferSpew(ISpewOps, "typeBarrier: #%u:%05u: %sT%p%s %s",
               script->id(), pc - script->code,
               InferSpewColor(target), target, InferSpewColorReset(),
               TypeString(type));
 
-    barrier = cx->typeLifoAlloc().new_<TypeBarrier>(target, type, (JSObject *) NULL, JSID_VOID);
+    barrier = cx->analysisLifoAlloc().new_<TypeBarrier>(target, type, (JSObject *) NULL, JSID_VOID);
 
     if (!barrier) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return;
     }
 
     barrier->next = code.typeBarriers;
     code.typeBarriers = barrier;
@@ -2327,27 +2475,25 @@ void
 ScriptAnalysis::addSingletonTypeBarrier(JSContext *cx, const jsbytecode *pc, TypeSet *target, JSObject *singleton, jsid singletonId)
 {
     JS_ASSERT(singletonId == MakeTypeId(cx, singletonId) && !JSID_IS_VOID(singletonId));
 
     Bytecode &code = getCode(pc);
 
     if (!code.typeBarriers) {
         /* Trigger recompilation as for normal type barriers. */
-        cx->compartment->types.addPendingRecompile(cx, script, const_cast<jsbytecode*>(pc));
-        if (script->function() && !script->function()->hasLazyType())
-            ObjectStateChange(cx, script->function()->type(), false, true);
+        AddPendingRecompile(cx, script, const_cast<jsbytecode*>(pc), RECOMPILE_CHECK_BARRIERS);
     }
 
     InferSpew(ISpewOps, "singletonTypeBarrier: #%u:%05u: %sT%p%s %p %s",
               script->id(), pc - script->code,
               InferSpewColor(target), target, InferSpewColorReset(),
               (void *) singleton, TypeIdString(singletonId));
 
-    TypeBarrier *barrier = cx->typeLifoAlloc().new_<TypeBarrier>(target, Type::UndefinedType(),
+    TypeBarrier *barrier = cx->analysisLifoAlloc().new_<TypeBarrier>(target, Type::UndefinedType(),
                               singleton, singletonId);
 
     if (!barrier) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return;
     }
 
     barrier->next = code.typeBarriers;
@@ -2682,29 +2828,28 @@ TypeObject::getFromPrototypes(JSContext 
     if (!force && types->hasPropagatedProperty())
         return;
 
     types->setPropagatedProperty();
 
     if (!proto)
         return;
 
-    RootedTypeObject self(cx, this);
     if (proto->getType(cx)->unknownProperties()) {
         types->addType(cx, Type::UnknownType());
         return;
     }
 
-    TypeSet *protoTypes = self->proto->getType(cx)->getProperty(cx, id, false);
+    HeapTypeSet *protoTypes = proto->getType(cx)->getProperty(cx, id, false);
     if (!protoTypes)
         return;
 
     protoTypes->addSubset(cx, types);
 
-    self->proto->getType(cx)->getFromPrototypes(cx, id, protoTypes);
+    proto->getType(cx)->getFromPrototypes(cx, id, protoTypes);
 }
 
 static inline void
 UpdatePropertyType(JSContext *cx, TypeSet *types, JSObject *obj, Shape *shape, bool force)
 {
     types->setOwnProperty(cx, false);
     if (!shape->writable())
         types->setOwnProperty(cx, true);
@@ -2805,17 +2950,17 @@ TypeObject::addDefiniteProperties(JSCont
 bool
 TypeObject::matchDefiniteProperties(JSObject *obj)
 {
     unsigned count = getPropertyCount();
     for (unsigned i = 0; i < count; i++) {
         Property *prop = getProperty(i);
         if (!prop)
             continue;
-        if (prop->types.isDefiniteProperty()) {
+        if (prop->types.definiteProperty()) {
             unsigned slot = prop->types.definiteSlot();
 
             bool found = false;
             Shape *shape = obj->lastProperty();
             while (!shape->isEmptyShape()) {
                 if (shape->slot() == slot && shape->propid() == prop->id) {
                     found = true;
                     break;
@@ -2993,17 +3138,17 @@ TypeObject::clearNewScript(JSContext *cx
      * bits on the object's properties, just mark such properties as having
      * been deleted/reconfigured, which will have the same effect on JITs
      * wanting to use the definite bits to optimize property accesses.
      */
     for (unsigned i = 0; i < getPropertyCount(); i++) {
         Property *prop = getProperty(i);
         if (!prop)
             continue;
-        if (prop->types.isDefiniteProperty())
+        if (prop->types.definiteProperty())
             prop->types.setOwnProperty(cx, true);
     }
 
     /*
      * If we cleared the new script while in the middle of initializing an
      * object, it will still have the new script's shape and reflect the no
      * longer correct state of the object once its initialization is completed.
      * We can't really detect the possibility of this statically, but the new
@@ -3167,16 +3312,26 @@ GetInitializerType(JSContext *cx, JSScri
     JSProtoKey key = isArray ? JSProto_Array : JSProto_Object;
 
     if (UseNewTypeForInitializer(cx, script, pc, key))
         return NULL;
 
     return TypeScript::InitObject(cx, script, pc, key);
 }
 
+static inline Type
+GetCalleeThisType(jsbytecode *pc)
+{
+    pc += GetBytecodeLength(pc);
+    if (*pc == JSOP_UNDEFINED)
+        return Type::UndefinedType();
+    JS_ASSERT(*pc == JSOP_IMPLICITTHIS);
+    return Type::UnknownType();
+}
+
 /* Analyze type information for a single bytecode. */
 bool
 ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
                                      TypeInferenceState &state)
 {
     jsbytecode *pc = script->code + offset;
     JSOp op = (JSOp)*pc;
 
@@ -3184,17 +3339,17 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
     JS_ASSERT(!code.pushedTypes);
 
     InferSpew(ISpewOps, "analyze: #%u:%05u", script->id(), offset);
 
     unsigned defCount = GetDefCount(script, offset);
     if (ExtendedDef(pc))
         defCount++;
 
-    TypeSet *pushed = cx->typeLifoAlloc().newArrayUninitialized<TypeSet>(defCount);
+    StackTypeSet *pushed = cx->analysisLifoAlloc().newArrayUninitialized<StackTypeSet>(defCount);
     if (!pushed)
         return false;
     PodZero(pushed, defCount);
     code.pushedTypes = pushed;
 
     /*
      * Add phi nodes introduced at this point to the list of all phi nodes in
      * the script. Types for these are not generated until after the script has
@@ -3214,31 +3369,34 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
              * node created should be in the phiValues list on some bytecode.
              */
             if (!state.phiNodes.append(newv->value.phiNode()))
                 return false;
             TypeSet &types = newv->value.phiNode()->types;
             InferSpew(ISpewOps, "typeSet: %sT%p%s phi #%u:%05u:%u",
                       InferSpewColor(&types), &types, InferSpewColorReset(),
                       script->id(), offset, newv->slot);
+            types.setPurged();
+
             newv++;
         }
     }
 
     /*
      * Treat decomposed ops as no-ops, we will analyze the decomposed version
      * instead. (We do, however, need to look at introduced phi nodes).
      */
     if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
         return true;
 
     for (unsigned i = 0; i < defCount; i++) {
         InferSpew(ISpewOps, "typeSet: %sT%p%s pushed%u #%u:%05u",
                   InferSpewColor(&pushed[i]), &pushed[i], InferSpewColorReset(),
                   i, script->id(), offset);
+        pushed[i].setPurged();
     }
 
     /* Add type constraints for the various opcodes. */
     switch (op) {
 
         /* Nop bytecodes. */
       case JSOP_POP:
       case JSOP_NOP:
@@ -3376,74 +3534,79 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
             poppedTypes(pc, i)->addSubset(cx, &pushed[pickedDepth - 1 - i]);
         break;
       }
 
       case JSOP_GETGNAME:
       case JSOP_CALLGNAME: {
         jsid id = GetAtomId(cx, script, pc, 0);
 
-        TypeSet *seen = bytecodeTypes(pc);
+        StackTypeSet *seen = bytecodeTypes(pc);
         seen->addSubset(cx, &pushed[0]);
 
         /*
          * Normally we rely on lazy standard class initialization to fill in
          * the types of global properties the script can access. In a few cases
          * the method JIT will bypass this, and we need to add the types direclty.
          */
         if (id == NameToId(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]))
             seen->addType(cx, Type::UndefinedType());
         if (id == NameToId(cx->runtime->atomState.NaNAtom))
             seen->addType(cx, Type::DoubleType());
         if (id == NameToId(cx->runtime->atomState.InfinityAtom))
             seen->addType(cx, Type::DoubleType());
 
+        TypeObject *global = script->global().getType(cx);
+
         /* Handle as a property access. */
-        PropertyAccess(cx, script, pc, script->global().getType(cx), false, seen, id);
+        if (state.hasPropertyReadTypes)
+            PropertyAccess<PROPERTY_READ_EXISTING>(cx, script, pc, global, seen, id);
+        else
+            PropertyAccess<PROPERTY_READ>(cx, script, pc, global, seen, id);
 
         if (op == JSOP_CALLGNAME)
-            pushed[0].addPropagateThis(cx, script, pc, Type::UnknownType());
+            pushed[0].addPropagateThis(cx, script, pc, GetCalleeThisType(pc));
 
         if (CheckNextTest(pc))
             pushed[0].addType(cx, Type::UndefinedType());
         break;
       }
 
       case JSOP_NAME:
       case JSOP_INTRINSICNAME:
       case JSOP_CALLNAME:
       case JSOP_CALLINTRINSIC: {
-        TypeSet *seen = bytecodeTypes(pc);
+        StackTypeSet *seen = bytecodeTypes(pc);
         addTypeBarrier(cx, pc, seen, Type::UnknownType());
         seen->addSubset(cx, &pushed[0]);
         if (op == JSOP_CALLNAME || op == JSOP_CALLINTRINSIC)
-            pushed[0].addPropagateThis(cx, script, pc, Type::UnknownType());
+            pushed[0].addPropagateThis(cx, script, pc, GetCalleeThisType(pc));
         break;
       }
 
       case JSOP_BINDGNAME:
       case JSOP_BINDNAME:
         break;
 
       case JSOP_SETGNAME: {
         jsid id = GetAtomId(cx, script, pc, 0);
-        PropertyAccess(cx, script, pc, script->global().getType(cx),
-                       true, poppedTypes(pc, 0), id);
+        TypeObject *global = script->global().getType(cx);
+        PropertyAccess<PROPERTY_WRITE>(cx, script, pc, global, poppedTypes(pc, 0), id);
         poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
       }
 
       case JSOP_SETNAME:
       case JSOP_SETCONST:
         cx->compartment->types.monitorBytecode(cx, script, offset);
         poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
 
       case JSOP_GETXPROP: {
-        TypeSet *seen = bytecodeTypes(pc);
+        StackTypeSet *seen = bytecodeTypes(pc);
         addTypeBarrier(cx, pc, seen, Type::UnknownType());
         seen->addSubset(cx, &pushed[0]);
         break;
       }
 
       case JSOP_GETARG:
       case JSOP_CALLARG:
       case JSOP_GETLOCAL:
@@ -3452,17 +3615,17 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
         if (trackSlot(slot)) {
             /*
              * Normally these opcodes don't pop anything, but they are given
              * an extended use holding the variable's SSA value before the
              * access. Use the types from here.
              */
             poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         } else if (slot < TotalSlots(script)) {
-            TypeSet *types = TypeScript::SlotTypes(script, slot);
+            StackTypeSet *types = TypeScript::SlotTypes(script, slot);
             types->addSubset(cx, &pushed[0]);
         } else {
             /* Local 'let' variable. Punt on types for these, for now. */
             pushed[0].addType(cx, Type::UnknownType());
         }
         if (op == JSOP_CALLARG || op == JSOP_CALLLOCAL)
             pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType());
         break;
@@ -3510,17 +3673,17 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
       case JSOP_INCLOCAL:
       case JSOP_DECLOCAL:
       case JSOP_LOCALINC:
       case JSOP_LOCALDEC: {
         uint32_t slot = GetBytecodeSlot(script, pc);
         if (trackSlot(slot)) {
             poppedTypes(pc, 0)->addArith(cx, script, pc, &pushed[0]);
         } else if (slot < TotalSlots(script)) {
-            TypeSet *types = TypeScript::SlotTypes(script, slot);
+            StackTypeSet *types = TypeScript::SlotTypes(script, slot);
             types->addArith(cx, script, pc, types);
             types->addSubset(cx, &pushed[0]);
         } else {
             pushed[0].addType(cx, Type::UnknownType());
         }
         break;
       }
 
@@ -3528,25 +3691,25 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
         /* Compute a precise type only when we know the arguments won't escape. */
         if (script->needsArgsObj())
             pushed[0].addType(cx, Type::UnknownType());
         else
             pushed[0].addType(cx, Type::MagicArgType());
         break;
 
       case JSOP_REST: {
-        TypeSet *types = script->analysis()->bytecodeTypes(pc);
+        StackTypeSet *types = script->analysis()->bytecodeTypes(pc);
         if (script->hasGlobal()) {
             TypeObject *rest = TypeScript::InitObject(cx, script, pc, JSProto_Array);
             if (!rest)
                 return false;
             types->addType(cx, Type::ObjectType(rest));
 
             // Simulate setting a element.
-            TypeSet *propTypes = rest->getProperty(cx, JSID_VOID, true);
+            HeapTypeSet *propTypes = rest->getProperty(cx, JSID_VOID, true);
             if (!propTypes)
                 return false;
             propTypes->addType(cx, Type::UnknownType());
         } else {
             types->addType(cx, Type::UnknownType());
         }
         types->addSubset(cx, &pushed[0]);
         break;
@@ -3559,38 +3722,62 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
         poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
       }
 
       case JSOP_LENGTH:
       case JSOP_GETPROP:
       case JSOP_CALLPROP: {
         jsid id = GetAtomId(cx, script, pc, 0);
-        TypeSet *seen = script->analysis()->bytecodeTypes(pc);
-
-        poppedTypes(pc, 0)->addGetProperty(cx, script, pc, seen, id);
-        if (op == JSOP_CALLPROP)
-            poppedTypes(pc, 0)->addCallProperty(cx, script, pc, id);
+        StackTypeSet *seen = script->analysis()->bytecodeTypes(pc);
+
+        HeapTypeSet *input = &script->types->propertyReadTypes[state.propertyReadIndex++];
+        poppedTypes(pc, 0)->addSubset(cx, input);
+
+        if (state.hasPropertyReadTypes) {
+            TypeConstraintGetPropertyExisting getProp(script, pc, seen, id);
+            input->addTypesToConstraint(cx, &getProp);
+            if (op == JSOP_CALLPROP) {
+                TypeConstraintCallPropertyExisting callProp(script, pc, id);
+                input->addTypesToConstraint(cx, &callProp);
+            }
+        } else {
+            input->addGetProperty(cx, script, pc, seen, id);
+            if (op == JSOP_CALLPROP)
+                input->addCallProperty(cx, script, pc, id);
+        }
 
         seen->addSubset(cx, &pushed[0]);
         if (CheckNextTest(pc))
             pushed[0].addType(cx, Type::UndefinedType());
         break;
       }
 
       /*
        * We only consider ELEM accesses on integers below. Any element access
        * which is accessing a non-integer property must be monitored.
        */
 
       case JSOP_GETELEM:
       case JSOP_CALLELEM: {
-        TypeSet *seen = script->analysis()->bytecodeTypes(pc);
-
-        poppedTypes(pc, 1)->addGetProperty(cx, script, pc, seen, JSID_VOID);
+        StackTypeSet *seen = script->analysis()->bytecodeTypes(pc);
+
+        /* Don't try to compute a precise callee for CALLELEM. */
+        if (op == JSOP_CALLELEM)
+            seen->addType(cx, Type::AnyObjectType());
+
+        HeapTypeSet *input = &script->types->propertyReadTypes[state.propertyReadIndex++];
+        poppedTypes(pc, 1)->addSubset(cx, input);
+
+        if (state.hasPropertyReadTypes) {
+            TypeConstraintGetPropertyExisting getProp(script, pc, seen, JSID_VOID);
+            input->addTypesToConstraint(cx, &getProp);
+        } else {
+            input->addGetProperty(cx, script, pc, seen, JSID_VOID);
+        }
 
         seen->addSubset(cx, &pushed[0]);
         if (op == JSOP_CALLELEM)
             pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType(), poppedTypes(pc, 1));
         if (CheckNextTest(pc))
             pushed[0].addType(cx, Type::UndefinedType());
         break;
       }
@@ -3658,22 +3845,22 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
       case JSOP_DEFVAR:
         break;
 
       case JSOP_CALL:
       case JSOP_EVAL:
       case JSOP_FUNCALL:
       case JSOP_FUNAPPLY:
       case JSOP_NEW: {
-        TypeSet *seen = script->analysis()->bytecodeTypes(pc);
+        StackTypeSet *seen = script->analysis()->bytecodeTypes(pc);
         seen->addSubset(cx, &pushed[0]);
 
         /* Construct the base call information about this site. */
         unsigned argCount = GetUseCount(script, offset) - 2;
-        TypeCallsite *callsite = cx->typeLifoAlloc().new_<TypeCallsite>(
+        TypeCallsite *callsite = cx->analysisLifoAlloc().new_<TypeCallsite>(
                                                         cx, script, pc, op == JSOP_NEW, argCount);
         if (!callsite || (argCount && !callsite->argumentTypes)) {
             cx->compartment->types.setPendingNukeTypes(cx);
             break;
         }
         callsite->thisTypes = poppedTypes(pc, argCount);
         callsite->returnTypes = seen;
 
@@ -3683,24 +3870,28 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
         /*
          * Mark FUNCALL and FUNAPPLY sites as monitored. The method JIT may
          * lower these into normal calls, and we need to make sure the
          * callee's argument types are checked on entry.
          */
         if (op == JSOP_FUNCALL || op == JSOP_FUNAPPLY)
             cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
 
+        /* Speculate that calls whose result is ignored may return undefined. */
+        if (JSOP_POP == *(pc + GetBytecodeLength(pc)))
+            seen->addType(cx, Type::UndefinedType());
+
         poppedTypes(pc, argCount + 1)->addCall(cx, callsite);
         break;
       }
 
       case JSOP_NEWINIT:
       case JSOP_NEWARRAY:
       case JSOP_NEWOBJECT: {
-        TypeSet *types = script->analysis()->bytecodeTypes(pc);
+        StackTypeSet *types = script->analysis()->bytecodeTypes(pc);
         types->addSubset(cx, &pushed[0]);
 
         bool isArray = (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && GET_UINT8(pc) == JSProto_Array));
         JSProtoKey key = isArray ? JSProto_Array : JSProto_Object;
 
         if (UseNewTypeForInitializer(cx, script, pc, key)) {
             /* Defer types pushed by this bytecode until runtime. */
             break;
@@ -3828,17 +4019,17 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
          * Use a per-script type set to unify the possible target types of all
          * 'for in' or 'for each' loops in the script. We need to mark the
          * value pushed by the ITERNEXT appropriately, but don't track the SSA
          * information to connect that ITERNEXT with the appropriate ITER.
          * This loses some precision when a script mixes 'for in' and
          * 'for each' loops together, oh well.
          */
         if (!state.forTypes) {
-          state.forTypes = TypeSet::make(cx, "forTypes");
+          state.forTypes = StackTypeSet::make(cx, "forTypes");
           if (!state.forTypes)
               return false;
         }
 
         if (GET_UINT8(pc) == JSITER_ENUMERATE)
             state.forTypes->addType(cx, Type::StringType());
         else
             state.forTypes->addType(cx, Type::UnknownType());
@@ -3994,30 +4185,60 @@ ScriptAnalysis::analyzeTypes(JSContext *
     ranInference_ = true;
 
     /* Make sure the initial type set of all local vars includes void. */
     for (unsigned i = 0; i < script->nfixed; i++)
         TypeScript::LocalTypes(script, i)->addType(cx, Type::UndefinedType());
 
     TypeInferenceState state(cx);
 
+    /*
+     * Generate type sets for the inputs to property reads in the script,
+     * unless it already has them. If we purge analysis information and end up
+     * reanalyzing types in the script, we don't want to regenerate constraints
+     * on these property inputs as they will be duplicating information on the
+     * property type sets previously added.
+     */
+    if (script->types->propertyReadTypes) {
+        state.hasPropertyReadTypes = true;
+    } else {
+        HeapTypeSet *typeArray =
+            (HeapTypeSet*) cx->typeLifoAlloc().alloc(sizeof(HeapTypeSet) * numPropertyReads());
+        if (!typeArray) {
+            cx->compartment->types.setPendingNukeTypes(cx);
+            return;
+        }
+        script->types->propertyReadTypes = typeArray;
+        PodZero(typeArray, numPropertyReads());
+
+#ifdef DEBUG
+        for (unsigned i = 0; i < numPropertyReads(); i++) {
+            InferSpew(ISpewOps, "typeSet: %sT%p%s propertyRead%u #%u",
+                      InferSpewColor(&typeArray[i]), &typeArray[i], InferSpewColorReset(),
+                      i, script->id());
+        }
+#endif
+    }
+
     unsigned offset = 0;
     while (offset < script->length) {
         Bytecode *code = maybeCode(offset);
 
         jsbytecode *pc = script->code + offset;
 
         if (code && !analyzeTypesBytecode(cx, offset, state)) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
 
         offset += GetBytecodeLength(pc);
     }
 
+    JS_ASSERT(state.propertyReadIndex == numPropertyReads());
+
     for (unsigned i = 0; i < state.phiNodes.length(); i++) {
         SSAPhiNode *node = state.phiNodes[i];
         for (unsigned j = 0; j < node->length; j++) {
             const SSAValue &v = node->options[j];
             getValueTypes(v)->addSubset(cx, &node->types);
         }
     }
 
@@ -4031,16 +4252,21 @@ ScriptAnalysis::analyzeTypes(JSContext *
         if (result->offset != UINT32_MAX) {
             pushedTypes(result->offset)->addType(cx, result->type);
         } else {
             /* Custom for-in loop iteration has happened in this script. */
             state.forTypes->addType(cx, Type::UnknownType());
         }
         result = result->next;
     }
+
+    if (!script->hasFreezeConstraints) {
+        TypeScript::AddFreezeConstraints(cx, script);
+        script->hasFreezeConstraints = true;
+    }
 }
 
 bool
 ScriptAnalysis::integerOperation(JSContext *cx, jsbytecode *pc)
 {
     JS_ASSERT(uint32_t(pc - script->code) < script->length);
 
     switch (JSOp(*pc)) {
@@ -4048,35 +4274,35 @@ ScriptAnalysis::integerOperation(JSConte
       case JSOP_INCARG:
       case JSOP_DECARG:
       case JSOP_ARGINC:
       case JSOP_ARGDEC:
       case JSOP_INCLOCAL:
       case JSOP_DECLOCAL:
       case JSOP_LOCALINC:
       case JSOP_LOCALDEC: {
-        if (pushedTypes(pc, 0)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
+        if (pushedTypes(pc, 0)->getKnownTypeTag() != JSVAL_TYPE_INT32)
             return false;
         uint32_t slot = GetBytecodeSlot(script, pc);
         if (trackSlot(slot)) {
-            if (poppedTypes(pc, 0)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
+            if (poppedTypes(pc, 0)->getKnownTypeTag() != JSVAL_TYPE_INT32)
                 return false;
         }
         return true;
       }
 
       case JSOP_ADD:
       case JSOP_SUB:
       case JSOP_MUL:
       case JSOP_DIV:
-        if (pushedTypes(pc, 0)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
+        if (pushedTypes(pc, 0)->getKnownTypeTag() != JSVAL_TYPE_INT32)
             return false;
-        if (poppedTypes(pc, 0)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
+        if (poppedTypes(pc, 0)->getKnownTypeTag() != JSVAL_TYPE_INT32)
             return false;
-        if (poppedTypes(pc, 1)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
+        if (poppedTypes(pc, 1)->getKnownTypeTag() != JSVAL_TYPE_INT32)
             return false;
         return true;
 
       default:
         return true;
     }
 }
 
@@ -4085,49 +4311,53 @@ ScriptAnalysis::integerOperation(JSConte
  * an object should a property on another object get a setter.
  */
 class TypeConstraintClearDefiniteSetter : public TypeConstraint
 {
 public:
     TypeObject *object;
 
     TypeConstraintClearDefiniteSetter(TypeObject *object)
-        : TypeConstraint("clearDefiniteSetter"), object(object)
+        : object(object)
     {}
 
+    const char *kind() { return "clearDefiniteSetter"; }
+
     void newPropertyState(JSContext *cx, TypeSet *source)
     {
         if (!object->newScript)
             return;
         /*
          * Clear out the newScript shape and definite property information from
          * an object if the source type set could be a setter or could be
          * non-writable, both of which are indicated by the source type set
          * being marked as configured.
          */
-        if (!(object->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED) && source->isOwnProperty(true))
+        if (!(object->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED) && source->ownProperty(true))
             object->clearNewScript(cx);
     }
 
     void newType(JSContext *cx, TypeSet *source, Type type) {}
 };
 
 /*
  * Constraint which clears definite properties on an object should a type set
  * contain any types other than a single object.
  */
 class TypeConstraintClearDefiniteSingle : public TypeConstraint
 {
 public:
     TypeObject *object;
 
     TypeConstraintClearDefiniteSingle(TypeObject *object)
-        : TypeConstraint("clearDefiniteSingle"), object(object)
+        : object(object)
     {}
 
+    const char *kind() { return "clearDefiniteSingle"; }
+
     void newType(JSContext *cx, TypeSet *source, Type type) {
         if (object->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED)
             return;
 
         if (source->baseFlags() || source->getObjectCount() > 1)
             object->clearNewScript(cx);
     }
 };
@@ -4329,18 +4559,18 @@ AnalyzePoppedThis(JSContext *cx, Vector<
              * a permanent property in any transitive prototype, the definite
              * properties get cleared from the shape.
              */
             JSObject *parent = type->proto;
             while (parent) {
                 TypeObject *parentObject = parent->getType(cx);
                 if (parentObject->unknownProperties())
                     return false;
-                TypeSet *parentTypes = parentObject->getProperty(cx, id, false);
-                if (!parentTypes || parentTypes->isOwnProperty(true))
+                HeapTypeSet *parentTypes = parentObject->getProperty(cx, id, false);
+                if (!parentTypes || parentTypes->ownProperty(true))
                     return false;
                 parentTypes->add(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteSetter>(type));
                 parent = parent->getProto();
             }
 
             unsigned slotSpan = obj->slotSpan();
             RootedValue value(cx, UndefinedValue());
             if (!DefineNativeProperty(cx, obj, id, value, NULL, NULL,
@@ -4394,37 +4624,37 @@ AnalyzePoppedThis(JSContext *cx, Vector<
 
             /*
              * This code may not have run yet, break any type barriers involved
              * in performing the call (for the greater good!).
              */
             analysis->breakTypeBarriersSSA(cx, analysis->poppedValue(calleepc, 0));
             analysis->breakTypeBarriers(cx, calleepc - script->code, true);
 
-            TypeSet *funcallTypes = analysis->poppedTypes(pc, GET_ARGC(pc) + 1);
-            TypeSet *scriptTypes = analysis->poppedTypes(pc, GET_ARGC(pc));
+            StackTypeSet *funcallTypes = analysis->poppedTypes(pc, GET_ARGC(pc) + 1);
+            StackTypeSet *scriptTypes = analysis->poppedTypes(pc, GET_ARGC(pc));
 
             /* Need to definitely be calling Function.call on a specific script. */
-            JSObject *funcallObj = funcallTypes->getSingleton(cx, false);
-            JSObject *scriptObj = scriptTypes->getSingleton(cx, false);
+            JSObject *funcallObj = funcallTypes->getSingleton();
+            JSObject *scriptObj = scriptTypes->getSingleton();
             if (!funcallObj || !scriptObj || !scriptObj->isFunction() ||
                 !scriptObj->toFunction()->isInterpreted()) {
                 return false;
             }
 
             JSFunction *function = scriptObj->toFunction();
 
             /*
              * Generate constraints to clear definite properties from the type
              * should the Function.call or callee itself change in the future.
              */
             funcallTypes->add(cx,
-                cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteSingle>(type));
+                cx->analysisLifoAlloc().new_<TypeConstraintClearDefiniteSingle>(type));
             scriptTypes->add(cx,
-                cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteSingle>(type));
+                cx->analysisLifoAlloc().new_<TypeConstraintClearDefiniteSingle>(type));
 
             TypeNewScript::Initializer pushframe(TypeNewScript::Initializer::FRAME_PUSH, uses->offset);
             if (!initializerList->append(pushframe)) {
                 cx->compartment->types.setPendingNukeTypes(cx);
                 *pbaseobj = NULL;
                 return false;
             }
 
@@ -4712,32 +4942,30 @@ MarkIteratorUnknownSlow(JSContext *cx)
     result = cx->new_<TypeResult>(UINT32_MAX, Type::UnknownType());
     if (!result) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return;
     }
     result->next = script->types->dynamicList;
     script->types->dynamicList = result;
 
+    AddPendingRecompile(cx, script, NULL);
+
     if (!script->hasAnalysis() || !script->analysis()->ranInference())
         return;
 
     ScriptAnalysis *analysis = script->analysis();
 
     for (unsigned i = 0; i < script->length; i++) {
         jsbytecode *pc = script->code + i;
         if (!analysis->maybeCode(pc))
             continue;
         if (JSOp(*pc) == JSOP_ITERNEXT)
             analysis->pushedTypes(pc, 0)->addType(cx, Type::UnknownType());
     }
-
-    /* Trigger recompilation of any inline callers. */
-    if (script->function() && !script->function()->hasLazyType())
-        ObjectStateChange(cx, script->function()->type(), false, true);
 }
 
 void
 TypeMonitorCallSlow(JSContext *cx, JSObject *callee,
                     const CallArgs &args, bool constructing)
 {
     unsigned nargs = callee->toFunction()->nargs;
     JSScript *script = callee->toFunction()->script();
@@ -4859,24 +5087,55 @@ TypeDynamicResult(JSContext *cx, JSScrip
     TypeResult *result = cx->new_<TypeResult>(pc - script->code, type);
     if (!result) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return;
     }
     result->next = script->types->dynamicList;
     script->types->dynamicList = result;
 
+    /*
+     * New type information normally requires all code in the entire script to
+     * be recompiled, as changes to types can flow through variables etc. into
+     * other chunks in the compiled script.
+     *
+     * We can do better than this, though, when we can prove the new type will
+     * only be visible at certain points in the script. Namely, for arithmetic
+     * operations which might produce doubles and are then passed to an
+     * expression that cancels out integer overflow, i.e.'OP & -1' or 'OP | 0',
+     * the new type will only affect OP and the bitwise operation.
+     *
+     * This can prevent a significant amount of recompilation in scripts which
+     * use these operations extensively, principally autotranslated code.
+     */
+
+    jsbytecode *ignorePC = pc + GetBytecodeLength(pc);
+    if (*ignorePC == JSOP_INT8 && GET_INT8(ignorePC) == -1) {
+        ignorePC += JSOP_INT8_LENGTH;
+        if (*ignorePC != JSOP_BITAND)
+            ignorePC = NULL;
+    } else if (*ignorePC == JSOP_ZERO) {
+        ignorePC += JSOP_ZERO_LENGTH;
+        if (*ignorePC != JSOP_BITOR)
+            ignorePC = NULL;
+    } else {
+        ignorePC = NULL;
+    }
+
+    if (ignorePC) {
+        AddPendingRecompile(cx, script, pc);
+        AddPendingRecompile(cx, script, ignorePC);
+    } else {
+        AddPendingRecompile(cx, script, NULL);
+    }
+
     if (script->hasAnalysis() && script->analysis()->ranInference()) {
         TypeSet *pushed = script->analysis()->pushedTypes(pc, 0);
         pushed->addType(cx, type);
     }
-
-    /* Trigger recompilation of any inline callers. */
-    if (script->function() && !script->function()->hasLazyType())
-        ObjectStateChange(cx, script->function()->type(), false, true);
 }
 
 void
 TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval)
 {
     /* Allow the non-TYPESET scenario to simplify stubs used in compound opcodes. */
     if (!(js_CodeSpec[*pc].format & JOF_TYPESET))
         return;
@@ -5000,23 +5259,30 @@ JSScript::makeTypes(JSContext *cx)
     types = (TypeScript *) cx->calloc_(sizeof(TypeScript) + (sizeof(TypeSet) * count));
     if (!types) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return false;
     }
 
     new(types) TypeScript();
 
+    TypeSet *typeArray = types->typeArray();
+    TypeSet *returnTypes = TypeScript::ReturnTypes(this);
+
+    for (unsigned i = 0; i < count; i++) {
+        TypeSet *types = &typeArray[i];
+        if (types != returnTypes)
+            types->setConstraintsPurged();
+    }
+
 #ifdef DEBUG
-    TypeSet *typeArray = types->typeArray();
     for (unsigned i = 0; i < nTypeSets; i++)
         InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u #%u",
                   InferSpewColor(&typeArray[i]), &typeArray[i], InferSpewColorReset(),
                   i, id());
-    TypeSet *returnTypes = TypeScript::ReturnTypes(this);
     InferSpew(ISpewOps, "typeSet: %sT%p%s return #%u",
               InferSpewColor(returnTypes), returnTypes, InferSpewColorReset(),
               id());
     TypeSet *thisTypes = TypeScript::ThisTypes(this);
     InferSpew(ISpewOps, "typeSet: %sT%p%s this #%u",
               InferSpewColor(thisTypes), thisTypes, InferSpewColorReset(),
               id());
     unsigned nargs = function() ? function()->nargs : 0;
@@ -5039,17 +5305,17 @@ JSScript::makeTypes(JSContext *cx)
 
 bool
 JSScript::makeAnalysis(JSContext *cx)
 {
     JS_ASSERT(types && !types->analysis);
 
     AutoEnterAnalysis enter(cx);
 
-    types->analysis = cx->typeLifoAlloc().new_<ScriptAnalysis>(this);
+    types->analysis = cx->analysisLifoAlloc().new_<ScriptAnalysis>(this);
 
     if (!types->analysis)
         return false;
 
     Rooted<JSScript*> self(cx, this);
 
     types->analysis->analyzeBytecode(cx);
 
@@ -5438,16 +5704,18 @@ JSCompartment::getLazyType(JSContext *cx
 
 /////////////////////////////////////////////////////////////////////
 // Tracing
 /////////////////////////////////////////////////////////////////////
 
 void
 TypeSet::sweep(JSCompartment *compartment)
 {
+    JS_ASSERT(!purged());
+
     /*
      * Purge references to type objects that are no longer live. Type sets hold
      * only weak references. For type sets containing more than one object,
      * live entries in the object hash need to be copied to the compartment's
      * new arena.
      */
     unsigned objectCount = baseObjectCount();
     if (objectCount >= 2) {
@@ -5456,17 +5724,17 @@ TypeSet::sweep(JSCompartment *compartmen
 
         clearObjects();
         objectCount = 0;
         for (unsigned i = 0; i < oldCapacity; i++) {
             TypeObjectKey *object = oldArray[i];
             if (object && !IsAboutToBeFinalized(object)) {
                 TypeObjectKey **pentry =
                     HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
-                        (compartment, objectSet, objectCount, object);
+                        (compartment->typeLifoAlloc, objectSet, objectCount, object);
                 if (pentry)
                     *pentry = object;
                 else
                     compartment->types.setPendingNukeTypesNoReport();
             }
         }
         setBaseObjectCount(objectCount);
     } else if (objectCount == 1) {
@@ -5538,37 +5806,37 @@ TypeObject::sweep(FreeOp *fop)
     if (propertyCount >= 2) {
         unsigned oldCapacity = HashSetCapacity(propertyCount);
         Property **oldArray = propertySet;
 
         clearProperties();
         propertyCount = 0;
         for (unsigned i = 0; i < oldCapacity; i++) {
             Property *prop = oldArray[i];
-            if (prop && prop->types.isOwnProperty(false)) {
+            if (prop && prop->types.ownProperty(false)) {
                 Property *newProp = compartment->typeLifoAlloc.new_<Property>(*prop);
                 if (newProp) {
                     Property **pentry =
                         HashSetInsert<jsid,Property,Property>
-                            (compartment, propertySet, propertyCount, prop->id);
+                            (compartment->typeLifoAlloc, propertySet, propertyCount, prop->id);
                     if (pentry) {
                         *pentry = newProp;
                         newProp->types.sweep(compartment);
                     } else {
                         compartment->types.setPendingNukeTypesNoReport();
                     }
                 } else {
                     compartment->types.setPendingNukeTypesNoReport();
                 }
             }
         }
         setBasePropertyCount(propertyCount);
     } else if (propertyCount == 1) {
         Property *prop = (Property *) propertySet;
-        if (prop->types.isOwnProperty(false)) {
+        if (prop->types.ownProperty(false)) {
             Property *newProp = compartment->typeLifoAlloc.new_<Property>(*prop);
             if (newProp) {
                 propertySet = (Property **) newProp;
                 newProp->types.sweep(compartment);
             } else {
                 compartment->types.setPendingNukeTypesNoReport();
             }
         } else {
@@ -5781,30 +6049,163 @@ TypeScript::Sweep(FreeOp *fop, JSScript 
             IsAboutToBeFinalized(type.objectKey()))
         {
             *presult = result->next;
             fop->delete_(result);
         } else {
             presult = &result->next;
         }
     }
+
+    /*
+     * Freeze constraints on stack type sets need to be regenerated the next
+     * time the script is analyzed.
+     */
+    script->hasFreezeConstraints = false;
 }
 
 void
 TypeScript::destroy()
 {
     while (dynamicList) {
         TypeResult *next = dynamicList->next;
         Foreground::delete_(dynamicList);
         dynamicList = next;
     }
 
     Foreground::free_(this);
 }
 
+/* static */ void
+TypeScript::AddFreezeConstraints(JSContext *cx, JSScript *script)
+{
+    /*
+     * Adding freeze constraints to a script ensures that code for the script
+     * will be recompiled any time any type set for stack values in the script
+     * change: these type sets are implicitly frozen during compilation.
+     *
+     * To ensure this occurs, we don't need to add freeze constraints to the
+     * type sets for every stack value, but rather only the input type sets
+     * to analysis of the stack in a script. The contents of the stack sets
+     * are completely determined by these input sets and by any dynamic types
+     * in the script (for which TypeDynamicResult will trigger recompilation).
+     *
+     * Add freeze constraints to each input type set, which includes sets for
+     * all arguments, locals, and monitored type sets in the script. This
+     * includes all type sets in the TypeScript except the script's return
+     * value types.
+     */
+
+    size_t count = TypeScript::NumTypeSets(script);
+    TypeSet *returnTypes = TypeScript::ReturnTypes(script);
+
+    TypeSet *array = script->types->typeArray();
+    for (size_t i = 0; i < count; i++) {
+        TypeSet *types = &array[i];
+        if (types == returnTypes)
+            continue;
+        JS_ASSERT(types->constraintsPurged());
+        types->add(cx, cx->analysisLifoAlloc().new_<TypeConstraintFreezeStack>(script), false);
+    }
+}
+
+/* static */ void
+TypeScript::Purge(JSContext *cx, JSScript *script)
+{
+    if (!script->types)
+        return;
+
+    unsigned num = NumTypeSets(script);
+    TypeSet *typeArray = script->types->typeArray();
+    TypeSet *returnTypes = ReturnTypes(script);
+
+    bool ranInference = script->hasAnalysis() && script->analysis()->ranInference();
+
+    script->clearAnalysis();
+
+    if (!ranInference && !script->hasFreezeConstraints) {
+        /*
+         * Even if the script hasn't been analyzed by TI, TypeConstraintCall
+         * can still add constraints on 'this' for 'new' calls.
+         */
+        ThisTypes(script)->constraintList = NULL;
+#ifdef DEBUG
+        for (size_t i = 0; i < num; i++) {
+            TypeSet *types = &typeArray[i];
+            JS_ASSERT_IF(types != returnTypes, !types->constraintList);
+        }
+#endif
+        return;
+    }
+
+    for (size_t i = 0; i < num; i++) {
+        TypeSet *types = &typeArray[i];
+        if (types != returnTypes)
+            types->constraintList = NULL;
+    }
+
+    if (script->hasFreezeConstraints)
+        TypeScript::AddFreezeConstraints(cx, script);
+}
+
+void
+TypeCompartment::maybePurgeAnalysis(JSContext *cx, bool force)
+{
+    // FIXME bug 781657
+    return;
+
+    JS_ASSERT(this == &cx->compartment->types);
+    JS_ASSERT(!cx->compartment->activeAnalysis);
+
+    if (!cx->typeInferenceEnabled())
+        return;
+
+    size_t triggerBytes = cx->runtime->analysisPurgeTriggerBytes;
+    size_t beforeUsed = cx->compartment->analysisLifoAlloc.used();
+
+    if (!force) {
+        if (!triggerBytes || triggerBytes >= beforeUsed)
+            return;
+    }
+
+    AutoEnterTypeInference enter(cx);
+
+    /* Reset the analysis pool, making its memory available for reuse. */
+    cx->compartment->analysisLifoAlloc.releaseAll();
+
+    uint64_t start = PRMJ_Now();
+
+    for (gc::CellIter i(cx->compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
+        JSScript *script = i.get<JSScript>();
+        TypeScript::Purge(cx, script);
+    }
+
+    uint64_t done = PRMJ_Now();
+
+    if (cx->runtime->analysisPurgeCallback) {
+        size_t afterUsed = cx->compartment->analysisLifoAlloc.used();
+        size_t typeUsed = cx->compartment->typeLifoAlloc.used();
+
+        char buf[1000];
+        JS_snprintf(buf, sizeof(buf),
+                    "Total Time %.2f ms, %d bytes before, %d bytes after\n",
+                    (done - start) / double(PRMJ_USEC_PER_MSEC),
+                    (int) (beforeUsed + typeUsed),
+                    (int) (afterUsed + typeUsed));
+
+        JSString *desc = JS_NewStringCopyZ(cx, buf);
+        if (!desc) {
+            cx->clearPendingException();
+            return;
+        }
+
+        cx->runtime->analysisPurgeCallback(cx->runtime, &desc->asFlat());
+    }
+}
+
 inline size_t
 TypeSet::computedSizeOfExcludingThis()
 {
     /*
      * This memory is allocated within the temp pool (but accounted for
      * elsewhere) so we can't use a JSMallocSizeOfFun to measure it.  We must
      * compute its size analytically.
      */
@@ -5876,16 +6277,17 @@ SizeOfScriptTypeInferenceData(JSScript *
 void
 JSCompartment::sizeOfTypeInferenceData(TypeInferenceSizes *sizes, JSMallocSizeOfFun mallocSizeOf)
 {
     /*
      * Note: not all data in the pool is temporary, and some will survive GCs
      * by being copied to the replacement pool. This memory will be counted
      * elsewhere and deducted from the amount of temporary data.
      */
+    sizes->temporary += analysisLifoAlloc.sizeOfExcludingThis(mallocSizeOf);
     sizes->temporary += typeLifoAlloc.sizeOfExcludingThis(mallocSizeOf);
 
     /* Pending arrays are cleared on GC along with the analysis pool. */
     sizes->temporary += mallocSizeOf(types.pendingArray);
 
     /* TypeCompartment::pendingRecompiles is non-NULL only while inference code is running. */
     JS_ASSERT(!types.pendingRecompiles);
 
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -130,53 +130,63 @@ class Type
 inline Type GetValueType(JSContext *cx, const Value &val);
 
 /*
  * Type inference memory management overview.
  *
  * Inference constructs a global web of constraints relating the contents of
  * type sets particular to various scripts and type objects within a
  * compartment. This data can consume a significant amount of memory, and to
- * avoid this building up we try to clear it with some regularity. On each GC
- * which occurs while we are not actively working with inference or other
- * analysis information, we clear out all generated constraints, all type sets
- * describing stack types within scripts, and (normally) all data describing
- * type objects for particular JS objects (see the lazy type objects overview
- * below). JIT code depends on this data and is cleared as well.
+ * avoid this building up we try to clear it with some regularity.
+ *
+ * There are two operations which can clear inference and analysis data.
+ *
+ * - Analysis purges clear analysis information while retaining jitcode.
+ *
+ * - 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.
+ *
+ * There are several categories of data affected differently by the above
+ * operations.
  *
- * All this data is allocated into compartment->pool. Some type inference data
- * lives across GCs: type sets for scripts and non-singleton type objects, and
- * propeties for such type objects. This data is also allocated into
- * compartment->pool, but everything still live is copied to a new arena on GC.
+ * - Data cleared by every analysis purge and non-preserving GC. This includes
+ *   the ScriptAnalysis for each analyzed script and data from each analysis
+ *   pass performed, type sets for stack values, and all type constraints for
+ *   such type sets and for observed/argument/local type sets on scripts
+ *   (TypeSet::constraintsPurged, aka StackTypeSet). This is exactly the data
+ *   allocated using compartment->analysisLifoAlloc.
+ *
+ * - Data cleared by non-preserving GCs. This includes property type sets for
+ *   singleton JS objects, property read input type sets, type constraints on
+ *   all type sets, and dead references in all type sets. This data is all
+ *   allocated using compartment->typeLifoAlloc; the GC copies live data into a
+ *   new allocator and clears the old one.
+ *
+ * - Data cleared occasionally by non-preserving GCs. TypeScripts and the data
+ *   in their sets are occasionally destroyed during GC. When a JSScript or
+ *   TypeObject is swept, type information for its contents is destroyed.
  */
 
 /*
  * A constraint which listens to additions to a type set and propagates those
  * changes to other type sets.
  */
 class TypeConstraint
 {
 public:
-#ifdef DEBUG
-    const char *kind_;
-    const char *kind() const { return kind_; }
-#else
-    const char *kind() const { return NULL; }
-#endif
-
     /* Next constraint listening to the same type set. */
     TypeConstraint *next;
 
-    TypeConstraint(const char *kind)
+    TypeConstraint()
         : next(NULL)
-    {
-#ifdef DEBUG
-        this->kind_ = kind;
-#endif
-    }
+    {}
+
+    /* Debugging name for this kind of constraint. */
+    virtual const char *kind() = 0;
 
     /* Register a new type for the set this constraint is listening to. */
     virtual void newType(JSContext *cx, TypeSet *source, Type type) = 0;
 
     /*
      * For constraints attached to an object property's type set, mark the
      * property as having been configured or received an own property.
      */
@@ -208,40 +218,55 @@ enum {
         TYPE_FLAG_OBJECT_COUNT_MASK >> TYPE_FLAG_OBJECT_COUNT_SHIFT,
 
     /* Whether the contents of this type set are totally unknown. */
     TYPE_FLAG_UNKNOWN             = 0x00010000,
 
     /* Mask of normal type flags on a type set. */
     TYPE_FLAG_BASE_MASK           = 0x000100ff,
 
+    /* Flags describing the kind of type set this is. */
+
+    /*
+     * Flag for type sets which describe stack values and are cleared on
+     * analysis purges.
+     */
+    TYPE_FLAG_PURGED              = 0x00020000,
+
+    /*
+     * Flag for type sets whose constraints are cleared on analysis purges.
+     * This includes all temporary type sets, as well as sets in TypeScript
+     * which propagate into temporary type sets.
+     */
+    TYPE_FLAG_CONSTRAINTS_PURGED  = 0x00040000,
+
     /* Flags for type sets which are on object properties. */
 
     /*
      * Whether there are subset constraints propagating the possible types
      * for this property inherited from the object's prototypes. Reset on GC.
      */
-    TYPE_FLAG_PROPAGATED_PROPERTY = 0x00020000,
+    TYPE_FLAG_PROPAGATED_PROPERTY = 0x00080000,
 
     /* Whether this property has ever been directly written. */
-    TYPE_FLAG_OWN_PROPERTY        = 0x00040000,
+    TYPE_FLAG_OWN_PROPERTY        = 0x00100000,
 
     /*
      * Whether the property has ever been deleted or reconfigured to behave
      * differently from a normal native property (e.g. made non-writable or
      * given a scripted getter or setter).
      */
-    TYPE_FLAG_CONFIGURED_PROPERTY = 0x00080000,
+    TYPE_FLAG_CONFIGURED_PROPERTY = 0x00200000,
 
     /*
      * Whether the property is definitely in a particular inline slot on all
      * objects from which it has not been deleted or reconfigured. Implies
      * OWN_PROPERTY and unlike OWN/CONFIGURED property, this cannot change.
      */
-    TYPE_FLAG_DEFINITE_PROPERTY   = 0x00100000,
+    TYPE_FLAG_DEFINITE_PROPERTY   = 0x00400000,
 
     /* If the property is definite, mask and shift storing the slot. */
     TYPE_FLAG_DEFINITE_MASK       = 0x0f000000,
     TYPE_FLAG_DEFINITE_SHIFT      = 24
 };
 typedef uint32_t TypeFlags;
 
 /* Flags and other state stored in TypeObject::flags */
@@ -310,16 +335,19 @@ enum {
     /* 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;
 
+class StackTypeSet;
+class HeapTypeSet;
+
 /* Information about the set of types associated with an lvalue. */
 class TypeSet
 {
     /* Flags for this type set. */
     TypeFlags flags;
 
     /* Possible objects this type set can represent. */
     TypeObjectKey **objectSet;
@@ -347,22 +375,22 @@ class TypeSet
 
     bool empty() const { return !baseFlags() && !baseObjectCount(); }
 
     bool hasAnyFlag(TypeFlags flags) const {
         JS_ASSERT((flags & TYPE_FLAG_BASE_MASK) == flags);
         return !!(baseFlags() & flags);
     }
 
-    bool isOwnProperty(bool configurable) const {
+    bool ownProperty(bool configurable) const {
         return flags & (configurable ? TYPE_FLAG_CONFIGURED_PROPERTY : TYPE_FLAG_OWN_PROPERTY);
     }
-    bool isDefiniteProperty() const { return flags & TYPE_FLAG_DEFINITE_PROPERTY; }
+    bool definiteProperty() const { return flags & TYPE_FLAG_DEFINITE_PROPERTY; }
     unsigned definiteSlot() const {
-        JS_ASSERT(isDefiniteProperty());
+        JS_ASSERT(definiteProperty());
         return flags >> TYPE_FLAG_DEFINITE_SHIFT;
     }
 
     /*
      * Add a type to this set, calling any constraint handlers if this is a new
      * possible type.
      */
     inline void addType(JSContext *cx, Type type);
@@ -388,114 +416,174 @@ class TypeSet
     void setDefinite(unsigned slot) {
         JS_ASSERT(slot <= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT));
         flags |= TYPE_FLAG_DEFINITE_PROPERTY | (slot << TYPE_FLAG_DEFINITE_SHIFT);
     }
 
     bool hasPropagatedProperty() { return !!(flags & TYPE_FLAG_PROPAGATED_PROPERTY); }
     void setPropagatedProperty() { flags |= TYPE_FLAG_PROPAGATED_PROPERTY; }
 
-    enum FilterKind {
-        FILTER_ALL_PRIMITIVES,
-        FILTER_NULL_VOID,
-        FILTER_VOID
-    };
+    bool constraintsPurged() { return !!(flags & TYPE_FLAG_CONSTRAINTS_PURGED); }
+    void setConstraintsPurged() { flags |= TYPE_FLAG_CONSTRAINTS_PURGED; }
+
+    bool purged() { return !!(flags & TYPE_FLAG_PURGED); }
+    void setPurged() { flags |= TYPE_FLAG_PURGED | TYPE_FLAG_CONSTRAINTS_PURGED; }
+
+    inline StackTypeSet *toStackTypeSet();
+    inline HeapTypeSet *toHeapTypeSet();
+
+    inline void addTypesToConstraint(JSContext *cx, TypeConstraint *constraint);
+    inline void add(JSContext *cx, TypeConstraint *constraint, bool callExisting = true);
+
+  protected:
+    uint32_t baseObjectCount() const {
+        return (flags & TYPE_FLAG_OBJECT_COUNT_MASK) >> TYPE_FLAG_OBJECT_COUNT_SHIFT;
+    }
+    inline void setBaseObjectCount(uint32_t count);
 
-    /* Add specific kinds of constraints to this set. */
-    inline void add(JSContext *cx, TypeConstraint *constraint, bool callExisting = true);
+    inline void clearObjects();
+};
+
+/*
+ * Type set for a stack value manipulated in a script, or the argument or
+ * local types of said script. Constraints on these type sets are cleared
+ * during analysis purges; the contents of the sets are implicitly frozen
+ * during compilation to ensure that changes to the sets trigger recompilation
+ * of the associated script.
+ */
+class StackTypeSet : public TypeSet
+{
+  public:
+
+    /*
+     * Make a type set with the specified debugging name, not embedded in
+     * another structure.
+     */
+    static StackTypeSet *make(JSContext *cx, const char *name);
+
+    /* Constraints for type inference. */
+
     void addSubset(JSContext *cx, TypeSet *target);
     void addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
-                        TypeSet *target, jsid id);
+                        StackTypeSet *target, jsid id);
     void addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
-                        TypeSet *target, jsid id);
-    void addCallProperty(JSContext *cx, JSScript *script, jsbytecode *pc, jsid id);
+                        StackTypeSet *target, jsid id);
     void addSetElement(JSContext *cx, JSScript *script, jsbytecode *pc,
-                       TypeSet *objectTypes, TypeSet *valueTypes);
+                       StackTypeSet *objectTypes, StackTypeSet *valueTypes);
     void addCall(JSContext *cx, TypeCallsite *site);
     void addArith(JSContext *cx, JSScript *script, jsbytecode *pc,
                   TypeSet *target, TypeSet *other = NULL);
     void addTransformThis(JSContext *cx, JSScript *script, TypeSet *target);
     void addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc,
-                          Type type, TypeSet *types = NULL);
-    void addFilterPrimitives(JSContext *cx, TypeSet *target, FilterKind filter);
+                          Type type, StackTypeSet *types = NULL);
     void addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target);
 
     /*
-     * Make an type set with the specified debugging name, not embedded in
-     * another structure.
+     * Constraints for JIT compilation.
+     *
+     * Methods for JIT compilation. These must be used when a script is
+     * currently being compiled (see AutoEnterCompilation) and will add
+     * constraints ensuring that if the return value change in the future due
+     * to new type information, the script's jitcode will be discarded.
      */
-    static TypeSet *make(JSContext *cx, const char *name);
+
+    /* Get any type tag which all values in this set must have. */
+    JSValueType getKnownTypeTag();
+
+    bool isMagicArguments() { return getKnownTypeTag() == JSVAL_TYPE_MAGIC; }
+
+    /* Whether the type set contains objects with any of a set of flags. */
+    bool hasObjectFlags(JSContext *cx, TypeObjectFlags flags);
 
     /*
-     * Methods for JIT compilation. If a script is currently being compiled
-     * (see AutoEnterCompilation) these will add constraints ensuring that if
-     * the return value change in the future due to new type information, the
-     * currently compiled script will be marked for recompilation.
+     * Get the typed array type of all objects in this set. Returns
+     * TypedArray::TYPE_MAX if the set contains different array types.
      */
+    int getTypedArrayType();
+
+    /* Get the single value which can appear in this type set, otherwise NULL. */
+    JSObject *getSingleton();
+
+    /* Whether any objects in the type set needs a barrier on id. */
+    bool propertyNeedsBarrier(JSContext *cx, jsid id);
+};
+
+/*
+ * Type set for a property of a TypeObject, or for the return value or property
+ * read inputs of a script. In contrast with stack type sets, constraints on
+ * these sets are not cleared during analysis purges, and are not implicitly
+ * frozen during compilation.
+ */
+class HeapTypeSet : public TypeSet
+{
+  public:
+
+    /* Constraints for type inference. */
+
+    void addSubset(JSContext *cx, TypeSet *target);
+    void addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
+                        StackTypeSet *target, jsid id);
+    void addCallProperty(JSContext *cx, JSScript *script, jsbytecode *pc, jsid id);
+    void addFilterPrimitives(JSContext *cx, TypeSet *target);
+    void addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target);
+
+    /* Constraints for JIT compilation. */
 
     /* Completely freeze the contents of this type set. */
     void addFreeze(JSContext *cx);
 
-    /* Get any type tag which all values in this set must have. */
-    JSValueType getKnownTypeTag(JSContext *cx);
-
-    bool isMagicArguments(JSContext *cx) { return getKnownTypeTag(cx) == JSVAL_TYPE_MAGIC; }
-
-    /* Whether the type set or a particular object has any of a set of flags. */
-    bool hasObjectFlags(JSContext *cx, TypeObjectFlags flags);
-    static bool HasObjectFlags(JSContext *cx, TypeObject *object, TypeObjectFlags flags);
 
     /*
      * Watch for a generic object state change on a type object. This currently
      * includes reallocations of slot pointers for global objects, and changes
      * to newScript data on types.
      */
     static void WatchObjectStateChange(JSContext *cx, TypeObject *object);
 
+    /* Whether an object has any of a set of flags. */
+    static bool HasObjectFlags(JSContext *cx, TypeObject *object, TypeObjectFlags flags);
+
     /*
      * For type sets on a property, return true if the property has any 'own'
      * values assigned. If configurable is set, return 'true' if the property
      * has additionally been reconfigured as non-configurable, non-enumerable
      * or non-writable (this only applies to properties that have changed after
      * having been created, not to e.g. properties non-writable on creation).
      */
     bool isOwnProperty(JSContext *cx, TypeObject *object, bool configurable);
 
     /* Get whether this type set is non-empty. */
     bool knownNonEmpty(JSContext *cx);
 
     /* Get whether this type set is known to be a subset of other. */
     bool knownSubset(JSContext *cx, TypeSet *other);
 
-    /*
-     * Get the typed array type of all objects in this set. Returns
-     * TypedArray::TYPE_MAX if the set contains different array types.
-     */
-    int getTypedArrayType(JSContext *cx);
-
     /* Get the single value which can appear in this type set, otherwise NULL. */
-    JSObject *getSingleton(JSContext *cx, bool freeze = true);
-
-    inline void clearObjects();
+    JSObject *getSingleton(JSContext *cx);
 
     /*
      * Whether a location with this TypeSet needs a write barrier (i.e., whether
      * it can hold GC things). The type set is frozen if no barrier is needed.
      */
     bool needsBarrier(JSContext *cx);
-
-    /* The type set is frozen if no barrier is needed. */
-    bool propertyNeedsBarrier(JSContext *cx, jsid id);
+};
 
-  private:
-    uint32_t baseObjectCount() const {
-        return (flags & TYPE_FLAG_OBJECT_COUNT_MASK) >> TYPE_FLAG_OBJECT_COUNT_SHIFT;
-    }
-    inline void setBaseObjectCount(uint32_t count);
-};
+inline StackTypeSet *
+TypeSet::toStackTypeSet()
+{
+    JS_ASSERT(constraintsPurged());
+    return (StackTypeSet *) this;
+}
+
+inline HeapTypeSet *
+TypeSet::toHeapTypeSet()
+{
+    JS_ASSERT(!constraintsPurged());
+    return (HeapTypeSet *) this;
+}
 
 /*
  * Handler which persists information about dynamic types pushed within a
  * script which can affect its behavior and are not covered by JOF_TYPESET ops,
  * such as integer operations which overflow to a double. These persist across
  * GCs, and are used to re-seed script types when they are reanalyzed.
  */
 struct TypeResult
@@ -596,17 +684,17 @@ struct TypeBarrier
 
 /* Type information about a property. */
 struct Property
 {
     /* Identifier for this property, JSID_VOID for the aggregate integer index property. */
     HeapId id;
 
     /* Possible types for this property, including types inherited from prototypes. */
-    TypeSet types;
+    HeapTypeSet types;
 
     inline Property(jsid id);
     inline Property(const Property &o);
 
     static uint32_t keyBits(jsid id) { return uint32_t(JSID_BITS(id)); }
     static jsid getKey(Property *p) { return p->id; }
 };
 
@@ -788,20 +876,20 @@ struct TypeObject : gc::Cell
     }
 
     /*
      * Get or create a property of this object. Only call this for properties which
      * a script accesses explicitly. 'assign' indicates whether this is for an
      * assignment, and the own types of the property will be used instead of
      * aggregate types.
      */
-    inline TypeSet *getProperty(JSContext *cx, jsid id, bool assign);
+    inline HeapTypeSet *getProperty(JSContext *cx, jsid id, bool assign);
 
     /* Get a property only if it already exists. */
-    inline TypeSet *maybeGetProperty(JSContext *cx, jsid id);
+    inline HeapTypeSet *maybeGetProperty(JSContext *cx, jsid id);
 
     inline unsigned getPropertyCount();
     inline Property *getProperty(unsigned i);
 
     /* Set flags on this object which are implied by the specified key. */
     inline void setFlagsFromKey(JSContext *cx, JSProtoKey kind);
 
     /*
@@ -896,23 +984,23 @@ struct TypeCallsite
     JSScript *script;
     jsbytecode *pc;
 
     /* Whether this is a 'NEW' call. */
     bool isNew;
 
     /* Types of each argument to the call. */
     unsigned argumentCount;
-    TypeSet **argumentTypes;
+    StackTypeSet **argumentTypes;
 
     /* Types of the this variable. */
-    TypeSet *thisTypes;
+    StackTypeSet *thisTypes;
 
     /* Type set receiving the return value of this call. */
-    TypeSet *returnTypes;
+    StackTypeSet *returnTypes;
 
     inline TypeCallsite(JSContext *cx, JSScript *script, jsbytecode *pc,
                         bool isNew, unsigned argumentCount);
 };
 
 /* Persistent type information for a script, retained across GCs. */
 class TypeScript
 {
@@ -920,28 +1008,35 @@ class TypeScript
 
     /* Analysis information for the script, cleared on each GC. */
     analyze::ScriptAnalysis *analysis;
 
   public:
     /* Dynamic types generated at points within this script. */
     TypeResult *dynamicList;
 
+    /*
+     * Array of type sets storing the possible inputs to property reads.
+     * Generated the first time the script is analyzed by inference and kept
+     * after analysis purges.
+     */
+    HeapTypeSet *propertyReadTypes;
+
     /* Array of type type sets for variables and JOF_TYPESET ops. */
     TypeSet *typeArray() { return (TypeSet *) (uintptr_t(this) + sizeof(TypeScript)); }
 
     static inline unsigned NumTypeSets(JSScript *script);
 
-    static inline TypeSet *ReturnTypes(JSScript *script);
-    static inline TypeSet *ThisTypes(JSScript *script);
-    static inline TypeSet *ArgTypes(JSScript *script, unsigned i);
-    static inline TypeSet *LocalTypes(JSScript *script, unsigned i);
+    static inline HeapTypeSet  *ReturnTypes(JSScript *script);
+    static inline StackTypeSet *ThisTypes(JSScript *script);
+    static inline StackTypeSet *ArgTypes(JSScript *script, unsigned i);
+    static inline StackTypeSet *LocalTypes(JSScript *script, unsigned i);
 
     /* Follows slot layout in jsanalyze.h, can get this/arg/local type sets. */
-    static inline TypeSet *SlotTypes(JSScript *script, unsigned slot);
+    static inline StackTypeSet *SlotTypes(JSScript *script, unsigned slot);
 
 #ifdef DEBUG
     /* Check that correct types were inferred for the values pushed by this bytecode. */
     static void CheckBytecode(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value *sp);
 #endif
 
     /* Get the default 'new' object for a given standard class, per the script's global. */
     static inline TypeObject *StandardType(JSContext *cx, JSScript *script, JSProtoKey kind);
@@ -979,16 +1074,19 @@ class TypeScript
     /* Add a type for a variable in a script. */
     static inline void SetThis(JSContext *cx, JSScript *script, Type type);
     static inline void SetThis(JSContext *cx, JSScript *script, const js::Value &value);
     static inline void SetLocal(JSContext *cx, JSScript *script, unsigned local, Type type);
     static inline void SetLocal(JSContext *cx, JSScript *script, unsigned local, const js::Value &value);
     static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type);
     static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value);
 
+    static void AddFreezeConstraints(JSContext *cx, JSScript *script);
+    static void Purge(JSContext *cx, JSScript *script);
+
     static void Sweep(FreeOp *fop, JSScript *script);
     void destroy();
 };
 
 struct ArrayTableKey;
 typedef HashMap<ArrayTableKey,ReadBarriered<TypeObject>,ArrayTableKey,SystemAllocPolicy> ArrayTypeTable;
 
 struct ObjectTableKey;
@@ -1161,16 +1259,19 @@ struct TypeCompartment
     void monitorBytecode(JSContext *cx, JSScript *script, uint32_t offset,
                          bool returnOnly = false);
 
     /* Mark any type set containing obj as having a generic object type. */
     void markSetsUnknown(JSContext *cx, TypeObject *obj);
 
     void sweep(FreeOp *fop);
     void sweepCompilerOutputs(FreeOp *fop);
+
+    void maybePurgeAnalysis(JSContext *cx, bool force = false);
+
     void finalizeObjects();
 };
 
 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
@@ -597,53 +597,58 @@ UseNewTypeForClone(JSFunction *fun)
 /////////////////////////////////////////////////////////////////////
 
 /* static */ inline unsigned
 TypeScript::NumTypeSets(JSScript *script)
 {
     return script->nTypeSets + analyze::TotalSlots(script);
 }
 
-/* static */ inline TypeSet *
+/* static */ inline HeapTypeSet *
 TypeScript::ReturnTypes(JSScript *script)
 {
-    return script->types->typeArray() + script->nTypeSets + js::analyze::CalleeSlot();
+    TypeSet *types = script->types->typeArray() + script->nTypeSets + js::analyze::CalleeSlot();
+    return types->toHeapTypeSet();
 }
 
-/* static */ inline TypeSet *
+/* static */ inline StackTypeSet *
 TypeScript::ThisTypes(JSScript *script)
 {
-    return script->types->typeArray() + script->nTypeSets + js::analyze::ThisSlot();
+    TypeSet *types = script->types->typeArray() + script->nTypeSets + js::analyze::ThisSlot();
+    return types->toStackTypeSet();
 }
 
 /*
  * Note: for non-escaping arguments and locals, argTypes/localTypes reflect
  * only the initial type of the variable (e.g. passed values for argTypes,
  * or undefined for localTypes) and not types from subsequent assignments.
  */
 
-/* static */ inline TypeSet *
+/* static */ inline StackTypeSet *
 TypeScript::ArgTypes(JSScript *script, unsigned i)
 {
     JS_ASSERT(i < script->function()->nargs);
-    return script->types->typeArray() + script->nTypeSets + js::analyze::ArgSlot(i);
+    TypeSet *types = script->types->typeArray() + script->nTypeSets + js::analyze::ArgSlot(i);
+    return types->toStackTypeSet();
 }
 
-/* static */ inline TypeSet *
+/* static */ inline StackTypeSet *
 TypeScript::LocalTypes(JSScript *script, unsigned i)
 {
     JS_ASSERT(i < script->nfixed);
-    return script->types->typeArray() + script->nTypeSets + js::analyze::LocalSlot(script, i);
+    TypeSet *types = script->types->typeArray() + script->nTypeSets + js::analyze::LocalSlot(script, i);
+    return types->toStackTypeSet();
 }
 
-/* static */ inline TypeSet *
+/* static */ inline StackTypeSet *
 TypeScript::SlotTypes(JSScript *script, unsigned slot)
 {
     JS_ASSERT(slot < js::analyze::TotalSlots(script));
-    return script->types->typeArray() + script->nTypeSets + slot;
+    TypeSet *types = script->types->typeArray() + script->nTypeSets + slot;
+    return types->toStackTypeSet();
 }
 
 /* static */ inline TypeObject *
 TypeScript::StandardType(JSContext *cx, JSScript *script, JSProtoKey key)
 {
     RootedObject proto(cx);
     RootedObject global(cx, &script->global());
     if (!js_GetClassPrototype(cx, global, key, &proto, NULL))
@@ -992,17 +997,17 @@ HashKey(T v)
 }
 
 /*
  * Insert space for an element into the specified set and grow its capacity if needed.
  * returned value is an existing or new entry (NULL if new).
  */
 template <class T, class U, class KEY>
 static U **
-HashSetInsertTry(JSCompartment *compartment, U **&values, unsigned &count, T key)
+HashSetInsertTry(LifoAlloc &alloc, U **&values, unsigned &count, T key)
 {
     unsigned capacity = HashSetCapacity(count);
     unsigned insertpos = HashKey<T,KEY>(key) & (capacity - 1);
 
     /* Whether we are converting from a fixed array to hashtable. */
     bool converting = (count == SET_ARRAY_SIZE);
 
     if (!converting) {
@@ -1016,17 +1021,17 @@ HashSetInsertTry(JSCompartment *compartm
     count++;
     unsigned newCapacity = HashSetCapacity(count);
 
     if (newCapacity == capacity) {
         JS_ASSERT(!converting);
         return &values[insertpos];
     }
 
-    U **newValues = compartment->typeLifoAlloc.newArray<U*>(newCapacity);
+    U **newValues = alloc.newArray<U*>(newCapacity);
     if (!newValues)
         return NULL;
     PodZero(newValues, newCapacity);
 
     for (unsigned i = 0; i < capacity; i++) {
         if (values[i]) {
             unsigned pos = HashKey<T,KEY>(KEY::getKey(values[i])) & (newCapacity - 1);
             while (newValues[pos] != NULL)
@@ -1044,30 +1049,30 @@ HashSetInsertTry(JSCompartment *compartm
 }
 
 /*
  * Insert an element into the specified set if it is not already there, returning
  * an entry which is NULL if the element was not there.
  */
 template <class T, class U, class KEY>
 static inline U **
-HashSetInsert(JSCompartment *compartment, U **&values, unsigned &count, T key)
+HashSetInsert(LifoAlloc &alloc, U **&values, unsigned &count, T key)
 {
     if (count == 0) {
         JS_ASSERT(values == NULL);
         count++;
         return (U **) &values;
     }
 
     if (count == 1) {
         U *oldData = (U*) values;
         if (KEY::getKey(oldData) == key)
             return (U **) &values;
 
-        values = compartment->typeLifoAlloc.newArray<U*>(SET_ARRAY_SIZE);
+        values = alloc.newArray<U*>(SET_ARRAY_SIZE);
         if (!values) {
             values = (U **) oldData;
             return NULL;
         }
         PodZero(values, SET_ARRAY_SIZE);
         count++;
 
         values[0] = oldData;
@@ -1081,17 +1086,17 @@ HashSetInsert(JSCompartment *compartment
         }
 
         if (count < SET_ARRAY_SIZE) {
             count++;
             return &values[count - 1];
         }
     }
 
-    return HashSetInsertTry<T,U,KEY>(compartment, values, count, key);
+    return HashSetInsertTry<T,U,KEY>(alloc, values, count, key);
 }
 
 /* Lookup an entry in a hash set, return NULL if it does not exist. */
 template <class T, class U, class KEY>
 static inline U *
 HashSetLookup(U **values, unsigned count, T key)
 {
     if (count == 0)
@@ -1203,20 +1208,24 @@ TypeSet::addType(JSContext *cx, Type typ
             flag |= TYPE_FLAG_INT32;
 
         flags |= flag;
     } else {
         if (flags & TYPE_FLAG_ANYOBJECT)
             return;
         if (type.isAnyObject())
             goto unknownObject;
+
+        LifoAlloc &alloc =
+            purged() ? cx->compartment->analysisLifoAlloc : cx->compartment->typeLifoAlloc;
+
         uint32_t objectCount = baseObjectCount();
         TypeObjectKey *object = type.objectKey();
         TypeObjectKey **pentry = HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
-                                     (cx->compartment, objectSet, objectCount, object);
+                                     (alloc, objectSet, objectCount, object);
         if (!pentry) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
         if (*pentry)
             return;
         *pentry = object;
 
@@ -1321,17 +1330,17 @@ TypeSet::getTypeObject(unsigned i)
 
 inline
 TypeCallsite::TypeCallsite(JSContext *cx, JSScript *script, jsbytecode *pc,
                            bool isNew, unsigned argumentCount)
     : script(script), pc(pc), isNew(isNew), argumentCount(argumentCount),
       thisTypes(NULL), returnTypes(NULL)
 {
     /* Caller must check for failure. */
-    argumentTypes = cx->typeLifoAlloc().newArray<TypeSet*>(argumentCount);
+    argumentTypes = cx->analysisLifoAlloc().newArray<StackTypeSet*>(argumentCount);
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeObject
 /////////////////////////////////////////////////////////////////////
 
 inline TypeObject::TypeObject(JSObject *proto, bool function, bool unknown)
 {
@@ -1359,50 +1368,60 @@ TypeObject::basePropertyCount() const
 inline void
 TypeObject::setBasePropertyCount(uint32_t count)
 {
     JS_ASSERT(count <= OBJECT_FLAG_PROPERTY_COUNT_LIMIT);
     flags = (flags & ~OBJECT_FLAG_PROPERTY_COUNT_MASK)
           | (count << OBJECT_FLAG_PROPERTY_COUNT_SHIFT);
 }
 
-inline TypeSet *
+inline HeapTypeSet *
 TypeObject::getProperty(JSContext *cx, jsid id, bool assign)
 {
     JS_ASSERT(cx->compartment->activeInference);
     JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
     JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == MakeTypeId(cx, id));
     JS_ASSERT(!unknownProperties());
 
     uint32_t propertyCount = basePropertyCount();
     Property **pprop = HashSetInsert<jsid,Property,Property>
-                           (cx->compartment, propertySet, propertyCount, id);
+                           (cx->compartment->typeLifoAlloc, propertySet, propertyCount, id);
     if (!pprop) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return NULL;
     }
 
     if (!*pprop) {
         setBasePropertyCount(propertyCount);
         if (!addProperty(cx, id, pprop)) {
             setBasePropertyCount(0);
             propertySet = NULL;
             return NULL;
         }
         if (propertyCount == OBJECT_FLAG_PROPERTY_COUNT_LIMIT) {
             markUnknown(cx);
-            TypeSet *types = TypeSet::make(cx, "propertyOverflow");
-            types->addType(cx, Type::UnknownType());
-            return types;
+
+            /*
+             * Return an arbitrary property in the object, as all have unknown
+             * type and are treated as configured.
+             */
+            unsigned count = getPropertyCount();
+            for (unsigned i = 0; i < count; i++) {
+                if (Property *prop = getProperty(i))
+                    return &prop->types;
+            }
+
+            JS_NOT_REACHED("Missing property");
+            return NULL;
         }
     }
 
-    TypeSet *types = &(*pprop)->types;
+    HeapTypeSet *types = &(*pprop)->types;
 
-    if (assign && !types->isOwnProperty(false)) {
+    if (assign && !types->ownProperty(false)) {
         /*
          * Normally, we just want to set the property as being an own property
          * when we got a set to it. The exception is when the set is actually
          * calling a setter higher on the prototype chain. Check to see if there
          * is a setter higher on the prototype chain, setter the property as an
          * own property if that is not the case.
          */
         bool foundSetter = false;
@@ -1426,17 +1445,17 @@ TypeObject::getProperty(JSContext *cx, j
 
         if (!foundSetter)
             types->setOwnProperty(cx, false);
     }
 
     return types;
 }
 
-inline TypeSet *
+inline HeapTypeSet *
 TypeObject::maybeGetProperty(JSContext *cx, jsid id)
 {
     JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
     JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == MakeTypeId(cx, id));
     JS_ASSERT(!unknownProperties());
 
     Property *prop = HashSetLookup<jsid,Property,Property>
         (propertySet, basePropertyCount(), id);
@@ -1620,16 +1639,23 @@ JSScript::analysis()
 inline void
 JSScript::clearAnalysis()
 {
     if (types)
         types->analysis = NULL;
 }
 
 inline void
+JSScript::clearPropertyReadTypes()
+{
+    if (types && types->propertyReadTypes)
+        types->propertyReadTypes = NULL;
+}
+
+inline void
 js::analyze::ScriptAnalysis::addPushedType(JSContext *cx, uint32_t offset, uint32_t which,
                                            js::types::Type type)
 {
     js::types::TypeSet *pushed = pushedTypes(offset, which);
     pushed->addType(cx, type);
 }
 
 inline js::types::TypeObject *
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -464,16 +464,19 @@ struct JSScript : public js::gc::Cell
     bool            failedBoundsCheck:1; /* script has had hoisted bounds checks fail */
 #endif
     bool            isGenerator:1;    /* is a generator */
     bool            isGeneratorExp:1; /* is a generator expression */
     bool            hasScriptCounts:1;/* script has an entry in
                                          JSCompartment::scriptCountsMap */
     bool            hasDebugScript:1; /* script has an entry in
                                          JSCompartment::debugScriptMap */
+    bool            hasFreezeConstraints:1; /* freeze constraints for stack
+                                             * type sets have been generated */
+
   private:
     /* See comments below. */
     bool            argsHasVarBinding_:1;
     bool            needsArgsAnalysis_:1;
     bool            needsArgsObj_:1;
 
     //
     // End of fields.  Start methods.
@@ -570,16 +573,18 @@ struct JSScript : public js::gc::Cell
 
     /* Ensure the script has type inference analysis information. */
     inline bool ensureRanInference(JSContext *cx);
 
     inline bool hasAnalysis();
     inline void clearAnalysis();
     inline js::analyze::ScriptAnalysis *analysis();
 
+    inline void clearPropertyReadTypes();
+
     inline bool hasGlobal() const;
     inline bool hasClearedGlobal() const;
 
     inline js::GlobalObject &global() const;
 
     /* See StaticScopeIter comment. */
     JSObject *enclosingStaticScope() const {
         JS_ASSERT(enclosingScriptsCompiledSuccessfully());
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -78,16 +78,18 @@ mjit::Compiler::Compiler(JSContext *cx, 
     getElemICs(CompilerAllocPolicy(cx, *thisFromCtor())),
     setElemICs(CompilerAllocPolicy(cx, *thisFromCtor())),
 #endif
     callPatches(CompilerAllocPolicy(cx, *thisFromCtor())),
     callSites(CompilerAllocPolicy(cx, *thisFromCtor())),
     doubleList(CompilerAllocPolicy(cx, *thisFromCtor())),
     rootedTemplates(CompilerAllocPolicy(cx, *thisFromCtor())),
     rootedRegExps(CompilerAllocPolicy(cx, *thisFromCtor())),
+    monitoredBytecodes(CompilerAllocPolicy(cx, *thisFromCtor())),
+    typeBarrierBytecodes(CompilerAllocPolicy(cx, *thisFromCtor())),
     fixedIntToDoubleEntries(CompilerAllocPolicy(cx, *thisFromCtor())),
     fixedDoubleToAnyEntries(CompilerAllocPolicy(cx, *thisFromCtor())),
     jumpTables(CompilerAllocPolicy(cx, *thisFromCtor())),
     jumpTableEdges(CompilerAllocPolicy(cx, *thisFromCtor())),
     loopEntries(CompilerAllocPolicy(cx, *thisFromCtor())),
     chunkEdges(CompilerAllocPolicy(cx, *thisFromCtor())),
     stubcc(cx, *thisFromCtor(), frame),
     debugMode_(cx->compartment->debugMode()),
@@ -220,19 +222,19 @@ mjit::Compiler::scanInlineCalls(uint32_t
         if (JSOp(*pc) != JSOP_CALL)
             continue;
 
         /* Not inlining at monitored call sites or those with type barriers. */
         if (code->monitoredTypes || code->monitoredTypesReturn || analysis->typeBarriers(cx, pc) != NULL)
             continue;
 
         uint32_t argc = GET_ARGC(pc);
-        types::TypeSet *calleeTypes = analysis->poppedTypes(pc, argc + 1);
-
-        if (calleeTypes->getKnownTypeTag(cx) != JSVAL_TYPE_OBJECT)
+        types::StackTypeSet *calleeTypes = analysis->poppedTypes(pc, argc + 1);
+
+        if (calleeTypes->getKnownTypeTag() != JSVAL_TYPE_OBJECT)
             continue;
 
         if (calleeTypes->getObjectCount() >= INLINE_SITE_LIMIT)
             continue;
 
         /*
          * Compute the maximum height we can grow the stack for inlined frames.
          * We always reserve space for loop temporaries, for an extra stack
@@ -327,39 +329,44 @@ mjit::Compiler::scanInlineCalls(uint32_t
             if (status != Compile_Okay)
                 return status;
 
             if (!script->analysis()->inlineable(argc)) {
                 okay = false;
                 break;
             }
 
-            if (types::TypeSet::HasObjectFlags(cx, fun->getType(cx),
-                                               types::OBJECT_FLAG_UNINLINEABLE)) {
+            if (types::HeapTypeSet::HasObjectFlags(cx, fun->getType(cx),
+                                                   types::OBJECT_FLAG_UNINLINEABLE)) {
                 okay = false;
                 break;
             }
 
             /*
+             * Watch for a generic state change in the callee's type, so that
+             * the outer script will be recompiled if any type information
+             * changes in stack values within the callee.
+             */
+            types::HeapTypeSet::WatchObjectStateChange(cx, fun->getType(cx));
+
+            /*
              * Don't inline scripts which use 'this' if it is possible they
              * could be called with a 'this' value requiring wrapping. During
              * inlining we do not want to modify frame entries belonging to the
              * caller.
              */
             if (script->analysis()->usesThisValue() &&
-                types::TypeScript::ThisTypes(script)->getKnownTypeTag(cx) != JSVAL_TYPE_OBJECT) {
+                types::TypeScript::ThisTypes(script)->getKnownTypeTag() != JSVAL_TYPE_OBJECT) {
                 okay = false;
                 break;
             }
         }
         if (!okay)
             continue;
 
-        calleeTypes->addFreeze(cx);
-
         /*
          * Add the inline frames to the cross script SSA. We will pick these
          * back up when compiling the call site.
          */
         for (unsigned i = 0; i < count; i++) {
             JSObject *obj = calleeTypes->getSingleObject(i);
             if (!obj)
                 continue;
@@ -1003,16 +1010,23 @@ mjit::CanMethodJIT(JSContext *cx, JSScri
     CompileStatus status;
     {
         types::AutoEnterTypeInference enter(cx, true);
 
         Compiler cc(cx, script, chunkIndex, construct);
         status = cc.compile();
     }
 
+    /*
+     * Check if we have hit the threshold for purging analysis data. This is
+     * done after compilation, rather than after another analysis stage, to
+     * ensure we don't throw away the work just performed.
+     */
+    cx->compartment->types.maybePurgeAnalysis(cx);
+
     if (status == Compile_Okay) {
         /*
          * Compiling a script can occasionally trigger its own recompilation,
          * so go back through the compilation logic.
          */
         goto restart;
     }
 
@@ -1178,17 +1192,17 @@ mjit::Compiler::generatePrologue()
 }
 
 void
 mjit::Compiler::ensureDoubleArguments()
 {
     /* Convert integer arguments which were inferred as (int|double) to doubles. */
     for (uint32_t i = 0; script->function() && i < script->function()->nargs; i++) {
         uint32_t slot = ArgSlot(i);
-        if (a->varTypes[slot].getTypeTag(cx) == JSVAL_TYPE_DOUBLE && analysis->trackSlot(slot))
+        if (a->varTypes[slot].getTypeTag() == JSVAL_TYPE_DOUBLE && analysis->trackSlot(slot))
             frame.ensureDouble(frame.getArg(i));
     }
 }
 
 void
 mjit::Compiler::markUndefinedLocal(uint32_t offset, uint32_t i)
 {
     uint32_t depth = ssa.getFrame(a->inlineIndex).depth;
@@ -1326,16 +1340,18 @@ mjit::Compiler::finishThisUp()
 
     /* Please keep in sync with JITChunk::sizeOfIncludingThis! */
     size_t dataSize = sizeof(JITChunk) +
                       sizeof(NativeMapEntry) * nNmapLive +
                       sizeof(InlineFrame) * inlineFrames.length() +
                       sizeof(CallSite) * callSites.length() +
                       sizeof(JSObject*) * rootedTemplates.length() +
                       sizeof(RegExpShared*) * rootedRegExps.length() +
+                      sizeof(uint32_t) * monitoredBytecodes.length() +
+                      sizeof(uint32_t) * typeBarrierBytecodes.length() +
 #if defined JS_MONOIC
                       sizeof(ic::GetGlobalNameIC) * getGlobalNames.length() +
                       sizeof(ic::SetGlobalNameIC) * setGlobalNames.length() +
                       sizeof(ic::CallICInfo) * callICs.length() +
                       sizeof(ic::EqualityICInfo) * equalityICs.length() +
 #endif
 #if defined JS_POLYIC
                       sizeof(ic::PICInfo) * pics.length() +
@@ -1469,16 +1485,28 @@ mjit::Compiler::finishThisUp()
     RegExpShared **jitRootedRegExps = (RegExpShared **)cursor;
     chunk->nRootedRegExps = rootedRegExps.length();
     cursor += sizeof(RegExpShared*) * chunk->nRootedRegExps;
     for (size_t i = 0; i < chunk->nRootedRegExps; i++) {
         jitRootedRegExps[i] = rootedRegExps[i];
         jitRootedRegExps[i]->incRef();
     }
 
+    uint32_t *jitMonitoredBytecodes = (uint32_t *)cursor;
+    chunk->nMonitoredBytecodes = monitoredBytecodes.length();
+    cursor += sizeof(uint32_t) * chunk->nMonitoredBytecodes;
+    for (size_t i = 0; i < chunk->nMonitoredBytecodes; i++)
+        jitMonitoredBytecodes[i] = monitoredBytecodes[i];
+
+    uint32_t *jitTypeBarrierBytecodes = (uint32_t *)cursor;
+    chunk->nTypeBarrierBytecodes = typeBarrierBytecodes.length();
+    cursor += sizeof(uint32_t) * chunk->nTypeBarrierBytecodes;
+    for (size_t i = 0; i < chunk->nTypeBarrierBytecodes; i++)
+        jitTypeBarrierBytecodes[i] = typeBarrierBytecodes[i];
+
 #if defined JS_MONOIC
     if (chunkIndex == 0 && script->function()) {
         JS_ASSERT(jit->argsCheckPool == NULL);
         if (cx->typeInferenceEnabled()) {
             jit->argsCheckStub = stubCode.locationOf(argsCheckStub);
             jit->argsCheckFallthrough = stubCode.locationOf(argsCheckFallthrough);
             jit->argsCheckJump = stubCode.locationOf(argsCheckJump);
         }
@@ -2086,17 +2114,17 @@ mjit::Compiler::generateMethod()
              * double type have the correct in memory representation.
              */
             const SlotValue *newv = analysis->newValues(PC);
             if (newv) {
                 while (newv->slot) {
                     if (newv->value.kind() == SSAValue::PHI &&
                         newv->value.phiOffset() == uint32_t(PC - script->code) &&
                         analysis->trackSlot(newv->slot) &&
-                        a->varTypes[newv->slot].getTypeTag(cx) == JSVAL_TYPE_DOUBLE) {
+                        a->varTypes[newv->slot].getTypeTag() == JSVAL_TYPE_DOUBLE) {
                         FrameEntry *fe = frame.getSlotEntry(newv->slot);
                         masm.ensureInMemoryDouble(frame.addressOf(fe));
                     }
                     newv++;
                 }
             }
         }
 
@@ -2108,17 +2136,17 @@ mjit::Compiler::generateMethod()
 
         SPEW_OPCODE();
         JS_ASSERT(frame.stackDepth() == opinfo->stackDepth);
 
         if (op == JSOP_LOOPHEAD && analysis->getLoop(PC)) {
             jsbytecode *backedge = script->code + analysis->getLoop(PC)->backedge;
             if (!bytecodeInChunk(backedge)){
                 for (uint32_t slot = ArgSlot(0); slot < TotalSlots(script); slot++) {
-                    if (a->varTypes[slot].getTypeTag(cx) == JSVAL_TYPE_DOUBLE) {
+                    if (a->varTypes[slot].getTypeTag() == JSVAL_TYPE_DOUBLE) {
                         FrameEntry *fe = frame.getSlotEntry(slot);
                         masm.ensureInMemoryDouble(frame.addressOf(fe));
                     }
                 }
             }
         }
 
         // If this is an exception entry point, then jsl_InternalThrow has set
@@ -3501,19 +3529,19 @@ mjit::Compiler::updateElemCounts(jsbytec
           default:                 count = PCCounts::ELEM_ID_OTHER;   break;
         }
     } else {
         count = PCCounts::ELEM_ID_UNKNOWN;
     }
     masm.bumpCount(&counts.get(count), reg);
 
     if (obj->mightBeType(JSVAL_TYPE_OBJECT)) {
-        types::TypeSet *types = frame.extra(obj).types;
+        types::StackTypeSet *types = frame.extra(obj).types;
         if (types && !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_TYPED_ARRAY) &&
-            types->getTypedArrayType(cx) != TypedArray::TYPE_MAX) {
+            types->getTypedArrayType() != TypedArray::TYPE_MAX) {
             count = PCCounts::ELEM_OBJECT_TYPED;
         } else if (types && !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY)) {
             if (!types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED_ARRAY))
                 count = PCCounts::ELEM_OBJECT_PACKED;
             else
                 count = PCCounts::ELEM_OBJECT_DENSE;
         } else {
             count = PCCounts::ELEM_OBJECT_OTHER;
@@ -4225,16 +4253,23 @@ mjit::Compiler::inlineCallHelper(uint32_
             callIC.frameSize.initStatic(frame.totalDepth(), argc);
         }
     }
 
     callFrameSize = callIC.frameSize;
 
     callIC.typeMonitored = monitored(PC) || hasTypeBarriers(PC);
 
+    if (script == outerScript) {
+        if (monitored(PC))
+            monitoredBytecodes.append(PC - script->code);
+        if (hasTypeBarriers(PC))
+            typeBarrierBytecodes.append(PC - script->code);
+    }
+
     /* Test the type if necessary. Failing this always takes a really slow path. */
     MaybeJump notObjectJump;
     if (icCalleeType.isSet())
         notObjectJump = masm.testObject(Assembler::NotEqual, icCalleeType.reg());
 
     Registers tempRegs(Registers::AvailRegs);
     tempRegs.takeReg(icCalleeData);
 
@@ -4839,17 +4874,17 @@ mjit::Compiler::jsop_getprop(PropertyNam
             frame.pushTypedPayload(JSVAL_TYPE_INT32, str);
         }
         return true;
     }
 
     /* Handle lenth accesses of optimize 'arguments'. */
     if (name == cx->runtime->atomState.lengthAtom &&
         cx->typeInferenceEnabled() &&
-        analysis->poppedTypes(PC, 0)->isMagicArguments(cx) &&
+        analysis->poppedTypes(PC, 0)->isMagicArguments() &&
         knownPushedType(0) == JSVAL_TYPE_INT32)
     {
         frame.pop();
         RegisterID reg = frame.allocReg();
         masm.load32(Address(JSFrameReg, StackFrame::offsetOfNumActual()), reg);
         frame.pushTypedPayload(JSVAL_TYPE_INT32, reg);
         if (script->hasScriptCounts)
             bumpPropCount(PC, PCCounts::PROP_DEFINITE);
@@ -4868,17 +4903,17 @@ mjit::Compiler::jsop_getprop(PropertyNam
                 frame.pop();
                 frame.pushCopyOf(fe);
                 if (script->hasScriptCounts)
                     bumpPropCount(PC, PCCounts::PROP_STATIC);
                 return true;
             }
         }
 
-        types::TypeSet *types = analysis->poppedTypes(PC, 0);
+        types::StackTypeSet *types = analysis->poppedTypes(PC, 0);
 
         /*
          * Check if we are accessing the 'length' property of a known dense array.
          * Note that if the types are known to indicate dense arrays, their lengths
          * must fit in an int32.
          */
         if (!types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY)) {
             bool isObject = top->isTypeKnown();
@@ -4998,22 +5033,21 @@ mjit::Compiler::jsop_getprop(PropertyNam
     types::TypeSet *types = frame.extra(top).types;
     if (types && !types->unknownObject() &&
         types->getObjectCount() == 1 &&
         types->getTypeObject(0) != NULL &&
         !types->getTypeObject(0)->unknownProperties() &&
         id == types::MakeTypeId(cx, id)) {
         JS_ASSERT(!forPrototype);
         types::TypeObject *object = types->getTypeObject(0);
-        types::TypeSet *propertyTypes = object->getProperty(cx, id, false);
+        types::HeapTypeSet *propertyTypes = object->getProperty(cx, id, false);
         if (!propertyTypes)
             return false;
-        if (propertyTypes->isDefiniteProperty() &&
+        if (propertyTypes->definiteProperty() &&
             !propertyTypes->isOwnProperty(cx, object, true)) {
-            types->addFreeze(cx);
             uint32_t slot = propertyTypes->definiteSlot();
             bool isObject = top->isTypeKnown();
             if (!isObject) {
                 Jump notObject = frame.testObject(Assembler::NotEqual, top);
                 stubcc.linkExit(notObject, Uses(1));
                 stubcc.leave();
                 stubcc.masm.move(ImmPtr(name), Registers::ArgReg1);
                 OOL_STUBCALL(stubs::GetProp, rejoin);
@@ -5202,29 +5236,29 @@ mjit::Compiler::testSingletonProperty(Ha
     return true;
 }
 
 bool
 mjit::Compiler::testSingletonPropertyTypes(FrameEntry *top, HandleId id, bool *testObject)
 {
     *testObject = false;
 
-    types::TypeSet *types = frame.extra(top).types;
+    types::StackTypeSet *types = frame.extra(top).types;
     if (!types || types->unknownObject())
         return false;
 
-    RootedObject singleton(cx, types->getSingleton(cx));
+    RootedObject singleton(cx, types->getSingleton());
     if (singleton)
         return testSingletonProperty(singleton, id);
 
     if (!globalObj)
         return false;
 
     JSProtoKey key;
-    JSValueType type = types->getKnownTypeTag(cx);
+    JSValueType type = types->getKnownTypeTag();
     switch (type) {
       case JSVAL_TYPE_STRING:
         key = JSProto_String;
         break;
 
       case JSVAL_TYPE_INT32:
       case JSVAL_TYPE_DOUBLE:
         key = JSProto_Number;
@@ -5238,17 +5272,16 @@ mjit::Compiler::testSingletonPropertyTyp
       case JSVAL_TYPE_UNKNOWN:
         if (types->getObjectCount() == 1 && !top->isNotType(JSVAL_TYPE_OBJECT)) {
             JS_ASSERT_IF(top->isTypeKnown(), top->isType(JSVAL_TYPE_OBJECT));
             types::TypeObject *object = types->getTypeObject(0);
             if (object && object->proto) {
                 Rooted<JSObject*> proto(cx, object->proto);
                 if (!testSingletonProperty(proto, id))
                     return false;
-                types->addFreeze(cx);
 
                 /* If we don't know this is an object, we will need a test. */
                 *testObject = (type != JSVAL_TYPE_OBJECT) && !top->isTypeKnown();
                 return true;
             }
         }
         return false;
 
@@ -5289,18 +5322,16 @@ mjit::Compiler::jsop_getprop_dispatch(Pr
         if (pushedTypes->getTypeObject(i) != NULL)
             return false;
     }
 
     types::TypeSet *objTypes = analysis->poppedTypes(PC, 0);
     if (objTypes->unknownObject() || objTypes->getObjectCount() == 0)
         return false;
 
-    pushedTypes->addFreeze(cx);
-
     /* Map each type in the object to the resulting pushed value. */
     Vector<JSObject *> results(CompilerAllocPolicy(cx, *this));
 
     /*
      * For each type of the base object, check it has no 'own' property for the
      * accessed id and that its prototype does have such a property.
      */
     uint32_t last = 0;
@@ -5309,42 +5340,40 @@ mjit::Compiler::jsop_getprop_dispatch(Pr
             return false;
         types::TypeObject *object = objTypes->getTypeObject(i);
         if (!object) {
             results.append((JSObject *) NULL);
             continue;
         }
         if (object->unknownProperties() || !object->proto)
             return false;
-        types::TypeSet *ownTypes = object->getProperty(cx, id, false);
+        types::HeapTypeSet *ownTypes = object->getProperty(cx, id, false);
         if (ownTypes->isOwnProperty(cx, object, false))
             return false;
 
         Rooted<JSObject*> proto(cx, object->proto);
         if (!testSingletonProperty(proto, id))
             return false;
 
         if (proto->getType(cx)->unknownProperties())
             return false;
-        types::TypeSet *protoTypes = proto->type()->getProperty(cx, id, false);
+        types::HeapTypeSet *protoTypes = proto->type()->getProperty(cx, id, false);
         if (!protoTypes)
             return false;
         JSObject *singleton = protoTypes->getSingleton(cx);
         if (!singleton)
             return false;
 
         results.append(singleton);
         last = i;
     }
 
     if (oomInVector)
         return false;
 
-    objTypes->addFreeze(cx);
-
     /* Done filtering, now generate code which dispatches on the type. */
 
     frame.forgetMismatchedObject(top);
 
     if (!top->isType(JSVAL_TYPE_OBJECT)) {
         Jump notObject = frame.testObject(Assembler::NotEqual, top);
         stubcc.linkExit(notObject, Uses(1));
     }
@@ -5425,30 +5454,29 @@ mjit::Compiler::jsop_setprop(PropertyNam
         return true;
     }
 
     /*
      * Set the property directly if we are accessing a known object which
      * always has the property in a particular inline slot.
      */
     jsid id = NameToId(name);
-    types::TypeSet *types = frame.extra(lhs).types;
+    types::StackTypeSet *types = frame.extra(lhs).types;
     if (JSOp(*PC) == JSOP_SETPROP && id == types::MakeTypeId(cx, id) &&
         types && !types->unknownObject() &&
         types->getObjectCount() == 1 &&
         types->getTypeObject(0) != NULL &&
         !types->getTypeObject(0)->unknownProperties())
     {
         types::TypeObject *object = types->getTypeObject(0);
-        types::TypeSet *propertyTypes = object->getProperty(cx, id, false);
+        types::HeapTypeSet *propertyTypes = object->getProperty(cx, id, false);
         if (!propertyTypes)
             return false;
-        if (propertyTypes->isDefiniteProperty() &&
+        if (propertyTypes->definiteProperty() &&
             !propertyTypes->isOwnProperty(cx, object, true)) {
-            types->addFreeze(cx);
             uint32_t slot = propertyTypes->definiteSlot();
             RegisterID reg = frame.tempRegForData(lhs);
             frame.pinReg(reg);
             bool isObject = lhs->isTypeKnown();
             MaybeJump notObject;
             if (!isObject)
                 notObject = frame.testObject(Assembler::NotEqual, lhs);
 #ifdef JSGC_INCREMENTAL_MJ
@@ -5492,29 +5520,21 @@ mjit::Compiler::jsop_setprop(PropertyNam
         return true;
     }
 #endif
 
     PICGenInfo pic(ic::PICInfo::SET, PC);
     pic.name = name;
 
     if (monitored(PC)) {
+        if (script == outerScript)
+            monitoredBytecodes.append(PC - script->code);
         pic.typeMonitored = true;
-        types::TypeSet *types = frame.extra(rhs).types;
-        if (!types) {
-            /* Handle FORNAME and other compound opcodes. Yuck. */
-            types = types::TypeSet::make(cx, "unknownRHS");
-            if (!types)
-                return false;
-            types->addType(cx, types::Type::UnknownType());
-        }
-        pic.rhsTypes = types;
     } else {
         pic.typeMonitored = false;
-        pic.rhsTypes = NULL;
     }
 
     RESERVE_IC_SPACE(masm);
     RESERVE_OOL_SPACE(stubcc.masm);
 
     /* Guard that the type is an object. */
     Jump typeCheck;
     if (!lhs->isTypeKnown()) {
@@ -5940,17 +5960,17 @@ mjit::Compiler::jsop_this()
              */
             if (cx->typeInferenceEnabled() && knownPushedType(0) != JSVAL_TYPE_OBJECT) {
                 prepareStubCall(Uses(1));
                 INLINE_STUBCALL(stubs::This, REJOIN_FALLTHROUGH);
                 return;
             }
 
             JSValueType type = cx->typeInferenceEnabled()
-                ? types::TypeScript::ThisTypes(script)->getKnownTypeTag(cx)
+                ? types::TypeScript::ThisTypes(script)->getKnownTypeTag()
                 : JSVAL_TYPE_UNKNOWN;
             if (type != JSVAL_TYPE_OBJECT) {
                 Jump notObj = frame.testObject(Assembler::NotEqual, thisFe);
                 stubcc.linkExit(notObj, Uses(1));
                 stubcc.leave();
                 OOL_STUBCALL(stubs::This, REJOIN_FALLTHROUGH);
                 stubcc.rejoin(Changes(1));
             }
@@ -6287,17 +6307,17 @@ mjit::Compiler::jsop_getgname(uint32_t i
             return true;
         }
     }
 
     jsid id = NameToId(name);
     JSValueType type = knownPushedType(0);
     if (cx->typeInferenceEnabled() && globalObj->isGlobal() && id == types::MakeTypeId(cx, id) &&
         !globalObj->getType(cx)->unknownProperties()) {
-        types::TypeSet *propertyTypes = globalObj->getType(cx)->getProperty(cx, id, false);
+        types::HeapTypeSet *propertyTypes = globalObj->getType(cx)->getProperty(cx, id, false);
         if (!propertyTypes)
             return false;
 
         /*
          * If we are accessing a defined global which is a normal data property
          * then bake its address into the jitcode and guard against future
          * reallocation of the global object's slots.
          */
@@ -6402,31 +6422,34 @@ mjit::Compiler::jsop_setgname_slow(Prope
     frame.popn(2);
     pushSyncedEntry(0);
 }
 
 bool
 mjit::Compiler::jsop_setgname(PropertyName *name, bool popGuaranteed)
 {
     if (monitored(PC)) {
+        if (script == outerScript)
+            monitoredBytecodes.append(PC - script->code);
+
         /* Global accesses are monitored only for a few names like __proto__. */
         jsop_setgname_slow(name);
         return true;
     }
 
     jsid id = NameToId(name);
     if (cx->typeInferenceEnabled() && globalObj->isGlobal() && id == types::MakeTypeId(cx, id) &&
         !globalObj->getType(cx)->unknownProperties()) {
         /*
          * Note: object branding is disabled when inference is enabled. With
          * branding there is no way to ensure that a non-function property
          * can't get a function later and cause the global object to become
          * branded, requiring a shape change if it changes again.
          */
-        types::TypeSet *types = globalObj->getType(cx)->getProperty(cx, id, false);
+        types::HeapTypeSet *types = globalObj->getType(cx)->getProperty(cx, id, false);
         if (!types)
             return false;
         js::Shape *shape = globalObj->nativeLookup(cx, NameToId(name));
         if (shape && shape->hasDefaultSetter() &&
             shape->writable() && shape->hasSlot() &&
             !types->isOwnProperty(cx, globalObj->getType(cx), true)) {
             watchGlobalReallocation();
             HeapSlot *value = &globalObj->getSlotRef(shape->slot());
@@ -6772,18 +6795,18 @@ mjit::Compiler::jsop_regexp()
 {
     JSObject *obj = script->getRegExp(GET_UINT32_INDEX(PC));
     RegExpStatics *res = globalObj ? globalObj->getRegExpStatics() : NULL;
 
     if (!globalObj ||
         &obj->global() != globalObj ||
         !cx->typeInferenceEnabled() ||
         analysis->localsAliasStack() ||
-        types::TypeSet::HasObjectFlags(cx, globalObj->getType(cx),
-                                       types::OBJECT_FLAG_REGEXP_FLAGS_SET))
+        types::HeapTypeSet::HasObjectFlags(cx, globalObj->getType(cx),
+                                           types::OBJECT_FLAG_REGEXP_FLAGS_SET))
     {
         prepareStubCall(Uses(0));
         masm.move(ImmPtr(obj), Registers::ArgReg1);
         INLINE_STUBCALL(stubs::RegExp, REJOIN_FALLTHROUGH);
         frame.pushSynced(JSVAL_TYPE_OBJECT);
         return true;
     }
 
@@ -6803,27 +6826,27 @@ mjit::Compiler::jsop_regexp()
      * these cases.
      */
     analyze::SSAUseChain *uses =
         analysis->useChain(analyze::SSAValue::PushedValue(PC - script->code, 0));
     if (uses && uses->popped && !uses->next && !reobj->global() && !reobj->sticky()) {
         jsbytecode *use = script->code + uses->offset;
         uint32_t which = uses->u.which;
         if (JSOp(*use) == JSOP_CALLPROP) {
-            JSObject *callee = analysis->pushedTypes(use, 0)->getSingleton(cx);
+            JSObject *callee = analysis->pushedTypes(use, 0)->getSingleton();
             if (callee && callee->isFunction()) {
                 Native native = callee->toFunction()->maybeNative();
                 if (native == js::regexp_exec || native == js::regexp_test) {
                     frame.push(ObjectValue(*obj));
                     return true;
                 }
             }
         } else if (JSOp(*use) == JSOP_CALL && which == 0) {
             uint32_t argc = GET_ARGC(use);
-            JSObject *callee = analysis->poppedTypes(use, argc + 1)->getSingleton(cx);
+            JSObject *callee = analysis->poppedTypes(use, argc + 1)->getSingleton();
             if (callee && callee->isFunction() && argc >= 1 && which == argc - 1) {
                 Native native = callee->toFunction()->maybeNative();
                 if (native == js::str_match ||
                     native == js::str_search ||
                     native == js::str_replace ||
                     native == js::str_split) {
                     frame.push(ObjectValue(*obj));
                     return true;
@@ -6968,17 +6991,17 @@ mjit::Compiler::finishLoop(jsbytecode *h
         /*
          * The interpreter may store integers in slots we assume are doubles,
          * make sure state is consistent before joining. Note that we don't
          * need any handling for other safe points the interpreter can enter
          * from, i.e. from switch and try blocks, as we don't assume double
          * variables are coherent in such cases.
          */
         for (uint32_t slot = ArgSlot(0); slot < TotalSlots(script); slot++) {
-            if (a->varTypes[slot].getTypeTag(cx) == JSVAL_TYPE_DOUBLE) {
+            if (a->varTypes[slot].getTypeTag() == JSVAL_TYPE_DOUBLE) {
                 FrameEntry *fe = frame.getSlotEntry(slot);
                 stubcc.masm.ensureInMemoryDouble(frame.addressOf(fe));
             }
         }
 
         /*
          * Also watch for slots which we assume are doubles at the loop head,
          * but are known to be int32s at this point.
@@ -6986,18 +7009,18 @@ mjit::Compiler::finishLoop(jsbytecode *h
         const SlotValue *newv = analysis->newValues(head);
         if (newv) {
             while (newv->slot) {
                 if (newv->value.kind() == SSAValue::PHI &&
                     newv->value.phiOffset() == uint32_t(head - script->code) &&
                     analysis->trackSlot(newv->slot))
                 {
                     JS_ASSERT(newv->slot < TotalSlots(script));
-                    types::TypeSet *targetTypes = analysis->getValueTypes(newv->value);
-                    if (targetTypes->getKnownTypeTag(cx) == JSVAL_TYPE_DOUBLE) {
+                    types::StackTypeSet *targetTypes = analysis->getValueTypes(newv->value);
+                    if (targetTypes->getKnownTypeTag() == JSVAL_TYPE_DOUBLE) {
                         FrameEntry *fe = frame.getSlotEntry(newv->slot);
                         stubcc.masm.ensureInMemoryDouble(frame.addressOf(fe));
                     }
                 }
                 newv++;
             }
         }
 
@@ -7072,17 +7095,17 @@ mjit::Compiler::jumpAndRun(Jump j, jsbyt
      * Unless we are coming from a branch which synced everything, syncForBranch
      * must have been called and ensured an allocation at the target.
      */
     RegisterAllocation *lvtarget = NULL;
     bool consistent = true;
     if (cx->typeInferenceEnabled()) {
         RegisterAllocation *&alloc = analysis->getAllocation(target);
         if (!alloc) {
-            alloc = cx->typeLifoAlloc().new_<RegisterAllocation>(false);
+            alloc = cx->analysisLifoAlloc().new_<RegisterAllocation>(false);
             if (!alloc) {
                 js_ReportOutOfMemory(cx);
                 return false;
             }
         }
         lvtarget = alloc;
         consistent = frame.consistentRegisters(target);
     }
@@ -7176,19 +7199,19 @@ mjit::Compiler::constructThis()
         if (!cx->typeInferenceEnabled() ||
             !fun->hasSingletonType() ||
             fun->getType(cx)->unknownProperties())
         {
             break;
         }
 
         jsid id = NameToId(cx->runtime->atomState.classPrototypeAtom);
-        types::TypeSet *protoTypes = fun->getType(cx)->getProperty(cx, id, false);
-
-        JSObject *proto = protoTypes->getSingleton(cx, true);
+        types::HeapTypeSet *protoTypes = fun->getType(cx)->getProperty(cx, id, false);
+
+        JSObject *proto = protoTypes->getSingleton(cx);
         if (!proto)
             break;
 
         /*
          * Generate an inline path to create a 'this' object with the given
          * prototype. Only do this if the type is actually known as a possible
          * 'this' type of the script.
          */
@@ -7203,26 +7226,26 @@ mjit::Compiler::constructThis()
             return false;
 
         /*
          * The template incorporates a shape and/or fixed slots from any
          * newScript on its type, so make sure recompilation is triggered
          * should this information change later.
          */
         if (templateObject->type()->newScript)
-            types::TypeSet::WatchObjectStateChange(cx, templateObject->type());
+            types::HeapTypeSet::WatchObjectStateChange(cx, templateObject->type());
 
         RegisterID result = frame.allocReg();
         Jump emptyFreeList = getNewObject(cx, result, templateObject);
 
         stubcc.linkExit(emptyFreeList, Uses(0));
         stubcc.leave();
 
         stubcc.masm.move(ImmPtr(proto), Registers::ArgReg1);
-        OOL_STUBCALL(stubs::CreateThis, REJOIN_RESUME);
+        OOL_STUBCALL(stubs::CreateThis, REJOIN_THIS_CREATED);
 
         frame.setThis(result);
 
         stubcc.rejoin(Changes(1));
         return true;
     } while (false);
 
     // Load the callee.
@@ -7246,17 +7269,17 @@ mjit::Compiler::constructThis()
     }
 
     // Done with the protoFe.
     frame.pop();
 
     prepareStubCall(Uses(0));
     if (protoReg != Registers::ArgReg1)
         masm.move(protoReg, Registers::ArgReg1);
-    INLINE_STUBCALL(stubs::CreateThis, REJOIN_RESUME);
+    INLINE_STUBCALL(stubs::CreateThis, REJOIN_THIS_CREATED);
     frame.freeReg(protoReg);
     return true;
 }
 
 bool
 mjit::Compiler::jsop_tableswitch(jsbytecode *pc)
 {
 #if defined JS_CPU_ARM
@@ -7371,17 +7394,17 @@ mjit::Compiler::jsop_toid()
 
 void
 mjit::Compiler::jsop_in()
 {
     FrameEntry *obj = frame.peek(-1);
     FrameEntry *id = frame.peek(-2);
 
     if (cx->typeInferenceEnabled() && id->isType(JSVAL_TYPE_INT32)) {
-        types::TypeSet *types = analysis->poppedTypes(PC, 0);
+        types::StackTypeSet *types = analysis->poppedTypes(PC, 0);
 
         if (obj->mightBeType(JSVAL_TYPE_OBJECT) &&
             !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY) &&
             !types::ArrayPrototypeHasIndexedProperty(cx, outerScript))
         {
             bool isPacked = !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED_ARRAY);
 
             if (!obj->isTypeKnown()) {
@@ -7473,21 +7496,21 @@ mjit::Compiler::fixDoubleTypes(jsbytecod
         while (newv->slot) {
             if (newv->value.kind() != SSAValue::PHI ||
                 newv->value.phiOffset() != uint32_t(target - script->code) ||
                 !analysis->trackSlot(newv->slot)) {
                 newv++;
                 continue;
             }
             JS_ASSERT(newv->slot < TotalSlots(script));
-            types::TypeSet *targetTypes = analysis->getValueTypes(newv->value);
+            types::StackTypeSet *targetTypes = analysis->getValueTypes(newv->value);
             FrameEntry *fe = frame.getSlotEntry(newv->slot);
             VarType &vt = a->varTypes[newv->slot];
-            JSValueType type = vt.getTypeTag(cx);
-            if (targetTypes->getKnownTypeTag(cx) == JSVAL_TYPE_DOUBLE) {
+            JSValueType type = vt.getTypeTag();
+            if (targetTypes->getKnownTypeTag() == JSVAL_TYPE_DOUBLE) {
                 if (type == JSVAL_TYPE_INT32) {
                     fixedIntToDoubleEntries.append(newv->slot);
                     frame.ensureDouble(fe);
                     frame.forgetLoopReg(fe);
                 } else if (type == JSVAL_TYPE_UNKNOWN) {
                     /*
                      * Unknown here but a double at the target. The type
                      * set for the existing value must be empty, so this
@@ -7509,65 +7532,65 @@ mjit::Compiler::fixDoubleTypes(jsbytecod
 }
 
 void
 mjit::Compiler::watchGlobalReallocation()
 {
     JS_ASSERT(cx->typeInferenceEnabled());
     if (hasGlobalReallocation)
         return;
-    types::TypeSet::WatchObjectStateChange(cx, globalObj->getType(cx));
+    types::HeapTypeSet::WatchObjectStateChange(cx, globalObj->getType(cx));
     hasGlobalReallocation = true;
 }
 
 void
 mjit::Compiler::updateVarType()
 {
     if (!cx->typeInferenceEnabled())
         return;
 
     /*
      * For any non-escaping variable written at the current opcode, update the
      * associated type sets according to the written type, keeping the type set
      * for each variable in sync with what the SSA analysis has determined
      * (see prepareInferenceTypes).
      */
 
-    types::TypeSet *types = pushedTypeSet(0);
+    types::StackTypeSet *types = pushedTypeSet(0);
     uint32_t slot = GetBytecodeSlot(script, PC);
 
     if (analysis->trackSlot(slot)) {
         VarType &vt = a->varTypes[slot];
         vt.setTypes(types);
 
         /*
          * Variables whose type has been inferred as a double need to be
          * maintained by the frame as a double. We might forget the exact
          * representation used by the next call to fixDoubleTypes, fix it now.
          */
-        if (vt.getTypeTag(cx) == JSVAL_TYPE_DOUBLE)
+        if (vt.getTypeTag() == JSVAL_TYPE_DOUBLE)
             frame.ensureDouble(frame.getSlotEntry(slot));
     }
 }
 
 void
 mjit::Compiler::updateJoinVarTypes()
 {
     if (!cx->typeInferenceEnabled())
         return;
 
     /* Update variable types for all new values at this bytecode. */
     const SlotValue *newv = analysis->newValues(PC);
     if (newv) {
         while (newv->slot) {
             if (newv->slot < TotalSlots(script)) {
                 VarType &vt = a->varTypes[newv->slot];
-                JSValueType type = vt.getTypeTag(cx);
+                JSValueType type = vt.getTypeTag();
                 vt.setTypes(analysis->getValueTypes(newv->value));
-                if (vt.getTypeTag(cx) != type) {
+                if (vt.getTypeTag() != type) {
                     /*
                      * If the known type of a variable changes (even if the
                      * variable itself has not been reassigned) then we can't
                      * carry a loop register for the var.
                      */
                     FrameEntry *fe = frame.getSlotEntry(newv->slot);
                     frame.forgetLoopReg(fe);
                 }
@@ -7588,33 +7611,33 @@ mjit::Compiler::restoreVarType()
     if (slot >= analyze::TotalSlots(script))
         return;
 
     /*
      * Restore the known type of a live local or argument. We ensure that types
      * of tracked variables match their inferred type (as tracked in varTypes),
      * but may have forgotten it due to a branch or syncAndForgetEverything.
      */
-    JSValueType type = a->varTypes[slot].getTypeTag(cx);
+    JSValueType type = a->varTypes[slot].getTypeTag();
     if (type != JSVAL_TYPE_UNKNOWN &&
         (type != JSVAL_TYPE_DOUBLE || analysis->trackSlot(slot))) {
         FrameEntry *fe = frame.getSlotEntry(slot);
         JS_ASSERT_IF(fe->isTypeKnown(), fe->isType(type));
         if (!fe->isTypeKnown())
             frame.learnType(fe, type, false);
     }
 }
 
 JSValueType
 mjit::Compiler::knownPushedType(uint32_t pushed)
 {
     if (!cx->typeInferenceEnabled())
         return JSVAL_TYPE_UNKNOWN;
-    types::TypeSet *types = analysis->pushedTypes(PC, pushed);
-    return types->getKnownTypeTag(cx);
+    types::StackTypeSet *types = analysis->pushedTypes(PC, pushed);
+    return types->getKnownTypeTag();
 }
 
 bool
 mjit::Compiler::mayPushUndefined(uint32_t pushed)
 {
     JS_ASSERT(cx->typeInferenceEnabled());
 
     /*
@@ -7622,17 +7645,17 @@ mjit::Compiler::mayPushUndefined(uint32_
      * undefined without going to a stub that can trigger recompilation.
      * If this returns false and undefined subsequently becomes a feasible
      * value pushed by the bytecode, recompilation will *NOT* be triggered.
      */
     types::TypeSet *types = analysis->pushedTypes(PC, pushed);
     return types->hasType(types::Type::UndefinedType());
 }
 
-types::TypeSet *
+types::StackTypeSet *
 mjit::Compiler::pushedTypeSet(uint32_t pushed)
 {
     if (!cx->typeInferenceEnabled())
         return NULL;
     return analysis->pushedTypes(PC, pushed);
 }
 
 bool
@@ -7659,18 +7682,18 @@ mjit::Compiler::pushSyncedEntry(uint32_t
 }
 
 JSObject *
 mjit::Compiler::pushedSingleton(unsigned pushed)
 {
     if (!cx->typeInferenceEnabled())
         return NULL;
 
-    types::TypeSet *types = analysis->pushedTypes(PC, pushed);
-    return types->getSingleton(cx);
+    types::StackTypeSet *types = analysis->pushedTypes(PC, pushed);
+    return types->getSingleton();
 }
 
 /*
  * Barriers overview.
  *
  * After a property fetch finishes, we may need to do type checks on it to make
  * sure it matches the pushed type set for this bytecode. This can be either
  * because there is a type barrier at the bytecode, or because we cannot rule
@@ -7714,27 +7737,27 @@ mjit::Compiler::pushAddressMaybeBarrier(
     RegisterID typeReg, dataReg;
     frame.loadIntoRegisters(address, reuseBase, &typeReg, &dataReg);
 
     frame.pushRegs(typeReg, dataReg, type);
     return testBarrier(typeReg, dataReg, testUndefined);
 }
 
 MaybeJump
-mjit::Compiler::trySingleTypeTest(types::TypeSet *types, RegisterID typeReg)
+mjit::Compiler::trySingleTypeTest(types::StackTypeSet *types, RegisterID typeReg)
 {
     /*
      * If a type set we have a barrier on is monomorphic, generate a single
      * jump taken if a type register has a match. This doesn't handle type sets
      * containing objects, as these require two jumps regardless (test for
      * object, then test the type of the object).
      */
     MaybeJump res;
 
-    switch (types->getKnownTypeTag(cx)) {
+    switch (types->getKnownTypeTag()) {
       case JSVAL_TYPE_INT32:
         res.setJump(masm.testInt32(Assembler::NotEqual, typeReg));
         return res;
 
       case JSVAL_TYPE_DOUBLE:
         res.setJump(masm.testNumber(Assembler::NotEqual, typeReg));
         return res;
 
@@ -7747,17 +7770,17 @@ mjit::Compiler::trySingleTypeTest(types:
         return res;
 
       default:
         return res;
     }
 }
 
 JSC::MacroAssembler::Jump
-mjit::Compiler::addTypeTest(types::TypeSet *types, RegisterID typeReg, RegisterID dataReg)
+mjit::Compiler::addTypeTest(types::StackTypeSet *types, RegisterID typeReg, RegisterID dataReg)
 {
     /*
      * :TODO: It would be good to merge this with GenerateTypeCheck, but the
      * two methods have a different format for the tested value (in registers
      * vs. in memory).
      */
 
     Vector<Jump> matches(CompilerAllocPolicy(cx, *this));
@@ -7817,17 +7840,17 @@ mjit::Compiler::testBarrier(RegisterID t
 {
     BarrierState state;
     state.typeReg = typeReg;
     state.dataReg = dataReg;
 
     if (!cx->typeInferenceEnabled() || !(js_CodeSpec[*PC].format & JOF_TYPESET))
         return state;
 
-    types::TypeSet *types = analysis->bytecodeTypes(PC);
+    types::StackTypeSet *types = analysis->bytecodeTypes(PC);
     if (types->unknown()) {
         /*
          * If the result of this opcode is already unknown, there is no way for
          * a type barrier to fail.
          */
         return state;
     }
 
@@ -7836,17 +7859,18 @@ mjit::Compiler::testBarrier(RegisterID t
         if (!analysis->getCode(PC).monitoredTypesReturn)
             return state;
     } else if (!hasTypeBarriers(PC) && !force) {
         if (testUndefined && !types->hasType(types::Type::UndefinedType()))
             state.jump.setJump(masm.testUndefined(Assembler::Equal, typeReg));
         return state;
     }
 
-    types->addFreeze(cx);
+    if (hasTypeBarriers(PC))
+        typeBarrierBytecodes.append(PC - script->code);
 
     /* Cannot have type barriers when the result of the operation is already unknown. */
     JS_ASSERT(!types->unknown());
 
     state.jump = trySingleTypeTest(types, typeReg);
     if (!state.jump.isSet())
         state.jump.setJump(addTypeTest(types, typeReg, dataReg));
 
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -204,17 +204,16 @@ class Compiler : public BaseCompiler
         RegisterID objReg;
         RegisterID typeReg;
         Label shapeGuard;
         jsbytecode *pc;
         PropertyName *name;
         bool hasTypeCheck;
         bool typeMonitored;
         bool cached;
-        types::TypeSet *rhsTypes;
         ValueRemat vr;
         union {
             ic::GetPropLabels getPropLabels_;
             ic::SetPropLabels setPropLabels_;
             ic::BindNameLabels bindNameLabels_;
             ic::ScopeNameLabels scopeNameLabels_;
         };
 
@@ -244,17 +243,16 @@ class Compiler : public BaseCompiler
             if (ic.isSet()) {
                 ic.u.vr = vr;
             } else if (ic.isGet()) {
                 ic.u.get.typeReg = typeReg;
                 ic.u.get.hasTypeCheck = hasTypeCheck;
             }
             ic.typeMonitored = typeMonitored;
             ic.cached = cached;
-            ic.rhsTypes = rhsTypes;
             if (ic.isGet())
                 ic.setLabels(getPropLabels());
             else if (ic.isSet())
                 ic.setLabels(setPropLabels());
             else if (ic.isBind())
                 ic.setLabels(bindNameLabels());
             else if (ic.isScopeName())
                 ic.setLabels(scopeNameLabels());
@@ -316,29 +314,29 @@ class Compiler : public BaseCompiler
 
     /*
      * Information about the current type of an argument or local in the
      * script. The known type tag of these types is cached when possible to
      * avoid generating duplicate dependency constraints.
      */
     class VarType {
         JSValueType type;
-        types::TypeSet *types;
+        types::StackTypeSet *types;
 
       public:
-        void setTypes(types::TypeSet *types) {
+        void setTypes(types::StackTypeSet *types) {
             this->types = types;
             this->type = JSVAL_TYPE_MISSING;
         }
 
         types::TypeSet *getTypes() { return types; }
 
-        JSValueType getTypeTag(JSContext *cx) {
+        JSValueType getTypeTag() {
             if (type == JSVAL_TYPE_MISSING)
-                type = types ? types->getKnownTypeTag(cx) : JSVAL_TYPE_UNKNOWN;
+                type = types ? types->getKnownTypeTag() : JSVAL_TYPE_UNKNOWN;
             return type;
         }
     };
 
     struct OutgoingChunkEdge {
         uint32_t source;
         uint32_t target;
 
@@ -428,16 +426,18 @@ private:
     js::Vector<GetElementICInfo, 16, CompilerAllocPolicy> getElemICs;
     js::Vector<SetElementICInfo, 16, CompilerAllocPolicy> setElemICs;
 #endif
     js::Vector<CallPatchInfo, 64, CompilerAllocPolicy> callPatches;
     js::Vector<InternalCallSite, 64, CompilerAllocPolicy> callSites;
     js::Vector<DoublePatch, 16, CompilerAllocPolicy> doubleList;
     js::Vector<JSObject*, 0, CompilerAllocPolicy> rootedTemplates;
     js::Vector<RegExpShared*, 0, CompilerAllocPolicy> rootedRegExps;
+    js::Vector<uint32_t> monitoredBytecodes;
+    js::Vector<uint32_t> typeBarrierBytecodes;
     js::Vector<uint32_t> fixedIntToDoubleEntries;
     js::Vector<uint32_t> fixedDoubleToAnyEntries;
     js::Vector<JumpTable, 16> jumpTables;
     js::Vector<JumpTableEdge, 16> jumpTableEdges;
     js::Vector<LoopEntry, 16> loopEntries;
     js::Vector<OutgoingChunkEdge, 16> chunkEdges;
     StubCompiler stubcc;
     Label fastEntryLabel;
@@ -542,33 +542,33 @@ private:
     void markUndefinedLocals();
     void fixDoubleTypes(jsbytecode *target);
     void watchGlobalReallocation();
     void updateVarType();
     void updateJoinVarTypes();
     void restoreVarType();
     JSValueType knownPushedType(uint32_t pushed);
     bool mayPushUndefined(uint32_t pushed);
-    types::TypeSet *pushedTypeSet(uint32_t which);
+    types::StackTypeSet *pushedTypeSet(uint32_t which);
     bool monitored(jsbytecode *pc);
     bool hasTypeBarriers(jsbytecode *pc);
     bool testSingletonProperty(HandleObject obj, HandleId id);
     bool testSingletonPropertyTypes(FrameEntry *top, HandleId id, bool *testObject);
     CompileStatus addInlineFrame(HandleScript script, uint32_t depth, uint32_t parent, jsbytecode *parentpc);
     CompileStatus scanInlineCalls(uint32_t index, uint32_t depth);
     CompileStatus checkAnalysis(HandleScript script);
 
     struct BarrierState {
         MaybeJump jump;
         RegisterID typeReg;
         RegisterID dataReg;
     };
 
-    MaybeJump trySingleTypeTest(types::TypeSet *types, RegisterID typeReg);
-    Jump addTypeTest(types::TypeSet *types, RegisterID typeReg, RegisterID dataReg);
+    MaybeJump trySingleTypeTest(types::StackTypeSet *types, RegisterID typeReg);
+    Jump addTypeTest(types::StackTypeSet *types, RegisterID typeReg, RegisterID dataReg);
     BarrierState pushAddressMaybeBarrier(Address address, JSValueType type, bool reuseBase,
                                          bool testUndefined = false);
     BarrierState testBarrier(RegisterID typeReg, RegisterID dataReg,
                              bool testUndefined = false, bool testReturn = false,
                              bool force = false);
     void finishBarrier(const BarrierState &barrier, RejoinState rejoin, uint32_t which);
 
     void testPushedType(RejoinState rejoin, int which, bool ool = true);
--- a/js/src/methodjit/FastBuiltins.cpp
+++ b/js/src/methodjit/FastBuiltins.cpp
@@ -610,30 +610,28 @@ mjit::Compiler::compileArrayConcat(types
     if (!thisType || &thisType->proto->global() != globalObj)
         return Compile_InlineAbort;
 
     /*
      * Constraints modeling this concat have not been generated by inference,
      * so check that type information already reflects possible side effects of
      * this call.
      */
-    thisTypes->addFreeze(cx);
-    argTypes->addFreeze(cx);
-    types::TypeSet *thisElemTypes = thisType->getProperty(cx, JSID_VOID, false);
+    types::HeapTypeSet *thisElemTypes = thisType->getProperty(cx, JSID_VOID, false);
     if (!thisElemTypes)
         return Compile_Error;
     if (!pushedTypeSet(0)->hasType(types::Type::ObjectType(thisType)))
         return Compile_InlineAbort;
     for (unsigned i = 0; i < argTypes->getObjectCount(); i++) {
         if (argTypes->getSingleObject(i))
             return Compile_InlineAbort;
         types::TypeObject *argType = argTypes->getTypeObject(i);
         if (!argType)
             continue;
-        types::TypeSet *elemTypes = argType->getProperty(cx, JSID_VOID, false);
+        types::HeapTypeSet *elemTypes = argType->getProperty(cx, JSID_VOID, false);
         if (!elemTypes)
             return Compile_Error;
         if (!elemTypes->knownSubset(cx, thisElemTypes))
             return Compile_InlineAbort;
     }
 
     /* Test for 'length == initializedLength' on both arrays. */
 
@@ -870,17 +868,17 @@ mjit::Compiler::compileParseInt(JSValueT
 CompileStatus
 mjit::Compiler::inlineNativeFunction(uint32_t argc, bool callingNew)
 {
     if (!cx->typeInferenceEnabled())
         return Compile_InlineAbort;
 
     FrameEntry *origCallee = frame.peek(-((int)argc + 2));
     FrameEntry *thisValue = frame.peek(-((int)argc + 1));
-    types::TypeSet *thisTypes = analysis->poppedTypes(PC, argc);
+    types::StackTypeSet *thisTypes = analysis->poppedTypes(PC, argc);
 
     if (!origCallee->isConstant() || !origCallee->isType(JSVAL_TYPE_OBJECT))
         return Compile_InlineAbort;
 
     JSObject *callee = &origCallee->getValue().toObject();
     if (!callee->isFunction())
         return Compile_InlineAbort;
 
@@ -942,17 +940,17 @@ mjit::Compiler::inlineNativeFunction(uin
                                            types::OBJECT_FLAG_ITERATED) &&
                 !types::ArrayPrototypeHasIndexedProperty(cx, outerScript)) {
                 bool packed = !thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED_ARRAY);
                 return compileArrayPopShift(thisValue, packed, native == js::array_pop);
             }
         }
     } else if (argc == 1) {
         FrameEntry *arg = frame.peek(-1);
-        types::TypeSet *argTypes = frame.extra(arg).types;
+        types::StackTypeSet *argTypes = frame.extra(arg).types;
         if (!argTypes)
             return Compile_InlineAbort;
         JSValueType argType = arg->isTypeKnown() ? arg->getKnownType() : JSVAL_TYPE_UNKNOWN;
 
         if (native == js_math_abs) {
             if (argType == JSVAL_TYPE_INT32 && type == JSVAL_TYPE_INT32)
                 return compileMathAbsInt(arg);
 
--- a/js/src/methodjit/FastOps.cpp
+++ b/js/src/methodjit/FastOps.cpp
@@ -382,18 +382,18 @@ mjit::Compiler::jsop_equality_obj_obj(JS
     JS_ASSERT(cx->typeInferenceEnabled() &&
               lhs->isType(JSVAL_TYPE_OBJECT) && rhs->isType(JSVAL_TYPE_OBJECT));
 
     /*
      * Handle equality between two objects. We have to ensure there is no
      * special equality operator on either object, if that passes then
      * this is a pointer comparison.
      */
-    types::TypeSet *lhsTypes = analysis->poppedTypes(PC, 1);
-    types::TypeSet *rhsTypes = analysis->poppedTypes(PC, 0);
+    types::StackTypeSet *lhsTypes = analysis->poppedTypes(PC, 1);
+    types::StackTypeSet *rhsTypes = analysis->poppedTypes(PC, 0);
     if (!lhsTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY) &&
         !rhsTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY)) {
         /* :TODO: Merge with jsop_relational_int? */
         JS_ASSERT_IF(!target, fused != JSOP_IFEQ);
         frame.forgetMismatchedObject(lhs);
         frame.forgetMismatchedObject(rhs);
         Assembler::Condition cond = GetCompareCondition(op, fused);
         if (target) {
@@ -891,18 +891,18 @@ mjit::Compiler::jsop_andor(JSOp op, jsby
     return booleanJumpScript(op, target);
 }
 
 bool
 mjit::Compiler::jsop_localinc(JSOp op, uint32_t slot)
 {
     restoreVarType();
 
-    types::TypeSet *types = pushedTypeSet(0);
-    JSValueType type = types ? types->getKnownTypeTag(cx) : JSVAL_TYPE_UNKNOWN;
+    types::StackTypeSet *types = pushedTypeSet(0);
+    JSValueType type = types ? types->getKnownTypeTag() : JSVAL_TYPE_UNKNOWN;
 
     int amt = (op == JSOP_LOCALINC || op == JSOP_INCLOCAL) ? 1 : -1;
 
     if (!analysis->incrementInitialValueObserved(PC)) {
         // Before:
         // After:  V
         frame.pushLocal(slot);
 
@@ -954,18 +954,18 @@ mjit::Compiler::jsop_localinc(JSOp op, u
     return true;
 }
 
 bool
 mjit::Compiler::jsop_arginc(JSOp op, uint32_t slot)
 {
     restoreVarType();
 
-    types::TypeSet *types = pushedTypeSet(0);
-    JSValueType type = types ? types->getKnownTypeTag(cx) : JSVAL_TYPE_UNKNOWN;
+    types::StackTypeSet *types = pushedTypeSet(0);
+    JSValueType type = types ? types->getKnownTypeTag() : JSVAL_TYPE_UNKNOWN;
 
     int amt = (op == JSOP_ARGINC || op == JSOP_INCARG) ? 1 : -1;
 
     if (!analysis->incrementInitialValueObserved(PC)) {
         // Before:
         // After:  V
         if (script->argsObjAliasesFormals())
             jsop_aliasedArg(slot, /* get = */ true);
@@ -1164,17 +1164,17 @@ mjit::Compiler::jsop_setelem_dense()
 
 #ifdef JSGC_INCREMENTAL_MJ
     /*
      * Write barrier.
      * We skip over the barrier if we incremented initializedLength above,
      * because in that case the slot we're overwriting was previously
      * undefined.
      */
-    types::TypeSet *types = frame.extra(obj).types;
+    types::StackTypeSet *types = frame.extra(obj).types;
     if (cx->compartment->compileBarriers() && (!types || types->propertyNeedsBarrier(cx, JSID_VOID))) {
         Label barrierStart = stubcc.masm.label();
         stubcc.linkExitDirect(masm.jump(), barrierStart);
 
         /*
          * The sync call below can potentially clobber key.reg() and slotsReg.
          * We pin key.reg() to avoid it being clobbered. If |hoisted| is true,
          * we can also pin slotsReg. If not, then slotsReg is owned by the
@@ -1533,37 +1533,40 @@ GetDenseArrayShape(JSContext *cx, JSObje
 bool
 mjit::Compiler::jsop_setelem(bool popGuaranteed)
 {
     FrameEntry *obj = frame.peek(-3);
     FrameEntry *id = frame.peek(-2);
     FrameEntry *value = frame.peek(-1);
 
     if (!IsCacheableSetElem(obj, id, value) || monitored(PC)) {
+        if (monitored(PC) && script == outerScript)
+            monitoredBytecodes.append(PC - script->code);
+
         jsop_setelem_slow();
         return true;
     }
 
     // If the object is definitely a dense array or a typed array we can generate
     // code directly without using an inline cache.
     if (cx->typeInferenceEnabled()) {
-        types::TypeSet *types = analysis->poppedTypes(PC, 2);
+        types::StackTypeSet *types = analysis->poppedTypes(PC, 2);
 
         if (!types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY) &&
             !types::ArrayPrototypeHasIndexedProperty(cx, outerScript)) {
             // Inline dense array path.
             jsop_setelem_dense();
             return true;
         }
 
 #ifdef JS_METHODJIT_TYPED_ARRAY
         if ((value->mightBeType(JSVAL_TYPE_INT32) || value->mightBeType(JSVAL_TYPE_DOUBLE)) &&
             !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_TYPED_ARRAY)) {
             // Inline typed array path.
-            int atype = types->getTypedArrayType(cx);
+            int atype = types->getTypedArrayType();
             if (atype != TypedArray::TYPE_MAX) {
                 jsop_setelem_typed(atype);
                 return true;
             }
         }
 #endif
     }
 
@@ -2140,18 +2143,18 @@ mjit::Compiler::jsop_getelem()
     if (!IsCacheableGetElem(obj, id)) {
         jsop_getelem_slow();
         return true;
     }
 
     // If the object is definitely an arguments object, a dense array or a typed array
     // we can generate code directly without using an inline cache.
     if (cx->typeInferenceEnabled() && !id->isType(JSVAL_TYPE_STRING)) {
-        types::TypeSet *types = analysis->poppedTypes(PC, 1);
-        if (types->isMagicArguments(cx) && !outerScript->analysis()->modifiesArguments()) {
+        types::StackTypeSet *types = analysis->poppedTypes(PC, 1);
+        if (types->isMagicArguments() && !outerScript->analysis()->modifiesArguments()) {
             // Inline arguments path.
             jsop_getelem_args();
             return true;
         }
 
         if (obj->mightBeType(JSVAL_TYPE_OBJECT) &&
             !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY) &&
             !types::ArrayPrototypeHasIndexedProperty(cx, outerScript)) {
@@ -2160,17 +2163,17 @@ mjit::Compiler::jsop_getelem()
             jsop_getelem_dense(packed);
             return true;
         }
 
 #ifdef JS_METHODJIT_TYPED_ARRAY
         if (obj->mightBeType(JSVAL_TYPE_OBJECT) &&
             !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_TYPED_ARRAY)) {
             // Inline typed array path.
-            int atype = types->getTypedArrayType(cx);
+            int atype = types->getTypedArrayType();
             if (atype != TypedArray::TYPE_MAX) {
                 if (jsop_getelem_typed(atype))
                     return true;
                 // Fallthrough to the normal GETELEM path.
             }
         }
 #endif
     }
@@ -2681,16 +2684,19 @@ mjit::Compiler::jsop_initprop()
 {
     FrameEntry *obj = frame.peek(-2);
     FrameEntry *fe = frame.peek(-1);
     PropertyName *name = script->getName(GET_UINT32_INDEX(PC));
 
     RootedObject baseobj(cx, frame.extra(obj).initObject);
 
     if (!baseobj || monitored(PC) || cx->compartment->compileBarriers()) {
+        if (monitored(PC) && script == outerScript)
+            monitoredBytecodes.append(PC - script->code);
+
         prepareStubCall(Uses(2));
         masm.move(ImmPtr(name), Registers::ArgReg1);
         INLINE_STUBCALL(stubs::InitProp, REJOIN_FALLTHROUGH);
         return;
     }
 
     RootedObject holder(cx);
     RootedShape shape(cx);
--- a/js/src/methodjit/FrameState.cpp
+++ b/js/src/methodjit/FrameState.cpp
@@ -24,17 +24,18 @@ FrameState::FrameState(JSContext *cx, mj
     loop(NULL), inTryBlock(false)
 {
 }
 
 FrameState::~FrameState()
 {
     while (a) {
         ActiveFrame *parent = a->parent;
-        a->script->analysis()->clearAllocations();
+        if (a->script->hasAnalysis())
+            a->script->analysis()->clearAllocations();
         cx->free_(a);
         a = parent;
     }
     cx->free_(entries);
 }
 
 void
 FrameState::pruneDeadEntries()
@@ -537,17 +538,17 @@ FrameState::dumpAllocation(RegisterAlloc
     printf("\n");
 }
 #endif
 
 RegisterAllocation *
 FrameState::computeAllocation(jsbytecode *target)
 {
     JS_ASSERT(cx->typeInferenceEnabled());
-    RegisterAllocation *alloc = cx->typeLifoAlloc().new_<RegisterAllocation>(false);
+    RegisterAllocation *alloc = cx->analysisLifoAlloc().new_<RegisterAllocation>(false);
     if (!alloc) {
         js_ReportOutOfMemory(cx);
         return NULL;
     }
 
     /*
      * State must be synced at exception and switch targets, at traps and when
      * crossing between compilation chunks.
@@ -587,18 +588,18 @@ FrameState::computeAllocation(jsbytecode
                 if (!a->analysis->trackSlot(entrySlot(fe)))
                     continue;
                 bool nonDoubleTarget = false;
                 const SlotValue *newv = a->analysis->newValues(target);
                 while (newv && newv->slot) {
                     if (newv->value.kind() == SSAValue::PHI &&
                         newv->value.phiOffset() == uint32_t(target - a->script->code) &&
                         newv->slot == entrySlot(fe)) {
-                        types::TypeSet *types = a->analysis->getValueTypes(newv->value);
-                        if (types->getKnownTypeTag(cx) != JSVAL_TYPE_DOUBLE)
+                        types::StackTypeSet *types = a->analysis->getValueTypes(newv->value);
+                        if (types->getKnownTypeTag() != JSVAL_TYPE_DOUBLE)
                             nonDoubleTarget = true;
                     }
                     newv++;
                 }
                 if (nonDoubleTarget)
                     continue;
             }
             alloc->set(reg, fe - entries, fe->data.synced());
@@ -820,17 +821,17 @@ FrameState::discardForJoin(RegisterAlloc
         return true;
     }
 
     if (!alloc) {
         /*
          * This shows up for loop entries which are not reachable from the
          * loop head, and for exception, switch target and trap safe points.
          */
-        alloc = cx->typeLifoAlloc().new_<RegisterAllocation>(false);
+        alloc = cx->analysisLifoAlloc().new_<RegisterAllocation>(false);
         if (!alloc) {
             js_ReportOutOfMemory(cx);
             return false;
         }
     }
 
     resetInternalState();
     PodArrayZero(regstate_);
--- a/js/src/methodjit/FrameState.h
+++ b/js/src/methodjit/FrameState.h
@@ -693,17 +693,17 @@ class FrameState
      * Discards a FrameEntry, tricking the FS into thinking it's synced.
      */
     void discardFe(FrameEntry *fe);
 
     /* Compiler-owned metadata about stack entries, reset on push/pop/copy. */
     struct StackEntryExtra {
         bool initArray;
         JSObject *initObject;
-        types::TypeSet *types;
+        types::StackTypeSet *types;
         JSAtom *name;
         void reset() { PodZero(this); }
     };
     StackEntryExtra& extra(const FrameEntry *fe) {
         JS_ASSERT(fe >= a->args && fe < a->sp);
         return extraArray[fe - entries];
     }
     StackEntryExtra& extra(uint32_t slot) { return extra(entries + slot); }
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -862,27 +862,34 @@ js_InternalInterpret(void *returnData, v
 
       case REJOIN_THIS_PROTOTYPE: {
         RootedObject callee(cx, &fp->callee());
         JSObject *proto = f.regs.sp[0].isObject() ? &f.regs.sp[0].toObject() : NULL;
         JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto);
         if (!obj)
             return js_InternalThrow(f);
         fp->thisValue() = ObjectValue(*obj);
+        /* FALLTHROUGH */
+      }
 
+      case REJOIN_THIS_CREATED: {
         Probes::enterScript(f.cx, f.script(), f.script()->function(), fp);
 
         if (script->debugMode) {
             JSTrapStatus status = js::ScriptDebugPrologue(f.cx, f.fp());
             switch (status) {
               case JSTRAP_CONTINUE:
                 break;
-              case JSTRAP_RETURN:
-                *f.returnAddressLocation() = f.cx->jaegerRuntime().forceReturnFromExternC();
-                return NULL;
+              case JSTRAP_RETURN: {
+                /* Advance to the JSOP_STOP at the end of the script. */
+                f.regs.pc = script->code + script->length - 1;
+                nextDepth = 0;
+                JS_ASSERT(*f.regs.pc == JSOP_STOP);
+                break;
+              }
               case JSTRAP_THROW:
               case JSTRAP_ERROR:
                 return js_InternalThrow(f);
               default:
                 JS_NOT_REACHED("bad ScriptDebugPrologue status");
             }
         }
 
--- a/js/src/methodjit/LoopState.cpp
+++ b/js/src/methodjit/LoopState.cpp
@@ -121,31 +121,31 @@ LoopState::init(jsbytecode *head, Jump e
         JaegerSpew(JSpew_Analysis, "loop modified property at %u: %s %s\n", lifetime->head,
                    types::TypeString(types::Type::ObjectType(modifiedProperties[i].object)),
                    TypeIdString(modifiedProperties[i].id));
     }
 
     RegisterAllocation *&alloc = outerAnalysis->getAllocation(head);
     JS_ASSERT(!alloc);
 
-    alloc = cx->typeLifoAlloc().new_<RegisterAllocation>(true);
+    alloc = cx->analysisLifoAlloc().new_<RegisterAllocation>(true);
     if (!alloc) {
         js_ReportOutOfMemory(cx);
         return false;
     }
 
     this->alloc = alloc;
     this->loopRegs = Registers::AvailAnyRegs;
 
     /*
      * Don't hoist bounds checks or loop invariant code in scripts that have
      * had indirect modification of their arguments.
      */
     if (outerScript->function()) {
-        if (TypeSet::HasObjectFlags(cx, outerScript->function()->getType(cx), OBJECT_FLAG_UNINLINEABLE))
+        if (HeapTypeSet::HasObjectFlags(cx, outerScript->function()->getType(cx), OBJECT_FLAG_UNINLINEABLE))
             this->skipAnalysis = true;
     }
 
     /*
      * Don't hoist bounds checks or loop invariant code in loops with safe
      * points in the middle, which the interpreter can join at directly without
      * performing hoisted bounds checks or doing initial computation of loop
      * invariant terms.
@@ -773,20 +773,20 @@ LoopState::invariantLength(const CrossSS
 {
     if (skipAnalysis)
         return NULL;
 
     uint32_t objSlot;
     int32_t objConstant;
     if (!getEntryValue(obj, &objSlot, &objConstant) || objSlot == UNASSIGNED || objConstant != 0)
         return NULL;
-    TypeSet *objTypes = ssa->getValueTypes(obj);
+    StackTypeSet *objTypes = ssa->getValueTypes(obj);
 
     /* Check for 'length' on the lazy arguments for the current frame. */
-    if (objTypes->isMagicArguments(cx)) {
+    if (objTypes->isMagicArguments()) {
         JS_ASSERT(obj.frame == CrossScriptSSA::OUTER_FRAME);
 
         for (unsigned i = 0; i < invariantEntries.length(); i++) {
             InvariantEntry &entry = invariantEntries[i];
             if (entry.kind == InvariantEntry::INVARIANT_ARGS_LENGTH)
                 return frame.getTemporary(entry.u.array.temporary);
         }
 
@@ -819,19 +819,16 @@ LoopState::invariantLength(const CrossSS
         }
     }
 
     if (!loopInvariantEntry(objSlot))
         return NULL;
 
     /* Hoist 'length' access on typed arrays. */
     if (!objTypes->hasObjectFlags(cx, OBJECT_FLAG_NON_TYPED_ARRAY)) {
-        /* Recompile if object type changes. */
-        objTypes->addFreeze(cx);
-
         uint32_t which = frame.allocTemporary();
         if (which == UINT32_MAX)
             return NULL;
         FrameEntry *fe = frame.getTemporary(which);
 
         JaegerSpew(JSpew_Analysis, "Using %s for loop invariant typed array length of %s\n",
                    frame.entryName(fe), frame.entryName(objSlot));
 
@@ -856,17 +853,16 @@ LoopState::invariantLength(const CrossSS
      */
     for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
         if (objTypes->getSingleObject(i) != NULL)
             return NULL;
         TypeObject *object = objTypes->getTypeObject(i);
         if (object && hasModifiedProperty(object, JSID_VOID))
             return NULL;
     }
-    objTypes->addFreeze(cx);
 
     uint32_t which = frame.allocTemporary();
     if (which == UINT32_MAX)
         return NULL;
     FrameEntry *fe = frame.getTemporary(which);
 
     JaegerSpew(JSpew_Analysis, "Using %s for loop invariant dense array length of %s\n",
                frame.entryName(fe), frame.entryName(objSlot));
@@ -908,22 +904,21 @@ LoopState::invariantProperty(const Cross
 
     /* Check that the property is definite and not written anywhere in the loop. */
     TypeSet *objTypes = ssa->getValueTypes(obj);
     if (objTypes->unknownObject() || objTypes->getObjectCount() != 1)
         return NULL;
     TypeObject *object = objTypes->getTypeObject(0);
     if (!object || object->unknownProperties() || hasModifiedProperty(object, id) || id != MakeTypeId(cx, id))
         return NULL;
-    TypeSet *propertyTypes = object->getProperty(cx, id, false);
+    HeapTypeSet *propertyTypes = object->getProperty(cx, id, false);
     if (!propertyTypes)
         return NULL;
-    if (!propertyTypes->isDefiniteProperty() || propertyTypes->isOwnProperty(cx, object, true))
+    if (!propertyTypes->definiteProperty() || propertyTypes->isOwnProperty(cx, object, true))
         return NULL;
-    objTypes->addFreeze(cx);
 
     uint32_t which = frame.allocTemporary();
     if (which == UINT32_MAX)
         return NULL;
     FrameEntry *fe = frame.getTemporary(which);
 
     JaegerSpew(JSpew_Analysis, "Using %s for loop invariant property of %s\n",
                frame.entryName(fe), frame.entryName(objSlot));
@@ -1173,18 +1168,18 @@ LoopState::ignoreIntegerOverflow(const C
              * Only ignore negative zero if this is the RHS of an addition.
              * Otherwise the result of the other side could change to a double
              * after the first LHS has been computed, and be affected by a
              * negative zero LHS.
              */
             return false;
         }
 
-        TypeSet *lhsTypes = outerAnalysis->poppedTypes(use->offset, 1);
-        if (lhsTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
+        StackTypeSet *lhsTypes = outerAnalysis->poppedTypes(use->offset, 1);
+        if (lhsTypes->getKnownTypeTag() != JSVAL_TYPE_INT32)
             return false;
 
         JaegerSpew(JSpew_Analysis, "Integer result is RHS in integer addition\n");
         return true;
     }
 
     return false;
 }
@@ -1566,18 +1561,18 @@ LoopState::analyzeLoopTest()
       default:
         return;
     }
 
     SSAValue one = outerAnalysis->poppedValue(test.pushedOffset(), 1);
     SSAValue two = outerAnalysis->poppedValue(test.pushedOffset(), 0);
 
     /* The test must be comparing known integers. */
-    if (outerAnalysis->getValueTypes(one)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32 ||
-        outerAnalysis->getValueTypes(two)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) {
+    if (outerAnalysis->getValueTypes(one)->getKnownTypeTag() != JSVAL_TYPE_INT32 ||
+        outerAnalysis->getValueTypes(two)->getKnownTypeTag() != JSVAL_TYPE_INT32) {
         return;
     }
 
     /* Reverse the condition if the RHS is modified by the loop. */
     uint32_t swapRHS;
     int32_t swapConstant;
     if (getLoopTestAccess(two, &swapRHS, &swapConstant)) {
         if (swapRHS != UNASSIGNED && outerAnalysis->liveness(swapRHS).firstWrite(lifetime) != UINT32_MAX) {
@@ -1670,21 +1665,21 @@ LoopState::definiteArrayAccess(const SSA
      * integer.
      *
      * This is used to determine if we can ignore possible integer overflow in
      * an operation; if this site could read a non-integer element out of the
      * array or invoke a scripted getter/setter, it could produce a string or
      * other value by which the overflow could be observed.
      */
 
-    TypeSet *objTypes = outerAnalysis->getValueTypes(obj);
-    TypeSet *elemTypes = outerAnalysis->getValueTypes(index);
+    StackTypeSet *objTypes = outerAnalysis->getValueTypes(obj);
+    StackTypeSet *elemTypes = outerAnalysis->getValueTypes(index);
 
-    if (objTypes->getKnownTypeTag(cx) != JSVAL_TYPE_OBJECT ||
-        elemTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) {
+    if (objTypes->getKnownTypeTag() != JSVAL_TYPE_OBJECT ||
+        elemTypes->getKnownTypeTag() != JSVAL_TYPE_INT32) {
         return false;
     }
 
     if (objTypes->hasObjectFlags(cx, OBJECT_FLAG_NON_DENSE_ARRAY))
         return false;
 
     if (ArrayPrototypeHasIndexedProperty(cx, outerScript))
         return false;
@@ -1796,29 +1791,28 @@ LoopState::analyzeLoopBody(unsigned fram
           case JSOP_NEW:
             skipAnalysis = true;
             break;
 
           case JSOP_SETELEM: {
             SSAValue objValue = analysis->poppedValue(pc, 2);
             SSAValue elemValue = analysis->poppedValue(pc, 1);
 
-            TypeSet *objTypes = analysis->getValueTypes(objValue);
-            TypeSet *elemTypes = analysis->getValueTypes(elemValue);
+            StackTypeSet *objTypes = analysis->getValueTypes(objValue);
+            StackTypeSet *elemTypes = analysis->getValueTypes(elemValue);
 
             /*
              * Mark the modset as unknown if the index might be non-integer,
              * we don't want to consider the SETELEM PIC here.
              */
-            if (objTypes->unknownObject() || elemTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) {
+            if (objTypes->unknownObject() || elemTypes->getKnownTypeTag() != JSVAL_TYPE_INT32) {
                 unknownModset = true;
                 break;
             }
 
-            objTypes->addFreeze(cx);
             for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
                 TypeObject *object = objTypes->getTypeObject(i);
                 if (!object)
                     continue;
                 if (!addModifiedProperty(object, JSID_VOID))
                     return;
                 if (analysis->getCode(pc).arrayWriteHole && !addGrowArray(object))
                     return;
@@ -1843,17 +1837,16 @@ LoopState::analyzeLoopBody(unsigned fram
             jsid id = MakeTypeId(cx, NameToId(name));
 
             TypeSet *objTypes = analysis->poppedTypes(pc, 1);
             if (objTypes->unknownObject()) {
                 unknownModset = true;
                 break;
             }
 
-            objTypes->addFreeze(cx);
             for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
                 TypeObject *object = objTypes->getTypeObject(i);
                 if (!object)
                     continue;
                 if (!addModifiedProperty(object, id))
                     continue;
             }
 
@@ -1911,26 +1904,26 @@ LoopState::analyzeLoopBody(unsigned fram
           case JSOP_EQ:
           case JSOP_NE:
           case JSOP_LT:
           case JSOP_LE:
           case JSOP_GT:
           case JSOP_GE:
           case JSOP_STRICTEQ:
           case JSOP_STRICTNE: {
-            JSValueType type = analysis->poppedTypes(pc, 1)->getKnownTypeTag(cx);
+            JSValueType type = analysis->poppedTypes(pc, 1)->getKnownTypeTag();
             if (type != JSVAL_TYPE_INT32 && type != JSVAL_TYPE_DOUBLE)
                 constrainedLoop = false;
           }
           /* FALLTHROUGH */
 
           case JSOP_POS:
           case JSOP_NEG:
           case JSOP_BITNOT: {
-            JSValueType type = analysis->poppedTypes(pc, 0)->getKnownTypeTag(cx);
+            JSValueType type = analysis->poppedTypes(pc, 0)->getKnownTypeTag();
             if (type != JSVAL_TYPE_INT32 && type != JSVAL_TYPE_DOUBLE)
                 constrainedLoop = false;
             break;
           }
 
           default:
             constrainedLoop = false;
             break;
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -1117,20 +1117,32 @@ JITChunk::rootedTemplates() const
 }
 
 RegExpShared **
 JITChunk::rootedRegExps() const
 {
     return (RegExpShared **)&rootedTemplates()[nRootedTemplates];
 }
 
+uint32_t *
+JITChunk::monitoredBytecodes() const
+{
+    return (uint32_t *)&rootedRegExps()[nRootedRegExps];
+}
+
+uint32_t *
+JITChunk::typeBarrierBytecodes() const
+{
+    return (uint32_t *)&monitoredBytecodes()[nMonitoredBytecodes];
+}
+
 char *
 JITChunk::commonSectionLimit() const
 {
-    return (char *)&rootedRegExps()[nRootedRegExps];
+    return (char *)&typeBarrierBytecodes()[nTypeBarrierBytecodes];
 }
 
 #ifdef JS_MONOIC
 ic::GetGlobalNameIC *
 JITChunk::getGlobalNames() const
 {
     return (ic::GetGlobalNameIC *) commonSectionLimit();
 }
@@ -1252,16 +1264,19 @@ JITChunk::~JITChunk()
 }
 
 void
 JITScript::destroy(FreeOp *fop)
 {
     for (unsigned i = 0; i < nchunks; i++)
         destroyChunk(fop, i);
 
+    if (liveness)
+        fop->free_(liveness);
+
     if (shimPool)
         shimPool->release();
 }
 
 void
 JITScript::destroyChunk(FreeOp *fop, unsigned chunkIndex, bool resetUses)
 {
     ChunkDescriptor &desc = chunkDescriptor(chunkIndex);
@@ -1379,16 +1394,18 @@ JSScript::sizeOfJitScripts(JSMallocSizeO
     }
     return n;
 }
 
 size_t
 mjit::JITScript::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf)
 {
     size_t n = mallocSizeOf(this);
+    if (liveness)
+        n += mallocSizeOf(liveness);
     for (unsigned i = 0; i < nchunks; i++) {
         const ChunkDescriptor &desc = chunkDescriptor(i);
         if (desc.chunk)
             n += desc.chunk->sizeOfIncludingThis(mallocSizeOf);
     }
     return n;
 }
 
@@ -1397,16 +1414,18 @@ size_t
 mjit::JITChunk::computedSizeOfIncludingThis()
 {
     return sizeof(JITChunk) +
            sizeof(NativeMapEntry) * nNmapPairs +
            sizeof(InlineFrame) * nInlineFrames +
            sizeof(CallSite) * nCallSites +
            sizeof(JSObject*) * nRootedTemplates +
            sizeof(RegExpShared*) * nRootedRegExps +
+           sizeof(uint32_t) * nMonitoredBytecodes +
+           sizeof(uint32_t) * nTypeBarrierBytecodes +
 #if defined JS_MONOIC
            sizeof(ic::GetGlobalNameIC) * nGetGlobalNames +
            sizeof(ic::SetGlobalNameIC) * nSetGlobalNames +
            sizeof(ic::CallICInfo) * nCallICs +
            sizeof(ic::EqualityICInfo) * nEqualityICs +
 #endif
 #if defined JS_POLYIC
            sizeof(ic::PICInfo) * nPICs +
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -32,16 +32,20 @@
 
 namespace js {
 
 namespace mjit {
     struct JITChunk;
     struct JITScript;
 }
 
+namespace analyze {
+    struct ScriptLiveness;
+}
+
 struct VMFrame
 {
 #if defined(JS_CPU_SPARC)
     void *savedL0;
     void *savedL1;
     void *savedL2;
     void *savedL3;
     void *savedL4;
@@ -295,16 +299,19 @@ enum RejoinState {
     REJOIN_PUSH_OBJECT,
 
     /*
      * During the prologue of constructing scripts, after the function's
      * .prototype property has been fetched.
      */
     REJOIN_THIS_PROTOTYPE,
 
+    /* As above, after the 'this' object has been created. */
+    REJOIN_THIS_CREATED,
+
     /*
      * Type check on arguments failed during prologue, need stack check and
      * the rest of the JIT prologue before the script can execute.
      */
     REJOIN_CHECK_ARGUMENTS,
 
     /*
      * The script's jitcode was discarded during one of the following steps of
@@ -644,16 +651,18 @@ struct JITChunk
      * changing nMICs() et al as well.
      */
     uint32_t        nNmapPairs : 31;    /* The NativeMapEntrys are sorted by .bcOff.
                                            .ncode values may not be NULL. */
     uint32_t        nInlineFrames;
     uint32_t        nCallSites;
     uint32_t        nRootedTemplates;
     uint32_t        nRootedRegExps;
+    uint32_t        nMonitoredBytecodes;
+    uint32_t        nTypeBarrierBytecodes;
 #ifdef JS_MONOIC
     uint32_t        nGetGlobalNames;
     uint32_t        nSetGlobalNames;
     uint32_t        nCallICs;
     uint32_t        nEqualityICs;
 #endif
 #ifdef JS_POLYIC
     uint32_t        nGetElems;
@@ -672,16 +681,25 @@ struct JITChunk
     // Additional ExecutablePools for native call and getter stubs.
     Vector<NativeCallStub, 0, SystemAllocPolicy> nativeCallStubs;
 
     NativeMapEntry *nmap() const;
     js::mjit::InlineFrame *inlineFrames() const;
     js::mjit::CallSite *callSites() const;
     JSObject **rootedTemplates() const;
     RegExpShared **rootedRegExps() const;
+
+    /*
+     * Offsets of bytecodes which were monitored or had type barriers at the
+     * point of compilation. Used to avoid unnecessary recompilation after
+     * analysis purges.
+     */
+    uint32_t *monitoredBytecodes() const;
+    uint32_t *typeBarrierBytecodes() const;
+
 #ifdef JS_MONOIC
     ic::GetGlobalNameIC *getGlobalNames() const;
     ic::SetGlobalNameIC *setGlobalNames() const;
     ic::CallICInfo *callICs() const;
     ic::EqualityICInfo *equalityICs() const;
 #endif
 #ifdef JS_POLYIC
     ic::GetElementIC *getElems() const;
@@ -781,16 +799,22 @@ struct JITScript
     uint32_t        nedges;
 
     /*
      * Pool for shims which transfer control to the interpreter on cross chunk
      * edges to chunks which do not have compiled code.
      */
     JSC::ExecutablePool *shimPool;
 
+    /*
+     * Optional liveness information attached to the JITScript if the analysis
+     * information is purged while retaining JIT info.
+     */
+    analyze::ScriptLiveness *liveness;
+
 #ifdef JS_MONOIC
     /* Inline cache at function entry for checking this/argument types. */
     JSC::CodeLocationLabel argsCheckStub;
     JSC::CodeLocationLabel argsCheckFallthrough;
     JSC::CodeLocationJump  argsCheckJump;
     JSC::ExecutablePool *argsCheckPool;
     void resetArgsCheck();
 #endif
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -405,17 +405,27 @@ class SetPropCompiler : public PICStubCo
         if (monitor.recompiled())
             return false;
 
         if (!type->unknownProperties()) {
             types::AutoEnterTypeInference enter(cx);
             types::TypeSet *types = type->getProperty(cx, types::MakeTypeId(cx, id), true);
             if (!types)
                 return false;
-            pic.rhsTypes->addSubset(cx, types);
+
+            jsbytecode *pc;
+            JSScript *script = cx->stack.currentScript(&pc);
+
+            if (!script->ensureRanInference(cx) || monitor.recompiled())
+                return false;
+
+            JS_ASSERT(*pc == JSOP_SETPROP || *pc == JSOP_SETNAME);
+
+            types::StackTypeSet *rhsTypes = script->analysis()->poppedTypes(pc, 0);
+            rhsTypes->addSubset(cx, types);
         }
 
         return !monitor.recompiled();
     }
 
     LookupStatus update()
     {
         JS_ASSERT(pic.hit);
--- a/js/src/methodjit/PolyIC.h
+++ b/js/src/methodjit/PolyIC.h
@@ -404,19 +404,16 @@ struct PICInfo : public BasePolyIC {
     bool typeMonitored : 1;
 
     // For GET caches, whether the access may use the property cache.
     bool cached : 1;
 
     // Offset from start of fast path to initial shape guard.
     uint32_t shapeGuard;
 
-    // Possible types of the RHS, for monitored SETPROP PICs.
-    types::TypeSet *rhsTypes;
-
     inline bool isSet() const {
         return kind == SET;
     }
     inline bool isGet() const {
         return kind == GET;
     }
     inline bool isBind() const {
         return kind == BIND;
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -720,16 +720,18 @@ pref("javascript.options.mem.gc_high_fre
 pref("javascript.options.mem.gc_high_frequency_low_limit_mb", 100);
 pref("javascript.options.mem.gc_high_frequency_high_limit_mb", 500);
 pref("javascript.options.mem.gc_high_frequency_heap_growth_max", 300);
 pref("javascript.options.mem.gc_high_frequency_heap_growth_min", 150);
 pref("javascript.options.mem.gc_low_frequency_heap_growth", 150);
 pref("javascript.options.mem.gc_dynamic_heap_growth", true);
 pref("javascript.options.mem.gc_dynamic_mark_slice", true);
 
+pref("javascript.options.mem.analysis_purge_mb", 100);
+
 // advanced prefs
 pref("advanced.mailftp",                    false);
 pref("image.animation_mode",                "normal");
 
 // Same-origin policy for file URIs, "false" is traditional
 pref("security.fileuri.strict_origin_policy", true);
 
 // If there is ever a security firedrill that requires