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 103012 d62929fa43251d582362225df3dcc9033b8c19be
parent 103011 5f0e24568cbb5801171fd94807f526f195f7f0dd
child 103013 4c0af103f0f84d44088302d82087f6f2a6c3de3d
push id13772
push userbhackett@mozilla.com
push dateWed, 22 Aug 2012 12:29:02 +0000
treeherdermozilla-inbound@d62929fa4325 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs778724
milestone17.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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