[INFER] Bytecode SSA analysis, bug 650715.
authorBrian Hackett <bhackett1024@gmail.com>
Fri, 22 Apr 2011 07:59:45 -0700
changeset 74971 90a7b141e0cf06506829bf47307c683541f206ec
parent 74970 dc74f51aad04ff8762b6c70f664a08e9c654ba18
child 74972 96b40c951d15e6b7046555e5ed1634e7ff2486b6
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
bugs650715
milestone6.0a1
[INFER] Bytecode SSA analysis, bug 650715.
js/src/jsanalyze.cpp
js/src/jsanalyze.h
js/src/jsarray.cpp
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsgc.cpp
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/FastArithmetic.cpp
js/src/methodjit/FastOps.cpp
js/src/methodjit/FrameState-inl.h
js/src/methodjit/FrameState.cpp
js/src/methodjit/FrameState.h
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/LoopState.cpp
js/src/methodjit/LoopState.h
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/PolyIC.cpp
js/src/methodjit/Retcon.cpp
js/src/methodjit/StubCalls.cpp
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -40,39 +40,32 @@
 #include "jsanalyze.h"
 #include "jsautooplen.h"
 #include "jscompartment.h"
 #include "jscntxt.h"
 
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
+void
+JSScript::makeAnalysis(JSContext *cx)
+{
+    JS_ASSERT(!analysis_);
+    analysis_ = cx->new_<js::analyze::ScriptAnalysis>(this);
+}
+
 namespace js {
 namespace analyze {
 
 /////////////////////////////////////////////////////////////////////
-// Script
-/////////////////////////////////////////////////////////////////////
-
-Script::Script()
-{
-    PodZero(this);
-}
-
-Script::~Script()
-{
-    JS_FinishArenaPool(&pool);
-}
-
-/////////////////////////////////////////////////////////////////////
 // Bytecode
 /////////////////////////////////////////////////////////////////////
 
 bool
-Bytecode::mergeDefines(JSContext *cx, Script *script, bool initial,
+Bytecode::mergeDefines(JSContext *cx, ScriptAnalysis *script, bool initial,
                        unsigned newDepth, uint32 *newArray, unsigned newCount)
 {
     if (initial) {
         /*
          * Haven't handled any incoming edges to this bytecode before.
          * Define arrays are copy on write, so just reuse the array for this bytecode.
          */
         stackDepth = newDepth;
@@ -118,17 +111,18 @@ Bytecode::mergeDefines(JSContext *cx, Sc
             }
             if (!found) {
                 /*
                  * Get a mutable copy of the defines.  This can end up making
                  * several copies for a bytecode if it has many incoming edges
                  * with progressively smaller sets of defined variables.
                  */
                 if (!owned) {
-                    uint32 *reallocArray = ArenaArray<uint32>(script->pool, defineCount);
+                    uint32 *reallocArray =
+                        ArenaArray<uint32>(cx->compartment->pool, defineCount);
                     if (!reallocArray) {
                         script->setOOM(cx);
                         return false;
                     }
                     memcpy(reallocArray, defineArray, defineCount * sizeof(uint32));
                     defineArray = reallocArray;
                     owned = true;
                 }
@@ -137,42 +131,60 @@ Bytecode::mergeDefines(JSContext *cx, Sc
                 defineArray[i--] = defineArray[--defineCount];
             }
         }
     }
 
     return true;
 }
 
+#ifdef DEBUG
+void
+PrintBytecode(JSContext *cx, JSScript *script, jsbytecode *pc)
+{
+    printf("#%u:", script->id());
+    void *mark = JS_ARENA_MARK(&cx->tempPool);
+    Sprinter sprinter;
+    INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0);
+    js_Disassemble1(cx, script, pc, pc - script->code, true, &sprinter);
+    fprintf(stdout, "%s", sprinter.base);
+    JS_ARENA_RELEASE(&cx->tempPool, mark);
+}
+#endif
+
 /////////////////////////////////////////////////////////////////////
-// Analysis
+// Bytecode Analysis
 /////////////////////////////////////////////////////////////////////
 
 inline bool
-Script::addJump(JSContext *cx, unsigned offset,
-                unsigned *currentOffset, unsigned *forwardJump,
-                unsigned stackDepth, uint32 *defineArray, unsigned defineCount)
+ScriptAnalysis::addJump(JSContext *cx, unsigned offset,
+                        unsigned *currentOffset, unsigned *forwardJump,
+                        unsigned stackDepth, uint32 *defineArray, unsigned defineCount)
 {
     JS_ASSERT(offset < script->length);
 
     Bytecode *&code = codeArray[offset];
     bool initial = (code == NULL);
     if (initial) {
-        code = ArenaNew<Bytecode>(pool);
+        code = ArenaNew<Bytecode>(cx->compartment->pool);
         if (!code) {
             setOOM(cx);
             return false;
         }
     }
 
     if (!code->mergeDefines(cx, this, initial, stackDepth, defineArray, defineCount))
         return false;
     code->jumpTarget = true;
 
     if (offset < *currentOffset) {
+        JSOp op = JSOp(script->code[offset]);
+        if (op == JSOP_TRACE || op == JSOP_NOTRACE)
+            code->loopHead = true;
+
         /* Scripts containing loops are never inlined. */
         isInlineable = false;
 
         /* Don't follow back edges to bytecode which has already been analyzed. */
         if (!code->analyzed) {
             if (*forwardJump == 0)
                 *forwardJump = *currentOffset;
             *currentOffset = offset;
@@ -180,47 +192,38 @@ Script::addJump(JSContext *cx, unsigned 
     } else if (offset > *forwardJump) {
         *forwardJump = offset;
     }
 
     return true;
 }
 
 inline void
-Script::setLocal(uint32 local, uint32 offset)
+ScriptAnalysis::setLocal(uint32 local, uint32 offset)
 {
-    JS_ASSERT(local < localCount());
+    JS_ASSERT(local < script->nfixed);
     JS_ASSERT(offset != LOCAL_CONDITIONALLY_DEFINED);
 
     /*
      * It isn't possible to change the point when a variable becomes unconditionally
      * defined, or to mark it as unconditionally defined after it has already been
      * marked as having a use before def.  It *is* possible to mark it as having
      * a use before def after marking it as unconditionally defined.  In a loop such as:
      *
      * while ((a = b) != 0) { x = a; }
      *
      * When walking through the body of this loop, we will first analyze the test
      * (which comes after the body in the bytecode stream) as unconditional code,
      * and mark a as definitely defined.  a is not in the define array when taking
      * the loop's back edge, so it is treated as possibly undefined when written to x.
      */
-    JS_ASSERT(locals[local] == LOCAL_CONDITIONALLY_DEFINED ||
-              locals[local] == offset || offset == LOCAL_USE_BEFORE_DEF);
-
-    locals[local] = offset;
-}
+    JS_ASSERT(definedLocals[local] == LOCAL_CONDITIONALLY_DEFINED ||
+              definedLocals[local] == offset || offset == LOCAL_USE_BEFORE_DEF);
 
-static inline ptrdiff_t
-GetJumpOffset(jsbytecode *pc, jsbytecode *pc2)
-{
-    uint32 type = JOF_OPTYPE(*pc);
-    if (JOF_TYPE_IS_EXTENDED_JUMP(type))
-        return GET_JUMPX_OFFSET(pc2);
-    return GET_JUMP_OFFSET(pc2);
+    definedLocals[local] = offset;
 }
 
 // return whether op bytecodes do not fallthrough (they may do a jump).
 static inline bool
 BytecodeNoFallThrough(JSOp op)
 {
     switch (op) {
       case JSOP_GOTO:
@@ -242,86 +245,95 @@ BytecodeNoFallThrough(JSOp op)
         // these fall through indirectly, after executing a 'finally'.
         return false;
       default:
         return false;
     }
 }
 
 void
-Script::analyze(JSContext *cx, JSScript *script)
+ScriptAnalysis::analyzeBytecode(JSContext *cx)
 {
-    JS_InitArenaPool(&pool, "script_analyze", 256, 8, NULL);
-
-    JS_ASSERT(script && !codeArray && !locals);
-    this->script = script;
+    JS_ASSERT(cx->compartment->activeAnalysis);
+    JS_ASSERT(!ranBytecode());
+    JSArenaPool &pool = cx->compartment->pool;
 
     unsigned length = script->length;
     unsigned nargs = script->fun ? script->fun->nargs : 0;
-    unsigned nfixed = localCount();
+
+    numSlots = TotalSlots(script);
 
     codeArray = ArenaArray<Bytecode*>(pool, length);
-    locals = ArenaArray<uint32>(pool, nfixed);
-    closedArgs = ArenaArray<JSPackedBool>(pool, nargs);
-    closedVars = ArenaArray<JSPackedBool>(pool, nfixed);
+    definedLocals = ArenaArray<uint32>(pool, script->nfixed);
+    escapedSlots = ArenaArray<JSPackedBool>(pool, numSlots);
 
-    if (!codeArray || !locals || !closedArgs || !closedVars) {
+    if (!codeArray || !definedLocals || !escapedSlots) {
         setOOM(cx);
         return;
     }
 
     PodZero(codeArray, length);
 
-    for (unsigned i = 0; i < nfixed; i++)
-        locals[i] = LOCAL_CONDITIONALLY_DEFINED;
+    for (unsigned i = 0; i < script->nfixed; i++)
+        definedLocals[i] = LOCAL_CONDITIONALLY_DEFINED;
+
+    /*
+     * Populate arg and local slots which can escape and be accessed in ways
+     * other than through ARG* and LOCAL* opcodes (though arguments can still
+     * be indirectly read but not written through 'arguments' properties).
+     * All escaping locals are treated as having possible use-before-defs.
+     */
 
-    PodZero(closedArgs, nargs);
-    for (uint32 i = 0; i < script->nClosedArgs; i++) {
-        unsigned arg = script->getClosedArg(i);
-        JS_ASSERT(arg < nargs);
-        closedArgs[arg] = true;
+    PodZero(escapedSlots, numSlots);
+
+    if (script->usesEval || script->usesArguments || script->compartment->debugMode) {
+        for (unsigned i = 0; i < nargs; i++)
+            escapedSlots[ArgSlot(i)] = true;
+    } else {
+        for (unsigned i = 0; i < script->nClosedArgs; i++) {
+            unsigned arg = script->getClosedArg(i);
+            JS_ASSERT(arg < nargs);
+            escapedSlots[ArgSlot(arg)] = true;
+        }
     }
 
-    PodZero(closedVars, nfixed);
-    for (uint32 i = 0; i < script->nClosedVars; i++) {
-        unsigned local = script->getClosedVar(i);
-        if (local < nfixed)
-            closedVars[local] = true;
+    if (script->usesEval || script->compartment->debugMode) {
+        for (unsigned i = 0; i < script->nfixed; i++) {
+            escapedSlots[LocalSlot(script, i)] = true;
+            setLocal(i, LOCAL_USE_BEFORE_DEF);
+        }
+    } else {
+        for (uint32 i = 0; i < script->nClosedVars; i++) {
+            unsigned local = script->getClosedVar(i);
+            escapedSlots[LocalSlot(script, local)] = true;
+            setLocal(local, LOCAL_USE_BEFORE_DEF);
+        }
     }
 
-    /*
-     * Treat locals as having a possible use-before-def if they could be accessed
-     * by debug code or by eval, or if they could be accessed by an inner script.
-     */
-
-    if (script->usesEval || cx->compartment->debugMode) {
-        for (uint32 i = 0; i < nfixed; i++)
-            setLocal(i, LOCAL_USE_BEFORE_DEF);
-    }
-
-    for (uint32 i = 0; i < script->nClosedVars; i++) {
-        uint32 slot = script->getClosedVar(i);
-        if (slot < nfixed)
-            setLocal(slot, LOCAL_USE_BEFORE_DEF);
-    }
+    /* Maximum number of locals we will keep track of in defined variables analysis. */
+    static const uint32 LOCAL_LIMIT = 50;
+    for (unsigned i = LOCAL_LIMIT; i < script->nfixed; i++)
+        setLocal(i, LOCAL_USE_BEFORE_DEF);
 
     /*
      * If the script is in debug mode, JS_SetFrameReturnValue can be called at
      * any safe point.
      */
     if (cx->compartment->debugMode)
         usesRval = true;
 
     isInlineable = true;
     if (script->nClosedArgs || script->nClosedVars || script->nfixed >= LOCAL_LIMIT ||
         (script->fun && script->fun->isHeavyweight()) ||
         script->usesEval || script->usesArguments || cx->compartment->debugMode) {
         isInlineable = false;
     }
 
+    canTrackVars = true;
+
     /*
      * If we are in the middle of one or more jumps, the offset of the highest
      * target jumping over this bytecode.  Includes implicit jumps from
      * try/catch/finally blocks.
      */
     unsigned forwardJump = 0;
 
     /*
@@ -381,17 +393,17 @@ Script::analyze(JSContext *cx, JSScript 
 
         code->analyzed = true;
 
         if (forwardCatch)
             code->inTryBlock = true;
 
         if (untrap.trap) {
             code->safePoint = true;
-            isInlineable = false;
+            isInlineable = canTrackVars = false;
         }
 
         unsigned stackDepth = code->stackDepth;
         uint32 *defineArray = code->defineArray;
         unsigned defineCount = code->defineCount;
 
         if (!forwardJump) {
             /*
@@ -400,20 +412,20 @@ Script::analyze(JSContext *cx, JSScript 
              * will be thrown which the script does not catch.  Either way,
              * any variables definitely defined at this bytecode will stay
              * defined throughout the rest of the script.  We just need to
              * remember the offset where the variable became unconditionally
              * defined, rather than continue to maintain it in define arrays.
              */
             for (unsigned i = 0; i < defineCount; i++) {
                 uint32 local = defineArray[i];
-                JS_ASSERT_IF(locals[local] != LOCAL_CONDITIONALLY_DEFINED &&
-                             locals[local] != LOCAL_USE_BEFORE_DEF,
-                             locals[local] <= offset);
-                if (locals[local] == LOCAL_CONDITIONALLY_DEFINED)
+                JS_ASSERT_IF(definedLocals[local] != LOCAL_CONDITIONALLY_DEFINED &&
+                             definedLocals[local] != LOCAL_USE_BEFORE_DEF,
+                             definedLocals[local] <= offset);
+                if (definedLocals[local] == LOCAL_CONDITIONALLY_DEFINED)
                     setLocal(local, offset);
             }
             defineArray = code->defineArray = NULL;
             defineCount = code->defineCount = 0;
         }
 
         unsigned nuses = GetUseCount(script, offset);
         unsigned ndefs = GetDefCount(script, offset);
@@ -439,29 +451,36 @@ Script::analyze(JSContext *cx, JSScript 
           case JSOP_DECNAME:
           case JSOP_NAMEINC:
           case JSOP_NAMEDEC:
           case JSOP_FORNAME:
             usesScope = true;
             isInlineable = false;
             break;
 
+          case JSOP_DEFFUN:
+          case JSOP_DEFVAR:
+          case JSOP_DEFCONST:
+          case JSOP_SETCONST:
+            isInlineable = canTrackVars = false;
+            break;
+
           case JSOP_THIS:
             usesThis = true;
             break;
 
           case JSOP_CALL:
           case JSOP_NEW:
             /* Only consider potentially inlineable calls here. */
             hasCalls = true;
             break;
 
           case JSOP_TABLESWITCH:
           case JSOP_TABLESWITCHX: {
-            isInlineable = false;
+            isInlineable = canTrackVars = false;
             jsbytecode *pc2 = pc;
             unsigned jmplen = (op == JSOP_TABLESWITCH) ? JUMP_OFFSET_LEN : JUMPX_OFFSET_LEN;
             unsigned defaultOffset = offset + GetJumpOffset(pc, pc2);
             pc2 += jmplen;
             jsint low = GET_JUMP_OFFSET(pc2);
             pc2 += JUMP_OFFSET_LEN;
             jsint high = GET_JUMP_OFFSET(pc2);
             pc2 += JUMP_OFFSET_LEN;
@@ -485,17 +504,17 @@ Script::analyze(JSContext *cx, JSScript 
                 getCode(targetOffset).safePoint = true;
                 pc2 += jmplen;
             }
             break;
           }
 
           case JSOP_LOOKUPSWITCH:
           case JSOP_LOOKUPSWITCHX: {
-            isInlineable = false;
+            isInlineable = canTrackVars = false;
             jsbytecode *pc2 = pc;
             unsigned jmplen = (op == JSOP_LOOKUPSWITCH) ? JUMP_OFFSET_LEN : JUMPX_OFFSET_LEN;
             unsigned defaultOffset = offset + GetJumpOffset(pc, pc2);
             pc2 += jmplen;
             unsigned npairs = GET_UINT16(pc2);
             pc2 += UINT16_LEN;
 
             if (!addJump(cx, defaultOffset, &nextOffset, &forwardJump,
@@ -522,17 +541,17 @@ Script::analyze(JSContext *cx, JSScript 
 
           case JSOP_TRY: {
             /*
              * Everything between a try and corresponding catch or finally is conditional.
              * Note that there is no problem with code which is skipped by a thrown
              * exception but is not caught by a later handler in the same function:
              * no more code will execute, and it does not matter what is defined.
              */
-            isInlineable = false;
+            isInlineable = canTrackVars = false;
             JSTryNote *tn = script->trynotes()->vector;
             JSTryNote *tnlimit = tn + script->trynotes()->length;
             for (; tn < tnlimit; tn++) {
                 unsigned startOffset = script->main - script->code + tn->start;
                 if (startOffset == offset + 1) {
                     unsigned catchOffset = startOffset + tn->length;
 
                     /* This will overestimate try block code, for multiple catch/finally. */
@@ -555,30 +574,30 @@ Script::analyze(JSContext *cx, JSScript 
           case JSOP_GETLOCAL:
             /*
              * Watch for uses of variables not known to be defined, and mark
              * them as having possible uses before definitions.  Ignore GETLOCAL
              * followed by a POP, these are generated for, e.g. 'var x;'
              */
             if (pc[JSOP_GETLOCAL_LENGTH] != JSOP_POP) {
                 uint32 local = GET_SLOTNO(pc);
-                if (local < nfixed && !localDefined(local, offset)) {
+                if (local < script->nfixed && !localDefined(local, offset)) {
                     setLocal(local, LOCAL_USE_BEFORE_DEF);
                     isInlineable = false;
                 }
             }
             break;
 
           case JSOP_CALLLOCAL:
           case JSOP_INCLOCAL:
           case JSOP_DECLOCAL:
           case JSOP_LOCALINC:
           case JSOP_LOCALDEC: {
             uint32 local = GET_SLOTNO(pc);
-            if (local < nfixed && !localDefined(local, offset)) {
+            if (local < script->nfixed && !localDefined(local, offset)) {
                 setLocal(local, LOCAL_USE_BEFORE_DEF);
                 isInlineable = false;
             }
             break;
           }
 
           case JSOP_SETLOCAL:
           case JSOP_FORLOCAL: {
@@ -586,17 +605,17 @@ Script::analyze(JSContext *cx, JSScript 
 
             /*
              * The local variable may already have been marked as unconditionally
              * defined at a later point in the script, if that definition was in the
              * condition for a loop which then jumped back here.  In such cases we
              * will not treat the variable as ever being defined in the loop body
              * (see setLocal).
              */
-            if (local < nfixed && locals[local] == LOCAL_CONDITIONALLY_DEFINED) {
+            if (local < script->nfixed && definedLocals[local] == LOCAL_CONDITIONALLY_DEFINED) {
                 if (forwardJump) {
                     /* Add this local to the variables defined after this bytecode. */
                     uint32 *newArray = ArenaArray<uint32>(pool, defineCount + 1);
                     if (!newArray) {
                         setOOM(cx);
                         return;
                     }
                     if (defineCount)
@@ -617,20 +636,16 @@ Script::analyze(JSContext *cx, JSScript 
           case JSOP_FORARG:
           case JSOP_SETARG:
           case JSOP_INCARG:
           case JSOP_DECARG:
           case JSOP_ARGINC:
           case JSOP_ARGDEC:
           case JSOP_THROW:
           case JSOP_EXCEPTION:
-          case JSOP_DEFFUN:
-          case JSOP_DEFVAR:
-          case JSOP_DEFCONST:
-          case JSOP_SETCONST:
           case JSOP_DEFLOCALFUN:
           case JSOP_DEFLOCALFUN_FC:
           case JSOP_LAMBDA:
           case JSOP_LAMBDA_FC:
           case JSOP_GETFCSLOT:
           case JSOP_CALLFCSLOT:
           case JSOP_ARGSUB:
           case JSOP_ARGCNT:
@@ -710,350 +725,230 @@ Script::analyze(JSContext *cx, JSScript 
                 nextcode->jumpTarget = true;
             else
                 nextcode->fallthrough = true;
         }
     }
 
     JS_ASSERT(!failed());
     JS_ASSERT(forwardJump == 0 && forwardCatch == 0);
+
+    ranBytecode_ = true;
 }
 
 /////////////////////////////////////////////////////////////////////
-// Stack Analysis
+// Lifetime Analysis
 /////////////////////////////////////////////////////////////////////
 
-bool
-StackAnalysis::analyze(JSArenaPool &pool, JSScript *script,
-                       uint32 start, uint32 length, Script *analysis)
+void
+ScriptAnalysis::analyzeLifetimes(JSContext *cx)
 {
-    this->script = script;
-    this->start = start;
-    this->length = length;
-
-    poppedArray = ArenaArray<PoppedValue*>(pool, length);
-    if (!poppedArray)
-        return false;
-    PodZero(poppedArray, length);
-
-    PoppedValue *stack = ArenaArray<PoppedValue>(pool, script->nslots - script->nfixed);
-    if (!stack)
-        return false;
-
-    unsigned depth = analysis->getCode(start).stackDepth;
-    for (unsigned i = 0; i < depth; i++)
-        stack[i].reset();
-
-    unsigned offset = start;
-    while (offset < start + length) {
-        jsbytecode *pc = script->code + offset;
-        uint32 successorOffset = offset + GetBytecodeLength(pc);
-
-        Bytecode *code = analysis->maybeCode(pc);
-        if (!code) {
-            offset = successorOffset;
-            continue;
-        }
+    JS_ASSERT(cx->compartment->activeAnalysis && !ranLifetimes() && !failed());
 
-        for (unsigned i = depth; i < code->stackDepth; i++)
-            stack[i].reset();
-        depth = code->stackDepth;
-
-        if (code->jumpTarget) {
-            for (unsigned i = 0; i < depth; i++)
-                stack[i].reset();
-        }
-
-        unsigned nuses = GetUseCount(script, offset);
-        unsigned ndefs = GetDefCount(script, offset);
-
-        if (nuses) {
-            PoppedValue *popped = ArenaArray<PoppedValue>(pool, nuses);
-            if (!popped)
-                return false;
-            for (unsigned i = 0; i < nuses; i++)
-                popped[i] = stack[depth - 1 - i];
-            poppedArray[offset - start] = popped;
-        }
-
-        for (unsigned i = 0; i < ndefs; i++) {
-            PoppedValue &value = stack[depth - nuses + i];
-            value.offset = offset;
-            value.which = i;
-        }
-
-        depth -= nuses;
-        depth += ndefs;
-
-        offset = successorOffset;
+    if (!ranBytecode()) {
+        analyzeBytecode(cx);
+        if (failed())
+            return;
     }
 
-    return true;
-}
-
-/////////////////////////////////////////////////////////////////////
-// Live Range Analysis
-/////////////////////////////////////////////////////////////////////
-
-LifetimeScript::LifetimeScript()
-{
-    PodZero(this);
-}
+    JSArenaPool &pool = cx->compartment->pool;
 
-LifetimeScript::~LifetimeScript()
-{
-    JS_FinishArenaPool(&pool);
-}
-
-bool
-LifetimeScript::analyze(JSContext *cx, analyze::Script *analysis, JSScript *script)
-{
-    JS_ASSERT(analysis->hasAnalyzed() && !analysis->failed());
-
-    JS_InitArenaPool(&pool, "script_liverange", 256, 8, NULL);
+    lifetimes = ArenaArray<LifetimeVariable>(pool, numSlots);
+    if (!lifetimes) {
+        setOOM(cx);
+        return;
+    }
+    PodZero(lifetimes, numSlots);
 
-    this->analysis = analysis;
-    this->script = script;
-
-    codeArray = ArenaArray<LifetimeBytecode>(pool, script->length);
-    if (!codeArray)
-        return false;
-    PodZero(codeArray, script->length);
-
-    unsigned nfixed = analysis->localCount();
-    unsigned nargs = script->fun ? script->fun->nargs : 0;
+    /*
+     * Variables which are currently dead. On forward branches to locations
+     * where these are live, they need to be marked as live.
+     */
+    LifetimeVariable **saved = (LifetimeVariable **)
+        cx->calloc_(numSlots * sizeof(LifetimeVariable*));
+    if (!saved) {
+        setOOM(cx);
+        return;
+    }
+    unsigned savedCount = 0;
 
-    nLifetimes = 2 + nargs + nfixed;
-    lifetimes = ArenaArray<LifetimeVariable>(pool, nLifetimes);
-    if (!lifetimes)
-        return false;
-    PodZero(lifetimes, nLifetimes);
-
-    LifetimeVariable *thisVar = lifetimes + 1;
-    LifetimeVariable *args = lifetimes + 2;
-    LifetimeVariable *locals = lifetimes + 2 + nargs;
-
-    saved = ArenaArray<LifetimeVariable*>(pool, nLifetimes);
-    if (!saved)
-        return false;
-    savedCount = 0;
-
-    LifetimeLoop *loop = NULL;
+    LoopAnalysis *loop = NULL;
 
     uint32 offset = script->length - 1;
     while (offset < script->length) {
-        Bytecode *code = analysis->maybeCode(offset);
+        Bytecode *code = maybeCode(offset);
         if (!code) {
             offset--;
             continue;
         }
 
         if (loop && code->safePoint)
             loop->hasSafePoints = true;
 
         UntrapOpcode untrap(cx, script, script->code + offset);
 
-        if (codeArray[offset].loop) {
+        if (code->loop) {
             /*
              * This is the head of a loop, we need to go and make sure that any
              * variables live at the head are live at the backedge and points prior.
              * For each such variable, look for the last lifetime segment in the body
              * and extend it to the end of the loop.
              */
-            JS_ASSERT(loop == codeArray[offset].loop);
-            unsigned backedge = codeArray[offset].loop->backedge;
-            for (unsigned i = 0; i < nfixed; i++) {
-                if (locals[i].lifetime && !extendVariable(cx, locals[i], offset, backedge))
-                    return false;
-            }
-            for (unsigned i = 0; i < nargs; i++) {
-                if (args[i].lifetime && !extendVariable(cx, args[i], offset, backedge))
-                    return false;
+            JS_ASSERT(loop == code->loop);
+            unsigned backedge = code->loop->backedge;
+            for (unsigned i = 0; i < numSlots; i++) {
+                if (lifetimes[i].lifetime)
+                    extendVariable(cx, lifetimes[i], offset, backedge);
             }
 
             loop = loop->parent;
             JS_ASSERT_IF(loop, loop->head < offset);
         }
 
         /* Find the last jump target in the loop, other than the initial entry point. */
         if (loop && code->jumpTarget && offset != loop->entry && offset > loop->lastBlock)
             loop->lastBlock = offset;
 
         jsbytecode *pc = script->code + offset;
         JSOp op = (JSOp) *pc;
 
         switch (op) {
           case JSOP_GETARG:
-          case JSOP_CALLARG: {
-            unsigned arg = GET_ARGNO(pc);
-            if (!analysis->argEscapes(arg)) {
-                if (!addVariable(cx, args[arg], offset))
-                    return false;
-            }
+          case JSOP_CALLARG:
+          case JSOP_GETLOCAL:
+          case JSOP_CALLLOCAL:
+          case JSOP_THIS: {
+            uint32 slot = GetBytecodeSlot(script, pc);
+            if (!slotEscapes(slot))
+                addVariable(cx, lifetimes[slot], offset, saved, savedCount);
             break;
           }
 
-          case JSOP_SETARG: {
-            unsigned arg = GET_ARGNO(pc);
-            if (!analysis->argEscapes(arg)) {
-                if (!killVariable(cx, args[arg], offset))
-                    return false;
-            }
+          case JSOP_SETARG:
+          case JSOP_SETLOCAL:
+          case JSOP_SETLOCALPOP:
+          case JSOP_DEFLOCALFUN:
+          case JSOP_DEFLOCALFUN_FC:
+          case JSOP_FORARG:
+          case JSOP_FORLOCAL: {
+            uint32 slot = GetBytecodeSlot(script, pc);
+            if (!slotEscapes(slot))
+                killVariable(cx, lifetimes[slot], offset, saved, savedCount);
             break;
           }
 
           case JSOP_INCARG:
           case JSOP_DECARG:
           case JSOP_ARGINC:
-          case JSOP_ARGDEC: {
-            unsigned arg = GET_ARGNO(pc);
-            if (!analysis->argEscapes(arg)) {
-                if (!killVariable(cx, args[arg], offset))
-                    return false;
-                if (!addVariable(cx, args[arg], offset))
-                    return false;
-            }
-            break;
-          }
-
-          case JSOP_GETLOCAL:
-          case JSOP_CALLLOCAL: {
-            unsigned local = GET_SLOTNO(pc);
-            if (!analysis->localEscapes(local)) {
-                JS_ASSERT(local < nfixed);
-                if (!addVariable(cx, locals[local], offset))
-                    return false;
-            }
-            break;
-          }
-
-          case JSOP_SETLOCAL:
-          case JSOP_SETLOCALPOP:
-          case JSOP_DEFLOCALFUN: {
-            unsigned local = GET_SLOTNO(pc);
-            if (!analysis->localEscapes(local)) {
-                JS_ASSERT(local < nfixed);
-                if (!killVariable(cx, locals[local], offset))
-                    return false;
-            }
-            break;
-          }
-
+          case JSOP_ARGDEC:
           case JSOP_INCLOCAL:
           case JSOP_DECLOCAL:
           case JSOP_LOCALINC:
           case JSOP_LOCALDEC: {
-            unsigned local = GET_SLOTNO(pc);
-            if (!analysis->localEscapes(local)) {
-                if (!killVariable(cx, locals[local], offset))
-                    return false;
-                if (!addVariable(cx, locals[local], offset))
-                    return false;
+            uint32 slot = GetBytecodeSlot(script, pc);
+            if (!slotEscapes(slot)) {
+                killVariable(cx, lifetimes[slot], offset, saved, savedCount);
+                addVariable(cx, lifetimes[slot], offset, saved, savedCount);
             }
             break;
           }
 
-          case JSOP_THIS:
-            if (!addVariable(cx, *thisVar, offset))
-                return false;
-            break;
-
           case JSOP_IFEQ:
           case JSOP_IFEQX:
           case JSOP_IFNE:
           case JSOP_IFNEX:
           case JSOP_OR:
           case JSOP_ORX:
           case JSOP_AND:
           case JSOP_ANDX:
           case JSOP_GOTO:
-          case JSOP_GOTOX: {
+          case JSOP_GOTOX:
+          case JSOP_ENDFILTER: {
             /*
              * Forward jumps need to pull in all variables which are live at
              * their target offset --- the variables live before the jump are
              * the union of those live at the fallthrough and at the target.
              */
-            uint32 targetOffset = offset + GetJumpOffset(pc, pc);
+            uint32 targetOffset = FollowBranch(script, offset);
+
+            /*
+             * Watch for 'continue' statements in the loop body, which are
+             * jumps to the entry offset separate from the initial jump.
+             */
+            if (loop && loop->entry == targetOffset && loop->entry > loop->lastBlock)
+                loop->lastBlock = loop->entry;
+
             if (targetOffset < offset) {
+                /* This is a loop back edge, no lifetime to pull in yet. */
                 JSOp nop = JSOp(script->code[targetOffset]);
-                if (nop == JSOP_GOTO || nop == JSOP_GOTOX) {
-                    /* This is a continue, short circuit the backwards goto. */
-                    jsbytecode *target = script->code + targetOffset;
-                    targetOffset = targetOffset + GetJumpOffset(target, target);
+                JS_ASSERT(nop == JSOP_TRACE || nop == JSOP_NOTRACE);
 
-                    /*
-                     * Unless this is continuing an outer loop, it is a jump to
-                     * the entry offset separate from the initial jump.
-                     * Prune the last block in the loop.
-                     */
-                    JS_ASSERT(loop);
-                    if (loop->entry == targetOffset && loop->entry > loop->lastBlock)
-                        loop->lastBlock = loop->entry;
-                } else {
-                    /* This is a loop back edge, no lifetime to pull in yet. */
-                    JS_ASSERT(nop == JSOP_TRACE || nop == JSOP_NOTRACE);
+                /*
+                 * 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;
 
-                    /*
-                     * 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;
-
-                    LifetimeLoop *nloop = ArenaNew<LifetimeLoop>(pool);
-                    if (!nloop)
-                        return false;
-                    PodZero(nloop);
+                LoopAnalysis *nloop = ArenaNew<LoopAnalysis>(pool);
+                if (!nloop) {
+                    cx->free_(saved);
+                    setOOM(cx);
+                    return;
+                }
+                PodZero(nloop);
 
-                    if (loop)
-                        loop->hasCallsLoops = true;
+                if (loop)
+                    loop->hasCallsLoops = true;
 
-                    nloop->parent = loop;
-                    loop = nloop;
+                nloop->parent = loop;
+                loop = nloop;
 
-                    codeArray[targetOffset].loop = loop;
-                    loop->head = targetOffset;
-                    loop->backedge = offset;
-                    loop->lastBlock = loop->head;
+                getCode(targetOffset).loop = loop;
+                loop->head = targetOffset;
+                loop->backedge = offset;
+                loop->lastBlock = loop->head;
 
-                    /*
-                     * Find the entry jump, which will be a GOTO for 'for' or
-                     * 'while' loops or a fallthrough for 'do while' loops.
-                     */
-                    uint32 entry = targetOffset;
-                    if (entry) {
-                        do {
-                            entry--;
-                        } while (!analysis->maybeCode(entry));
+                /*
+                 * Find the entry jump, which will be a GOTO for 'for' or
+                 * 'while' loops or a fallthrough for 'do while' loops.
+                 */
+                uint32 entry = targetOffset;
+                if (entry) {
+                    do {
+                        entry--;
+                    } while (!maybeCode(entry));
 
-                        jsbytecode *entrypc = script->code + entry;
-                        if (JSOp(*entrypc) == JSOP_GOTO || JSOp(*entrypc) == JSOP_GOTOX)
-                            loop->entry = entry + GetJumpOffset(entrypc, entrypc);
-                        else
-                            loop->entry = targetOffset;
-                    } else {
-                        /* Do-while loop at the start of the script. */
+                    jsbytecode *entrypc = script->code + entry;
+                    if (JSOp(*entrypc) == JSOP_GOTO || JSOp(*entrypc) == JSOP_GOTOX)
+                        loop->entry = entry + GetJumpOffset(entrypc, entrypc);
+                    else
                         loop->entry = targetOffset;
-                    }
+                } else {
+                    /* Do-while loop at the start of the script. */
+                    loop->entry = targetOffset;
+                }
 
-                    break;
-                }
+                break;
             }
             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 = ArenaNew<Lifetime>(pool, offset, var.savedEnd, var.saved);
-                    if (!var.lifetime)
-                        return false;
+                    if (!var.lifetime) {
+                        cx->free_(saved);
+                        setOOM(cx);
+                        return;
+                    }
                     var.saved = NULL;
                     saved[i--] = saved[--savedCount];
                 } else if (loop && !var.savedEnd) {
                     /*
                      * This jump precedes the basic block which killed the variable,
                      * remember it and use it for the end of the next lifetime
                      * segment should the variable become live again. This is needed
                      * for loops, as if we wrap liveness around the loop the isLive
@@ -1068,18 +963,21 @@ LifetimeScript::analyze(JSContext *cx, a
           case JSOP_LOOKUPSWITCH:
           case JSOP_LOOKUPSWITCHX:
           case JSOP_TABLESWITCH:
           case JSOP_TABLESWITCHX:
             /* Restore all saved variables. :FIXME: maybe do this precisely. */
             for (unsigned i = 0; i < savedCount; i++) {
                 LifetimeVariable &var = *saved[i];
                 var.lifetime = ArenaNew<Lifetime>(pool, offset, var.savedEnd, var.saved);
-                if (!var.lifetime)
-                    return false;
+                if (!var.lifetime) {
+                    cx->free_(saved);
+                    setOOM(cx);
+                    return;
+                }
                 var.saved = NULL;
                 saved[i--] = saved[--savedCount];
             }
             savedCount = 0;
             break;
 
           case JSOP_NEW:
           case JSOP_CALL:
@@ -1091,92 +989,98 @@ LifetimeScript::analyze(JSContext *cx, a
             break;
 
           default:;
         }
 
         offset--;
     }
 
-    return true;
+    cx->free_(saved);
+
+    ranLifetimes_ = true;
 }
 
 #ifdef DEBUG
 void
-LifetimeScript::dumpVariable(LifetimeVariable &var)
+LifetimeVariable::print() const
 {
-    Lifetime *segment = var.lifetime ? var.lifetime : var.saved;
+    Lifetime *segment = lifetime ? lifetime : saved;
     while (segment) {
         printf(" (%u,%u%s)", segment->start, segment->end, segment->loopTail ? ",tail" : "");
         segment = segment->next;
     }
     printf("\n");
 }
 #endif /* DEBUG */
 
-inline bool
-LifetimeScript::addVariable(JSContext *cx, LifetimeVariable &var, unsigned offset)
+inline void
+ScriptAnalysis::addVariable(JSContext *cx, LifetimeVariable &var, unsigned offset,
+                            LifetimeVariable **&saved, unsigned &savedCount)
 {
     if (var.lifetime) {
         JS_ASSERT(offset < var.lifetime->start);
         var.lifetime->start = offset;
     } else {
         if (var.saved) {
             /* Remove from the list of saved entries. */
             for (unsigned i = 0; i < savedCount; i++) {
                 if (saved[i] == &var) {
                     JS_ASSERT(savedCount);
                     saved[i--] = saved[--savedCount];
                     break;
                 }
             }
         }
-        var.lifetime = ArenaNew<Lifetime>(pool, offset, var.savedEnd, var.saved);
-        if (!var.lifetime)
-            return false;
+        var.lifetime = ArenaNew<Lifetime>(cx->compartment->pool, offset, var.savedEnd, var.saved);
+        if (!var.lifetime) {
+            setOOM(cx);
+            return;
+        }
         var.saved = NULL;
     }
-    return true;
 }
 
-inline bool
-LifetimeScript::killVariable(JSContext *cx, LifetimeVariable &var, unsigned offset)
+inline void
+ScriptAnalysis::killVariable(JSContext *cx, LifetimeVariable &var, unsigned offset,
+                             LifetimeVariable **&saved, unsigned &savedCount)
 {
     if (!var.lifetime) {
         /* Make a point lifetime indicating the write. */
         if (!var.saved)
             saved[savedCount++] = &var;
-        var.saved = ArenaNew<Lifetime>(pool, offset, var.savedEnd, var.saved);
-        if (!var.saved)
-            return false;
+        var.saved = ArenaNew<Lifetime>(cx->compartment->pool, offset, var.savedEnd, var.saved);
+        if (!var.saved) {
+            setOOM(cx);
+            return;
+        }
         var.saved->write = true;
         var.savedEnd = 0;
-        return true;
+        return;
     }
     JS_ASSERT(offset < var.lifetime->start);
 
     /*
      * The variable is considered to be live at the bytecode which kills it
      * (just not at earlier bytecodes). This behavior is needed by downstream
      * register allocation (see FrameState::bestEvictReg).
      */
     var.lifetime->start = offset;
     var.lifetime->write = true;
 
     var.saved = var.lifetime;
     var.savedEnd = 0;
     var.lifetime = NULL;
 
     saved[savedCount++] = &var;
-
-    return true;
 }
 
-inline bool
-LifetimeScript::extendVariable(JSContext *cx, LifetimeVariable &var, unsigned start, unsigned end)
+inline void
+ScriptAnalysis::extendVariable(JSContext *cx, LifetimeVariable &var,
+                               unsigned start, unsigned end)
 {
     JS_ASSERT(var.lifetime);
     var.lifetime->start = start;
 
     /*
      * When walking backwards through loop bodies, we don't know which vars
      * are live at the loop's backedge. We save the endpoints for lifetime
      * segments which we *would* use if the variables were live at the backedge
@@ -1210,19 +1114,21 @@ LifetimeScript::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 = ArenaNew<Lifetime>(pool, savedEnd, 0, segment->next);
-            if (!tail)
-                return false;
+            Lifetime *tail = ArenaNew<Lifetime>(cx->compartment->pool, savedEnd, 0, segment->next);
+            if (!tail) {
+                setOOM(cx);
+                return;
+            }
             tail->start = segment->end;
             tail->loopTail = true;
 
             /*
              * Clear the segment's saved end, but preserve in the tail if this
              * is the last segment in the loop and the variable is killed in an
              * outer loop before the backedge.
              */
@@ -1234,14 +1140,582 @@ LifetimeScript::extendVariable(JSContext
 
             segment->next = tail;
             segment = tail->next;
         } else {
             JS_ASSERT(segment->savedEnd == 0);
             segment = segment->next;
         }
     }
+}
 
+void
+ScriptAnalysis::clearAllocations()
+{
+    /*
+     * Clear out storage used for register allocations in a compilation once
+     * that compilation has finished. Register allocations are only used for
+     * a single compilation.
+     */
+    for (unsigned i = 0; i < script->length; i++) {
+        Bytecode *code = maybeCode(i);
+        if (code)
+            code->allocation = NULL;
+    }
+}
+
+/////////////////////////////////////////////////////////////////////
+// SSA Analysis
+/////////////////////////////////////////////////////////////////////
+
+void
+ScriptAnalysis::analyzeSSA(JSContext *cx)
+{
+    JS_ASSERT(cx->compartment->activeAnalysis && !ranSSA() && !failed());
+
+    if (!ranLifetimes()) {
+        analyzeLifetimes(cx);
+        if (failed())
+            return;
+    }
+
+    JSArenaPool &pool = cx->compartment->pool;
+    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.
+     */
+    SSAValue *values = (SSAValue *)
+        cx->calloc_((numSlots + maxDepth) * sizeof(SSAValue));
+    if (!values) {
+        setOOM(cx);
+        return;
+    }
+
+    SSAValue *stack = values + numSlots;
+    uint32 stackDepth = 0;
+
+    for (uint32 slot = ArgSlot(0); slot < numSlots; slot++) {
+        if (trackSlot(slot))
+            values[slot].initInitial(slot);
+    }
+
+    /*
+     * All target offsets for forward jumps we in the middle of. We lazily add
+     * pending entries at these targets for the original value of variables
+     * modified before the branch rejoins.
+     */
+    Vector<uint32> branchTargets(cx);
+
+    uint32 offset = 0;
+    while (offset < script->length) {
+        jsbytecode *pc = script->code + offset;
+        UntrapOpcode untrap(cx, script, pc);
+
+        uint32 successorOffset = offset + GetBytecodeLength(pc);
+
+        Bytecode *code = maybeCode(pc);
+        if (!code) {
+            offset = successorOffset;
+            continue;
+        }
+
+        if (code->stackDepth > stackDepth)
+            PodZero(stack + stackDepth, code->stackDepth - stackDepth);
+        stackDepth = code->stackDepth;
+
+        if (code->loop) {
+            /*
+             * Make sure there is a pending value array for phi nodes at the
+             * loop head. We won't be able to clear these until we reach the
+             * loop's back edge.
+             *
+             * We need phi nodes for all variables which might be modified
+             * during the loop. This ensures that in the loop body we have
+             * already updated state to reflect possible changes that happen
+             * before the back edge, and don't need to go back and fix things
+             * up when we *do* get to the back edge. This could be made lazier.
+             *
+             * We don't make phi nodes for values on the stack at the head of
+             * the loop. These may be popped during the loop (i.e. for ITER
+             * loops), but in such cases the original value is pushed back.
+             */
+            Vector<SlotValue> *&pending = code->pendingValues;
+            if (pending) {
+                removeBranchTarget(branchTargets, offset);
+            } else {
+                pending = cx->new_< Vector<SlotValue> >(cx);
+                if (!pending) {
+                    setOOM(cx);
+                    return;
+                }
+            }
+
+            /*
+             * Make phi nodes and update state for slots which are already in
+             * pending from previous branches to the loop head, and which are
+             * modified in the body of the loop.
+             */
+            for (unsigned i = 0; i < pending->length(); i++) {
+                SlotValue &v = (*pending)[i];
+                if (v.slot < numSlots && liveness(v.slot).firstWrite(code->loop) != uint32(-1)) {
+                    if (v.value.kind() != SSAValue::PHI || v.value.phiOffset() < offset) {
+                        SSAValue ov = v.value;
+                        if (!makePhi(cx, v.slot, offset, &ov))
+                            return;
+                        insertPhi(cx, ov, v.value);
+                        v.value = ov;
+                    }
+                }
+                if (code->fallthrough || code->jumpFallthrough)
+                    mergeValue(cx, offset, values[v.slot], &v);
+                mergeBranchTarget(cx, values[v.slot], v.slot, branchTargets);
+                values[v.slot] = v.value;
+            }
+
+            /*
+             * Make phi nodes for all other slots which might be modified
+             * during the loop. This ensures that in the loop body we have
+             * already updated state to reflect possible changes that happen
+             * before the back edge, and don't need to go back and fix things
+             * up when we *do* get to the back edge. This could be made lazier.
+             */
+            for (uint32 slot = ArgSlot(0); slot < numSlots + stackDepth; slot++) {
+                if (slot >= numSlots || !trackSlot(slot))
+                    continue;
+                if (liveness(slot).firstWrite(code->loop) == uint32(-1))
+                    continue;
+                if (values[slot].kind() == SSAValue::PHI && values[slot].phiOffset() == offset) {
+                    /* There is already a pending entry for this slot. */
+                    continue;
+                }
+                SSAValue ov;
+                if (!makePhi(cx, slot, offset, &ov))
+                    return;
+                if (code->fallthrough || code->jumpFallthrough)
+                    insertPhi(cx, ov, values[slot]);
+                mergeBranchTarget(cx, values[slot], slot, branchTargets);
+                values[slot] = ov;
+                if (!pending->append(SlotValue(slot, ov))) {
+                    setOOM(cx);
+                    return;
+                }
+            }
+        } else if (code->pendingValues) {
+            /*
+             * New values at this point from a previous jump to this bytecode.
+             * If there is fallthrough from the previous instruction, merge
+             * with the current state and create phi nodes where necessary,
+             * otherwise replace current values with the new values.
+             */
+            removeBranchTarget(branchTargets, offset);
+            Vector<SlotValue> *pending = code->pendingValues;
+            for (unsigned i = 0; i < pending->length(); i++) {
+                SlotValue &v = (*pending)[i];
+                if (code->fallthrough || code->jumpFallthrough)
+                    mergeValue(cx, offset, values[v.slot], &v);
+                mergeBranchTarget(cx, values[v.slot], v.slot, branchTargets);
+                values[v.slot] = v.value;
+            }
+            freezeNewValues(cx, offset);
+        }
+
+        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 = (SSAValue *)ArenaArray<SSAValue>(pool, xuses);
+            if (!code->poppedValues) {
+                setOOM(cx);
+                return;
+            }
+            for (unsigned i = 0; i < nuses; i++) {
+                SSAValue &v = stack[stackDepth - 1 - i];
+                code->poppedValues[i] = v;
+                v.clear();
+            }
+            if (xuses > nuses) {
+                /*
+                 * For SETLOCAL, INCLOCAL, etc. opcodes, add an extra popped
+                 * value holding the value of the local before the op.
+                 */
+                uint32 slot = GetBytecodeSlot(script, pc);
+                if (trackSlot(slot))
+                    code->poppedValues[nuses] = values[slot];
+                else
+                    code->poppedValues[nuses].clear();
+            }
+        }
+
+        stackDepth -= nuses;
+
+        for (unsigned i = 0; i < ndefs; i++)
+            stack[stackDepth + i].initPushed(offset, i);
+
+        stackDepth += ndefs;
+
+        JSOp op = (JSOp)*pc;
+        switch (op) {
+          case JSOP_SETARG:
+          case JSOP_SETLOCAL:
+          case JSOP_SETLOCALPOP:
+          case JSOP_DEFLOCALFUN:
+          case JSOP_DEFLOCALFUN_FC:
+          case JSOP_FORARG:
+          case JSOP_FORLOCAL:
+          case JSOP_INCARG:
+          case JSOP_DECARG:
+          case JSOP_ARGINC:
+          case JSOP_ARGDEC:
+          case JSOP_INCLOCAL:
+          case JSOP_DECLOCAL:
+          case JSOP_LOCALINC:
+          case JSOP_LOCALDEC: {
+            uint32 slot = GetBytecodeSlot(script, pc);
+            if (trackSlot(slot)) {
+                mergeBranchTarget(cx, values[slot], slot, branchTargets);
+                values[slot].initWritten(slot, offset);
+            }
+            if (op == JSOP_FORARG || op == JSOP_FORLOCAL)
+                stack[stackDepth - 1] = code->poppedValues[0];
+            break;
+          }
+
+          case JSOP_GETARG:
+          case JSOP_CALLARG:
+          case JSOP_GETLOCAL:
+          case JSOP_CALLLOCAL: {
+            uint32 slot = GetBytecodeSlot(script, pc);
+            if (trackSlot(slot)) {
+                /*
+                 * Propagate the current value of the local to the pushed value,
+                 * and remember it with an extended use on the opcode.
+                 */
+                stack[stackDepth - 1] = code->poppedValues[0] = values[slot];
+            }
+            break;
+          }
+
+          /* Short circuit ops which push back one of their operands. */
+
+          case JSOP_MOREITER:
+          case JSOP_FORELEM:
+            stack[stackDepth - 2] = code->poppedValues[0];
+            break;
+
+          case JSOP_FORNAME:
+          case JSOP_FORGNAME:
+            stack[stackDepth - 1] = code->poppedValues[0];
+            break;
+
+          case JSOP_FORPROP:
+          case JSOP_INITPROP:
+          case JSOP_INITMETHOD:
+            stack[stackDepth - 1] = code->poppedValues[1];
+            break;
+
+          case JSOP_INITELEM:
+            stack[stackDepth - 1] = code->poppedValues[2];
+            break;
+
+          default:;
+        }
+
+        uint32 type = JOF_TYPE(js_CodeSpec[op].format);
+        if (type == JOF_JUMP || type == JOF_JUMPX) {
+            unsigned targetOffset = FollowBranch(script, offset);
+
+            unsigned targetDepth = getCode(targetOffset).stackDepth;
+            JS_ASSERT(targetDepth <= stackDepth);
+
+            /*
+             * If there is already an active branch to target, make sure its
+             * pending values reflect any changes made since the first branch.
+             * Otherwise, add a new pending branch and determine its pending
+             * values lazily.
+             */
+            Vector<SlotValue> *&pending = getCode(targetOffset).pendingValues;
+            if (pending) {
+                for (unsigned i = 0; i < pending->length(); i++) {
+                    SlotValue &v = (*pending)[i];
+                    mergeValue(cx, targetOffset, values[v.slot], &v);
+                }
+            } else {
+                JS_ASSERT(targetOffset > offset);
+                pending = cx->new_< Vector<SlotValue> >(cx);
+                if (!pending || !branchTargets.append(targetOffset)) {
+                    setOOM(cx);
+                    return;
+                }
+            }
+
+            /*
+             * Make sure there is a pending entry for each value on the stack.
+             * The number of stack entries at join points is usually zero, and
+             * we don't want to look at the active branches while popping and
+             * pushing values in each opcode.
+             */
+            for (unsigned i = 0; i < targetDepth; i++)
+                checkPendingValue(cx, stack[i], StackSlot(script, i), pending);
+
+            /*
+             * If this is a back edge, we're done with the loop and can freeze
+             * the phi values at the head now.
+             */
+            if (targetOffset < offset)
+                freezeNewValues(cx, targetOffset);
+        }
+
+        offset = successorOffset;
+    }
+
+    ranSSA_ = true;
+}
+
+/* Get a phi node's capacity for a given length. */
+static inline unsigned
+PhiNodeCapacity(unsigned length)
+{
+    if (length <= 4)
+        return 4;
+
+    unsigned log2;
+    JS_FLOOR_LOG2(log2, length - 1);
+    return 1 << (log2 + 1);
+}
+
+bool
+ScriptAnalysis::makePhi(JSContext *cx, uint32 slot, uint32 offset, SSAValue *pv)
+{
+    SSAPhiNode *node = ArenaNew<SSAPhiNode>(cx->compartment->pool);
+    SSAValue *options = ArenaArray<SSAValue>(cx->compartment->pool, PhiNodeCapacity(0));
+    if (!node || !options) {
+        setOOM(cx);
+        return false;
+    }
+    node->slot = slot;
+    node->options = options;
+    pv->initPhi(offset, node);
     return true;
 }
 
+void
+ScriptAnalysis::insertPhi(JSContext *cx, SSAValue &phi, const SSAValue &v)
+{
+    JS_ASSERT(phi.kind() == SSAValue::PHI);
+    SSAPhiNode *node = phi.phiNode();
+
+    /*
+     * Filter dupes inserted into small nodes to keep things clean and avoid
+     * extra type constraints, but don't bother on large phi nodes to avoid
+     * quadratic behavior.
+     */
+    if (node->length <= 8) {
+        for (unsigned i = 0; i < node->length; i++) {
+            if (v.equals(node->options[i]))
+                return;
+        }
+    }
+
+    if (node->length < PhiNodeCapacity(node->length)) {
+        node->options[node->length++] = v;
+        return;
+    }
+
+    SSAValue *newOptions = ArenaArray<SSAValue>(cx->compartment->pool,
+                                                PhiNodeCapacity(node->length + 1));
+    if (!newOptions) {
+        setOOM(cx);
+        return;
+    }
+
+    PodCopy(newOptions, node->options, node->length);
+    node->options = newOptions;
+    node->options[node->length++] = v;
+}
+
+inline void
+ScriptAnalysis::mergeValue(JSContext *cx, uint32 offset, const SSAValue &v, SlotValue *pv)
+{
+    JS_ASSERT(v.kind() != SSAValue::EMPTY && pv->value.kind() != SSAValue::EMPTY);
+
+    if (v.equals(pv->value))
+        return;
+
+    if (pv->value.kind() != SSAValue::PHI || pv->value.phiOffset() < offset) {
+        SSAValue ov = pv->value;
+        if (makePhi(cx, pv->slot, offset, &pv->value)) {
+            insertPhi(cx, pv->value, v);
+            insertPhi(cx, pv->value, ov);
+        }
+        return;
+    }
+
+    JS_ASSERT(pv->value.phiOffset() == offset);
+    insertPhi(cx, pv->value, v);
+}
+
+void
+ScriptAnalysis::checkPendingValue(JSContext *cx, const SSAValue &v, uint32 slot,
+                                  Vector<SlotValue> *pending)
+{
+    JS_ASSERT(v.kind() != SSAValue::EMPTY);
+
+    for (unsigned i = 0; i < pending->length(); i++) {
+        if ((*pending)[i].slot == slot)
+            return;
+    }
+
+    if (!pending->append(SlotValue(slot, v)))
+        setOOM(cx);
+}
+
+void
+ScriptAnalysis::mergeBranchTarget(JSContext *cx, const SSAValue &value, uint32 slot,
+                                  const Vector<uint32> &branchTargets)
+{
+    if (slot >= numSlots) {
+        /*
+         * There is no need to lazily check that there are pending values at
+         * branch targets for slots on the stack, these are added to pending
+         * eagerly.
+         */
+        return;
+    }
+
+    JS_ASSERT(trackSlot(slot));
+
+    /*
+     * Before changing the value of a variable, make sure the old value is
+     * marked at the target of any branches jumping over the current opcode.
+     */
+    for (unsigned i = 0; i < branchTargets.length(); i++) {
+        Vector<SlotValue> *pending = getCode(branchTargets[i]).pendingValues;
+        checkPendingValue(cx, value, slot, pending);
+    }
+}
+
+void
+ScriptAnalysis::removeBranchTarget(Vector<uint32> &branchTargets, uint32 offset)
+{
+    for (unsigned i = 0; i < branchTargets.length(); i++) {
+        if (branchTargets[i] == offset) {
+            branchTargets[i] = branchTargets.back();
+            branchTargets.popBack();
+            return;
+        }
+    }
+    JS_NOT_REACHED("Missing target");
+}
+
+void
+ScriptAnalysis::freezeNewValues(JSContext *cx, uint32 offset)
+{
+    Bytecode &code = getCode(offset);
+
+    Vector<SlotValue> *pending = code.pendingValues;
+    code.pendingValues = NULL;
+
+    unsigned count = pending->length();
+    if (count == 0) {
+        cx->delete_(pending);
+        return;
+    }
+
+    code.newValues = ArenaArray<SlotValue>(cx->compartment->pool, 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;
+    code.newValues[count].value.clear();
+
+    cx->delete_(pending);
+}
+
+#ifdef DEBUG
+
+void
+ScriptAnalysis::printSSA(JSContext *cx)
+{
+    printf("\n");
+
+    for (unsigned offset = 0; offset < script->length; offset++) {
+        Bytecode *code = maybeCode(offset);
+        if (!code)
+            continue;
+
+        jsbytecode *pc = script->code + offset;
+        PrintBytecode(cx, script, pc);
+
+        SlotValue *newv = code->newValues;
+        if (newv) {
+            while (newv->slot) {
+                if (newv->value.kind() != SSAValue::PHI || newv->value.phiOffset() != offset) {
+                    newv++;
+                    continue;
+                }
+                printf("  phi ");
+                newv->value.print();
+                printf(" [");
+                for (unsigned i = 0; i < newv->value.phiLength(); i++) {
+                    if (i)
+                        printf(",");
+                    newv->value.phiValue(i).print();
+                }
+                printf("]\n");
+                newv++;
+            }
+        }
+
+        unsigned nuses = GetUseCount(script, offset);
+        unsigned xuses = ExtendedUse(pc) ? nuses + 1 : nuses;
+
+        for (unsigned i = 0; i < xuses; i++) {
+            printf("  popped%d: ", i);
+            code->poppedValues[i].print();
+            printf("\n");
+        }
+    }
+
+    printf("\n"); 
+}
+
+void
+SSAValue::print() const
+{
+    switch (kind()) {
+
+      case EMPTY:
+        printf("empty");
+        break;
+
+      case PUSHED:
+        printf("pushed:%05u#%u", pushedOffset(), pushedIndex());
+        break;
+
+      case VAR:
+        if (varInitial())
+            printf("initial:%u", varSlot());
+        else
+            printf("write:%05u", varOffset());
+        break;
+
+      case PHI:
+        printf("phi:%05u#%u", phiOffset(), phiSlot());
+        break;
+
+      default:
+        JS_NOT_REACHED("Bad kind");
+    }
+}
+
+#endif  /* DEBUG */
+
 } /* namespace analyze */
 } /* namespace js */
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -50,33 +50,71 @@
 struct JSScript;
 
 /* Forward declaration of downstream register allocations computed for join points. */
 namespace js { namespace mjit { struct RegisterAllocation; } }
 
 namespace js {
 namespace analyze {
 
-class Script;
+/*
+ * There are three analyses we can perform on a JSScript, outlined below.
+ * The results of all three are stored in ScriptAnalysis, but the analyses
+ * themselves can be performed separately. Along with type inference results,
+ * per-script analysis results are tied to the per-compartment analysis pool
+ * and are freed on every garbage collection.
+ *
+ * - Basic bytecode analysis. For each bytecode, determine the stack depth at
+ * that point and control flow information needed for compilation. Also does
+ * a defined-variables analysis to look for local variables which have uses
+ * before definitions.
+ *
+ * - Lifetime analysis. Makes a backwards pass over the script to approximate
+ * the regions where each variable is live, avoiding a full fixpointing
+ * live-variables pass. This is based on the algorithm described in:
+ *
+ *     "Quality and Speed in Linear-scan Register Allocation"
+ *     Traub et. al.
+ *     PLDI, 1998
+ *
+ * - SSA analysis of the script's variables and stack values. For each stack
+ * value popped and non-escaping local variable or argument read, determines
+ * which push(es) or write(s) produced that value.
+ *
+ * Intermediate type inference results are additionally stored here. The above
+ * analyses are independent from type inference.
+ */
+
+class SSAValue;
+struct LoopAnalysis;
+struct SlotValue;
 
 /* Information about a bytecode instruction. */
-struct Bytecode
+class Bytecode
 {
-    friend class Script;
+    friend class ScriptAnalysis;
+
+  public:
+    Bytecode() { PodZero(this); }
+
+    /* --------- Bytecode analysis --------- */
 
     /* Whether there are any incoming jumps to this instruction. */
-    bool jumpTarget : 1;    
+    bool jumpTarget : 1;
+
+    /* There is a backwards jump to this instruction. */
+    bool loopHead : 1;
 
     /* Whether there is fallthrough to this instruction from a non-branching instruction. */
     bool fallthrough : 1;
 
     /* Whether this instruction is the fall through point of a conditional jump. */
     bool jumpFallthrough : 1;
 
-    /* Whether this instruction can be branched to from a switch statement.  Implies jumpTarget. */
+    /* Whether this instruction can be branched to from a switch statement. Implies jumpTarget. */
     bool switchTarget : 1;
 
     /* Whether this instruction has been analyzed to get its output defines and stack. */
     bool analyzed : 1;
 
     /* Whether this is a catch/finally entry point. */
     bool exceptionEntry : 1;
 
@@ -84,168 +122,83 @@ struct Bytecode
     bool inTryBlock : 1;
 
     /* Method JIT safe point. */
     bool safePoint : 1;
 
     /* Stack depth before this opcode. */
     uint32 stackDepth;
 
+  private:
     /*
-     * The set of locals defined at this point.  This does not include locals which
+     * The set of locals defined at this point. This does not include locals which
      * were unconditionally defined at an earlier point in the script.
      */
     uint32 defineCount;
     uint32 *defineArray;
 
-    Bytecode()
-    {
-        PodZero(this);
-    }
+    /* --------- Lifetime analysis --------- */
+
+    /* If this is a loop head, information about the loop. */
+    LoopAnalysis *loop;
+
+    /* Any allocation computed downstream for this bytecode. */
+    mjit::RegisterAllocation *allocation;
+
+    /* --------- SSA analysis --------- */
+
+    /* Generated location of each value popped by this bytecode. */
+    SSAValue *poppedValues;
+
+    union {
+        /*
+         * If this is a join point (implies jumpTarget), any slots at this
+         * point which can have a different values than at the immediate
+         * predecessor in the bytecode. Array is terminated by an entry with
+         * a zero slot.
+         */
+        SlotValue *newValues;
 
-  private:
-    bool mergeDefines(JSContext *cx, Script *script, bool initial,
+        /*
+         * Vector used during SSA analysis to store values in need of merging
+         * at this point. If this has incoming forward jumps and we have not
+         * yet reached this point, stores values for entries on the stack and
+         * for variables which have changed since the branch. If this is a loop
+         * head and we haven't reached the back edge yet, stores loop phi nodes
+         * for variables and entries live at the head of the loop.
+         */
+        Vector<SlotValue> *pendingValues;
+    };
+
+    /* --------- Type inference --------- */
+
+    /*
+     * Types for all values pushed by this bytecode. Low bit is set for
+     * bytecodes which are monitored (side effects were not determined
+     * statically).
+     */
+    types::TypeSet *pushedTypes;
+
+    /* --------- Helpers --------- */
+
+    bool mergeDefines(JSContext *cx, ScriptAnalysis *script, bool initial,
                       uint32 newDepth, uint32 *newArray, uint32 newCount);
 
     /* Whether a local variable is in the define set at this bytecode. */
     bool isDefined(uint32 slot)
     {
         JS_ASSERT(analyzed);
         for (unsigned i = 0; i < defineCount; i++) {
             if (defineArray[i] == slot)
                 return true;
         }
         return false;
     }
 };
 
-/* Information about a script. */
-class Script
-{
-    friend struct Bytecode;
-
-    JSScript *script;
-
-    Bytecode **codeArray;
-
-    /* Maximum number of locals to consider for a script. */
-    static const unsigned LOCAL_LIMIT = 50;
-
-    /* Offsets at which each local becomes unconditionally defined, or a value below. */
-    uint32 *locals;
-
-    static const uint32 LOCAL_USE_BEFORE_DEF = uint32(-1);
-    static const uint32 LOCAL_CONDITIONALLY_DEFINED = uint32(-2);
-
-    bool outOfMemory;
-    bool hadFailure;
-    bool usesRval;
-    bool usesScope;
-    bool usesThis;
-    bool hasCalls;
-
-    bool isInlineable;
-
-    JSPackedBool *closedVars;
-    JSPackedBool *closedArgs;
-
-  public:
-    /* Pool for allocating analysis structures which will not outlive this script. */
-    JSArenaPool pool;
-
-    Script();
-    ~Script();
-
-    void analyze(JSContext *cx, JSScript *script);
-
-    bool OOM() { return outOfMemory; }
-    bool failed() { return hadFailure; }
-    bool inlineable(uint32 argc) { return isInlineable && argc == script->fun->nargs; }
-
-    /* Whether there are POPV/SETRVAL bytecodes which can write to the frame's rval. */
-    bool usesReturnValue() const { return usesRval; }
-
-    /* Whether there are NAME bytecodes which can access the frame's scope chain. */
-    bool usesScopeChain() const { return usesScope; }
-
-    bool usesThisValue() const { return usesThis; }
-    bool hasFunctionCalls() const { return hasCalls; }
-
-    bool hasAnalyzed() const { return !!codeArray; }
-    JSScript *getScript() const { return script; }
-
-    /* Accessors for bytecode information. */
-
-    Bytecode& getCode(uint32 offset) {
-        JS_ASSERT(offset < script->length);
-        JS_ASSERT(codeArray[offset]);
-        return *codeArray[offset];
-    }
-    Bytecode& getCode(const jsbytecode *pc) { return getCode(pc - script->code); }
-
-    Bytecode* maybeCode(uint32 offset) {
-        JS_ASSERT(offset < script->length);
-        return codeArray[offset];
-    }
-    Bytecode* maybeCode(const jsbytecode *pc) { return maybeCode(pc - script->code); }
-
-    bool jumpTarget(uint32 offset) {
-        JS_ASSERT(offset < script->length);
-        return codeArray[offset] && codeArray[offset]->jumpTarget;
-    }
-    bool jumpTarget(const jsbytecode *pc) { return jumpTarget(pc - script->code); }
-
-    /* Accessors for local variable information. */
-
-    unsigned localCount() {
-        return (script->nfixed >= LOCAL_LIMIT) ? LOCAL_LIMIT : script->nfixed;
-    }
-
-    bool localHasUseBeforeDef(uint32 local) {
-        JS_ASSERT(!failed());
-        return local >= localCount() || locals[local] == LOCAL_USE_BEFORE_DEF;
-    }
-
-    /* These return true for variables that may have a use before def. */
-    bool localDefined(uint32 local, uint32 offset) {
-        return localHasUseBeforeDef(local) || (locals[local] <= offset) ||
-            getCode(offset).isDefined(local);
-    }
-    bool localDefined(uint32 local, jsbytecode *pc) {
-        return localDefined(local, pc - script->code);
-    }
-
-    bool argEscapes(unsigned arg)
-    {
-        JS_ASSERT(script->fun && arg < script->fun->nargs);
-        return script->usesEval || script->usesArguments || script->compartment->debugMode ||
-            closedArgs[arg];
-    }
-
-    bool localEscapes(unsigned local)
-    {
-        return script->usesEval || script->compartment->debugMode || local >= localCount() ||
-            closedVars[local];
-    }
-
-  private:
-    void setOOM(JSContext *cx) {
-        if (!outOfMemory)
-            js_ReportOutOfMemory(cx);
-        outOfMemory = true;
-        hadFailure = true;
-    }
-
-    inline bool addJump(JSContext *cx, unsigned offset,
-                        unsigned *currentOffset, unsigned *forwardJump,
-                        unsigned stackDepth, uint32 *defineArray, unsigned defineCount);
-
-    inline void setLocal(uint32 local, uint32 offset);
-};
-
 static inline unsigned
 GetBytecodeLength(jsbytecode *pc)
 {
     JSOp op = (JSOp)*pc;
     JS_ASSERT(op < JSOP_LIMIT);
     JS_ASSERT(op != JSOP_TRAP);
 
     if (js_CodeSpec[op].length != -1)
@@ -258,16 +211,67 @@ GetUseCount(JSScript *script, unsigned o
 {
     JS_ASSERT(offset < script->length);
     jsbytecode *pc = script->code + offset;
     if (js_CodeSpec[*pc].nuses == -1)
         return js_GetVariableStackUses(JSOp(*pc), pc);
     return js_CodeSpec[*pc].nuses;
 }
 
+/*
+ * For opcodes which access local variables or arguments, we track an extra
+ * use during SSA analysis for the value of the variable before/after the op.
+ */
+static inline bool
+ExtendedUse(jsbytecode *pc)
+{
+    switch ((JSOp)*pc) {
+      case JSOP_SETARG:
+      case JSOP_GETARG:
+      case JSOP_CALLARG:
+      case JSOP_INCARG:
+      case JSOP_DECARG:
+      case JSOP_ARGINC:
+      case JSOP_ARGDEC:
+      case JSOP_SETLOCAL:
+      case JSOP_SETLOCALPOP:
+      case JSOP_GETLOCAL:
+      case JSOP_CALLLOCAL:
+      case JSOP_DEFLOCALFUN:
+      case JSOP_DEFLOCALFUN_FC:
+      case JSOP_INCLOCAL:
+      case JSOP_DECLOCAL:
+      case JSOP_LOCALINC:
+      case JSOP_LOCALDEC:
+        return true;
+      default:
+        return false;
+    }
+}
+
+/*
+ * For opcodes which assign to a local variable or argument but don't push a
+ * value with the same type set, we track an extra def during type inference
+ * for the value assigned here.
+ */
+static inline bool
+ExtendedDef(jsbytecode *pc)
+{
+    switch ((JSOp)*pc) {
+      case JSOP_DEFLOCALFUN:
+      case JSOP_DEFLOCALFUN_FC:
+      case JSOP_SETLOCALPOP:
+      case JSOP_FORARG:
+      case JSOP_FORLOCAL:
+        return true;
+      default:
+        return false;
+    }
+}
+
 static inline unsigned
 GetDefCount(JSScript *script, unsigned offset)
 {
     JS_ASSERT(offset < script->length);
     jsbytecode *pc = script->code + offset;
     if (js_CodeSpec[*pc].ndefs == -1)
         return js_GetEnterBlockStackDefs(NULL, script, pc);
 
@@ -283,16 +287,45 @@ GetDefCount(JSScript *script, unsigned o
         return 1;
       case JSOP_FILTER:
         return 2;
       default:
         return js_CodeSpec[*pc].ndefs;
     }
 }
 
+static inline ptrdiff_t
+GetJumpOffset(jsbytecode *pc, jsbytecode *pc2)
+{
+    uint32 type = JOF_OPTYPE(*pc);
+    if (JOF_TYPE_IS_EXTENDED_JUMP(type))
+        return GET_JUMPX_OFFSET(pc2);
+    return GET_JUMP_OFFSET(pc2);
+}
+
+static inline unsigned
+FollowBranch(JSScript *script, unsigned offset)
+{
+    /*
+     * Get the target offset of a branch. For GOTO opcodes implementing
+     * 'continue' statements, short circuit any artificial backwards jump
+     * inserted by the emitter.
+     */
+    jsbytecode *pc = script->code + offset;
+    unsigned targetOffset = offset + GetJumpOffset(pc, pc);
+    if (targetOffset < offset) {
+        JSOp nop = JSOp(script->code[targetOffset]);
+        if (nop == JSOP_GOTO || nop == JSOP_GOTOX) {
+            jsbytecode *target = script->code + targetOffset;
+            return targetOffset + GetJumpOffset(target, target);
+        }
+    }
+    return targetOffset;
+}
+
 static inline JSOp
 ReverseCompareOp(JSOp op)
 {
     switch (op) {
       case JSOP_GT:
         return JSOP_LT;
       case JSOP_GE:
         return JSOP_LE;
@@ -321,58 +354,88 @@ struct UntrapOpcode
 
     ~UntrapOpcode()
     {
         if (trap)
             *pc = JSOP_TRAP;
     }
 };
 
-/*
- * Analysis over a range of bytecode to determine where each popped value was
- * originally pushed.
- */
-struct StackAnalysis
-{
-    /* For values whose pushed location is not known. */
-    static const uint32 UNKNOWN_PUSHED = uint32(-1);
+/* Common representation of slots throughout analyses and the compiler. */
+static inline uint32 CalleeSlot() {
+    return 0;
+}
+static inline uint32 ThisSlot() {
+    return 1;
+}
+static inline uint32 ArgSlot(uint32 arg) {
+    return 2 + arg;
+}
+static inline uint32 LocalSlot(JSScript *script, uint32 local) {
+    return 2 + (script->fun ? script->fun->nargs : 0) + local;
+}
+static inline uint32 TotalSlots(JSScript *script) {
+    return 2 + (script->fun ? script->fun->nargs : 0) + script->nfixed;
+}
 
-    struct PoppedValue {
-        uint32 offset;
-        uint32 which;
-        void reset() { offset = UNKNOWN_PUSHED; which = 0; }
-    };
+static inline uint32 StackSlot(JSScript *script, uint32 index) {
+    return TotalSlots(script) + index;
+}
+
+static inline uint32 GetBytecodeSlot(JSScript *script, jsbytecode *pc)
+{
+    switch (JSOp(*pc)) {
 
-    PoppedValue **poppedArray;
-    uint32 start;
-    uint32 length;
-
-    JSScript *script;
+      case JSOP_GETARG:
+      case JSOP_CALLARG:
+      case JSOP_SETARG:
+      case JSOP_INCARG:
+      case JSOP_DECARG:
+      case JSOP_ARGINC:
+      case JSOP_ARGDEC:
+      case JSOP_FORARG:
+        return ArgSlot(GET_SLOTNO(pc));
 
-    bool analyze(JSArenaPool &pool, JSScript *script, uint32 start, uint32 length,
-                 Script *analysis);
+      case JSOP_GETLOCAL:
+      case JSOP_CALLLOCAL:
+      case JSOP_SETLOCAL:
+      case JSOP_SETLOCALPOP:
+      case JSOP_DEFLOCALFUN:
+      case JSOP_DEFLOCALFUN_FC:
+      case JSOP_INCLOCAL:
+      case JSOP_DECLOCAL:
+      case JSOP_LOCALINC:
+      case JSOP_LOCALDEC:
+      case JSOP_FORLOCAL:
+        return LocalSlot(script, GET_SLOTNO(pc));
 
-    const PoppedValue &popped(uint32 offset, uint32 which) {
-        return poppedArray[offset - start][which];
-    }
-    const PoppedValue &popped(jsbytecode *pc, uint32 which) {
-        return popped(pc - script->code, which);
-    }
-};
+      case JSOP_THIS:
+        return ThisSlot();
 
-/*
- * Lifetime analysis. The goal of this analysis is to make a single backwards pass
- * over a script to approximate the regions where each variable is live, without
- * doing a full fixpointing live-variables pass. This is based on the algorithm
- * described in:
- *
- * "Quality and Speed in Linear-scan Register Allocation"
- * Traub et. al.
- * PLDI, 1998
- */
+      default:
+        JS_NOT_REACHED("Bad slot opcode");
+        return 0;
+    }
+}
+
+static inline int32
+GetBytecodeInteger(jsbytecode *pc)
+{
+    switch (JSOp(*pc)) {
+      case JSOP_ZERO:   return 0;
+      case JSOP_ONE:    return 1;
+      case JSOP_UINT16: return GET_UINT16(pc);
+      case JSOP_UINT24: return GET_UINT24(pc);
+      case JSOP_INT8:   return GET_INT8(pc);
+      case JSOP_INT32:  return GET_INT32(pc);
+      default:
+        JS_NOT_REACHED("Bad op");
+        return 0;
+    }
+}
 
 /*
  * Information about the lifetime of a local or argument. These form a linked
  * list describing successive intervals in the program where the variable's
  * value may be live. At points in the script not in one of these segments
  * (points in a 'lifetime hole'), the variable is dead and registers containing
  * its type/payload can be discarded without needing to be synced.
  */
@@ -408,21 +471,21 @@ struct Lifetime
     Lifetime *next;
 
     Lifetime(uint32 offset, uint32 savedEnd, Lifetime *next)
         : start(offset), end(offset), savedEnd(savedEnd),
           loopTail(false), write(false), next(next)
     {}
 };
 
-/* Lifetime and modset information for a loop. */
-struct LifetimeLoop
+/* Basic information for a loop. */
+struct LoopAnalysis
 {
     /* Any loop this one is nested in. */
-    LifetimeLoop *parent;
+    LoopAnalysis *parent;
 
     /* Offset of the head of the loop. */
     uint32 head;
 
     /*
      * Offset of the unique jump going to the head of the loop. The code
      * between the head and the backedge forms the loop body.
      */
@@ -444,67 +507,57 @@ struct LifetimeLoop
      * join at directly.
      */
     bool hasSafePoints;
 
     /* This loop has calls or inner loops. */
     bool hasCallsLoops;
 };
 
-/* Lifetime and register information for a bytecode. */
-struct LifetimeBytecode
-{
-    /* If this is a loop head, information about the loop. */
-    LifetimeLoop *loop;
-
-    /* Any allocation computed downstream for this bytecode. */
-    mjit::RegisterAllocation *allocation;
-};
-
 /* Current lifetime information for a variable. */
 struct LifetimeVariable
 {
     /* If the variable is currently live, the lifetime segment. */
     Lifetime *lifetime;
 
     /* If the variable is currently dead, the next live segment. */
     Lifetime *saved;
 
     /* Jump preceding the basic block which killed this variable. */
     uint32 savedEnd;
 
     /* Whether this variable is live at offset. */
-    Lifetime * live(uint32 offset) {
+    Lifetime * live(uint32 offset) const {
         if (lifetime && lifetime->end >= offset)
             return lifetime;
         Lifetime *segment = lifetime ? lifetime : saved;
         while (segment && segment->start <= offset) {
             if (segment->end >= offset)
                 return segment;
             segment = segment->next;
         }
         return NULL;
     }
 
     /*
      * Get the offset of the first write to the variable in the body of a loop,
      * -1 if the loop never writes the variable.
      */
-    uint32 firstWrite(LifetimeLoop *loop) {
+    uint32 firstWrite(LoopAnalysis *loop) const {
         Lifetime *segment = lifetime ? lifetime : saved;
         while (segment && segment->start <= loop->backedge) {
             if (segment->start >= loop->head && segment->write)
                 return segment->start;
             segment = segment->next;
         }
         return uint32(-1);
     }
 
     /* Return true if the variable cannot decrease during the body of a loop. */
-    bool nonDecreasing(JSScript *script, LifetimeLoop *loop) {
+    bool nonDecreasing(JSScript *script, LoopAnalysis *loop) const {
         Lifetime *segment = lifetime ? lifetime : saved;
         while (segment && segment->start <= loop->backedge) {
             if (segment->start >= loop->head && segment->write) {
                 switch (JSOp(script->code[segment->start])) {
                   case JSOP_INCLOCAL:
                   case JSOP_LOCALINC:
                   case JSOP_INCARG:
                   case JSOP_ARGINC:
@@ -517,93 +570,517 @@ struct LifetimeVariable
         }
         return true;
     }
 
     /*
      * If the variable is only written once in the body of a loop, offset of
      * that write. -1 otherwise.
      */
-    uint32 onlyWrite(LifetimeLoop *loop) {
+    uint32 onlyWrite(LoopAnalysis *loop) const {
         uint32 offset = uint32(-1);
         Lifetime *segment = lifetime ? lifetime : saved;
         while (segment && segment->start <= loop->backedge) {
             if (segment->start >= loop->head && segment->write) {
                 if (offset != uint32(-1))
                     return uint32(-1);
                 offset = segment->start;
             }
             segment = segment->next;
         }
         return offset;
     }
+
+#ifdef DEBUG
+    void print() const;
+#endif
+};
+
+struct SSAPhiNode;
+
+/*
+ * Representation of values on stack or in slots at each point in the script.
+ * Values are independent from the bytecode position, and mean the same thing
+ * everywhere in the script. SSA values are immutable, except for contents of
+ * the values and types in an SSAPhiNode.
+ */
+class SSAValue
+{
+    friend class ScriptAnalysis;
+
+  public:
+    enum Kind {
+        EMPTY = 0, /* Empty entry in a phi node. */
+        PUSHED,    /* Value pushed by some bytecode. */
+        VAR,       /* Initial or written value to some argument or local. */
+        PHI        /* Selector for one of several values. */
+    };
+
+    Kind kind() const {
+        JS_ASSERT(u.pushed.kind == u.var.kind && u.pushed.kind == u.phi.kind);
+        return u.pushed.kind;
+    }
+
+    bool equals(const SSAValue &o) const {
+        return !memcmp(this, &o, sizeof(SSAValue));
+    }
+
+    /* Accessors for values pushed by a bytecode within this script. */
+
+    uint32 pushedOffset() const {
+        JS_ASSERT(kind() == PUSHED);
+        return u.pushed.offset;
+    }
+
+    uint32 pushedIndex() const {
+        JS_ASSERT(kind() == PUSHED);
+        return u.pushed.index;
+    }
+
+    /* Accessors for initial and written values of arguments and (undefined) locals. */
+
+    bool varInitial() const {
+        JS_ASSERT(kind() == VAR);
+        return u.var.initial;
+    }
+
+    uint32 varSlot() const {
+        JS_ASSERT(kind() == VAR);
+        return u.var.slot;
+    }
+
+    uint32 varOffset() const {
+        JS_ASSERT(!varInitial());
+        return u.var.offset;
+    }
+
+    /* Accessors for phi nodes. */
+
+    uint32 phiSlot() const;
+    uint32 phiLength() const;
+    const SSAValue &phiValue(uint32 i) const;
+    types::TypeSet *phiTypes() const;
+
+    /* Offset at which this phi node was created. */
+    uint32 phiOffset() const {
+        JS_ASSERT(kind() == PHI);
+        return u.phi.offset;
+    }
+
+#ifdef DEBUG
+    void print() const;
+#endif
+
+  private:
+    union {
+        struct {
+            Kind kind : 2;
+            uint32 offset : 30;
+            uint32 index;
+        } pushed;
+        struct {
+            Kind kind : 2;
+            bool initial : 1;
+            uint32 slot : 29;
+            uint32 offset;
+        } var;
+        struct {
+            Kind kind : 2;
+            uint32 offset : 30;
+            SSAPhiNode *node;
+        } phi;
+    } u;
+
+    void clear() {
+        PodZero(this);
+        JS_ASSERT(kind() == EMPTY);
+    }
+
+    void initPushed(uint32 offset, uint32 index) {
+        clear();
+        u.pushed.kind = PUSHED;
+        u.pushed.offset = offset;
+        u.pushed.index = index;
+    }
+
+    void initInitial(uint32 slot) {
+        clear();
+        u.var.kind = VAR;
+        u.var.initial = true;
+        u.var.slot = slot;
+    }
+
+    void initWritten(uint32 slot, uint32 offset) {
+        clear();
+        u.var.kind = VAR;
+        u.var.initial = false;
+        u.var.slot = slot;
+        u.var.offset = offset;
+    }
+
+    void initPhi(uint32 offset, SSAPhiNode *node) {
+        clear();
+        u.phi.kind = PHI;
+        u.phi.offset = offset;
+        u.phi.node = node;
+    }
+
+    SSAPhiNode *phiNode() const {
+        JS_ASSERT(kind() == PHI);
+        return u.phi.node;
+    }
 };
 
 /*
- * Analysis approximating variable liveness information at points in a script.
- * This is separate from analyze::Script as it is computed on every compilation
- * and thrown away afterwards.
+ * 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.
  */
-class LifetimeScript
+struct SSAPhiNode
+{
+    types::TypeSet types;
+    uint32 slot;
+    uint32 length;
+    SSAValue *options;
+    SSAPhiNode() { PodZero(this); }
+};
+
+inline uint32
+SSAValue::phiSlot() const
+{
+    return u.phi.node->slot;
+}
+
+inline uint32
+SSAValue::phiLength() const
 {
-    analyze::Script *analysis;
+    JS_ASSERT(kind() == PHI);
+    return u.phi.node->length;
+}
+
+inline const SSAValue &
+SSAValue::phiValue(uint32 i) const
+{
+    JS_ASSERT(kind() == PHI && i < phiLength());
+    return u.phi.node->options[i];
+}
+
+inline types::TypeSet *
+SSAValue::phiTypes() const
+{
+    JS_ASSERT(kind() == PHI);
+    return &u.phi.node->types;
+}
+
+struct SlotValue
+{
+    uint32 slot;
+    SSAValue value;
+    SlotValue(uint32 slot, const SSAValue &value) : slot(slot), value(value) {}
+};
+
+/* Analysis information about a script. */
+class ScriptAnalysis
+{
+    friend struct Bytecode;
+
     JSScript *script;
 
-    LifetimeBytecode *codeArray;
+    Bytecode **codeArray;
+
+    uint32 numSlots;
+
+    bool outOfMemory;
+    bool hadFailure;
+
+    JSPackedBool *escapedSlots;
+
+    /* Which analyses have been performed. */
+    bool ranBytecode_;
+    bool ranSSA_;
+    bool ranLifetimes_;
+    bool ranInference_;
+
+    /* --------- Bytecode analysis --------- */
+
+    bool usesRval;
+    bool usesScope;
+    bool usesThis;
+    bool hasCalls;
+    bool canTrackVars;
+    bool isInlineable;
+
+    /* Offsets at which each local becomes unconditionally defined, or a value below. */
+    uint32 *definedLocals;
+
+    static const uint32 LOCAL_USE_BEFORE_DEF = uint32(-1);
+    static const uint32 LOCAL_CONDITIONALLY_DEFINED = uint32(-2);
+
+    /* --------- Lifetime analysis --------- */
+
     LifetimeVariable *lifetimes;
-    uint32 nLifetimes;
-
-    LifetimeVariable **saved;
-    unsigned savedCount;
 
   public:
-    JSArenaPool pool;
+
+    ScriptAnalysis(JSScript *script) { PodZero(this); this->script = script; }
+
+    bool ranBytecode() { return ranBytecode_; }
+    bool ranSSA() { return ranSSA_; }
+    bool ranLifetimes() { return ranLifetimes_; }
+    bool ranInference() { return ranInference_; }
 
-    LifetimeScript();
-    ~LifetimeScript();
+    void analyzeBytecode(JSContext *cx);
+    void analyzeSSA(JSContext *cx);
+    void analyzeLifetimes(JSContext *cx);
+    void analyzeTypes(JSContext *cx);
+
+    /* Analyze the effect of invoking 'new' on script. */
+    void analyzeTypesNew(JSContext *cx);
+
+    bool OOM() { return outOfMemory; }
+    bool failed() { return hadFailure; }
+    bool inlineable(uint32 argc) { return isInlineable && argc == script->fun->nargs; }
 
-    bool analyze(JSContext *cx, analyze::Script *analysis, JSScript *script);
+    /* Whether there are POPV/SETRVAL bytecodes which can write to the frame's rval. */
+    bool usesReturnValue() const { return usesRval; }
+
+    /* Whether there are NAME bytecodes which can access the frame's scope chain. */
+    bool usesScopeChain() const { return usesScope; }
+
+    bool usesThisValue() const { return usesThis; }
+    bool hasFunctionCalls() const { return hasCalls; }
+
+    /* Accessors for bytecode information. */
 
-    LifetimeBytecode &getCode(uint32 offset) {
-        JS_ASSERT(analysis->maybeCode(offset));
+    Bytecode& getCode(uint32 offset) {
+        JS_ASSERT(script->compartment->activeAnalysis);
+        JS_ASSERT(offset < script->length);
+        JS_ASSERT(codeArray[offset]);
+        return *codeArray[offset];
+    }
+    Bytecode& getCode(const jsbytecode *pc) { return getCode(pc - script->code); }
+
+    Bytecode* maybeCode(uint32 offset) {
+        JS_ASSERT(script->compartment->activeAnalysis);
+        JS_ASSERT(offset < script->length);
         return codeArray[offset];
     }
-    LifetimeBytecode &getCode(jsbytecode *pc) { return getCode(pc - script->code); }
+    Bytecode* maybeCode(const jsbytecode *pc) { return maybeCode(pc - script->code); }
+
+    bool jumpTarget(uint32 offset) {
+        JS_ASSERT(offset < script->length);
+        return codeArray[offset] && codeArray[offset]->jumpTarget;
+    }
+    bool jumpTarget(const jsbytecode *pc) { return jumpTarget(pc - script->code); }
+
+    const SSAValue &poppedValue(uint32 offset, uint32 which) {
+        JS_ASSERT(offset < script->length);
+        JS_ASSERT_IF(script->code[offset] != JSOP_TRAP,
+                     which < GetUseCount(script, offset) +
+                     (ExtendedUse(script->code + offset) ? 1 : 0));
+        return getCode(offset).poppedValues[which];
+    }
+    const SSAValue &poppedValue(const jsbytecode *pc, uint32 which) {
+        return poppedValue(pc - script->code, which);
+    }
+
+    const SlotValue *newValues(uint32 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 offset, uint32 which = 0) {
+        JS_ASSERT(offset < script->length);
+        JS_ASSERT_IF(script->code[offset] != JSOP_TRAP,
+                     which < GetDefCount(script, offset) +
+                     (ExtendedDef(script->code + offset) ? 1 : 0));
+        types::TypeSet *array = (types::TypeSet *) (~0x1 & (size_t) getCode(offset).pushedTypes);
+        JS_ASSERT(array);
+        return array + which;
+    }
+    types::TypeSet *pushedTypes(const jsbytecode *pc, uint32 which) {
+        return pushedTypes(pc - script->code, which);
+    }
+
+    inline void addPushedType(JSContext *cx, uint32 offset, uint32 which, types::jstype type);
+
+    bool monitoredTypes(uint32 offset) {
+        JS_ASSERT(offset < script->length);
+        return 0x1 & (size_t) getCode(offset).pushedTypes;
+    }
+
+    void setMonitoredTypes(uint32 offset) {
+        JS_ASSERT(offset < script->length);
+        types::TypeSet *&array = getCode(offset).pushedTypes;
+        array = (types::TypeSet *) (0x1 | (size_t) array);
+    }
+
+    types::TypeSet *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 script->slotTypes(v.varSlot());
+            } else {
+                /*
+                 * Results of intermediate assignments have the same type as
+                 * the first type pushed by the assignment op, except FOR* ops
+                 * which push the iterator first. Note that this may not be the
+                 * exact same value as was pushed, due to post-inc/dec ops.
+                 */
+                switch (script->code[v.varOffset()]) {
+                  case JSOP_FORARG:
+                  case JSOP_FORLOCAL:
+                    return pushedTypes(v.varOffset(), 1);
+                  default:
+                    return pushedTypes(v.varOffset(), 0);
+                }
+            }
+          case SSAValue::PHI:
+            return &v.phiNode()->types;
+          default:
+            /* Cannot compute types for empty SSA values. */
+            JS_NOT_REACHED("Bad SSA value");
+            return NULL;
+        }
+    }
+
+    types::TypeSet *poppedTypes(uint32 offset, uint32 which) {
+        return getValueTypes(poppedValue(offset, which));
+    }
+    types::TypeSet *poppedTypes(const jsbytecode *pc, uint32 which) {
+        return getValueTypes(poppedValue(pc, which));
+    }
+
+    mjit::RegisterAllocation *&getAllocation(uint32 offset) {
+        JS_ASSERT(offset < script->length);
+        return getCode(offset).allocation;
+    }
+    mjit::RegisterAllocation *&getAllocation(const jsbytecode *pc) {
+        return getAllocation(pc - script->code);
+    }
+
+    LoopAnalysis *getLoop(uint32 offset) {
+        JS_ASSERT(offset < script->length);
+        JS_ASSERT(getCode(offset).loop);
+        return getCode(offset).loop;
+    }
+    LoopAnalysis *getLoop(const jsbytecode *pc) { return getLoop(pc - script->code); }
+
+    /* Accessors for local variable information. */
+
+    bool localHasUseBeforeDef(uint32 local) {
+        JS_ASSERT(!failed());
+        return slotEscapes(LocalSlot(script, local)) ||
+            definedLocals[local] == LOCAL_USE_BEFORE_DEF;
+    }
+
+    /* These return true for variables that may have a use before def. */
+    bool localDefined(uint32 local, uint32 offset) {
+        return localHasUseBeforeDef(local) || (definedLocals[local] <= offset) ||
+            getCode(offset).isDefined(local);
+    }
+    bool localDefined(uint32 local, jsbytecode *pc) {
+        return localDefined(local, pc - script->code);
+    }
+
+    bool slotEscapes(uint32 slot) {
+        JS_ASSERT(script->compartment->activeAnalysis);
+        if (slot >= numSlots)
+            return true;
+        return escapedSlots[slot];
+    }
+
+    /*
+     * Whether we distinguish different writes of this variable while doing
+     * SSA analysis. Escaping locals can be written in other scripts, and the
+     * presence of NAME opcodes, switch or try blocks keeps us from tracking
+     * variable values at each point.
+     */
+    bool trackSlot(uint32 slot) { return !slotEscapes(slot) && canTrackVars; }
+
+    const LifetimeVariable & liveness(uint32 slot) {
+        JS_ASSERT(script->compartment->activeAnalysis);
+        JS_ASSERT(!slotEscapes(slot));
+        return lifetimes[slot];
+    }
+
+    void printSSA(JSContext *cx);
+    void printTypes(JSContext *cx);
+
+    void clearAllocations();
+
+  private:
+    void setOOM(JSContext *cx) {
+        if (!outOfMemory)
+            js_ReportOutOfMemory(cx);
+        outOfMemory = true;
+        hadFailure = true;
+    }
+
+    /* Bytecode helpers */
+    inline bool addJump(JSContext *cx, unsigned offset,
+                        unsigned *currentOffset, unsigned *forwardJump,
+                        unsigned stackDepth, uint32 *defineArray, unsigned defineCount);
+    inline void setLocal(uint32 local, uint32 offset);
+
+    /* Lifetime helpers */
+    inline void addVariable(JSContext *cx, LifetimeVariable &var, unsigned offset,
+                            LifetimeVariable **&saved, unsigned &savedCount);
+    inline void killVariable(JSContext *cx, LifetimeVariable &var, unsigned offset,
+                             LifetimeVariable **&saved, unsigned &savedCount);
+    inline void extendVariable(JSContext *cx, LifetimeVariable &var, unsigned start, unsigned end);
+
+    /* SSA helpers */
+    bool makePhi(JSContext *cx, uint32 slot, uint32 offset, SSAValue *pv);
+    void insertPhi(JSContext *cx, SSAValue &phi, const SSAValue &v);
+    void mergeValue(JSContext *cx, uint32 offset, const SSAValue &v, SlotValue *pv);
+    void checkPendingValue(JSContext *cx, const SSAValue &v, uint32 slot,
+                           Vector<SlotValue> *pending);
+    void mergeBranchTarget(JSContext *cx, const SSAValue &value, uint32 slot,
+                           const Vector<uint32> &branchTargets);
+    void removeBranchTarget(Vector<uint32> &branchTargets, uint32 offset);
+    void freezeNewValues(JSContext *cx, uint32 offset);
+
+    struct TypeInferenceState {
+        Vector<SSAPhiNode *> phiNodes;
+        bool hasGetSet;
+        bool hasHole;
+        TypeInferenceState(JSContext *cx)
+            : phiNodes(cx), hasGetSet(false), hasHole(false)
+        {}
+    };
+
+    /* Type inference helpers */
+    bool analyzeTypesBytecode(JSContext *cx, unsigned offset, TypeInferenceState &state);
+    inline void setForTypes(JSContext *cx, jsbytecode *pc, types::TypeSet *types);
+};
+
+/* Protect analysis structures from GC while they are being used. */
+struct AutoEnterAnalysis
+{
+    JSContext *cx;
+    bool oldActiveAnalysis;
+
+    AutoEnterAnalysis(JSContext *cx)
+        : cx(cx), oldActiveAnalysis(cx->compartment->activeAnalysis)
+    {
+        cx->compartment->activeAnalysis = true;
+    }
+
+    ~AutoEnterAnalysis()
+    {
+        cx->compartment->activeAnalysis = oldActiveAnalysis;
+    }
+};
 
 #ifdef DEBUG
-    void dumpVariable(LifetimeVariable &var);
-    void dumpSlot(unsigned slot) {
-        JS_ASSERT(slot < nLifetimes);
-        dumpVariable(lifetimes[slot]);
-    }
+void PrintBytecode(JSContext *cx, JSScript *script, jsbytecode *pc);
 #endif
 
-    Lifetime * live(uint32 slot, uint32 offset) {
-        JS_ASSERT(slot < nLifetimes);
-        return lifetimes[slot].live(offset);
-    }
-
-    uint32 firstWrite(uint32 slot, LifetimeLoop *loop) {
-        JS_ASSERT(slot < nLifetimes);
-        return lifetimes[slot].firstWrite(loop);
-    }
-
-    bool nonDecreasing(uint32 slot, LifetimeLoop *loop) {
-        JS_ASSERT(slot < nLifetimes);
-        return lifetimes[slot].nonDecreasing(script, loop);
-    }
-
-    uint32 onlyWrite(uint32 slot, LifetimeLoop *loop) {
-        JS_ASSERT(slot < nLifetimes);
-        return lifetimes[slot].onlyWrite(loop);
-    }
-
-  private:
-
-    inline bool addVariable(JSContext *cx, LifetimeVariable &var, unsigned offset);
-    inline bool killVariable(JSContext *cx, LifetimeVariable &var, unsigned offset);
-    inline bool extendVariable(JSContext *cx, LifetimeVariable &var, unsigned start, unsigned end);
-};
-
 } /* namespace analyze */
 } /* namespace js */
 
 #endif // jsanalyze_h___
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1487,26 +1487,24 @@ array_toLocaleString(JSContext *cx, uint
 static inline bool
 InitArrayTypes(JSContext *cx, TypeObject *type, const Value *vector, unsigned count)
 {
     if (cx->typeInferenceEnabled() && !type->unknownProperties()) {
         AutoEnterTypeInference enter(cx);
 
         TypeSet *types = type->getProperty(cx, JSID_VOID, true);
         if (!types)
-            return JS_FALSE;
+            return false;
 
         for (unsigned i = 0; i < count; i++) {
             if (vector[i].isMagic(JS_ARRAY_HOLE))
                 continue;
             jstype valtype = GetValueType(cx, vector[i]);
             types->addType(cx, valtype);
         }
-
-        return cx->compartment->types.checkPendingRecompiles(cx);
     }
     return true;
 }
 
 static JSBool
 InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, Value *vector, bool updateTypes)
 {
     JS_ASSERT(count < MAXINDEX);
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -126,18 +126,22 @@ JSCompartment::init(JSContext *cx)
     chunk = NULL;
     for (unsigned i = 0; i < FINALIZE_LIMIT; i++)
         arenas[i].init();
     for (unsigned i = 0; i < FINALIZE_LIMIT; i++)
         freeLists.finalizables[i] = NULL;
 #ifdef JS_GCMETER
     memset(&compartmentStats, 0, sizeof(JSGCArenaStats) * FINALIZE_LIMIT);
 #endif
+
+    activeAnalysis = activeInference = false;
     types.init(cx);
 
+    JS_InitArenaPool(&pool, "analysis", 4096, 8, NULL);
+
     if (!crossCompartmentWrappers.init())
         return false;
 
 #ifdef DEBUG
     if (rt->meterEmptyShapes()) {
         if (!emptyShapes.init())
             return false;
     }
@@ -490,17 +494,17 @@ JSCompartment::markCrossCompartmentWrapp
     for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront())
         MarkValue(trc, e.front().key, "cross-compartment wrapper");
 }
 
 void
 JSCompartment::markTypes(JSTracer *trc)
 {
     /* Mark all scripts and type objects in the compartment. */ 
-    JS_ASSERT(types.inferenceDepth);
+    JS_ASSERT(activeAnalysis);
 
     for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
         JSScript *script = reinterpret_cast<JSScript *>(cursor);
         js_TraceScript(trc, script);
     }
 
     types::TypeObject *obj = types.objects;
     while (obj) {
@@ -580,35 +584,35 @@ JSCompartment::sweep(JSContext *cx, uint
                 ScriptTryDestroyCode(cx, script, true, releaseInterval, counter);
                 ScriptTryDestroyCode(cx, script, false, releaseInterval, counter);
             }
         }
     }
 
 #endif
 
-    if (!types.inferenceDepth && types.inferenceEnabled) {
+    if (!activeAnalysis && types.inferenceEnabled) {
         for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
             JSScript *script = reinterpret_cast<JSScript *>(cursor);
             script->condenseTypes(cx);
         }
 
         types.condense(cx);
     }
 
     types.sweep(cx);
 
     for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
         JSScript *script = reinterpret_cast<JSScript *>(cursor);
-        script->sweepTypes(cx);
+        script->sweepAnalysis(cx);
     }
 
-    if (!types.inferenceDepth) {
-        /* Reset the inference pool, releasing all intermediate type data. */
-        JS_FinishArenaPool(&types.pool);
+    if (!activeAnalysis) {
+        /* Reset the analysis pool, releasing all analysis and intermediate type data. */
+        JS_FinishArenaPool(&pool);
 
         /*
          * Destroy eval'ed scripts, now that any type inference information referring
          * to eval scripts has been removed.
          */
         js_DestroyScriptsToGC(cx, this);
     }
 
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -382,16 +382,25 @@ struct JS_FRIEND_API(JSCompartment) {
     size_t                       gcLastBytes;
 
     bool                         hold;
 
 #ifdef JS_GCMETER
     js::gc::JSGCArenaStats       compartmentStats[js::gc::FINALIZE_LIMIT];
 #endif
 
+    /*
+     * 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).
+     */
+    JSArenaPool                  pool;
+    bool                         activeAnalysis;
+    bool                         activeInference;
+
     /* Type information about the scripts and objects in this compartment. */
     js::types::TypeCompartment   types;
 
 #ifdef JS_TRACER
     /* Trace-tree JIT recorder/interpreter state. */
     js::TraceMonitor             traceMonitor;
 #endif
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1824,17 +1824,17 @@ MarkRuntime(JSTracer *trc)
     js_TraceAtomState(trc);
     js_MarkTraps(trc);
 
     JSContext *iter = NULL;
     while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter))
         MarkContext(trc, acx);
 
     for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) {
-        if ((*c)->types.inferenceDepth)
+        if ((*c)->activeAnalysis)
             (*c)->markTypes(trc);
 #ifdef JS_TRACER
         (*c)->traceMonitor.mark(trc);
 #endif
     }
 
     for (ThreadDataIter i(rt); !i.empty(); i.popFront())
         i.threadData()->mark(trc);
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -65,16 +65,19 @@
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #ifdef JS_HAS_XML_SUPPORT
 #include "jsxml.h"
 #endif
 
+using namespace js;
+using namespace js::types;
+
 static inline jsid
 id_prototype(JSContext *cx) {
     return ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
 }
 
 static inline jsid
 id_arguments(JSContext *cx) {
     return ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom);
@@ -107,28 +110,18 @@ id_toString(JSContext *cx)
 }
 
 static inline jsid
 id_toSource(JSContext *cx)
 {
     return ATOM_TO_JSID(cx->runtime->atomState.toSourceAtom);
 }
 
-namespace js {
-namespace types {
-
-static const char *js_CodeNameTwo[] = {
-#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
-    name,
-#include "jsopcode.tbl"
-#undef OPDEF
-};
-
 const char *
-TypeIdStringImpl(jsid id)
+types::TypeIdStringImpl(jsid id)
 {
     if (JSID_IS_VOID(id))
         return "(index)";
     if (JSID_IS_EMPTY(id))
         return "(new)";
     static char bufs[4][100];
     static unsigned which = 0;
     which = (which + 1) & 3;
@@ -160,17 +153,17 @@ static bool InferSpewActive(SpewChannel 
         }
     }
     return active[channel];
 }
 
 #ifdef DEBUG
 
 const char *
-TypeString(jstype type)
+types::TypeString(jstype type)
 {
     switch (type) {
       case TYPE_UNDEFINED:
         return "void";
       case TYPE_NULL:
         return "null";
       case TYPE_BOOLEAN:
         return "bool";
@@ -185,17 +178,18 @@ TypeString(jstype type)
       default: {
         JS_ASSERT(TypeIsObject(type));
         TypeObject *object = (TypeObject *) type;
         return object->name();
       }
     }
 }
 
-void InferSpew(SpewChannel channel, const char *fmt, ...)
+void
+types::InferSpew(SpewChannel channel, const char *fmt, ...)
 {
     if (!InferSpewActive(channel))
         return;
 
     va_list ap;
     va_start(ap, fmt);
     fprintf(stdout, "[infer] ");
     vfprintf(stdout, fmt, ap);
@@ -224,17 +218,17 @@ TypeSetMatches(JSContext *cx, TypeSet *t
                 return true;
         }
     }
 
     return false;
 }
 
 bool
-TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &value)
+types::TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &value)
 {
     /*
      * Check the correctness of the type information in the object's property
      * against an actual value. Note that we are only checking the .types set,
      * not the .ownTypes set, and could miss cases where a type set is missing
      * entries from its ownTypes set when they are shadowed by a prototype property.
      */
     if (cx->typeInferenceEnabled() && !obj->unknownProperties() && !value.isUndefined()) {
@@ -256,25 +250,24 @@ TypeHasProperty(JSContext *cx, TypeObjec
 
         AutoEnterTypeInference enter(cx);
 
         TypeSet *types = obj->getProperty(cx, id, false);
         if (types && !TypeSetMatches(cx, types, type)) {
             TypeFailure(cx, "Missing type in object %s %s: %s",
                         obj->name(), TypeIdString(id), TypeString(type));
         }
-
-        cx->compartment->types.checkPendingRecompiles(cx);
     }
     return true;
 }
 
 #endif
 
-void TypeFailure(JSContext *cx, const char *fmt, ...)
+void
+types::TypeFailure(JSContext *cx, const char *fmt, ...)
 {
     va_list ap;
     va_start(ap, fmt);
     fprintf(stderr, "[infer failure] ");
     vfprintf(stderr, fmt, ap);
     fprintf(stderr, "\n");
     va_end(ap);
 
@@ -283,88 +276,16 @@ void TypeFailure(JSContext *cx, const ch
     fflush(stderr);
     *((int*)NULL) = 0;  /* Type warnings */
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeSet
 /////////////////////////////////////////////////////////////////////
 
-/* Type state maintained during the inference pass through the script. */
-
-struct AnalyzeStateStack {
-    TypeSet *types;
-
-    /* Whether this node is the iterator for a 'for each' loop. */
-    bool isForEach;
-
-    /* Any active initializer. */
-    TypeObject *initializer;
-};
-
-struct AnalyzeState {
-    JSContext *cx;
-
-    analyze::Script &analysis;
-    JSArenaPool &pool;
-
-    AnalyzeStateStack *stack;
-
-    /* Current stack depth. */
-    unsigned stackDepth;
-
-    /* Stack types at join points. */
-    TypeSet ***joinTypes;
-
-    /* Last opcode was JSOP_GETTER or JSOP_SETTER. */
-    bool hasGetSet;
-
-    /* Last opcode was JSOP_HOLE. */
-    bool hasHole;
-
-    AnalyzeState(JSContext *cx, analyze::Script &analysis)
-        : cx(cx), analysis(analysis), pool(analysis.pool),
-          stack(NULL), stackDepth(0), hasGetSet(false), hasHole(false)
-    {}
-
-    bool init(JSScript *script)
-    {
-        unsigned length = (script->nslots * sizeof(AnalyzeStateStack))
-                        + (script->length * sizeof(TypeSet**));
-        unsigned char *cursor = (unsigned char *) cx->calloc_(length);
-        if (!cursor)
-            return false;
-
-        stack = (AnalyzeStateStack *) cursor;
-
-        cursor += (script->nslots * sizeof(AnalyzeStateStack));
-        joinTypes = (TypeSet ***) cursor;
-        return true;
-    }
-
-    ~AnalyzeState()
-    {
-        cx->free_(stack);
-    }
-
-    AnalyzeStateStack &popped(unsigned i) {
-        JS_ASSERT(i < stackDepth);
-        return stack[stackDepth - 1 - i];
-    }
-
-    const AnalyzeStateStack &popped(unsigned i) const {
-        JS_ASSERT(i < stackDepth);
-        return stack[stackDepth - 1 - i];
-    }
-};
-
-/////////////////////////////////////////////////////////////////////
-// TypeSet
-/////////////////////////////////////////////////////////////////////
-
 void
 TypeSet::addTypeSet(JSContext *cx, ClonedTypeSet *types)
 {
     if (types->typeFlags & TYPE_FLAG_UNKNOWN) {
         addType(cx, TYPE_UNKNOWN);
         return;
     }
 
@@ -381,17 +302,17 @@ TypeSet::addTypeSet(JSContext *cx, Clone
     }
 }
 
 inline void
 TypeSet::add(JSContext *cx, TypeConstraint *constraint, bool callExisting)
 {
     JS_ASSERT_IF(!constraint->condensed() && !constraint->persistentObject(),
                  constraint->script->compartment == cx->compartment);
-    JS_ASSERT_IF(!constraint->condensed(), cx->compartment->types.inferenceDepth);
+    JS_ASSERT_IF(!constraint->condensed(), cx->compartment->activeInference);
     JS_ASSERT_IF(typeFlags & TYPE_FLAG_INTERMEDIATE_SET,
                  !constraint->persistentObject() && !constraint->condensed());
 
     if (!constraint) {
         /* OOM failure while constructing the constraint. */
         cx->compartment->types.setPendingNukeTypes(cx);
     }
 
@@ -495,17 +416,17 @@ public:
     }
 
     void newType(JSContext *cx, TypeSet *source, jstype type);
 };
 
 void
 TypeSet::addSubset(JSContext *cx, JSScript *script, TypeSet *target)
 {
-    add(cx, ArenaNew<TypeConstraintSubset>(cx->compartment->types.pool, script, target));
+    add(cx, ArenaNew<TypeConstraintSubset>(cx->compartment->pool, script, target));
 }
 
 /* Subset constraint not associated with a script's analysis. */
 class TypeConstraintBaseSubset : public TypeConstraint
 {
 public:
     TypeObject *object;
     TypeSet *target;
@@ -553,31 +474,31 @@ class TypeConstraintCondensed : public T
 {
 public:
     TypeConstraintCondensed(JSScript *script)
         : TypeConstraint("condensed", script)
     {}
 
     void checkAnalysis(JSContext *cx)
     {
-        if (script->types) {
+        if (script->hasAnalysis() && script->analysis(cx)->ranInference()) {
             /*
              * The script was analyzed, had the analysis collected/condensed,
              * and then was reanalyzed. There are other constraints specifying
              * exactly what the script depends on to trigger recompilation, and
              * we can ignore this new type.
              *
              * Note that for this to hold, reanalysis of a script must always
              * trigger recompilation, to ensure the freeze constraints which
              * describe what the compiler depends on are in place.
              */
             return;
         }
 
-        AnalyzeScriptTypes(cx, script);
+        script->analysis(cx)->analyzeTypes(cx);
     }
 
     void newType(JSContext *cx, TypeSet*, jstype) { checkAnalysis(cx); }
     void newPropertyState(JSContext *cx, TypeSet*) { checkAnalysis(cx); }
     void newObjectState(JSContext *cx, TypeObject*) { checkAnalysis(cx); }
     void slotsReallocation(JSContext *cx) { checkAnalysis(cx); }
 
     bool condensed() { return true; }
@@ -626,24 +547,24 @@ public:
 
     void newType(JSContext *cx, TypeSet *source, jstype type);
 };
 
 void
 TypeSet::addGetProperty(JSContext *cx, JSScript *script, const jsbytecode *pc,
                         TypeSet *target, jsid id)
 {
-    add(cx, ArenaNew<TypeConstraintProp>(cx->compartment->types.pool, script, pc, target, id, false));
+    add(cx, ArenaNew<TypeConstraintProp>(cx->compartment->pool, script, pc, target, id, false));
 }
 
 void
 TypeSet::addSetProperty(JSContext *cx, JSScript *script, const jsbytecode *pc,
                         TypeSet *target, jsid id)
 {
-    add(cx, ArenaNew<TypeConstraintProp>(cx->compartment->types.pool, script, pc, target, id, true));
+    add(cx, ArenaNew<TypeConstraintProp>(cx->compartment->pool, script, pc, target, id, true));
 }
 
 /* Constraints for determining the 'this' object at sites invoked using 'new'. */
 class TypeConstraintNewObject : public TypeConstraint
 {
     TypeFunction *fun;
     TypeSet *target;
 
@@ -653,17 +574,17 @@ class TypeConstraintNewObject : public T
     {}
 
     void newType(JSContext *cx, TypeSet *source, jstype type);
 };
 
 void
 TypeSet::addNewObject(JSContext *cx, JSScript *script, TypeFunction *fun, TypeSet *target)
 {
-    add(cx, ArenaNew<TypeConstraintNewObject>(cx->compartment->types.pool, script, fun, target));
+    add(cx, ArenaNew<TypeConstraintNewObject>(cx->compartment->pool, script, fun, target));
 }
 
 /*
  * 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
@@ -677,17 +598,17 @@ public:
     {}
 
     void newType(JSContext *cx, TypeSet *source, jstype type);
 };
 
 void
 TypeSet::addCall(JSContext *cx, TypeCallsite *site)
 {
-    add(cx, ArenaNew<TypeConstraintCall>(cx->compartment->types.pool, site));
+    add(cx, ArenaNew<TypeConstraintCall>(cx->compartment->pool, site));
 }
 
 /* Constraints for arithmetic operations. */
 class TypeConstraintArith : public TypeConstraint
 {
 public:
     /* Type set receiving the result of the arithmetic. */
     TypeSet *target;
@@ -702,17 +623,17 @@ public:
     }
 
     void newType(JSContext *cx, TypeSet *source, jstype type);
 };
 
 void
 TypeSet::addArith(JSContext *cx, JSScript *script, TypeSet *target, TypeSet *other)
 {
-    add(cx, ArenaNew<TypeConstraintArith>(cx->compartment->types.pool, script, target, other));
+    add(cx, ArenaNew<TypeConstraintArith>(cx->compartment->pool, script, target, other));
 }
 
 /* Subset constraint which transforms primitive values into appropriate objects. */
 class TypeConstraintTransformThis : public TypeConstraint
 {
 public:
     TypeSet *target;
 
@@ -721,17 +642,17 @@ public:
     {}
 
     void newType(JSContext *cx, TypeSet *source, jstype type);
 };
 
 void
 TypeSet::addTransformThis(JSContext *cx, JSScript *script, TypeSet *target)
 {
-    add(cx, ArenaNew<TypeConstraintTransformThis>(cx->compartment->types.pool, script, target));
+    add(cx, ArenaNew<TypeConstraintTransformThis>(cx->compartment->pool, script, target));
 }
 
 /* Subset constraint which filters out primitive types. */
 class TypeConstraintFilterPrimitive : public TypeConstraint
 {
 public:
     TypeSet *target;
 
@@ -743,17 +664,17 @@ public:
     {}
 
     void newType(JSContext *cx, TypeSet *source, jstype type);
 };
 
 void
 TypeSet::addFilterPrimitives(JSContext *cx, JSScript *script, TypeSet *target, bool onlyNullVoid)
 {
-    add(cx, ArenaNew<TypeConstraintFilterPrimitive>(cx->compartment->types.pool,
+    add(cx, ArenaNew<TypeConstraintFilterPrimitive>(cx->compartment->pool,
                                                     script, target, onlyNullVoid));
 }
 
 /*
  * Subset constraint for property reads which monitors accesses on properties
  * with scripted getters and polymorphic types.
  */
 class TypeConstraintMonitorRead : public TypeConstraint
@@ -766,17 +687,17 @@ public:
     {}
 
     void newType(JSContext *cx, TypeSet *source, jstype type);
 };
 
 void
 TypeSet::addMonitorRead(JSContext *cx, JSScript *script, TypeSet *target)
 {
-    add(cx, ArenaNew<TypeConstraintMonitorRead>(cx->compartment->types.pool, script, target));
+    add(cx, ArenaNew<TypeConstraintMonitorRead>(cx->compartment->pool, script, target));
 }
 
 /*
  * Type constraint which marks the result of 'for in' loops as unknown if the
  * iterated value could be a generator.
  */
 class TypeConstraintGenerator : public TypeConstraint
 {
@@ -785,29 +706,16 @@ public:
 
     TypeConstraintGenerator(JSScript *script, TypeSet *target)
         : TypeConstraint("generator", script), target(target)
     {}
 
     void newType(JSContext *cx, TypeSet *source, jstype type);
 };
 
-/* Update types with the possible values bound by the for loop in code. */
-static inline void
-SetForTypes(JSContext *cx, JSScript *script, const AnalyzeState &state, TypeSet *types)
-{
-    if (state.popped(0).isForEach)
-        types->addType(cx, TYPE_UNKNOWN);
-    else
-        types->addType(cx, TYPE_STRING);
-
-    state.popped(0).types->add(cx,
-        ArenaNew<TypeConstraintGenerator>(cx->compartment->types.pool, script, types));
-}
-
 /////////////////////////////////////////////////////////////////////
 // TypeConstraint
 /////////////////////////////////////////////////////////////////////
 
 void
 TypeConstraintSubset::newType(JSContext *cx, TypeSet *source, jstype type)
 {
     /* Basic subset constraint, move all types to the target. */
@@ -890,17 +798,17 @@ PropertyAccess(JSContext *cx, JSScript *
         if (assign)
             cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
         else
             target->addType(cx, TYPE_UNKNOWN);
         return;
     }
 
     /* Reads from objects with unknown properties are unknown, writes to such objects are ignored. */
-    if (object->unknownProperties()) {
+    if (object->unknownProperties() || cx->compartment->debugMode) {
         if (!assign)
             target->addType(cx, TYPE_UNKNOWN);
         return;
     }
 
     /* Capture the effects of a standard property access. */
     if (target) {
         TypeSet *types = object->getProperty(cx, id, assign);
@@ -1024,17 +932,17 @@ TypeConstraintCall::newType(JSContext *c
              * first argument is null, which will transform to the global object.
              */
 
             TypeSet *thisTypes = TypeSet::make(cx, "genericthis");
             if (!thisTypes)
                 return;
             callsite->argumentTypes[0]->addTransformThis(cx, script, thisTypes);
 
-            TypeCallsite *newSite = ArenaNew<TypeCallsite>(cx->compartment->types.pool,
+            TypeCallsite *newSite = ArenaNew<TypeCallsite>(cx->compartment->pool,
                                                            cx, script, pc, callsite->isNew,
                                                            callsite->argumentCount - 1);
             if (!newSite || (callsite->argumentCount > 1 && !newSite->argumentTypes)) {
                 cx->compartment->types.setPendingNukeTypes(cx);
                 return;
             }
 
             newSite->thisTypes = thisTypes;
@@ -1050,18 +958,24 @@ TypeConstraintCall::newType(JSContext *c
 
         return;
     }
 
     JSScript *callee = function->script;
     unsigned nargs = callee->fun->nargs;
 
     /* Analyze the function if we have not already done so. */
-    if (!callee->analyzed)
-        AnalyzeScriptTypes(cx, callee);
+    if (!callee->analyzed) {
+        analyze::ScriptAnalysis *calleeAnalysis = callee->analysis(cx);
+        if (!calleeAnalysis) {
+            cx->compartment->types.setPendingNukeTypes(cx);
+            return;
+        }
+        calleeAnalysis->analyzeTypes(cx);
+    }
 
     /* 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 = callee->argTypes(i);
         argTypes->addSubset(cx, script, types);
     }
 
@@ -1255,17 +1169,17 @@ public:
     {
         cx->compartment->types.dynamicPush(cx, script, pc - script->code, type);
     }
 };
 
 void
 TypeSet::pushAllTypes(JSContext *cx, JSScript *script, const jsbytecode *pc)
 {
-    add(cx, ArenaNew<TypeConstraintPushAll>(cx->compartment->types.pool, script, pc));
+    add(cx, ArenaNew<TypeConstraintPushAll>(cx->compartment->pool, script, pc));
 }
 
 /* Constraint which triggers recompilation of a script if any type is added to a type set. */
 class TypeConstraintFreeze : public TypeConstraint
 {
 public:
     /* Whether a new type has already been added, triggering recompilation. */
     bool typeAdded;
@@ -1282,17 +1196,17 @@ public:
         typeAdded = true;
         cx->compartment->types.addPendingRecompile(cx, script);
     }
 };
 
 void
 TypeSet::addFreeze(JSContext *cx)
 {
-    add(cx, ArenaNew<TypeConstraintFreeze>(cx->compartment->types.pool,
+    add(cx, ArenaNew<TypeConstraintFreeze>(cx->compartment->pool,
                                            cx->compartment->types.compiledScript), false);
 }
 
 void
 TypeSet::Clone(JSContext *cx, TypeSet *source, ClonedTypeSet *target)
 {
     if (!source) {
         target->typeFlags = TYPE_FLAG_UNKNOWN;
@@ -1387,17 +1301,17 @@ TypeSet::getKnownTypeTag(JSContext *cx)
     JSValueType type;
 
     if (objectCount)
         type = flags ? JSVAL_TYPE_UNKNOWN : JSVAL_TYPE_OBJECT;
     else
         type = GetValueTypeFromTypeFlags(flags);
 
     if (cx->compartment->types.compiledScript && type != JSVAL_TYPE_UNKNOWN) {
-        add(cx, ArenaNew<TypeConstraintFreezeTypeTag>(cx->compartment->types.pool,
+        add(cx, ArenaNew<TypeConstraintFreezeTypeTag>(cx->compartment->pool,
                                                       cx->compartment->types.compiledScript), false);
     }
 
     return type;
 }
 
 static inline bool
 ObjectKindPair(ObjectKind v0, ObjectKind v1, ObjectKind cmp0, ObjectKind cmp1)
@@ -1514,17 +1428,17 @@ public:
                 /*
                  * Add a constraint on the element type of the object to pick up
                  * changes in the object's array-ness or any unknown properties.
                  */
                 TypeSet *elementTypes = object->getProperty(cx, JSID_VOID, false);
                 if (!elementTypes)
                     return;
                 elementTypes->add(cx,
-                    ArenaNew<TypeConstraintFreezeObjectKind>(cx->compartment->types.pool,
+                    ArenaNew<TypeConstraintFreezeObjectKind>(cx->compartment->pool,
                                                              &kind, script), false);
             }
 
             if (nkind == kind) {
                 /* New object with the same kind we are interested in. */
                 return;
             }
             kind = nkind;
@@ -1551,34 +1465,34 @@ TypeSet::getKnownObjectKind(JSContext *c
     if (kind == OBJECT_NONE)
         kind = OBJECT_UNKNOWN;
 
     if (kind != OBJECT_UNKNOWN) {
         /*
          * Watch for new objects of different kind, and re-traverse existing types
          * in this set to add any needed FreezeArray constraints.
          */
-        add(cx, ArenaNew<TypeConstraintFreezeObjectKindSet>(cx->compartment->types.pool, kind,
+        add(cx, ArenaNew<TypeConstraintFreezeObjectKindSet>(cx->compartment->pool, kind,
                                                             cx->compartment->types.compiledScript));
     }
 
     return kind;
 }
 
 ObjectKind
 TypeSet::GetObjectKind(JSContext *cx, TypeObject *object)
 {
     ObjectKind kind = CombineObjectKind(object, OBJECT_NONE);
 
     if (kind != OBJECT_UNKNOWN) {
         TypeSet *elementTypes = object->getProperty(cx, JSID_VOID, false);
         if (!elementTypes)
             return OBJECT_UNKNOWN;
         elementTypes->add(cx,
-            ArenaNew<TypeConstraintFreezeObjectKind>(cx->compartment->types.pool,
+            ArenaNew<TypeConstraintFreezeObjectKind>(cx->compartment->pool,
                                                      kind, cx->compartment->types.compiledScript), false);
     }
 
     return kind;
 }
 
 static inline void
 ObjectStateChange(JSContext *cx, TypeObject *object, bool markingUnknown)
@@ -1615,17 +1529,17 @@ public:
 void
 TypeSet::WatchObjectReallocation(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isGlobal() && !obj->getType()->unknownProperties());
     TypeSet *types = obj->getType()->getProperty(cx, JSID_VOID, false);
     if (!types)
         return;
 
-    types->add(cx, ArenaNew<TypeConstraintWatchReallocation>(cx->compartment->types.pool,
+    types->add(cx, ArenaNew<TypeConstraintWatchReallocation>(cx->compartment->pool,
                                                              cx->compartment->types.compiledScript), false);
 }
 
 class TypeConstraintFreezeOwnProperty : public TypeConstraint
 {
 public:
     bool updated;
     bool configurable;
@@ -1654,45 +1568,45 @@ bool
 TypeSet::isOwnProperty(JSContext *cx, bool configurable)
 {
     if (hasAnyFlag(configurable
                    ? TYPE_FLAG_CONFIGURED_PROPERTY
                    : TYPE_FLAG_OWN_PROPERTY)) {
         return true;
     }
 
-    add(cx, ArenaNew<TypeConstraintFreezeOwnProperty>(cx->compartment->types.pool,
+    add(cx, ArenaNew<TypeConstraintFreezeOwnProperty>(cx->compartment->pool,
                                                       cx->compartment->types.compiledScript,
                                                       configurable), false);
     return false;
 }
 
 bool
 TypeSet::knownNonEmpty(JSContext *cx)
 {
     if (baseFlags() != 0 || objectCount != 0)
         return true;
 
-    add(cx, ArenaNew<TypeConstraintFreeze>(cx->compartment->types.pool,
+    add(cx, ArenaNew<TypeConstraintFreeze>(cx->compartment->pool,
                                            cx->compartment->types.compiledScript), false);
 
     return false;
 }
 
 JSObject *
 TypeSet::getSingleton(JSContext *cx)
 {
     if (baseFlags() != 0 || objectCount != 1)
         return NULL;
 
     TypeObject *object = (TypeObject *) objectSet;
     if (!object->singleton)
         return NULL;
 
-    add(cx, ArenaNew<TypeConstraintFreeze>(cx->compartment->types.pool,
+    add(cx, ArenaNew<TypeConstraintFreeze>(cx->compartment->pool,
                                            cx->compartment->types.compiledScript), false);
 
     return object->singleton;
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeCompartment
 /////////////////////////////////////////////////////////////////////
@@ -1709,18 +1623,16 @@ TypeCompartment::init(JSContext *cx)
      */
 #ifdef DEBUG
     typeEmpty.name_ = JSID_VOID;
 #endif
     typeEmpty.flags = OBJECT_FLAG_UNKNOWN_MASK;
 
     if (cx && cx->getRunOptions() & JSOPTION_TYPE_INFERENCE)
         inferenceEnabled = true;
-
-    JS_InitArenaPool(&pool, "typeinfer", 512, 8, NULL);
 }
 
 TypeObject *
 TypeCompartment::newTypeObject(JSContext *cx, JSScript *script, const char *name,
                                bool isFunction, bool isArray, JSObject *proto)
 {
 #ifdef DEBUG
 #if 0
@@ -1830,17 +1742,17 @@ GetScriptObject(JSContext *cx, JSScript 
 static inline const Value &
 GetScriptConst(JSContext *cx, JSScript *script, const jsbytecode *pc)
 {
     unsigned index = js_GetIndexFromBytecode(cx, script, (jsbytecode*) pc, 0);
     return script->getConst(index);
 }
 
 bool
-UseNewType(JSContext *cx, JSScript *script, jsbytecode *pc)
+types::UseNewType(JSContext *cx, JSScript *script, jsbytecode *pc)
 {
     JS_ASSERT(cx->typeInferenceEnabled());
 
     /*
      * Make a heuristic guess at a use of JSOP_NEW that the constructed object
      * should have a fresh type object. We do this when the NEW is immediately
      * followed by a simple assignment to an object's .prototype field.
      * This is designed to catch common patterns for subclassing in JS:
@@ -1918,30 +1830,31 @@ TypeCompartment::dynamicCall(JSContext *
     }
 
     return true;
 }
 
 bool
 TypeCompartment::dynamicPush(JSContext *cx, JSScript *script, uint32 offset, jstype type)
 {
+    JS_ASSERT(cx->typeInferenceEnabled());
+    AutoEnterTypeInference enter(cx);
+
     /*
      * For inc/dec ops, we need to go back and reanalyze the affected opcode
      * taking the overflow into account. We won't see an explicit adjustment
      * of the type of the thing being inc/dec'ed, nor will adding TYPE_DOUBLE to
      * the pushed value affect that type. We only handle inc/dec operations
      * that do not have an object lvalue; INCNAME/INCPROP/INCELEM and friends
      * should call typeMonitorAssign to update the property type.
      */
     jsbytecode *pc = script->code + offset;
     JSOp op = JSOp(*pc);
     const JSCodeSpec *cs = &js_CodeSpec[op];
     if (cs->format & (JOF_INC | JOF_DEC)) {
-        AutoEnterTypeInference enter(cx);
-
         switch (op) {
           case JSOP_INCGNAME:
           case JSOP_DECGNAME:
           case JSOP_GNAMEINC:
           case JSOP_GNAMEDEC: {
             jsid id = GetAtomId(cx, script, pc, 0);
             TypeObject *global = script->getGlobalType();
             if (!global->unknownProperties()) {
@@ -1952,87 +1865,90 @@ TypeCompartment::dynamicPush(JSContext *
             }
             break;
           }
 
           case JSOP_INCLOCAL:
           case JSOP_DECLOCAL:
           case JSOP_LOCALINC:
           case JSOP_LOCALDEC:
-            if (GET_SLOTNO(pc) < script->nfixed) {
-                TypeSet *types = script->localTypes(GET_SLOTNO(pc));
-                types->addType(cx, type);
-            }
-            break;
-
           case JSOP_INCARG:
           case JSOP_DECARG:
           case JSOP_ARGINC:
           case JSOP_ARGDEC: {
-            TypeSet *types = script->argTypes(GET_SLOTNO(pc));
-            types->addType(cx, type);
+            /*
+             * Just mark the slot's type as holding the new type. This captures
+             * the effect if the slot is not being tracked, and if the slot
+             * doesn't escape we will update the pushed types below to capture
+             * the slot's value after this write.
+             */
+            uint32 slot = analyze::GetBytecodeSlot(script, pc);
+            if (slot < analyze::TotalSlots(script)) {
+                TypeSet *types = script->slotTypes(slot);
+                types->addType(cx, type);
+            }
             break;
           }
 
           default:;
         }
-
-        if (!checkPendingRecompiles(cx))
-            return false;
     }
 
-    if (script->types) {
+    if (script->hasAnalysis() && script->analysis(cx)->ranInference()) {
         /*
          * If the pushed set already has this type, we don't need to ensure
          * there is a TypeResult. Either there already is a TypeResult, or the
          * type could be determined from the script's other input type sets.
          */
-        js::types::TypeSet *pushed = script->types->pushed(offset, 0);
+        TypeSet *pushed = script->analysis(cx)->pushedTypes(offset, 0);
         if (pushed->hasType(type))
             return true;
     } else {
         /* Scan all TypeResults on the script to check for a duplicate. */
-        js::types::TypeResult *result, **presult = &script->typeResults;
+        TypeResult *result, **presult = &script->typeResults;
         while (*presult) {
             result = *presult;
             if (result->offset == offset && result->type == type) {
                 if (presult != &script->typeResults) {
                     /* Move this result to the head of the list, maintain LRU order. */
                     *presult = result->next;
                     result->next = script->typeResults;
                     script->typeResults = result;
                 }
                 return true;
             }
             presult = &result->next;
         }
     }
 
-    AutoEnterTypeInference enter(cx);
-
     InferSpew(ISpewOps, "externalType: monitorResult #%u:%05u: %s",
                script->id(), offset, TypeString(type));
 
     TypeResult *result = (TypeResult *) cx->calloc_(sizeof(TypeResult));
     if (!result) {
         setPendingNukeTypes(cx);
-        return checkPendingRecompiles(cx);
+        return false;
     }
 
     result->offset = offset;
     result->type = type;
     result->next = script->typeResults;
     script->typeResults = result;
 
-    if (script->types) {
-        TypeSet *pushed = script->types->pushed(offset, 0);
+    if (script->hasAnalysis() && script->analysis(cx)->ranInference()) {
+        TypeSet *pushed = script->analysis(cx)->pushedTypes(offset, 0);
         pushed->addType(cx, type);
     } else if (script->analyzed) {
         /* Any new dynamic result triggers reanalysis and recompilation. */
-        AnalyzeScriptTypes(cx, script);
+        analyze::ScriptAnalysis *analysis = script->analysis(cx);
+        if (!analysis) {
+            setPendingNukeTypes(cx);
+            return false;
+        }
+        analysis->analyzeTypes(cx);
     }
 
     /*
      * If this script was inlined into a parent, we need to make sure the
      * parent has constraints listening to type changes in this one (it won't
      * necessarily, if we have condensed the constraints but not reanalyzed the
      * parent). The parent is listening for isUninlineable changes on the
      * function, so we can treat this as a state change on the function to
@@ -2040,39 +1956,41 @@ TypeCompartment::dynamicPush(JSContext *
      */
     if (script->fun && !script->fun->getType()->unknownProperties())
         ObjectStateChange(cx, script->fun->getType(), false);
 
     /*
      * If we unexpectedly read a hole out of an array, mark all array reads in
      * the script as undefined. :FIXME: bug 650163 remove hack.
      */
-    if (script->types && JSOp(*pc) == JSOP_GETELEM && type == TYPE_UNDEFINED) {
+    if (script->hasAnalysis() && script->analysis(cx)->ranInference() &&
+        JSOp(*pc) == JSOP_GETELEM && type == TYPE_UNDEFINED) {
+        analyze::ScriptAnalysis *analysis = script->analysis(cx);
         unsigned offset = 0;
         while (offset < script->length) {
-            if (JSOp(script->code[offset]) == JSOP_GETELEM && script->types->pushedArray[offset]) {
-                js::types::TypeSet *pushed = script->types->pushed(offset, 0);
+            if (JSOp(script->code[offset]) == JSOP_GETELEM && analysis->maybeCode(offset)) {
+                TypeSet *pushed = analysis->pushedTypes(offset, 0);
                 if (!pushed->hasType(TYPE_UNDEFINED)) {
                     pushed->addType(cx, TYPE_UNDEFINED);
                     TypeResult *result = (TypeResult *) cx->calloc_(sizeof(TypeResult));
                     if (!result) {
                         setPendingNukeTypes(cx);
-                        return checkPendingRecompiles(cx);
+                        return false;
                     }
                     result->offset = offset;
                     result->type = TYPE_UNDEFINED;
                     result->next = script->typeResults;
                     script->typeResults = result;
                 }
             }
             offset += analyze::GetBytecodeLength(pc);
         }
     }
 
-    return checkPendingRecompiles(cx);
+    return true;
 }
 
 bool
 TypeCompartment::processPendingRecompiles(JSContext *cx)
 {
     /* Steal the list of scripts to recompile, else we will try to recursively recompile them. */
     Vector<JSScript*> *pending = pendingRecompiles;
     pendingRecompiles = NULL;
@@ -2184,29 +2102,29 @@ TypeCompartment::dynamicAssign(JSContext
     JSOp op = JSOp(*cx->regs->pc);
     if (id == id___proto__(cx) || (op == JSOP_SETELEM && !JSID_IS_VOID(id)))
         return cx->markTypeObjectUnknownProperties(object);
 
     AutoEnterTypeInference enter(cx);
 
     TypeSet *assignTypes = object->getProperty(cx, id, true);
     if (!assignTypes || assignTypes->hasType(rvtype))
-        return cx->compartment->types.checkPendingRecompiles(cx);
+        return true;
 
     InferSpew(ISpewOps, "externalType: monitorAssign %s %s: %s",
               object->name(), TypeIdString(id), TypeString(rvtype));
     assignTypes->addType(cx, rvtype);
 
-    return cx->compartment->types.checkPendingRecompiles(cx);
+    return true;
 }
 
 void
 TypeCompartment::monitorBytecode(JSContext *cx, JSScript *script, uint32 offset)
 {
-    if (script->types->monitored(offset))
+    if (script->analysis(cx)->monitoredTypes(offset))
         return;
 
     /*
      * Make sure monitoring is limited to property sets and calls where the
      * target of the set/call could be statically unknown, and mark the bytecode
      * results as unknown.
      */
     JSOp op = JSOp(script->code[offset]);
@@ -2246,25 +2164,25 @@ TypeCompartment::monitorBytecode(JSConte
       case JSOP_DECPROP:
       case JSOP_PROPINC:
       case JSOP_PROPDEC:
       case JSOP_CALL:
       case JSOP_EVAL:
       case JSOP_FUNCALL:
       case JSOP_FUNAPPLY:
       case JSOP_NEW:
-        script->types->addType(cx, offset, 0, TYPE_UNKNOWN);
+        script->analysis(cx)->addPushedType(cx, offset, 0, TYPE_UNKNOWN);
         break;
       default:
-        TypeFailure(cx, "Monitoring unknown bytecode: %s", js_CodeNameTwo[op]);
+        TypeFailure(cx, "Monitoring unknown bytecode at #%u:%05u", script->id(), offset);
     }
 
     InferSpew(ISpewOps, "addMonitorNeeded: #%u:%05u", script->id(), offset);
 
-    script->types->setMonitored(offset);
+    script->analysis(cx)->setMonitoredTypes(offset);
 
     /* :FIXME: Also mark scripts this was inlined into as needing recompilation? */
     if (script->hasJITCode())
         cx->compartment->types.addPendingRecompile(cx, script);
 }
 
 void
 TypeCompartment::print(JSContext *cx, JSCompartment *compartment)
@@ -2272,18 +2190,18 @@ TypeCompartment::print(JSContext *cx, JS
     JS_ASSERT(this == &compartment->types);
 
     if (!InferSpewActive(ISpewResult) || JS_CLIST_IS_EMPTY(&compartment->scripts))
         return;
 
     for (JSScript *script = (JSScript *)compartment->scripts.next;
          &script->links != &compartment->scripts;
          script = (JSScript *)script->links.next) {
-        if (script->types)
-            script->types->print(cx, script);
+        if (script->hasAnalysis() && script->analysis(cx)->ranInference())
+            script->analysis(cx)->printTypes(cx);
         TypeObject *object = script->typeObjects;
         while (object) {
             object->print(cx);
             object = object->next;
         }
     }
 
 #ifdef DEBUG
@@ -2322,17 +2240,17 @@ TypeCompartment::print(JSContext *cx, JS
  */
 
 static inline bool
 NumberTypes(jstype a, jstype b)
 {
     return (a == TYPE_INT32 || a == TYPE_DOUBLE) && (b == TYPE_INT32 || b == TYPE_DOUBLE);
 }
 
-struct ArrayTableKey
+struct types::ArrayTableKey
 {
     jstype type;
     JSObject *proto;
 
     typedef ArrayTableKey Lookup;
 
     static inline uint32 hash(const ArrayTableKey &v) {
         return (uint32) (v.type ^ ((uint32)(size_t)v.proto >> 2));
@@ -2411,17 +2329,17 @@ TypeCompartment::fixArrayType(JSContext 
 }
 
 /*
  * N.B. We could also use the initial shape of the object (before its type is
  * fixed) as the key in the object table, but since all references in the table
  * are weak the hash entries would usually be collected on GC even if objects
  * with the new type/shape are still live.
  */
-struct ObjectTableKey
+struct types::ObjectTableKey
 {
     jsid *ids;
     uint32 nslots;
     uint32 nfixed;
     JSObject *proto;
 
     typedef JSObject * Lookup;
 
@@ -2442,17 +2360,17 @@ struct ObjectTableKey
             if (shape->id != v.ids[shape->slot])
                 return false;
             shape = shape->previous();
         }
         return true;
     }
 };
 
-struct ObjectTableEntry
+struct types::ObjectTableEntry
 {
     TypeObject *object;
     Shape *newShape;
     jstype *types;
 };
 
 bool
 TypeCompartment::fixObjectType(JSContext *cx, JSObject *obj)
@@ -2644,18 +2562,16 @@ TypeObject::splicePrototype(JSContext *c
      * added to an instance of this object.
      */
     unsigned count = getPropertyCount();
     for (unsigned i = 0; i < count; i++) {
         Property *prop = getProperty(i);
         if (prop && !JSID_IS_EMPTY(prop->id))
             getFromPrototypes(cx, prop);
     }
-
-    JS_ALWAYS_TRUE(cx->compartment->types.checkPendingRecompiles(cx));
 }
 
 bool
 TypeObject::addProperty(JSContext *cx, jsid id, Property **pprop)
 {
     JS_ASSERT(!*pprop);
     Property *base = cx->new_<Property>(id);
     if (!base) {
@@ -2709,52 +2625,50 @@ TypeObject::addDefiniteProperties(JSCont
                  * a setter in the direct prototype (and thus its transitive
                  * prototypes), the definite properties and new shape attached
                  * to this object get cleared out. clearUnknown is set if the
                  * definite properties are affected by prototype setters
                  * (i.e. objects from scripted 'new', but not objects from
                  * initializers).
                  */
                 TypeSet *parentTypes = proto->getType()->getProperty(cx, id, false);
-                if (!parentTypes || parentTypes->unknown()) {
-                    cx->compartment->types.checkPendingRecompiles(cx);
+                if (!parentTypes || parentTypes->unknown())
                     return false;
-                }
                 parentTypes->addBaseClearDefinite(cx, this);
             }
         } else {
             /*
              * We should have filtered these properties out before adding them
              * to the shape associated with the new type.
              */
             JS_ASSERT(!clearUnknown);
         }
         shape = shape->previous();
     }
 
-    return cx->compartment->types.checkPendingRecompiles(cx);
+    return true;
 }
 
 void
 TypeObject::setFlags(JSContext *cx, TypeObjectFlags flags)
 {
-    JS_ASSERT(cx->compartment->types.inferenceDepth);
+    JS_ASSERT(cx->compartment->activeInference);
     JS_ASSERT((this->flags & flags) != flags);
 
     this->flags |= flags;
 
     InferSpew(ISpewOps, "%s: setFlags %u", name(), flags);
 
     ObjectStateChange(cx, this, false);
 }
 
 void
 TypeObject::markUnknown(JSContext *cx)
 {
-    JS_ASSERT(cx->compartment->types.inferenceDepth);
+    JS_ASSERT(cx->compartment->activeInference);
     JS_ASSERT(!unknownProperties());
 
     InferSpew(ISpewOps, "UnknownProperties: %s", name());
 
     ObjectStateChange(cx, this, true);
 
     /* Mark existing instances as unknown. */
 
@@ -2803,18 +2717,16 @@ TypeObject::clearNewScript(JSContext *cx
      */
     for (unsigned i = 0; i < getPropertyCount(); i++) {
         Property *prop = getProperty(i);
         if (!prop)
             continue;
         if (prop->types.isDefiniteProperty())
             prop->types.setOwnProperty(cx, true);
     }
-
-    cx->compartment->types.checkPendingRecompiles(cx); // :XXX: handle failure
 }
 
 void
 TypeObject::print(JSContext *cx)
 {
     printf("%s : %s", name(), proto ? proto->getType()->name() : "(null)");
 
     if (unknownProperties()) {
@@ -2845,17 +2757,17 @@ TypeObject::print(JSContext *cx)
             prop->types.print(cx);
         }
     }
 
     printf("\n}\n");
 }
 
 /////////////////////////////////////////////////////////////////////
-// TypeScript
+// Type Analysis
 /////////////////////////////////////////////////////////////////////
 
 static inline ptrdiff_t
 GetJumpOffset(jsbytecode *pc, jsbytecode *pc2)
 {
     uint32 type = JOF_OPTYPE(*pc);
     if (JOF_TYPE_IS_EXTENDED_JUMP(type))
         return GET_JUMPX_OFFSET(pc2);
@@ -2885,40 +2797,16 @@ BytecodeNoFallThrough(JSOp op)
       case JSOP_GOSUBX:
         /* These fall through indirectly, after executing a 'finally'. */
         return false;
       default:
         return false;
     }
 }
 
-/* Merge any types currently in the state with those computed for the join point at offset. */
-void
-MergeTypes(JSContext *cx, AnalyzeState &state, JSScript *script, uint32 offset)
-{
-    unsigned targetDepth = state.analysis.getCode(offset).stackDepth;
-    JS_ASSERT(state.stackDepth >= targetDepth);
-    if (!state.joinTypes[offset]) {
-        TypeSet **joinTypes = ArenaArray<TypeSet*>(state.pool, targetDepth);
-        if (!joinTypes) {
-            cx->compartment->types.setPendingNukeTypes(cx);
-            return;
-        }
-        state.joinTypes[offset] = joinTypes;
-        for (unsigned i = 0; i < targetDepth; i++)
-            joinTypes[i] = state.stack[i].types;
-    }
-    for (unsigned i = 0; i < targetDepth; i++) {
-        if (!state.joinTypes[offset][i])
-            state.joinTypes[offset][i] = state.stack[i].types;
-        else if (state.stack[i].types && state.joinTypes[offset][i] != state.stack[i].types)
-            state.stack[i].types->addSubset(cx, script, state.joinTypes[offset][i]);
-    }
-}
-
 /*
  * If the bytecode immediately following code/pc is a test of the value
  * pushed by code, that value should be marked as possibly void.
  */
 static inline bool
 CheckNextTest(jsbytecode *pc)
 {
     jsbytecode *next = pc + analyze::GetBytecodeLength(pc);
@@ -2933,65 +2821,104 @@ CheckNextTest(jsbytecode *pc)
       case JSOP_TYPEOF:
       case JSOP_TYPEOFEXPR:
         return true;
       default:
         return false;
     }
 }
 
+static inline TypeObject *
+GetInitializerType(JSContext *cx, JSScript *script, jsbytecode *pc)
+{
+    if (!script->compileAndGo)
+        return NULL;
+
+    JSOp op = JSOp(*pc);
+    JS_ASSERT(op == JSOP_NEWARRAY || op == JSOP_NEWOBJECT || op == JSOP_NEWINIT);
+
+    bool isArray = (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && pc[1] == JSProto_Array));
+    return script->getTypeInitObject(cx, pc, isArray);
+}
+
+inline void
+analyze::ScriptAnalysis::setForTypes(JSContext *cx, jsbytecode *pc, TypeSet *types)
+{
+    /* Find the initial ITER opcode which constructed the active iterator. */
+    const SSAValue &iterv = poppedValue(pc, 0);
+    jsbytecode *iterpc = script->code + iterv.pushedOffset();
+    JS_ASSERT(JSOp(*iterpc) == JSOP_ITER);
+
+    uintN flags = iterpc[1];
+    if (flags & JSITER_FOREACH) {
+        types->addType(cx, TYPE_UNKNOWN);
+        return;
+    }
+
+    /*
+     * This is a plain 'for in' loop. The value bound is a string, unless the
+     * iterated object is a generator or has an __iterator__ hook, which we'll
+     * detect dynamically.
+     */
+    types->addType(cx, TYPE_STRING);
+
+    pushedTypes(iterpc, 0)->add(cx,
+        ArenaNew<TypeConstraintGenerator>(cx->compartment->pool, script, types));
+}
+
 /* Analyze type information for a single bytecode. */
-static bool
-AnalyzeBytecode(JSContext *cx, AnalyzeState &state, JSScript *script, uint32 offset)
+bool
+analyze::ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
+                                              TypeInferenceState &state)
 {
     jsbytecode *pc = script->code + offset;
     JSOp op = (JSOp)*pc;
 
+    Bytecode &code = getCode(offset);
+    JS_ASSERT(!code.pushedTypes);
+
     InferSpew(ISpewOps, "analyze: #%u:%05u", script->id(), offset);
 
+    unsigned defCount = analyze::GetDefCount(script, offset);
+    if (analyze::ExtendedDef(pc))
+        defCount++;
+
+    TypeSet *pushed = ArenaArray<TypeSet>(cx->compartment->pool, defCount);
+    if (!pushed)
+        return false;
+    PodZero(pushed, defCount);
+    code.pushedTypes = pushed;
+
     /*
-     * Track the state's stack depth against the stack depth computed by the bytecode
-     * analysis, and adjust as necessary.
+     * 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
+     * been processed, as types can flow backwards into phi nodes and the
+     * source sets may not exist if we try to process these eagerly.
      */
-    uint32 stackDepth = state.analysis.getCode(offset).stackDepth;
-    if (stackDepth > state.stackDepth) {
-#ifdef DEBUG
-        /*
-         * Check that we aren't destroying any useful information. This should only
-         * occur around exception handling bytecode.
-         */
-        for (unsigned i = state.stackDepth; i < stackDepth; i++)
-            JS_ASSERT(!state.stack[i].isForEach);
-#endif
-        unsigned ndefs = stackDepth - state.stackDepth;
-        memset(&state.stack[state.stackDepth], 0, ndefs * sizeof(AnalyzeStateStack));
+    if (code.newValues) {
+        SlotValue *newv = code.newValues;
+        while (newv->slot) {
+            if (newv->value.kind() != SSAValue::PHI || newv->value.phiOffset() != offset) {
+                newv++;
+                continue;
+            }
+
+            /*
+             * The phi nodes at join points should all be unique, and every phi
+             * node created should be in the phiValues list on some bytecode.
+             */
+            if (!state.phiNodes.append(newv->value.phiNode()))
+                return false;
+            TypeSet &types = newv->value.phiNode()->types;
+            types.setIntermediate();
+            InferSpew(ISpewOps, "typeSet: T%p phi #%u:%05u:%u", &types,
+                      script->id(), offset, newv->slot);
+            newv++;
+        }
     }
-    state.stackDepth = stackDepth;
-
-    /*
-     * If this is a join point, merge existing types with the join and then pull
-     * in the types already computed.
-     */
-    if (state.joinTypes[offset]) {
-        MergeTypes(cx, state, script, offset);
-        for (unsigned i = 0; i < stackDepth; i++)
-            state.stack[i].types = state.joinTypes[offset][i];
-    }
-
-    TypeObject *initializer = NULL;
-
-    unsigned defCount = analyze::GetDefCount(script, offset);
-    TypeSet *pushed = ArenaArray<TypeSet>(cx->compartment->types.pool, defCount);
-    if (!pushed)
-        return false;
-
-    JS_ASSERT(!script->types->pushedArray[offset]);
-    script->types->pushedArray[offset] = pushed;
-
-    PodZero(pushed, defCount);
 
     for (unsigned i = 0; i < defCount; i++) {
         pushed[i].setIntermediate();
         InferSpew(ISpewOps, "typeSet: T%p pushed%u #%u:%05u", &pushed[i], i, script->id(), offset);
     }
 
     /* Add type constraints for the various opcodes. */
     switch (op) {
@@ -3031,16 +2958,21 @@ AnalyzeBytecode(JSContext *cx, AnalyzeSt
       case JSOP_INDEXBASE3:
       case JSOP_RESETBASE:
       case JSOP_RESETBASE0:
       case JSOP_BLOCKCHAIN:
       case JSOP_NULLBLOCKCHAIN:
       case JSOP_POPV:
       case JSOP_DEBUGGER:
       case JSOP_SETCALL:
+      case JSOP_TABLESWITCH:
+      case JSOP_TABLESWITCHX:
+      case JSOP_LOOKUPSWITCH:
+      case JSOP_LOOKUPSWITCHX:
+      case JSOP_TRY:
         break;
 
         /* Bytecodes pushing values of known type. */
       case JSOP_VOID:
       case JSOP_PUSH:
         pushed[0].addType(cx, TYPE_UNDEFINED);
         break;
       case JSOP_ZERO:
@@ -3116,29 +3048,29 @@ AnalyzeBytecode(JSContext *cx, AnalyzeSt
             script->returnTypes()->addType(cx, TYPE_UNDEFINED);
         break;
 
       case JSOP_OR:
       case JSOP_ORX:
       case JSOP_AND:
       case JSOP_ANDX:
         /* OR/AND push whichever operand determined the result. */
-        state.popped(0).types->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
         break;
 
       case JSOP_DUP:
-        state.popped(0).types->addSubset(cx, script, &pushed[0]);
-        state.popped(0).types->addSubset(cx, script, &pushed[1]);
+        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[1]);
         break;
 
       case JSOP_DUP2:
-        state.popped(1).types->addSubset(cx, script, &pushed[0]);
-        state.popped(0).types->addSubset(cx, script, &pushed[1]);
-        state.popped(1).types->addSubset(cx, script, &pushed[2]);
-        state.popped(0).types->addSubset(cx, script, &pushed[3]);
+        poppedTypes(pc, 1)->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[1]);
+        poppedTypes(pc, 1)->addSubset(cx, script, &pushed[2]);
+        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[3]);
         break;
 
       case JSOP_GETGLOBAL:
       case JSOP_CALLGLOBAL:
       case JSOP_GETGNAME:
       case JSOP_CALLGNAME: {
         jsid id;
         if (op == JSOP_GETGLOBAL || op == JSOP_CALLGLOBAL)
@@ -3165,62 +3097,62 @@ AnalyzeBytecode(JSContext *cx, AnalyzeSt
         if (op == JSOP_CALLGLOBAL || op == JSOP_CALLGNAME)
             pushed[1].addType(cx, TYPE_UNKNOWN);
 
         if (CheckNextTest(pc))
             pushed[0].addType(cx, TYPE_UNDEFINED);
         break;
       }
 
+      case JSOP_INCGNAME:
+      case JSOP_DECGNAME:
+      case JSOP_GNAMEINC:
+      case JSOP_GNAMEDEC: {
+        jsid id = GetAtomId(cx, script, pc, 0);
+        PropertyAccess(cx, script, pc, script->getGlobalType(), true, NULL, id);
+        PropertyAccess(cx, script, pc, script->getGlobalType(), false, &pushed[0], id);
+        break;
+      }
+
       case JSOP_NAME:
       case JSOP_CALLNAME:
         /* The first value pushed by NAME/CALLNAME must always be reported to inference. */
         if (op == JSOP_CALLNAME)
             pushed[1].addType(cx, TYPE_UNKNOWN);
         break;
 
       case JSOP_BINDGNAME:
       case JSOP_BINDNAME:
         break;
 
       case JSOP_SETGNAME: {
         jsid id = GetAtomId(cx, script, pc, 0);
         PropertyAccess(cx, script, pc, script->getGlobalType(),
-                       true, state.popped(0).types, id);
-        state.popped(0).types->addSubset(cx, script, &pushed[0]);
+                       true, poppedTypes(pc, 0), id);
+        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
         break;
       }
 
       case JSOP_SETNAME:
       case JSOP_SETCONST:
         cx->compartment->types.monitorBytecode(cx, script, offset);
-        state.popped(0).types->addSubset(cx, script, &pushed[0]);
-        break;
-
-      case JSOP_GETXPROP:
-        pushed[0].addType(cx, TYPE_UNKNOWN);
+        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
         break;
 
-      case JSOP_INCGNAME:
-      case JSOP_DECGNAME:
-      case JSOP_GNAMEINC:
-      case JSOP_GNAMEDEC: {
-        jsid id = GetAtomId(cx, script, pc, 0);
-        PropertyAccess(cx, script, pc, script->getGlobalType(), true, NULL, id);
-        PropertyAccess(cx, script, pc, script->getGlobalType(), false, &pushed[0], id);
-        break;
-      }
-
       case JSOP_INCNAME:
       case JSOP_DECNAME:
       case JSOP_NAMEINC:
       case JSOP_NAMEDEC:
         cx->compartment->types.monitorBytecode(cx, script, offset);
         break;
 
+      case JSOP_GETXPROP:
+        pushed[0].addType(cx, TYPE_UNKNOWN);
+        break;
+
       case JSOP_GETFCSLOT:
       case JSOP_CALLFCSLOT: {
         unsigned index = GET_UINT16(pc);
         TypeSet *types = script->upvarTypes(index);
         types->addSubset(cx, script, &pushed[0]);
         if (op == JSOP_CALLFCSLOT)
             pushed[1].addType(cx, TYPE_UNDEFINED);
         break;
@@ -3229,199 +3161,186 @@ AnalyzeBytecode(JSContext *cx, AnalyzeSt
       case JSOP_GETUPVAR_DBG:
       case JSOP_CALLUPVAR_DBG:
         pushed[0].addType(cx, TYPE_UNKNOWN);
         if (op == JSOP_CALLUPVAR_DBG)
             pushed[1].addType(cx, TYPE_UNDEFINED);
         break;
 
       case JSOP_GETARG:
+      case JSOP_CALLARG:
+      case JSOP_GETLOCAL:
+      case JSOP_CALLLOCAL: {
+        uint32 slot = GetBytecodeSlot(script, pc);
+        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, script, &pushed[0]);
+        } else if (slot < TotalSlots(script)) {
+            TypeSet *types = script->slotTypes(slot);
+            types->addSubset(cx, script, &pushed[0]);
+        } else {
+            /* Local 'let' variable. Punt on types for these, for now. */
+            pushed[0].addType(cx, TYPE_UNKNOWN);
+        }
+        if (op == JSOP_CALLARG || op == JSOP_CALLLOCAL)
+            pushed[1].addType(cx, TYPE_UNDEFINED);
+        break;
+      }
+
       case JSOP_SETARG:
-      case JSOP_CALLARG: {
-        TypeSet *types = script->argTypes(GET_ARGNO(pc));
-        types->addSubset(cx, script, &pushed[0]);
-        if (op == JSOP_SETARG)
-            state.popped(0).types->addSubset(cx, script, types);
-        if (op == JSOP_CALLARG)
-            pushed[1].addType(cx, TYPE_UNDEFINED);
+      case JSOP_SETLOCAL:
+      case JSOP_SETLOCALPOP: {
+        uint32 slot = GetBytecodeSlot(script, pc);
+        if (!trackSlot(slot) && slot < TotalSlots(script)) {
+            TypeSet *types = script->slotTypes(slot);
+            poppedTypes(pc, 0)->addSubset(cx, script, types);
+        }
+
+        /*
+         * For assignments to non-escaping locals/args, we don't need to update
+         * the possible types of the var, as for each read of the var SSA gives
+         * us the writes that could have produced that read.
+         */
+        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
         break;
       }
 
       case JSOP_INCARG:
       case JSOP_DECARG:
       case JSOP_ARGINC:
-      case JSOP_ARGDEC: {
-        TypeSet *types = script->argTypes(GET_ARGNO(pc));
-        types->addArith(cx, script, types);
-        types->addSubset(cx, script, &pushed[0]);
-        break;
-      }
-
-      case JSOP_ARGSUB:
-        pushed[0].addType(cx, TYPE_UNKNOWN);
-        break;
-
-      case JSOP_GETLOCAL:
-      case JSOP_SETLOCAL:
-      case JSOP_SETLOCALPOP:
-      case JSOP_CALLLOCAL: {
-        uint32 local = GET_SLOTNO(pc);
-        TypeSet *types = local < script->nfixed ? script->localTypes(local) : NULL;
-
-        if (op != JSOP_SETLOCALPOP) {
-            if (types)
-                types->addSubset(cx, script, &pushed[0]);
-            else
-                pushed[0].addType(cx, TYPE_UNKNOWN);
-        }
-        if (op == JSOP_CALLLOCAL)
-            pushed[1].addType(cx, TYPE_UNDEFINED);
-
-        if (op == JSOP_SETLOCAL || op == JSOP_SETLOCALPOP) {
-            if (types)
-                state.popped(0).types->addSubset(cx, script, types);
-        } else {
-            /*
-             * Add void type if the variable might be undefined. TODO: monitor for
-             * undefined read instead?
-             */
-            if (state.analysis.localHasUseBeforeDef(local) ||
-                !state.analysis.localDefined(local, pc)) {
-                pushed[0].addType(cx, TYPE_UNDEFINED);
-            }
-        }
-
-        break;
-      }
-
+      case JSOP_ARGDEC:
       case JSOP_INCLOCAL:
       case JSOP_DECLOCAL:
       case JSOP_LOCALINC:
       case JSOP_LOCALDEC: {
-        uint32 local = GET_SLOTNO(pc);
-        TypeSet *types = local < script->nfixed ? script->localTypes(local) : NULL;
-        if (types) {
+        uint32 slot = GetBytecodeSlot(script, pc);
+        if (trackSlot(slot)) {
+            poppedTypes(pc, 0)->addArith(cx, script, &pushed[0]);
+        } else if (slot < TotalSlots(script)) {
+            TypeSet *types = script->slotTypes(slot);
             types->addArith(cx, script, types);
             types->addSubset(cx, script, &pushed[0]);
         } else {
             pushed[0].addType(cx, TYPE_UNKNOWN);
         }
         break;
       }
 
+      case JSOP_ARGSUB:
+        pushed[0].addType(cx, TYPE_UNKNOWN);
+        break;
+
       case JSOP_ARGUMENTS:
       case JSOP_ARGCNT:
         pushed[0].addType(cx, TYPE_UNKNOWN);
         break;
 
       case JSOP_SETPROP:
       case JSOP_SETMETHOD: {
         jsid id = GetAtomId(cx, script, pc, 0);
-        state.popped(1).types->addSetProperty(cx, script, pc, state.popped(0).types, id);
-        state.popped(0).types->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 1)->addSetProperty(cx, script, pc, poppedTypes(pc, 0), id);
+        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
         break;
       }
 
+      case JSOP_LENGTH:
       case JSOP_GETPROP:
-      case JSOP_CALLPROP: {
-        jsid id = GetAtomId(cx, script, pc, 0);
-        state.popped(0).types->addGetProperty(cx, script, pc, &pushed[0], id);
-
-        if (op == JSOP_CALLPROP)
-            state.popped(0).types->addFilterPrimitives(cx, script, &pushed[1], true);
-        if (CheckNextTest(pc))
-            pushed[0].addType(cx, TYPE_UNDEFINED);
-        break;
-      }
-
+      case JSOP_CALLPROP:
       case JSOP_INCPROP:
       case JSOP_DECPROP:
       case JSOP_PROPINC:
       case JSOP_PROPDEC: {
         jsid id = GetAtomId(cx, script, pc, 0);
-        state.popped(0).types->addGetProperty(cx, script, pc, &pushed[0], id);
-        state.popped(0).types->addSetProperty(cx, script, pc, NULL, id);
+        poppedTypes(pc, 0)->addGetProperty(cx, script, pc, &pushed[0], id);
+
+        if (op == JSOP_CALLPROP)
+            poppedTypes(pc, 0)->addFilterPrimitives(cx, script, &pushed[1], true);
+        if (CheckNextTest(pc))
+            pushed[0].addType(cx, TYPE_UNDEFINED);
         break;
       }
 
       case JSOP_GETELEM:
       case JSOP_CALLELEM:
+      case JSOP_INCELEM:
+      case JSOP_DECELEM:
+      case JSOP_ELEMINC:
+      case JSOP_ELEMDEC:
         /*
          * We only consider ELEM accesses on integers here. Any element access
          * which is accessing a non-integer property must be monitored.
          */
-        state.popped(1).types->addGetProperty(cx, script, pc, &pushed[0], JSID_VOID);
+        poppedTypes(pc, 1)->addGetProperty(cx, script, pc, &pushed[0], JSID_VOID);
 
         if (op == JSOP_CALLELEM)
-            state.popped(1).types->addFilterPrimitives(cx, script, &pushed[1], true);
+            poppedTypes(pc, 1)->addFilterPrimitives(cx, script, &pushed[1], true);
         if (CheckNextTest(pc))
             pushed[0].addType(cx, TYPE_UNDEFINED);
         break;
 
       case JSOP_SETELEM:
       case JSOP_SETHOLE:
-        state.popped(2).types->addSetProperty(cx, script, pc, state.popped(0).types, JSID_VOID);
-        state.popped(0).types->addSubset(cx, script, &pushed[0]);
-        break;
-
-      case JSOP_INCELEM:
-      case JSOP_DECELEM:
-      case JSOP_ELEMINC:
-      case JSOP_ELEMDEC:
-        state.popped(1).types->addGetProperty(cx, script, pc, &pushed[0], JSID_VOID);
-        state.popped(1).types->addSetProperty(cx, script, pc, NULL, JSID_VOID);
-        break;
-
-      case JSOP_LENGTH:
-        /* Treat this as an access to the length property. */
-        state.popped(0).types->addGetProperty(cx, script, pc, &pushed[0], id_length(cx));
+        poppedTypes(pc, 2)->addSetProperty(cx, script, pc, poppedTypes(pc, 0), JSID_VOID);
+        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
         break;
 
       case JSOP_THIS:
         script->thisTypes()->addTransformThis(cx, script, &pushed[0]);
         break;
 
       case JSOP_RETURN:
       case JSOP_SETRVAL:
         if (script->fun)
-            state.popped(0).types->addSubset(cx, script, script->returnTypes());
+            poppedTypes(pc, 0)->addSubset(cx, script, script->returnTypes());
         break;
 
       case JSOP_ADD:
-        state.popped(0).types->addArith(cx, script, &pushed[0], state.popped(1).types);
-        state.popped(1).types->addArith(cx, script, &pushed[0], state.popped(0).types);
+        poppedTypes(pc, 0)->addArith(cx, script, &pushed[0], poppedTypes(pc, 1));
+        poppedTypes(pc, 1)->addArith(cx, script, &pushed[0], poppedTypes(pc, 0));
         break;
 
       case JSOP_SUB:
       case JSOP_MUL:
       case JSOP_MOD:
       case JSOP_DIV:
-        state.popped(0).types->addArith(cx, script, &pushed[0]);
-        state.popped(1).types->addArith(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addArith(cx, script, &pushed[0]);
+        poppedTypes(pc, 1)->addArith(cx, script, &pushed[0]);
         break;
 
       case JSOP_NEG:
       case JSOP_POS:
-        state.popped(0).types->addArith(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addArith(cx, script, &pushed[0]);
         break;
 
       case JSOP_LAMBDA:
       case JSOP_LAMBDA_FC:
       case JSOP_DEFFUN:
       case JSOP_DEFFUN_FC:
       case JSOP_DEFLOCALFUN:
       case JSOP_DEFLOCALFUN_FC: {
         unsigned off = (op == JSOP_DEFLOCALFUN || op == JSOP_DEFLOCALFUN_FC) ? SLOTNO_LEN : 0;
         JSObject *obj = GetScriptObject(cx, script, pc, off);
 
         TypeSet *res = NULL;
-        if (op == JSOP_LAMBDA || op == JSOP_LAMBDA_FC)
+        if (op == JSOP_LAMBDA || op == JSOP_LAMBDA_FC) {
             res = &pushed[0];
-        else if (op == JSOP_DEFLOCALFUN || op == JSOP_DEFLOCALFUN_FC)
-            res = script->localTypes(GET_SLOTNO(pc));
+        } else if (op == JSOP_DEFLOCALFUN || op == JSOP_DEFLOCALFUN_FC) {
+            uint32 slot = GetBytecodeSlot(script, pc);
+            if (trackSlot(slot)) {
+                res = &pushed[0];
+            } else {
+                /* Should not see 'let' vars here. */
+                JS_ASSERT(slot < TotalSlots(script));
+                res = script->slotTypes(slot);
+            }
+        }
 
         if (res) {
             if (script->compileAndGo)
                 res->addType(cx, (jstype) obj->getType());
             else
                 res->addType(cx, TYPE_UNKNOWN);
         } else {
             cx->compartment->types.monitorBytecode(cx, script, offset);
@@ -3434,51 +3353,55 @@ AnalyzeBytecode(JSContext *cx, AnalyzeSt
 
       case JSOP_CALL:
       case JSOP_EVAL:
       case JSOP_FUNCALL:
       case JSOP_FUNAPPLY:
       case JSOP_NEW: {
         /* Construct the base call information about this site. */
         unsigned argCount = analyze::GetUseCount(script, offset) - 2;
-        TypeCallsite *callsite = ArenaNew<TypeCallsite>(cx->compartment->types.pool,
+        TypeCallsite *callsite = ArenaNew<TypeCallsite>(cx->compartment->pool,
                                                         cx, script, pc, op == JSOP_NEW, argCount);
         if (!callsite || (argCount && !callsite->argumentTypes)) {
             cx->compartment->types.setPendingNukeTypes(cx);
             break;
         }
-        callsite->thisTypes = state.popped(argCount).types;
+        callsite->thisTypes = poppedTypes(pc, argCount);
         callsite->returnTypes = &pushed[0];
 
         for (unsigned i = 0; i < argCount; i++)
-            callsite->argumentTypes[i] = state.popped(argCount - 1 - i).types;
-
-        state.popped(argCount + 1).types->addCall(cx, callsite);
+            callsite->argumentTypes[i] = poppedTypes(pc, argCount - 1 - i);
+
+        poppedTypes(pc, argCount + 1)->addCall(cx, callsite);
         break;
       }
 
       case JSOP_NEWINIT:
       case JSOP_NEWARRAY:
-      case JSOP_NEWOBJECT:
+      case JSOP_NEWOBJECT: {
+        TypeObject *initializer = GetInitializerType(cx, script, pc);
         if (script->compileAndGo) {
-            bool isArray = (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && pc[1] == JSProto_Array));
-            initializer = script->getTypeInitObject(cx, pc, isArray);
             if (!initializer)
                 return false;
             pushed[0].addType(cx, (jstype) initializer);
         } else {
+            JS_ASSERT(!initializer);
             pushed[0].addType(cx, TYPE_UNKNOWN);
         }
         break;
+      }
 
       case JSOP_ENDINIT:
         break;
 
-      case JSOP_INITELEM:
-        initializer = state.popped(2).initializer;
+      case JSOP_INITELEM: {
+        const SSAValue &objv = poppedValue(pc, 2);
+        jsbytecode *initpc = script->code + objv.pushedOffset();
+        TypeObject *initializer = GetInitializerType(cx, script, initpc);
+
         JS_ASSERT((initializer != NULL) == script->compileAndGo);
         if (initializer) {
             pushed[0].addType(cx, (jstype) initializer);
             if (!initializer->unknownProperties()) {
                 /*
                  * Assume the initialized element is an integer. INITELEM can be used
                  * for doubles which don't map to the JSID_VOID property, which must
                  * be caught with dynamic monitoring.
@@ -3486,58 +3409,63 @@ AnalyzeBytecode(JSContext *cx, AnalyzeSt
                 TypeSet *types = initializer->getProperty(cx, JSID_VOID, true);
                 if (!types)
                     return false;
                 if (state.hasGetSet)
                     types->addType(cx, TYPE_UNKNOWN);
                 else if (state.hasHole)
                     cx->markTypeArrayNotPacked(initializer, false);
                 else
-                    state.popped(0).types->addSubset(cx, script, types);
+                    poppedTypes(pc, 0)->addSubset(cx, script, types);
             }
         } else {
             pushed[0].addType(cx, TYPE_UNKNOWN);
         }
         state.hasGetSet = false;
         state.hasHole = false;
         break;
+      }
 
       case JSOP_GETTER:
       case JSOP_SETTER:
         state.hasGetSet = true;
         break;
 
       case JSOP_HOLE:
         state.hasHole = true;
         break;
 
       case JSOP_INITPROP:
-      case JSOP_INITMETHOD:
-        initializer = state.popped(1).initializer;
+      case JSOP_INITMETHOD: {
+        const SSAValue &objv = poppedValue(pc, 1);
+        jsbytecode *initpc = script->code + objv.pushedOffset();
+        TypeObject *initializer = GetInitializerType(cx, script, initpc);
+
         JS_ASSERT((initializer != NULL) == script->compileAndGo);
         if (initializer) {
             pushed[0].addType(cx, (jstype) initializer);
             if (!initializer->unknownProperties()) {
                 jsid id = GetAtomId(cx, script, pc, 0);
                 TypeSet *types = initializer->getProperty(cx, id, true);
                 if (!types)
                     return false;
                 if (id == id___proto__(cx) || id == id_prototype(cx))
                     cx->compartment->types.monitorBytecode(cx, script, offset);
                 else if (state.hasGetSet)
                     types->addType(cx, TYPE_UNKNOWN);
                 else
-                    state.popped(0).types->addSubset(cx, script, types);
+                    poppedTypes(pc, 0)->addSubset(cx, script, types);
             }
         } else {
             pushed[0].addType(cx, TYPE_UNKNOWN);
         }
         state.hasGetSet = false;
         JS_ASSERT(!state.hasHole);
         break;
+      }
 
       case JSOP_ENTERWITH:
       case JSOP_ENTERBLOCK:
         /*
          * Scope lookups can occur on the values being pushed here. We don't track
          * the value or its properties, and just monitor all name opcodes in the
          * script.
          */
@@ -3545,71 +3473,64 @@ AnalyzeBytecode(JSContext *cx, AnalyzeSt
 
       case JSOP_ITER:
         /*
          * The actual pushed value is an iterator object, which we don't care about.
          * Propagate the target of the iteration itself so that we'll be able to detect
          * when an object of Iterator class flows to the JSOP_FOR* opcode, which could
          * be a generator that produces arbitrary values with 'for in' syntax.
          */
-        state.popped(0).types->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
         break;
 
       case JSOP_MOREITER:
-        state.popped(0).types->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
         pushed[1].addType(cx, TYPE_BOOLEAN);
         break;
 
       case JSOP_FORGNAME: {
         jsid id = GetAtomId(cx, script, pc, 0);
         TypeObject *global = script->getGlobalType();
         if (!global->unknownProperties()) {
             TypeSet *types = global->getProperty(cx, id, true);
             if (!types)
                 return false;
-            SetForTypes(cx, script, state, types);
+            setForTypes(cx, pc, types);
         }
         break;
       }
 
       case JSOP_FORNAME:
         cx->compartment->types.monitorBytecode(cx, script, offset);
         break;
 
+      case JSOP_FORARG:
       case JSOP_FORLOCAL: {
-        uint32 local = GET_SLOTNO(pc);
-        TypeSet *types = local < script->nfixed ? script->localTypes(local) : NULL;
-        if (types)
-            SetForTypes(cx, script, state, types);
-        break;
-      }
-
-      case JSOP_FORARG: {
-        TypeSet *types = script->argTypes(GET_ARGNO(pc));
-        SetForTypes(cx, script, state, types);
+        uint32 slot = GetBytecodeSlot(script, pc);
+        if (trackSlot(slot)) {
+            setForTypes(cx, pc, &pushed[1]);
+        } else {
+            if (slot < TotalSlots(script))
+                setForTypes(cx, pc, script->slotTypes(slot));
+        }
         break;
       }
 
       case JSOP_FORELEM:
-        state.popped(0).types->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
         pushed[1].addType(cx, TYPE_UNKNOWN);
         break;
 
       case JSOP_FORPROP:
       case JSOP_ENUMELEM:
       case JSOP_ENUMCONSTELEM:
+      case JSOP_ARRAYPUSH:
         cx->compartment->types.monitorBytecode(cx, script, offset);
         break;
 
-      case JSOP_ARRAYPUSH: {
-        TypeSet *types = state.stack[GET_SLOTNO(pc) - script->nfixed].types;
-        types->addSetProperty(cx, script, pc, state.popped(0).types, JSID_VOID);
-        break;
-      }
-
       case JSOP_THROW:
         /* There will be a monitor on the bytecode catching the exception. */
         break;
 
       case JSOP_FINALLY:
         /* Pushes information about whether an exception was thrown. */
         break;
 
@@ -3620,26 +3541,26 @@ AnalyzeBytecode(JSContext *cx, AnalyzeSt
       case JSOP_DELPROP:
       case JSOP_DELELEM:
       case JSOP_DELNAME:
         /* TODO: watch for deletes on the global object. */
         pushed[0].addType(cx, TYPE_BOOLEAN);
         break;
 
       case JSOP_LEAVEBLOCKEXPR:
-        state.popped(0).types->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
         break;
 
       case JSOP_CASE:
       case JSOP_CASEX:
-        state.popped(1).types->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 1)->addSubset(cx, script, &pushed[0]);
         break;
 
       case JSOP_UNBRAND:
-        state.popped(0).types->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
         break;
 
       case JSOP_GENERATOR:
         if (script->fun) {
             if (script->compileAndGo) {
                 TypeObject *object = script->getTypeNewObject(cx, JSProto_Generator);
                 if (!object)
                     return false;
@@ -3658,17 +3579,17 @@ AnalyzeBytecode(JSContext *cx, AnalyzeSt
         pushed[1].addType(cx, TYPE_UNKNOWN);
         /* FALLTHROUGH */
       case JSOP_XMLNAME:
         pushed[0].addType(cx, TYPE_UNKNOWN);
         break;
 
       case JSOP_SETXMLNAME:
         cx->compartment->types.monitorBytecode(cx, script, offset);
-        state.popped(0).types->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
         break;
 
       case JSOP_BINDXMLNAME:
         break;
 
       case JSOP_TOXML:
       case JSOP_TOXMLLIST:
       case JSOP_XMLPI:
@@ -3680,218 +3601,125 @@ AnalyzeBytecode(JSContext *cx, AnalyzeSt
       case JSOP_QNAME:
       case JSOP_ANYNAME:
       case JSOP_GETFUNNS:
         pushed[0].addType(cx, TYPE_UNKNOWN);
         break;
 
       case JSOP_FILTER:
         /* Note: the second value pushed by filter is a hole, and not modelled. */
-        state.popped(0).types->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 0)->addSubset(cx, script, &pushed[0]);
         break;
 
       case JSOP_ENDFILTER:
-        state.popped(1).types->addSubset(cx, script, &pushed[0]);
+        poppedTypes(pc, 1)->addSubset(cx, script, &pushed[0]);
         break;
 
       case JSOP_DEFSHARP:
         break;
 
       case JSOP_USESHARP:
         pushed[0].addType(cx, TYPE_UNKNOWN);
         break;
 
       case JSOP_CALLEE:
         if (script->compileAndGo)
             pushed[0].addType(cx, (jstype) script->fun->getType());
         else
             pushed[0].addType(cx, TYPE_UNKNOWN);
         break;
 
-      case JSOP_TABLESWITCH:
-      case JSOP_TABLESWITCHX: {
-        jsbytecode *pc2 = pc;
-        unsigned jmplen = (op == JSOP_TABLESWITCH) ? JUMP_OFFSET_LEN : JUMPX_OFFSET_LEN;
-        unsigned defaultOffset = offset + GetJumpOffset(pc, pc2);
-        pc2 += jmplen;
-        jsint low = GET_JUMP_OFFSET(pc2);
-        pc2 += JUMP_OFFSET_LEN;
-        jsint high = GET_JUMP_OFFSET(pc2);
-        pc2 += JUMP_OFFSET_LEN;
-
-        MergeTypes(cx, state, script, defaultOffset);
-
-        for (jsint i = low; i <= high; i++) {
-            unsigned targetOffset = offset + GetJumpOffset(pc, pc2);
-            if (targetOffset != offset)
-                MergeTypes(cx, state, script, targetOffset);
-            pc2 += jmplen;
-        }
-        break;
-      }
-
-      case JSOP_LOOKUPSWITCH:
-      case JSOP_LOOKUPSWITCHX: {
-        jsbytecode *pc2 = pc;
-        unsigned jmplen = (op == JSOP_LOOKUPSWITCH) ? JUMP_OFFSET_LEN : JUMPX_OFFSET_LEN;
-        unsigned defaultOffset = offset + GetJumpOffset(pc, pc2);
-        pc2 += jmplen;
-        unsigned npairs = GET_UINT16(pc2);
-        pc2 += UINT16_LEN;
-
-        MergeTypes(cx, state, script, defaultOffset);
-
-        while (npairs) {
-            pc2 += INDEX_LEN;
-            unsigned targetOffset = offset + GetJumpOffset(pc, pc2);
-            MergeTypes(cx, state, script, targetOffset);
-            pc2 += jmplen;
-            npairs--;
-        }
-        break;
-      }
-
-      case JSOP_TRY: {
-        JSTryNote *tn = script->trynotes()->vector;
-        JSTryNote *tnlimit = tn + script->trynotes()->length;
-        for (; tn < tnlimit; tn++) {
-            unsigned startOffset = script->main - script->code + tn->start;
-            if (startOffset == offset + 1) {
-                unsigned catchOffset = startOffset + tn->length;
-                if (tn->kind != JSTRY_ITER)
-                    MergeTypes(cx, state, script, catchOffset);
-            }
-        }
-        break;
-      }
-
       default:
-        TypeFailure(cx, "Unknown bytecode: %s", js_CodeNameTwo[op]);
-    }
-
-    /* Compute temporary analysis state after the bytecode. */
-
-    if (op == JSOP_DUP) {
-        state.stack[stackDepth] = state.stack[stackDepth - 1];
-        state.stackDepth = stackDepth + 1;
-    } else if (op == JSOP_DUP2) {
-        state.stack[stackDepth]     = state.stack[stackDepth - 2];
-        state.stack[stackDepth + 1] = state.stack[stackDepth - 1];
-        state.stackDepth = stackDepth + 2;
-    } else {
-        unsigned nuses = analyze::GetUseCount(script, offset);
-        unsigned ndefs = analyze::GetDefCount(script, offset);
-        memset(&state.stack[stackDepth - nuses], 0, ndefs * sizeof(AnalyzeStateStack));
-        state.stackDepth = stackDepth - nuses + ndefs;
-    }
-
-    for (unsigned i = 0; i < defCount; i++)
-        state.popped(defCount -1 - i).types = &pushed[i];
-
-    switch (op) {
-      case JSOP_ITER: {
-        uintN flags = pc[1];
-        if (flags & JSITER_FOREACH)
-            state.popped(0).isForEach = true;
-        break;
-      }
-
-      case JSOP_NEWINIT:
-      case JSOP_NEWARRAY:
-      case JSOP_NEWOBJECT:
-      case JSOP_INITELEM:
-      case JSOP_INITPROP:
-      case JSOP_INITMETHOD:
-        state.popped(0).initializer = initializer;
-        break;
-
-      default:;
-    }
-
-    /* Merge types with other jump targets of this opcode. */
-    uint32 type = JOF_TYPE(js_CodeSpec[op].format);
-    if (type == JOF_JUMP || type == JOF_JUMPX) {
-        unsigned targetOffset = offset + GetJumpOffset(pc, pc);
-        MergeTypes(cx, state, script, targetOffset);
+        TypeFailure(cx, "Unknown bytecode at #%u:%05u", script->id(), offset);
     }
 
     return true;
 }
 
 void
-AnalyzeScriptTypes(JSContext *cx, JSScript *script)
+analyze::ScriptAnalysis::analyzeTypes(JSContext *cx)
 {
-    JS_ASSERT(!script->types && !script->isUncachedEval);
-
-    analyze::Script analysis;
-    analysis.analyze(cx, script);
-
-    AnalyzeState state(cx, analysis);
-
-    unsigned length = sizeof(TypeScript)
-        + (script->length * sizeof(TypeScript*));
-    unsigned char *cursor = (unsigned char *) cx->calloc_(length);
-
-    if (analysis.failed() || !script->ensureVarTypes(cx) || !state.init(script) || !cursor) {
-        cx->compartment->types.setPendingNukeTypes(cx);
+    JS_ASSERT(!ranInference() && !failed());
+
+    if (!ranSSA()) {
+        analyzeSSA(cx);
+        if (failed())
+            return;
+    }
+
+    if (!script->ensureVarTypes(cx)) {
+        setOOM(cx);
         return;
     }
 
     if (script->analyzed) {
         /*
          * Reanalyzing this script after discarding from GC.
          * Discard/recompile any JIT code for this script,
          * to preserve invariant in TypeConstraintCondensed.
          */
         cx->compartment->types.addPendingRecompile(cx, script);
     }
 
-    TypeScript *types = (TypeScript *) cursor;
-    script->types = types;
+    /* Future OOM failures need to setPendingNukeTypes. */
     script->analyzed = true;
-#ifdef DEBUG
-    types->script = script;
-#endif
-
-    cursor += sizeof(TypeScript);
-    types->pushedArray = (TypeSet **) cursor;
+
+    /*
+     * Set this early to avoid reentrance. Any failures are OOMs, and will nuke
+     * all types in the compartment.
+     */
+    ranInference_ = true;
 
     if (script->calledWithNew)
-        AnalyzeScriptNew(cx, script);
+        analyzeTypesNew(cx);
+
+    /* Make sure the initial type set of all local vars includes void. */
+    for (unsigned i = 0; i < script->nfixed; i++)
+        script->localTypes(i)->addType(cx, TYPE_UNDEFINED);
+
+    TypeInferenceState state(cx);
 
     unsigned offset = 0;
     while (offset < script->length) {
-        analyze::Bytecode *code = analysis.maybeCode(offset);
+        analyze::Bytecode *code = maybeCode(offset);
 
         jsbytecode *pc = script->code + offset;
         analyze::UntrapOpcode untrap(cx, script, pc);
 
-        if (code && !AnalyzeBytecode(cx, state, script, offset)) {
+        if (code && !analyzeTypesBytecode(cx, offset, state)) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
 
         offset += analyze::GetBytecodeLength(pc);
     }
 
+    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, script, &node->types);
+        }
+    }
+
     /*
      * Sync with any dynamic types previously generated either because
      * we ran the interpreter some before analyzing or because we
      * are reanalyzing after a GC.
      */
     TypeResult *result = script->typeResults;
     while (result) {
-        TypeSet *pushed = script->types->pushed(result->offset);
+        TypeSet *pushed = pushedTypes(result->offset);
         pushed->addType(cx, result->type);
         result = result->next;
     }
 }
 
 void
-AnalyzeScriptNew(JSContext *cx, JSScript *script)
+analyze::ScriptAnalysis::analyzeTypesNew(JSContext *cx)
 {
     JS_ASSERT(script->calledWithNew && script->fun);
 
     /*
      * Compute the 'this' type when called with 'new'. We do not distinguish regular
      * from 'new' calls to the function.
      */
 
@@ -4072,142 +3900,36 @@ AnalyzeScriptProperties(JSContext *cx, J
     JS_NOT_REACHED("Mystery!");
     return baseobj;
 }
 
 /////////////////////////////////////////////////////////////////////
 // Printing
 /////////////////////////////////////////////////////////////////////
 
-#ifdef DEBUG
-
 void
-PrintBytecode(JSContext *cx, JSScript *script, jsbytecode *pc)
+analyze::ScriptAnalysis::printTypes(JSContext *cx)
 {
-    unsigned offset = pc - script->code;
-
-    JSOp op = (JSOp)*pc;
-    JS_ASSERT(op < JSOP_LIMIT);
-
-    const JSCodeSpec *cs = &js_CodeSpec[op];
-    const char *name = js_CodeNameTwo[op];
-
-    uint32 type = JOF_TYPE(cs->format);
-    switch (type) {
-      case JOF_BYTE:
-      case JOF_TABLESWITCH:
-      case JOF_TABLESWITCHX:
-      case JOF_LOOKUPSWITCH:
-      case JOF_LOOKUPSWITCHX:
-        printf("%s", name);
-        break;
-
-      case JOF_JUMP:
-      case JOF_JUMPX: {
-        ptrdiff_t off = GetJumpOffset(pc, pc);
-        printf("%s %u", name, unsigned(offset + off));
-        break;
-      }
-
-      case JOF_ATOM: {
-        if (op == JSOP_DOUBLE) {
-            printf("%s", name);
-        } else {
-            jsid id = GetAtomId(cx, script, pc, 0);
-            if (JSID_IS_STRING(id))
-                printf("%s %s", name, TypeIdString(id));
-            else
-                printf("%s (index)", name);
-        }
-        break;
-      }
-
-      case JOF_OBJECT:
-        printf("%s (object)", name);
-        break;
-
-      case JOF_REGEXP:
-        printf("%s (regexp)", name);
-        break;
-
-      case JOF_UINT16PAIR:
-        printf("%s %d %d", name, GET_UINT16(pc), GET_UINT16(pc + UINT16_LEN));
-        break;
-
-      case JOF_UINT16:
-        printf("%s %d", name, GET_UINT16(pc));
-        break;
-
-      case JOF_QARG:
-        printf("%s %d", name, GET_ARGNO(pc));
-        break;
-
-      case JOF_GLOBAL:
-        printf("%s %s", name, TypeIdString(GetGlobalId(cx, script, pc)));
-        break;
-
-      case JOF_LOCAL:
-        printf("%s %d", name, GET_SLOTNO(pc));
-        break;
-
-      case JOF_SLOTATOM: {
-        jsid id = GetAtomId(cx, script, pc, SLOTNO_LEN);
-
-        printf("%s %d %s", name, GET_SLOTNO(pc), TypeIdString(id));
-        break;
-      }
-
-      case JOF_SLOTOBJECT:
-        printf("%s %u (object)", name, GET_SLOTNO(pc));
-        break;
-
-      case JOF_UINT24:
-        JS_ASSERT(op == JSOP_UINT24 || op == JSOP_NEWARRAY);
-        printf("%s %d", name, (jsint)GET_UINT24(pc));
-        break;
-
-      case JOF_UINT8:
-        printf("%s %d", name, (jsint)pc[1]);
-        break;
-
-      case JOF_INT8:
-        printf("%s %d", name, (jsint)GET_INT8(pc));
-        break;
-
-      case JOF_INT32:
-        printf("%s %d", name, (jsint)GET_INT32(pc));
-        break;
-
-      default:
-        JS_NOT_REACHED("Unknown opcode type");
-    }
-}
-
-#endif
-
-void
-TypeScript::print(JSContext *cx, JSScript *script)
-{
+    AutoEnterAnalysis enter(cx);
     TypeCompartment *compartment = &script->compartment->types;
 
     /*
      * Check if there are warnings for used values with unknown types, and build
      * statistics about the size of type sets found for stack values.
      */
     for (unsigned offset = 0; offset < script->length; offset++) {
-        TypeSet *array = pushed(offset);
-        if (!array)
+        if (!maybeCode(offset))
             continue;
 
         unsigned defCount = analyze::GetDefCount(script, offset);
         if (!defCount)
             continue;
 
         for (unsigned i = 0; i < defCount; i++) {
-            TypeSet *types = &array[i];
+            TypeSet *types = pushedTypes(offset, i);
 
             unsigned typeCount = types->getObjectCount() ? 1 : 0;
             for (jstype type = TYPE_UNDEFINED; type <= TYPE_STRING; type++) {
                 if (types->hasAnyFlag(1 << type))
                     typeCount++;
             }
 
             /*
@@ -4246,71 +3968,68 @@ TypeScript::print(JSContext *cx, JSScrip
     printf("\n    this:");
     script->thisTypes()->print(cx);
 
     for (unsigned i = 0; script->fun && i < script->fun->nargs; i++) {
         printf("\n    arg%u:", i);
         script->argTypes(i)->print(cx);
     }
     for (unsigned i = 0; i < script->nfixed; i++) {
-        printf("\n    local%u:", i);
-        script->localTypes(i)->print(cx);
+        if (!trackSlot(LocalSlot(script, i))) {
+            printf("\n    local%u:", i);
+            script->localTypes(i)->print(cx);
+        }
     }
     for (unsigned i = 0; i < script->bindings.countUpvars(); i++) {
         printf("\n    upvar%u:", i);
         script->upvarTypes(i)->print(cx);
     }
     printf("\n");
 
     for (unsigned offset = 0; offset < script->length; offset++) {
-        TypeSet *array = pushed(offset);
-        if (!array)
+        if (!maybeCode(offset))
             continue;
 
-        printf("#%u:%05u:  ", script->id(), offset);
         PrintBytecode(cx, script, script->code + offset);
-        printf("\n");
 
         unsigned defCount = analyze::GetDefCount(script, offset);
         for (unsigned i = 0; i < defCount; i++) {
             printf("  type %d:", i);
-            array[i].print(cx);
+            pushedTypes(offset, i)->print(cx);
             printf("\n");
         }
 
-        if (monitored(offset))
+        if (monitoredTypes(offset))
             printf("  monitored\n");
     }
 
     printf("\n");
 
 #endif /* DEBUG */
 
 }
 
-} } /* namespace js::types */
-
 /////////////////////////////////////////////////////////////////////
 // JSContext
 /////////////////////////////////////////////////////////////////////
 
-js::types::TypeFunction *
+TypeFunction *
 JSContext::newTypeFunction(const char *name, JSObject *proto)
 {
-    return (js::types::TypeFunction *)
+    return (TypeFunction *)
         compartment->types.newTypeObject(this, NULL, name, true, false, proto);
 }
 
-js::types::TypeObject *
+TypeObject *
 JSContext::newTypeObject(const char *name, JSObject *proto)
 {
     return compartment->types.newTypeObject(this, NULL, name, false, false, proto);
 }
 
-js::types::TypeObject *
+TypeObject *
 JSContext::newTypeObject(const char *base, const char *postfix, JSObject *proto, bool isFunction)
 {
     char *name = NULL;
 #ifdef DEBUG
     unsigned len = strlen(base) + strlen(postfix) + 5;
     name = (char *)alloca(len);
     JS_snprintf(name, len, "%s:%s", base, postfix);
 #endif
@@ -4391,29 +4110,29 @@ IgnorePushed(JSOp op, unsigned index)
 
 bool
 JSScript::makeVarTypes(JSContext *cx)
 {
     JS_ASSERT(!varTypes);
 
     unsigned nargs = fun ? fun->nargs : 0;
     unsigned count = 2 + nargs + nfixed + bindings.countUpvars();
-    varTypes = (js::types::TypeSet *) cx->calloc_(sizeof(js::types::TypeSet) * count);
+    varTypes = (TypeSet *) cx->calloc_(sizeof(TypeSet) * count);
     if (!varTypes)
         return false;
 
 #ifdef DEBUG
-    InferSpew(js::types::ISpewOps, "typeSet: T%p return #%u", returnTypes(), id());
-    InferSpew(js::types::ISpewOps, "typeSet: T%p this #%u", thisTypes(), id());
+    InferSpew(ISpewOps, "typeSet: T%p return #%u", returnTypes(), id());
+    InferSpew(ISpewOps, "typeSet: T%p this #%u", thisTypes(), id());
     for (unsigned i = 0; i < nargs; i++)
-        InferSpew(js::types::ISpewOps, "typeSet: T%p arg%u #%u", argTypes(i), i, id());
+        InferSpew(ISpewOps, "typeSet: T%p arg%u #%u", argTypes(i), i, id());
     for (unsigned i = 0; i < nfixed; i++)
-        InferSpew(js::types::ISpewOps, "typeSet: T%p local%u #%u", localTypes(i), i, id());
+        InferSpew(ISpewOps, "typeSet: T%p local%u #%u", localTypes(i), i, id());
     for (unsigned i = 0; i < bindings.countUpvars(); i++)
-        InferSpew(js::types::ISpewOps, "typeSet: T%p upvar%u #%u", upvarTypes(i), i, id());
+        InferSpew(ISpewOps, "typeSet: T%p upvar%u #%u", upvarTypes(i), i, id());
 #endif
 
     return true;
 }
 
 bool
 JSScript::typeSetFunction(JSContext *cx, JSFunction *fun)
 {
@@ -4422,67 +4141,68 @@ JSScript::typeSetFunction(JSContext *cx,
     if (!cx->typeInferenceEnabled())
         return true;
 
     char *name = NULL;
 #ifdef DEBUG
     name = (char *) alloca(10);
     JS_snprintf(name, 10, "#%u", id());
 #endif
-    js::types::TypeFunction *type = cx->newTypeFunction(name, fun->getProto())->asFunction();
+    TypeFunction *type = cx->newTypeFunction(name, fun->getProto())->asFunction();
     if (!type)
         return false;
 
     if (!fun->setTypeAndUniqueShape(cx, type))
         return false;
     type->script = this;
     this->fun = fun;
 
     return true;
 }
 
 #ifdef DEBUG
 
 void
 JSScript::typeCheckBytecode(JSContext *cx, const jsbytecode *pc, const js::Value *sp)
 {
-    if (!types)
+    AutoEnterTypeInference enter(cx);
+
+    if (!(analysis_ && analysis_->ranInference()))
         return;
 
     int defCount = js::analyze::GetDefCount(this, pc - code);
-    js::types::TypeSet *array = types->pushed(pc - code);
 
     for (int i = 0; i < defCount; i++) {
         const js::Value &val = sp[-defCount + i];
-        js::types::TypeSet *types = &array[i];
+        TypeSet *types = analysis_->pushedTypes(pc, i);
         if (IgnorePushed(JSOp(*pc), i))
             continue;
 
-        js::types::jstype type = js::types::GetValueType(cx, val);
-
-        if (!js::types::TypeSetMatches(cx, types, type)) {
-            js::types::TypeFailure(cx, "Missing type at #%u:%05u pushed %u: %s",
-                                   id(), pc - code, i, js::types::TypeString(type));
+        jstype type = GetValueType(cx, val);
+
+        if (!TypeSetMatches(cx, types, type)) {
+            TypeFailure(cx, "Missing type at #%u:%05u pushed %u: %s",
+                                   id(), pc - code, i, TypeString(type));
         }
 
-        if (js::types::TypeIsObject(type)) {
+        if (TypeIsObject(type)) {
             JS_ASSERT(val.isObject());
             JSObject *obj = &val.toObject();
-            js::types::TypeObject *object = (js::types::TypeObject *) type;
+            TypeObject *object = (TypeObject *) type;
 
             if (object->unknownProperties())
                 continue;
 
             /* Make sure information about the array status of this object is right. */
-            bool dense = !object->hasFlags(js::types::OBJECT_FLAG_NON_DENSE_ARRAY);
-            bool packed = !object->hasFlags(js::types::OBJECT_FLAG_NON_PACKED_ARRAY);
+            bool dense = !object->hasFlags(OBJECT_FLAG_NON_DENSE_ARRAY);
+            bool packed = !object->hasFlags(OBJECT_FLAG_NON_PACKED_ARRAY);
             JS_ASSERT_IF(packed, dense);
             if (dense) {
                 if (!obj->isDenseArray() || (packed && !obj->isPackedDenseArray())) {
-                    js::types::TypeFailure(cx, "Object not %s array at #%u:%05u popped %u: %s",
+                    TypeFailure(cx, "Object not %s array at #%u:%05u popped %u: %s",
                         packed ? "packed" : "dense",
                         id(), pc - code, i, object->name());
                 }
             }
         }
     }
 }
 
@@ -4492,80 +4212,70 @@ JSScript::typeCheckBytecode(JSContext *c
 // JSObject
 /////////////////////////////////////////////////////////////////////
 
 void
 JSObject::makeNewType(JSContext *cx, JSScript *newScript)
 {
     JS_ASSERT(!newType);
 
-    js::types::TypeObject *type = cx->newTypeObject(getType()->name(), "new", this);
+    TypeObject *type = cx->newTypeObject(getType()->name(), "new", this);
     if (!type)
         return;
 
     if (!cx->typeInferenceEnabled()) {
         newType = type;
         setDelegate();
         return;
     }
 
-    js::types::AutoEnterTypeInference enter(cx);
+    AutoEnterTypeInference enter(cx);
 
     if (!getType()->unknownProperties()) {
         /* Update the possible 'new' types for all prototype objects sharing the same type object. */
-        js::types::TypeSet *types = getType()->getProperty(cx, JSID_EMPTY, true);
+        TypeSet *types = getType()->getProperty(cx, JSID_EMPTY, true);
         if (types)
-            types->addType(cx, (js::types::jstype) type);
+            types->addType(cx, (jstype) type);
     }
 
     if (newScript && !type->unknownProperties()) {
-        JSObject *baseobj = js::types::AnalyzeScriptProperties(cx, newScript);
+        JSObject *baseobj = AnalyzeScriptProperties(cx, newScript);
         if (baseobj && baseobj->slotSpan() > 0) {
             js::gc::FinalizeKind kind = js::gc::GetGCObjectKind(baseobj->slotSpan());
 
             /* We should not have overflowed the maximum number of fixed slots for an object. */
             JS_ASSERT(js::gc::GetGCKindSlots(kind) >= baseobj->slotSpan());
 
             /*
              * The base object was created with a different type and
              * finalize kind than we will use for subsequent new objects.
              * Generate an object with the appropriate final shape.
              */
             baseobj = NewReshapedObject(cx, type, baseobj->getParent(), kind,
                                         (const js::Shape *) baseobj->lastProperty());
-            if (!baseobj) {
-                cx->compartment->types.checkPendingRecompiles(cx);
+            if (!baseobj)
                 return;
-            }
-
-            if (!type->addDefiniteProperties(cx, baseobj, true)) {
-                cx->compartment->types.checkPendingRecompiles(cx);
+
+            if (!type->addDefiniteProperties(cx, baseobj, true))
                 return;
-            }
 
             type->newScript = newScript;
             type->newScriptFinalizeKind = unsigned(kind);
             type->newScriptShape = (js::Shape *) baseobj->lastProperty();
         }
     }
 
-    if (!cx->compartment->types.checkPendingRecompiles(cx))
-        return;
-
     newType = type;
     setDelegate();
 }
 
 /////////////////////////////////////////////////////////////////////
 // Tracing
 /////////////////////////////////////////////////////////////////////
 
-namespace js {
-namespace types {
-
 void
 types::TypeObject::trace(JSTracer *trc)
 {
     JS_ASSERT(!marked);
 
     /*
      * Only mark types if the Mark/Sweep GC is running; the bit won't be cleared
      * by the cycle collector.
@@ -4893,22 +4603,20 @@ TypeCompartment::~TypeCompartment()
 
     if (arrayTypeTable)
         Foreground::delete_(arrayTypeTable);
 
     if (objectTypeTable)
         Foreground::delete_(objectTypeTable);
 }
 
-} } /* namespace js::types */
-
 void
 JSScript::condenseTypes(JSContext *cx)
 {
-    js::types::CondenseTypeObjectList(cx, &compartment->types, typeObjects);
+    CondenseTypeObjectList(cx, &compartment->types, typeObjects);
 
     if (varTypes) {
         js::HashSet<JSScript *> condensed(cx), *pcondensed = &condensed;
         if (!condensed.init()) {
             compartment->types.setPendingNukeTypes(cx);
             pcondensed = NULL;
         }
 
@@ -4918,41 +4626,44 @@ JSScript::condenseTypes(JSContext *cx)
             (u.object && IsAboutToBeFinalized(cx, u.object)) ||
             (fun && IsAboutToBeFinalized(cx, fun))) {
             for (unsigned i = 0; i < num; i++)
                 varTypes[i].destroy(cx);
             cx->free_(varTypes);
             varTypes = NULL;
         } else {
             for (unsigned i = 0; i < num; i++)
-                js::types::TypeSet::CondenseSweepTypeSet(cx, &compartment->types, pcondensed, &varTypes[i]);
+                TypeSet::CondenseSweepTypeSet(cx, &compartment->types, pcondensed, &varTypes[i]);
         }
     }
 
-    js::types::TypeResult **presult = &typeResults;
+    TypeResult **presult = &typeResults;
     while (*presult) {
-        js::types::TypeResult *result = *presult;
-        if (js::types::TypeIsObject(result->type)) {
-            js::types::TypeObject *object = (js::types::TypeObject *) result->type;
+        TypeResult *result = *presult;
+        if (TypeIsObject(result->type)) {
+            TypeObject *object = (TypeObject *) result->type;
             if (!object->marked) {
                 if (!object->unknownProperties()) {
                     *presult = result->next;
                     cx->free_(result);
                     continue;
                 } else {
-                    result->type = (js::types::jstype) &compartment->types.typeEmpty;
+                    result->type = (jstype) &compartment->types.typeEmpty;
                 }
             }
         }
         presult = &result->next;
     }
 }
 
 void
-JSScript::sweepTypes(JSContext *cx)
+JSScript::sweepAnalysis(JSContext *cx)
 {
     SweepTypeObjectList(cx, typeObjects);
 
-    if (types && !compartment->types.inferenceDepth) {
-        cx->free_(types);
-        types = NULL;
+    if (analysis_ && !compartment->activeAnalysis) {
+        /*
+         * The analysis and everything in it is allocated using the analysis
+         * pool in the compartment (to be cleared shortly).
+         */
+        analysis_ = NULL;
     }
 }
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -44,20 +44,17 @@
 
 #include "jsarena.h"
 #include "jstl.h"
 #include "jsprvtd.h"
 #include "jsvalue.h"
 
 namespace js {
     struct CallArgs;
-namespace analyze {
-    struct Bytecode;
-    class Script;
-} }
+}
 
 namespace js {
 namespace types {
 
 /* Forward declarations. */
 class TypeSet;
 struct TypeCallsite;
 struct TypeObject;
@@ -325,17 +322,17 @@ class TypeSet
     /*
      * Iterate through the objects in this set. getObjectCount overapproximates
      * in the hash case (see SET_ARRAY_SIZE in jsinferinlines.h), and getObject
      * may return NULL.
      */
     inline unsigned getObjectCount();
     inline TypeObject *getObject(unsigned i);
 
-    void setIntermediate() { typeFlags |= TYPE_FLAG_INTERMEDIATE_SET; }
+    void setIntermediate() { JS_ASSERT(!typeFlags); typeFlags = TYPE_FLAG_INTERMEDIATE_SET; }
     void setOwnProperty(bool configurable) {
         typeFlags |= TYPE_FLAG_OWN_PROPERTY;
         if (configurable)
             typeFlags |= TYPE_FLAG_CONFIGURED_PROPERTY;
     }
     void setDefinite(unsigned slot) {
         JS_ASSERT(slot <= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT));
         typeFlags |= TYPE_FLAG_DEFINITE_PROPERTY | (slot << TYPE_FLAG_DEFINITE_SHIFT);
@@ -684,69 +681,32 @@ struct TypeResult
 
     /* Type which was pushed. */
     jstype type;
 
     /* Next dynamic result for the script. */
     TypeResult *next;
 };
 
-/* Type information for a script, result of AnalyzeTypes. */
-struct TypeScript
-{
-#ifdef DEBUG
-    JSScript *script;
-#endif
-
-    /*
-     * Stack values pushed by all bytecodes in the script. Low bit is set for
-     * bytecodes which are monitored (side effects were not determined statically).
-     */
-    TypeSet **pushedArray;
-
-    /* Gather statistics off this script and print it if necessary. */
-    void print(JSContext *cx, JSScript *script);
-
-    inline bool monitored(uint32 offset);
-    inline void setMonitored(uint32 offset);
-
-    inline TypeSet *pushed(uint32 offset);
-    inline TypeSet *pushed(uint32 offset, uint32 index);
-
-    inline void addType(JSContext *cx, uint32 offset, uint32 index, jstype type);
-};
-
-/* Analyzes all types in script, constructing its TypeScript. */
-void AnalyzeScriptTypes(JSContext *cx, JSScript *script);
-
-/* Analyze the effect of invoking 'new' on script. */
-void AnalyzeScriptNew(JSContext *cx, JSScript *script);
-
 struct ArrayTableKey;
 typedef HashMap<ArrayTableKey,TypeObject*,ArrayTableKey,SystemAllocPolicy> ArrayTypeTable;
 
 struct ObjectTableKey;
 struct ObjectTableEntry;
 typedef HashMap<ObjectTableKey,ObjectTableEntry,ObjectTableKey,SystemAllocPolicy> ObjectTypeTable;
 
 /* Type information for a compartment. */
 struct TypeCompartment
 {
     /* List of objects not associated with a script. */
     TypeObject *objects;
 
     /* Whether type inference is enabled in this compartment. */
     bool inferenceEnabled;
 
-    /* Whether type inference is active, see AutoEnterTypeInference. */
-    unsigned inferenceDepth;
-
-    /* Pool for all intermediate type information in this compartment. Cleared on every GC. */
-    JSArenaPool pool;
-
     /* Number of scripts in this compartment. */
     unsigned scriptCount;
 
     /* Object to use throughout the compartment as the default type of objects with no prototype. */
     TypeObject typeEmpty;
 
     /*
      * Bit set if all current types must be marked as unknown, and all scripts
@@ -837,18 +797,16 @@ struct TypeCompartment
     /*
      * Add the specified type to the specified set, do any necessary reanalysis
      * stemming from the change and recompile any affected scripts.
      */
     bool dynamicPush(JSContext *cx, JSScript *script, uint32 offset, jstype type);
     bool dynamicAssign(JSContext *cx, JSObject *obj, jsid id, const Value &rval);
     bool dynamicCall(JSContext *cx, JSObject *callee, const CallArgs &args, bool constructing);
 
-    inline bool checkPendingRecompiles(JSContext *cx);
-
     bool nukeTypes(JSContext *cx);
     bool processPendingRecompiles(JSContext *cx);
 
     /* Mark all types as needing destruction once inference has 'finished'. */
     void setPendingNukeTypes(JSContext *cx);
 
     /* Mark a script as needing recompilation once inference has finished. */
     void addPendingRecompile(JSContext *cx, JSScript *script);
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -138,60 +138,49 @@ TypeIdString(jsid id)
  *
  * Pins inference results so that intermediate type information, TypeObjects
  * and JSScripts won't be collected during GC. Does additional sanity checking
  * that inference is not reentrant and that recompilations occur properly.
  */
 struct AutoEnterTypeInference
 {
     JSContext *cx;
-#ifdef DEBUG
-    unsigned depth;
-#endif
+    bool oldActiveAnalysis;
+    bool oldActiveInference;
 
     AutoEnterTypeInference(JSContext *cx, bool compiling = false)
-        : cx(cx)
+        : cx(cx), oldActiveAnalysis(cx->compartment->activeAnalysis),
+          oldActiveInference(cx->compartment->activeInference)
     {
-#ifdef DEBUG
-        depth = cx->compartment->types.inferenceDepth;
-#endif
         JS_ASSERT_IF(!compiling, cx->compartment->types.inferenceEnabled);
-        cx->compartment->types.inferenceDepth++;
+        cx->compartment->activeAnalysis = true;
+        cx->compartment->activeInference = true;
     }
 
     ~AutoEnterTypeInference()
     {
+        cx->compartment->activeAnalysis = oldActiveAnalysis;
+        cx->compartment->activeInference = oldActiveInference;
+
         /*
-         * This should have been reset by checkPendingRecompiles.
-         * :FIXME: be more tolerant and clean up anyways, the caller may be
-         * propagating an OOM or other error.
+         * If there are no more type inference activations on the stack,
+         * process any triggered recompilations. Note that we should not be
+         * invoking any scripted code while type inference is running.
+         * :TODO: assert this.
          */
-        JS_ASSERT(cx->compartment->types.inferenceDepth == depth);
+        if (!cx->compartment->activeInference) {
+            TypeCompartment *types = &cx->compartment->types;
+            if (types->pendingNukeTypes)
+                types->nukeTypes(cx);
+            else if (types->pendingRecompiles)
+                types->processPendingRecompiles(cx);
+        }
     }
 };
 
-bool
-TypeCompartment::checkPendingRecompiles(JSContext *cx)
-{
-    JS_ASSERT(inferenceDepth);
-    if (--inferenceDepth != 0) {
-        /*
-         * There is still a type inference activation on the stack, wait for it to
-         * finish before handling any recompilations. Note that we should not be
-         * invoking any scripted code while the inference is running :TODO: assert this.
-         */
-        return true;
-    }
-    if (pendingNukeTypes)
-        return nukeTypes(cx);
-    else if (pendingRecompiles && !processPendingRecompiles(cx))
-        return false;
-    return true;
-}
-
 /*
  * Structure marking the currently compiled script, for constraints which can
  * trigger recompilation.
  */
 struct AutoEnterCompilation
 {
     JSContext *cx;
     JSScript *script;
@@ -340,24 +329,24 @@ JSContext::addTypePropertyId(js::types::
 
     /* Convert string index properties into the common index property. */
     id = js::types::MakeTypeId(this, id);
 
     js::types::AutoEnterTypeInference enter(this);
 
     js::types::TypeSet *types = obj->getProperty(this, id, true);
     if (!types || types->hasType(type))
-        return compartment->types.checkPendingRecompiles(this);
+        return true;
 
     js::types::InferSpew(js::types::ISpewOps, "externalType: property %s %s: %s",
                          obj->name(), js::types::TypeIdString(id),
                          js::types::TypeString(type));
     types->addType(this, type);
 
-    return compartment->types.checkPendingRecompiles(this);
+    return true;
 }
 
 inline bool
 JSContext::addTypePropertyId(js::types::TypeObject *obj, jsid id, const js::Value &value)
 {
     if (typeInferenceEnabled() && !obj->unknownProperties())
         return addTypePropertyId(obj, id, js::types::GetValueType(this, value));
     return true;
@@ -369,23 +358,23 @@ JSContext::addTypePropertyId(js::types::
     if (obj->unknownProperties())
         return true;
     id = js::types::MakeTypeId(this, id);
 
     js::types::AutoEnterTypeInference enter(this);
 
     js::types::TypeSet *types = obj->getProperty(this, id, true);
     if (!types)
-        return compartment->types.checkPendingRecompiles(this);
+        return true;
 
     js::types::InferSpew(js::types::ISpewOps, "externalType: property %s %s",
                          obj->name(), js::types::TypeIdString(id));
     types->addTypeSet(this, set);
 
-    return compartment->types.checkPendingRecompiles(this);
+    return true;
 }
 
 inline js::types::TypeObject *
 JSContext::getTypeEmpty()
 {
     return &compartment->types.typeEmpty;
 }
 
@@ -403,28 +392,28 @@ JSContext::aliasTypeProperties(js::types
     js::types::TypeSet *firstTypes = obj->getProperty(this, first, true);
     js::types::TypeSet *secondTypes = obj->getProperty(this, second, true);
     if (!firstTypes || !secondTypes)
         return false;
 
     firstTypes->addBaseSubset(this, obj, secondTypes);
     secondTypes->addBaseSubset(this, obj, firstTypes);
 
-    return compartment->types.checkPendingRecompiles(this);
+    return true;
 }
 
 inline bool
 JSContext::addTypeFlags(js::types::TypeObject *obj, js::types::TypeObjectFlags flags)
 {
     if (!typeInferenceEnabled() || obj->hasFlags(flags))
         return true;
 
     js::types::AutoEnterTypeInference enter(this);
     obj->setFlags(this, flags);
-    return compartment->types.checkPendingRecompiles(this);
+    return true;
 }
 
 inline bool
 JSContext::markTypeArrayNotPacked(js::types::TypeObject *obj, bool notDense)
 {
     return addTypeFlags(obj, js::types::OBJECT_FLAG_NON_PACKED_ARRAY |
                         (notDense ? js::types::OBJECT_FLAG_NON_DENSE_ARRAY : 0));
 }
@@ -451,17 +440,17 @@ JSContext::markTypePropertyConfigured(js
         return true;
     id = js::types::MakeTypeId(this, id);
 
     js::types::AutoEnterTypeInference enter(this);
     js::types::TypeSet *types = obj->getProperty(this, id, true);
     if (types)
         types->setOwnProperty(this, true);
 
-    return compartment->types.checkPendingRecompiles(this);
+    return true;
 }
 
 inline bool
 JSContext::markGlobalReallocation(JSObject *obj)
 {
     JS_ASSERT(obj->isGlobal());
 
     if (!typeInferenceEnabled())
@@ -475,28 +464,28 @@ JSContext::markGlobalReallocation(JSObje
     if (types) {
         js::types::TypeConstraint *constraint = types->constraintList;
         while (constraint) {
             constraint->slotsReallocation(this);
             constraint = constraint->next;
         }
     }
 
-    return compartment->types.checkPendingRecompiles(this);
+    return true;
 }
 
 inline bool
 JSContext::markTypeObjectUnknownProperties(js::types::TypeObject *obj)
 {
     if (!typeInferenceEnabled() || obj->unknownProperties())
         return true;
 
     js::types::AutoEnterTypeInference enter(this);
     obj->markUnknown(this);
-    return compartment->types.checkPendingRecompiles(this);
+    return true;
 }
 
 inline bool
 JSContext::typeMonitorAssign(JSObject *obj, jsid id, const js::Value &rval)
 {
     if (typeInferenceEnabled())
         return compartment->types.dynamicAssign(this, obj, id, rval);
     return true;
@@ -538,49 +527,58 @@ JSScript::ensureVarTypes(JSContext *cx)
         return true;
     return makeVarTypes(cx);
 }
 
 inline js::types::TypeSet *
 JSScript::returnTypes()
 {
     JS_ASSERT(varTypes);
-    return &varTypes[0];
+    return &varTypes[js::analyze::CalleeSlot()];
 }
 
 inline js::types::TypeSet *
 JSScript::thisTypes()
 {
     JS_ASSERT(varTypes);
-    return &varTypes[1];
+    return &varTypes[js::analyze::ThisSlot()];
 }
 
+/*
+ * 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.
+ */
+
 inline js::types::TypeSet *
 JSScript::argTypes(unsigned i)
 {
     JS_ASSERT(varTypes && fun && i < fun->nargs);
-    return &varTypes[2 + i];
+    return &varTypes[js::analyze::ArgSlot(i)];
 }
 
 inline js::types::TypeSet *
 JSScript::localTypes(unsigned i)
 {
     JS_ASSERT(varTypes && i < nfixed);
-    if (fun)
-        i += fun->nargs;
-    return &varTypes[2 + i];
+    return &varTypes[js::analyze::LocalSlot(this, i)];
 }
 
 inline js::types::TypeSet *
 JSScript::upvarTypes(unsigned i)
 {
     JS_ASSERT(varTypes && i < bindings.countUpvars());
-    if (fun)
-        i += fun->nargs;
-    return &varTypes[2 + nfixed + i];
+    return &varTypes[js::analyze::LocalSlot(this, nfixed) + i];
+}
+
+inline js::types::TypeSet *
+JSScript::slotTypes(unsigned slot)
+{
+    JS_ASSERT(slot < js::analyze::TotalSlots(this));
+    return &varTypes[slot];
 }
 
 inline JSObject *
 JSScript::getGlobal()
 {
     JS_ASSERT(compileAndGo && global);
     return global;
 }
@@ -671,29 +669,34 @@ JSScript::typeMonitorUnknown(JSContext *
 inline bool
 JSScript::typeSetThis(JSContext *cx, js::types::jstype type)
 {
     JS_ASSERT(cx->typeInferenceEnabled());
     if (!ensureVarTypes(cx))
         return false;
 
     /* Analyze the script regardless if -a was used. */
-    bool analyze = !types && cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) && !isUncachedEval;
+    bool analyze = !(analysis_ && analysis_->ranInference()) &&
+        cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) && !isUncachedEval;
 
     if (!thisTypes()->hasType(type) || analyze) {
         js::types::AutoEnterTypeInference enter(cx);
 
         js::types::InferSpew(js::types::ISpewOps, "externalType: setThis #%u: %s",
                              id(), js::types::TypeString(type));
         thisTypes()->addType(cx, type);
 
-        if (analyze && !types)
-            js::types::AnalyzeScriptTypes(cx, this);
+        if (analyze && !(analysis_ && analysis_->ranInference())) {
+            js::analyze::ScriptAnalysis *analysis = this->analysis(cx);
+            if (!analysis)
+                return false;
+            analysis->analyzeTypes(cx);
+        }
 
-        return cx->compartment->types.checkPendingRecompiles(cx);
+        return true;
     }
 
     return true;
 }
 
 inline bool
 JSScript::typeSetThis(JSContext *cx, const js::Value &value)
 {
@@ -707,17 +710,17 @@ JSScript::typeSetThis(JSContext *cx, js:
 {
     if (!ensureVarTypes(cx))
         return false;
     js::types::AutoEnterTypeInference enter(cx);
 
     js::types::InferSpew(js::types::ISpewOps, "externalType: setThis #%u", id());
     thisTypes()->addTypeSet(cx, set);
 
-    return cx->compartment->types.checkPendingRecompiles(cx);
+    return true;
 }
 
 inline bool
 JSScript::typeSetNewCalled(JSContext *cx)
 {
     if (!cx->typeInferenceEnabled() || calledWithNew)
         return true;
     calledWithNew = true;
@@ -727,19 +730,20 @@ JSScript::typeSetNewCalled(JSContext *cx
      * happens during the script's prologue, so we don't try to pick it up from
      * dynamic calls. Instead, generate constraints modeling the construction
      * of 'this' when the script is analyzed or reanalyzed after an invoke with 'new',
      * and if 'new' is first invoked after the script has already been analyzed.
      */
     if (analyzed) {
         /* Regenerate types for the function. */
         js::types::AutoEnterTypeInference enter(cx);
-        js::types::AnalyzeScriptNew(cx, this);
-        if (!cx->compartment->types.checkPendingRecompiles(cx))
+        js::analyze::ScriptAnalysis *analysis = this->analysis(cx);
+        if (!analysis)
             return false;
+        analysis->analyzeTypesNew(cx);
     }
     return true;
 }
 
 inline bool
 JSScript::typeSetLocal(JSContext *cx, unsigned local, js::types::jstype type)
 {
     if (!cx->typeInferenceEnabled())
@@ -748,17 +752,17 @@ JSScript::typeSetLocal(JSContext *cx, un
         return false;
     if (!localTypes(local)->hasType(type)) {
         js::types::AutoEnterTypeInference enter(cx);
 
         js::types::InferSpew(js::types::ISpewOps, "externalType: setLocal #%u %u: %s",
                              id(), local, js::types::TypeString(type));
         localTypes(local)->addType(cx, type);
 
-        return compartment->types.checkPendingRecompiles(cx);
+        return true;
     }
     return true;
 }
 
 inline bool
 JSScript::typeSetLocal(JSContext *cx, unsigned local, const js::Value &value)
 {
     if (cx->typeInferenceEnabled()) {
@@ -773,34 +777,34 @@ JSScript::typeSetLocal(JSContext *cx, un
 {
     if (!ensureVarTypes(cx))
         return false;
     js::types::AutoEnterTypeInference enter(cx);
 
     js::types::InferSpew(js::types::ISpewOps, "externalType: setLocal #%u %u", id(), local);
     localTypes(local)->addTypeSet(cx, set);
 
-    return compartment->types.checkPendingRecompiles(cx);
+    return true;
 }
 
 inline bool
 JSScript::typeSetArgument(JSContext *cx, unsigned arg, js::types::jstype type)
 {
     if (!cx->typeInferenceEnabled())
         return true;
     if (!ensureVarTypes(cx))
         return false;
     if (!argTypes(arg)->hasType(type)) {
         js::types::AutoEnterTypeInference enter(cx);
 
         js::types::InferSpew(js::types::ISpewOps, "externalType: setArg #%u %u: %s",
                              id(), arg, js::types::TypeString(type));
         argTypes(arg)->addType(cx, type);
 
-        return cx->compartment->types.checkPendingRecompiles(cx);
+        return true;
     }
     return true;
 }
 
 inline bool
 JSScript::typeSetArgument(JSContext *cx, unsigned arg, const js::Value &value)
 {
     if (cx->typeInferenceEnabled()) {
@@ -815,17 +819,17 @@ JSScript::typeSetArgument(JSContext *cx,
 {
     if (!ensureVarTypes(cx))
         return false;
     js::types::AutoEnterTypeInference enter(cx);
 
     js::types::InferSpew(js::types::ISpewOps, "externalType: setArg #%u %u", id(), arg);
     argTypes(arg)->addTypeSet(cx, set);
 
-    return cx->compartment->types.checkPendingRecompiles(cx);
+    return true;
 }
 
 inline bool
 JSScript::typeSetUpvar(JSContext *cx, unsigned upvar, const js::Value &value)
 {
     if (!cx->typeInferenceEnabled())
         return true;
     if (!ensureVarTypes(cx))
@@ -833,17 +837,17 @@ JSScript::typeSetUpvar(JSContext *cx, un
     js::types::jstype type = js::types::GetValueType(cx, value);
     if (!upvarTypes(upvar)->hasType(type)) {
         js::types::AutoEnterTypeInference enter(cx);
 
         js::types::InferSpew(js::types::ISpewOps, "externalType: setUpvar #%u %u: %s",
                              id(), upvar, js::types::TypeString(type));
         upvarTypes(upvar)->addType(cx, type);
 
-        return cx->compartment->types.checkPendingRecompiles(cx);
+        return true;
     }
     return true;
 }
 
 namespace js {
 namespace types {
 
 /////////////////////////////////////////////////////////////////////
@@ -958,17 +962,17 @@ HashSetInsertTry(JSContext *cx, U **&val
     unsigned newCapacity = HashSetCapacity(count);
 
     if (newCapacity == capacity) {
         JS_ASSERT(!converting);
         return &values[insertpos];
     }
 
     U **newValues = pool
-        ? ArenaArray<U*>(cx->compartment->types.pool, newCapacity)
+        ? ArenaArray<U*>(cx->compartment->pool, newCapacity)
         : (U **) js::OffTheBooks::malloc_(newCapacity * sizeof(U*));
     if (!newValues) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return NULL;
     }
     PodZero(newValues, newCapacity);
 
     for (unsigned i = 0; i < capacity; i++) {
@@ -1005,17 +1009,17 @@ HashSetInsert(JSContext *cx, U **&values
     }
 
     if (count == 1) {
         U *oldData = (U*) values;
         if (KEY::getKey(oldData) == key)
             return (U **) &values;
 
         values = pool
-            ? ArenaArray<U*>(cx->compartment->types.pool, SET_ARRAY_SIZE)
+            ? ArenaArray<U*>(cx->compartment->pool, SET_ARRAY_SIZE)
             : (U **) js::OffTheBooks::malloc_(SET_ARRAY_SIZE * sizeof(U*));
         if (!values) {
             values = (U **) oldData;
             cx->compartment->types.setPendingNukeTypes(cx);
             return NULL;
         }
         PodZero(values, SET_ARRAY_SIZE);
         count++;
@@ -1114,17 +1118,17 @@ TypeSet::markUnknown(JSContext *cx)
     objectCount = 0;
     objectSet = NULL;
 }
 
 inline void
 TypeSet::addType(JSContext *cx, jstype type)
 {
     JS_ASSERT(type);
-    JS_ASSERT(cx->compartment->types.inferenceDepth);
+    JS_ASSERT(cx->compartment->activeInference);
 
     if (unknown())
         return;
 
     if (type == TYPE_UNKNOWN) {
         markUnknown(cx);
     } else if (TypeIsPrimitive(type)) {
         TypeFlags flag = 1 << type;
@@ -1203,19 +1207,19 @@ TypeSet::getObject(unsigned i)
         return (TypeObject *) objectSet;
     }
     return objectSet[i];
 }
 
 inline TypeSet *
 TypeSet::make(JSContext *cx, const char *name)
 {
-    JS_ASSERT(cx->compartment->types.inferenceDepth);
+    JS_ASSERT(cx->compartment->activeInference);
 
-    TypeSet *res = ArenaNew<TypeSet>(cx->compartment->types.pool);
+    TypeSet *res = ArenaNew<TypeSet>(cx->compartment->pool);
     if (!res) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return NULL;
     }
 
     InferSpew(ISpewOps, "typeSet: T%p intermediate %s", res, name);
     res->setIntermediate();
 
@@ -1228,17 +1232,17 @@ TypeSet::make(JSContext *cx, const char 
 
 inline
 TypeCallsite::TypeCallsite(JSContext *cx, JSScript *script, const jsbytecode *pc,
                            bool isNew, unsigned argumentCount)
     : script(script), pc(pc), isNew(isNew), argumentCount(argumentCount),
       thisTypes(NULL), thisType(0), returnTypes(NULL)
 {
     /* Caller must check for failure. */
-    argumentTypes = ArenaArray<TypeSet*>(cx->compartment->types.pool, argumentCount);
+    argumentTypes = ArenaArray<TypeSet*>(cx->compartment->pool, argumentCount);
 }
 
 inline bool
 TypeCallsite::forceThisTypes(JSContext *cx)
 {
     if (thisTypes)
         return true;
     thisTypes = TypeSet::make(cx, "site_this");
@@ -1264,17 +1268,17 @@ TypeCallsite::compileAndGo()
 
 /////////////////////////////////////////////////////////////////////
 // TypeObject
 /////////////////////////////////////////////////////////////////////
 
 inline TypeSet *
 TypeObject::getProperty(JSContext *cx, jsid id, bool assign)
 {
-    JS_ASSERT(cx->compartment->types.inferenceDepth);
+    JS_ASSERT(cx->compartment->activeInference);
     JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
     JS_ASSERT_IF(JSID_IS_STRING(id), JSID_TO_STRING(id) != NULL);
     JS_ASSERT(!unknownProperties());
 
     Property **pprop = HashSetInsert<jsid,Property,Property>
                            (cx, propertySet, propertyCount, id, false);
     if (!pprop || (!*pprop && !addProperty(cx, id, pprop)))
         return NULL;
@@ -1299,56 +1303,19 @@ TypeObject::getProperty(unsigned i)
     if (propertyCount == 1) {
         JS_ASSERT(i == 0);
         return (Property *) propertySet;
     }
     return propertySet[i];
 }
 
 /////////////////////////////////////////////////////////////////////
-// TypeScript
+// TypeObject
 /////////////////////////////////////////////////////////////////////
 
-inline bool
-TypeScript::monitored(uint32 offset)
-{
-    JS_ASSERT(offset < script->length);
-    return 0x1 & (size_t) pushedArray[offset];
-}
-
-inline void
-TypeScript::setMonitored(uint32 offset)
-{
-    JS_ASSERT(script->compartment->types.inferenceDepth);
-    JS_ASSERT(offset < script->length);
-    pushedArray[offset] = (TypeSet *) (0x1 | (size_t) pushedArray[offset]);
-}
-
-inline TypeSet *
-TypeScript::pushed(uint32 offset)
-{
-    JS_ASSERT(offset < script->length);
-    return (TypeSet *) (~0x1 & (size_t) pushedArray[offset]);
-}
-
-inline TypeSet *
-TypeScript::pushed(uint32 offset, uint32 index)
-{
-    JS_ASSERT(offset < script->length);
-    JS_ASSERT(index < js::analyze::GetDefCount(script, offset));
-    return pushed(offset) + index;
-}
-
-inline void
-TypeScript::addType(JSContext *cx, uint32 offset, uint32 index, jstype type)
-{
-    TypeSet *types = pushed(offset, index);
-    types->addType(cx, type);
-}
-
 inline const char *
 TypeObject::name()
 {
 #ifdef DEBUG
     return TypeIdString(name_);
 #else
     return NULL;
 #endif
@@ -1415,9 +1382,17 @@ class AutoTypeRooter : private AutoGCRoo
 
   private:
     TypeObject *type;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 } } /* namespace js::types */
 
+inline void
+js::analyze::ScriptAnalysis::addPushedType(JSContext *cx, uint32 offset, uint32 which,
+                                           js::types::jstype type)
+{
+    js::types::TypeSet *pushed = pushedTypes(offset, which);
+    pushed->addType(cx, type);
+}
+
 #endif // jsinferinlines_h___
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1497,17 +1497,17 @@ DestroyScript(JSContext *cx, JSScript *s
         JS_ASSERT(script->owner == cx->thread);
 #endif
     }
 
 #ifdef JS_TRACER
     PurgeScriptFragments(&script->compartment->traceMonitor, script);
 #endif
 
-    JS_ASSERT(!script->types);
+    JS_ASSERT(!script->hasAnalysis());
 
     /* Migrate any type objects associated with this script to the compartment. */
     types::TypeObject *obj = script->typeObjects;
     while (obj) {
         types::TypeObject *next = obj->next;
         obj->next = script->compartment->types.objects;
         script->compartment->types.objects = obj;
         obj = next;
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -387,17 +387,17 @@ namespace JSC {
 enum JITScriptStatus {
     JITScript_None,
     JITScript_Invalid,
     JITScript_Valid
 };
 
 namespace js {
 namespace mjit { struct JITScript; }
-namespace analyze { class Script; }
+namespace analyze { class ScriptAnalysis; }
 }
 #endif
 
 struct JSScript {
     /*
      * Two successively less primitive ways to make a new JSScript.  The first
      * does *not* call a non-null cx->runtime->newScriptHook -- only the second,
      * NewScriptFromCG, calls this optional debugger hook.
@@ -527,48 +527,62 @@ struct JSScript {
     js::types::TypeSet *varTypes;
 
     /* Any type objects associated with this script, including initializer objects. */
     js::types::TypeObject *typeObjects;
 
     /* Any possibly unexpected values pushed by opcodes in this script. */
     js::types::TypeResult *typeResults;
 
-    /* Results of type inference analysis for this script. Destroyed on GC. */
-    js::types::TypeScript *types;
+    /* Bytecode analysis and type inference results for this script. Destroyed on GC. */
+  private:
+    js::analyze::ScriptAnalysis *analysis_;
+    void makeAnalysis(JSContext *cx);
+  public:
+
+    bool hasAnalysis() { return analysis_ != NULL; }
+
+    js::analyze::ScriptAnalysis *analysis(JSContext *cx) {
+        if (!analysis_)
+            makeAnalysis(cx);
+        return analysis_;
+    }
 
     inline JSObject *getGlobal();
     inline js::types::TypeObject *getGlobalType();
 
     /*
      * Make sure there are type sets for variables in this script.
      * After it has been called or executed, the script will have such sets.
      */
     inline bool ensureVarTypes(JSContext *cx);
 
     inline js::types::TypeSet *returnTypes();
     inline js::types::TypeSet *thisTypes();
     inline js::types::TypeSet *argTypes(unsigned i);
     inline js::types::TypeSet *localTypes(unsigned i);
     inline js::types::TypeSet *upvarTypes(unsigned i);
 
+    /* Follows slot layout in jsanalyze.h, can get this/arg/local type sets. */
+    inline js::types::TypeSet *slotTypes(unsigned slot);
+
   private:
     bool makeVarTypes(JSContext *cx);
   public:
 
 #ifdef DEBUG
     /* Check that correct types were inferred for the values pushed by this bytecode. */
     void typeCheckBytecode(JSContext *cx, const jsbytecode *pc, const js::Value *sp);
 #endif
 
     /* Get the default 'new' object for a given standard class, per the script's global. */
     inline js::types::TypeObject *getTypeNewObject(JSContext *cx, JSProtoKey key);
 
     void condenseTypes(JSContext *cx);
-    void sweepTypes(JSContext *cx);
+    void sweepAnalysis(JSContext *cx);
 
     /* Get a type object for an allocation site in this script. */
     inline js::types::TypeObject *
     getTypeInitObject(JSContext *cx, const jsbytecode *pc, bool isArray);
 
     /* Monitor a bytecode pushing an unexpected value. */
     inline bool typeMonitorResult(JSContext *cx, const jsbytecode *pc, js::types::jstype type);
     inline bool typeMonitorResult(JSContext *cx, const jsbytecode *pc, const js::Value &val);
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -128,16 +128,18 @@ mjit::Compiler::Compiler(JSContext *cx, 
 #else
     addTraceHints(false),
 #endif
     inlining(false),
     hasGlobalReallocation(false),
     oomInVector(false),
     applyTricks(NoApplyTricks)
 {
+    JS_ASSERT(!outerScript->isUncachedEval);
+
     /* :FIXME: bug 637856 disabling traceJit if inference is enabled */
     if (cx->typeInferenceEnabled())
         addTraceHints = false;
 
     /*
      * Note: we use callCount_ to count both calls and backedges in scripts
      * after they have been compiled and we are checking to recompile a version
      * with inline calls. :FIXME: should remove compartment->incBackEdgeCount
@@ -193,52 +195,64 @@ mjit::Compiler::pushActiveFrame(JSScript
     if (outer) {
         newa->inlineIndex = uint32(inlineFrames.length());
         inlineFrames.append(newa);
     } else {
         newa->inlineIndex = uint32(-1);
         outer = newa;
     }
 
-    newa->analysis.analyze(cx, script);
-
-    if (newa->analysis.OOM())
+    analyze::ScriptAnalysis *newAnalysis = script->analysis(cx);
+    if (!newAnalysis)
         return Compile_Error;
-    if (newa->analysis.failed()) {
+    if (!newAnalysis->failed() && !newAnalysis->ranBytecode())
+        newAnalysis->analyzeBytecode(cx);
+
+    if (newAnalysis->OOM())
+        return Compile_Error;
+    if (newAnalysis->failed()) {
         JaegerSpew(JSpew_Abort, "couldn't analyze bytecode; probably switchX or OOM\n");
         return Compile_Abort;
     }
 
-    if (cx->typeInferenceEnabled() && !newa->liveness.analyze(cx, &newa->analysis, script)) {
-        js_ReportOutOfMemory(cx);
-        return Compile_Error;
+    if (cx->typeInferenceEnabled()) {
+        if (!newAnalysis->ranSSA())
+            newAnalysis->analyzeSSA(cx);
+        if (!newAnalysis->failed() && !newAnalysis->ranLifetimes())
+            newAnalysis->analyzeLifetimes(cx);
+        if (newAnalysis->failed()) {
+            js_ReportOutOfMemory(cx);
+            return Compile_Error;
+        }
     }
 
 #ifdef JS_METHODJIT_SPEW
     if (cx->typeInferenceEnabled() && IsJaegerSpewChannelActive(JSpew_Regalloc)) {
         unsigned nargs = script->fun ? script->fun->nargs : 0;
         for (unsigned i = 0; i < nargs; i++) {
-            if (!newa->analysis.argEscapes(i)) {
+            uint32 slot = analyze::ArgSlot(i);
+            if (!newAnalysis->slotEscapes(slot)) {
                 JaegerSpew(JSpew_Regalloc, "Argument %u:", i);
-                newa->liveness.dumpSlot(2 + i);
+                newAnalysis->liveness(slot).print();
             }
         }
         for (unsigned i = 0; i < script->nfixed; i++) {
-            if (!newa->analysis.localEscapes(i)) {
+            uint32 slot = analyze::LocalSlot(script, i);
+            if (!newAnalysis->slotEscapes(slot)) {
                 JaegerSpew(JSpew_Regalloc, "Local %u:", i);
-                newa->liveness.dumpSlot(2 + nargs + i);
+                newAnalysis->liveness(slot).print();
             }
         }
     }
 #endif
 
     if (a)
         frame.getUnsyncedEntries(&newa->depth, &newa->unsyncedEntries);
 
-    if (!frame.pushActiveFrame(script, argc, &newa->analysis, &newa->liveness)) {
+    if (!frame.pushActiveFrame(script, argc)) {
         js_ReportOutOfMemory(cx);
         return Compile_Error;
     }
 
     newa->jumpMap = (Label *)cx->malloc_(sizeof(Label) * script->length);
     if (!newa->jumpMap) {
         js_ReportOutOfMemory(cx);
         return Compile_Error;
@@ -250,31 +264,33 @@ mjit::Compiler::pushActiveFrame(JSScript
 
     if (cx->typeInferenceEnabled()) {
         CompileStatus status = prepareInferenceTypes(script, newa);
         if (status != Compile_Okay)
             return status;
     }
 
     this->script = script;
+    this->analysis = newAnalysis;
     this->PC = script->code;
     this->a = newa;
 
     variadicRejoin = false;
 
     return Compile_Okay;
 }
 
 void
 mjit::Compiler::popActiveFrame()
 {
     JS_ASSERT(a->parent);
     this->PC = a->parentPC;
     this->a = a->parent;
     this->script = a->script;
+    this->analysis = this->script->analysis(cx);
 
     frame.popActiveFrame();
 }
 
 #define CHECK_STATUS(expr)                                           \
     JS_BEGIN_MACRO                                                   \
         CompileStatus status_ = (expr);                              \
         if (status_ != Compile_Okay) {                               \
@@ -299,17 +315,17 @@ mjit::Compiler::performCompilation(JITSc
     Profiler prof;
     prof.start();
 #endif
 
 #ifdef JS_METHODJIT
     outerScript->debugMode = debugMode();
 #endif
 
-    JS_ASSERT(cx->compartment->types.inferenceDepth);
+    JS_ASSERT(cx->compartment->activeInference);
 
     {
         types::AutoEnterCompilation enter(cx, outerScript);
 
         CHECK_STATUS(pushActiveFrame(outerScript, 0));
         CHECK_STATUS(generatePrologue());
         CHECK_STATUS(generateMethod());
         CHECK_STATUS(generateEpilogue());
@@ -367,111 +383,114 @@ mjit::Compiler::performCompilation(JITSc
 
     return Compile_Okay;
 }
 
 #undef CHECK_STATUS
 
 mjit::Compiler::ActiveFrame::ActiveFrame(JSContext *cx)
     : parent(NULL), parentPC(NULL), script(NULL), inlineIndex(uint32(-1)),
-      jumpMap(NULL), hasThisType(false), argumentTypes(NULL), localTypes(NULL),
-      unsyncedEntries(cx),
+      jumpMap(NULL), unsyncedEntries(cx),
       needReturnValue(false), syncReturnValue(false),
       returnValueDouble(false), returnSet(false), returnParentRegs(0),
       temporaryParentRegs(0), returnJumps(NULL)
 {}
 
 mjit::Compiler::ActiveFrame::~ActiveFrame()
 {
     js::Foreground::free_(jumpMap);
-    js::Foreground::array_delete(argumentTypes);
-    js::Foreground::array_delete(localTypes);
 }
 
 mjit::Compiler::~Compiler()
 {
     if (outer)
         cx->delete_(outer);
     for (unsigned i = 0; i < inlineFrames.length(); i++)
         cx->delete_(inlineFrames[i]);
 
     cx->free_(savedTraps);
 }
 
 CompileStatus
 mjit::Compiler::prepareInferenceTypes(JSScript *script, ActiveFrame *a)
 {
     /* Analyze the script if we have not already done so. */
-    if (!script->types) {
-        /* Uncached eval scripts are not analyzed or compiled. */
-        if (script->isUncachedEval)
-            return Compile_Abort;
-        types::AnalyzeScriptTypes(cx, script);
-        if (!script->types)
-            return Compile_Error;
-    }
-
-    /* Get the known types of arguments and locals. */
-
-    uint32 nargs = script->fun ? script->fun->nargs : 0;
-    if (nargs) {
-        a->argumentTypes = cx->array_new<JSValueType>(nargs);
-        if (!a->argumentTypes)
+    analyze::ScriptAnalysis *analysis = script->analysis(cx);
+    if (!analysis->ranInference()) {
+        analysis->analyzeTypes(cx);
+        if (!analysis->ranInference())
             return Compile_Error;
-        for (unsigned i = 0; i < nargs; i++) {
-            JSValueType type = JSVAL_TYPE_UNKNOWN;
-            if (!a->analysis.argEscapes(i))
-                type = script->argTypes(i)->getKnownTypeTag(cx);
-            a->argumentTypes[i] = type;
-        }
-    }
-
-    if (script->nfixed) {
-        a->localTypes = cx->array_new<JSValueType>(script->nfixed);
-        if (!a->localTypes)
-            return Compile_Error;
-        for (unsigned i = 0; i < script->nfixed; i++) {
-            JSValueType type = JSVAL_TYPE_UNKNOWN;
-            if (!a->analysis.localHasUseBeforeDef(i))
-                type = script->localTypes(i)->getKnownTypeTag(cx);
-            a->localTypes[i] = type;
-        }
+    }
+
+    /*
+     * During our walk of the script, we need to preserve the invariant that at
+     * join points the in memory type tag is always in sync with the known type
+     * tag of the variable's SSA value at that join point. In particular, SSA
+     * values inferred as (int|double) must in fact be doubles, stored either
+     * in floating point registers or in memory. (There is an exception for
+     * locals with a dead value at the current point, whose type may or may not
+     * be synced).
+     *
+     * To ensure this, we need to know the SSA values for each variable at each
+     * join point, which the SSA analysis does not store explicitly. These can
+     * be recovered, though. During the forward walk, the SSA value of a var
+     * (and its associated type set) change only when we see an explicit assign
+     * to the var or get to a join point with a phi node for that var. So we
+     * can duplicate the effects of that walk here by watching for writes to
+     * vars (updateVarTypes) and new phi nodes at join points.
+     *
+     * When we get to a branch and need to know a variable's value at the
+     * branch target, we know it will either be a phi node at the target or
+     * the variable's current value, as no phi node is created at the target
+     * only if a variable has the same value on all incoming edges.
+     */
+
+    a->varTypes = (VarType *)
+        cx->calloc_(analyze::TotalSlots(script) * sizeof(VarType));
+    if (!a->varTypes)
+        return Compile_Error;
+
+    for (uint32 slot = analyze::ArgSlot(0); slot < analyze::TotalSlots(script); slot++) {
+        VarType &vt = a->varTypes[slot];
+        vt.types = script->slotTypes(slot);
+        vt.type = vt.types->getKnownTypeTag(cx);
     }
 
     return Compile_Okay;
 }
 
 CompileStatus JS_NEVER_INLINE
 mjit::TryCompile(JSContext *cx, JSStackFrame *fp)
 {
     JS_ASSERT(cx->fp() == fp);
 
 #if JS_HAS_SHARP_VARS
     if (fp->script()->hasSharps)
         return Compile_Abort;
 #endif
 
+    // Uncached eval scripts are not analyzed or compiled.
+    if (fp->script()->isUncachedEval)
+        return Compile_Abort;
+
     // Ensure that constructors have at least one slot.
     if (fp->isConstructing() && !fp->script()->nslots)
         fp->script()->nslots++;
 
     types::AutoEnterTypeInference enter(cx, true);
 
     // If there were recoverable compilation failures in the function from
     // static overflow or bad inline callees, try recompiling a few times
     // before giving up.
     CompileStatus status = Compile_Retry;
     for (unsigned i = 0; status == Compile_Retry && i < 5; i++) {
         Compiler cc(cx, fp->script(), fp->isConstructing(), NULL);
         status = cc.compile();
     }
 
-    if (!cx->compartment->types.checkPendingRecompiles(cx))
-        return Compile_Error;
-
     return status;
 }
 
 bool
 mjit::Compiler::loadOldTraps(const Vector<CallSite> &sites)
 {
     savedTraps = (bool *)cx->calloc_(sizeof(bool) * outerScript->length);
     if (!savedTraps)
@@ -571,31 +590,31 @@ mjit::Compiler::generatePrologue()
         }
 
         /*
          * Set locals to undefined, as in initCallFrameLatePrologue.
          * Skip locals which aren't closed and are known to be defined before used,
          * :FIXME: bug 604541: write undefined if we might be using the tracer, so it works.
          */
         for (uint32 i = 0; i < script->nfixed; i++) {
-            if (a->analysis.localHasUseBeforeDef(i) || addTraceHints) {
+            if (analysis->localHasUseBeforeDef(i) || addTraceHints) {
                 Address local(JSFrameReg, sizeof(JSStackFrame) + i * sizeof(Value));
                 masm.storeValue(UndefinedValue(), local);
             }
         }
 
         /* Create the call object. */
         if (script->fun->isHeavyweight()) {
             prepareStubCall(Uses(0));
             INLINE_STUBCALL_NO_REJOIN(stubs::CreateFunCallObject);
         }
 
         j.linkTo(masm.label(), &masm);
 
-        if (a->analysis.usesScopeChain() && !script->fun->isHeavyweight()) {
+        if (analysis->usesScopeChain() && !script->fun->isHeavyweight()) {
             /*
              * Load the scope chain into the frame if necessary.  The scope chain
              * is always set for global and eval frames, and will have been set by
              * CreateFunCallObject for heavyweight function frames.
              */
             RegisterID t0 = Registers::ReturnReg;
             Jump hasScope = masm.branchTest32(Assembler::NonZero,
                                               FrameFlagsAddress(), Imm32(JSFRAME_HAS_SCOPECHAIN));
@@ -609,93 +628,30 @@ mjit::Compiler::generatePrologue()
     if (isConstructing)
         constructThis();
 
     if (debugMode() || Probes::callTrackingActive(cx)) {
         REJOIN_SITE(stubs::ScriptDebugPrologue);
         INLINE_STUBCALL(stubs::ScriptDebugPrologue);
     }
 
-    /*
-     * Set initial types of locals with known type. These will stay synced
-     * through the rest of the script, allowing us to avoid syncing the types
-     * of locals after writing their payloads. Notes:
-     *
-     * - We don't call generatePrologue and perform this syncing when inlining
-     *   frames; such locals are not assumed to be synced after being assigned.
-     *
-     * - If we are recompiling, the earlier compilation might not have known
-     *   the type of the local (its type set was empty, say), in which case
-     *   it wouldn't have stored that type tag. We need to walk the frames and
-     *   fixup the type tags accordingly.
-     */
-    for (uint32 i = 0; i < script->nfixed; i++) {
-        JSValueType type = knownLocalType(i);
-        if (type != JSVAL_TYPE_UNKNOWN) {
-            JS_ASSERT(!a->analysis.localHasUseBeforeDef(i));
-            /* Doubles will be written entirely when syncing. */
-            if (type != JSVAL_TYPE_DOUBLE) {
-                Address local(JSFrameReg, sizeof(JSStackFrame) + i * sizeof(Value));
-                masm.storeTypeTag(ImmType(type), local);
-                for (unsigned j = 0; patchFrames && j < patchFrames->length(); j++) {
-                    JSStackFrame *patchfp = (*patchFrames)[j].fp;
-                    patchfp->varSlot(i).boxNonDoubleFrom(type, (uint64*) &patchfp->varSlot(i));
-                }
-            }
-            frame.learnType(frame.getLocal(i), type, false);
-        }
-    }
-
-    /*
-     * Learn types of arguments with known type, and make sure double arguments
-     * are actually doubles and not ints.
-     */
-    for (uint32 i = 0; script->fun && i < script->fun->nargs; i++) {
-        JSValueType type = knownArgumentType(i);
-        if (type != JSVAL_TYPE_UNKNOWN) {
-            if (type == JSVAL_TYPE_DOUBLE) {
-                if (!a->analysis.argEscapes(i))
-                    frame.ensureDouble(frame.getArg(i));
-            } else {
-                frame.learnType(frame.getArg(i), type, false);
-            }
+    if (cx->typeInferenceEnabled()) {
+        /* Convert integer arguments which were inferred as (int|double) to doubles. */
+        for (uint32 i = 0; script->fun && i < script->fun->nargs; i++) {
+            uint32 slot = analyze::ArgSlot(i);
+            if (a->varTypes[slot].type == JSVAL_TYPE_DOUBLE && analysis->trackSlot(slot))
+                frame.ensureDouble(frame.getArg(i));
         }
     }
 
     recompileCheckHelper();
 
     return Compile_Okay;
 }
 
-void
-mjit::Compiler::generateInlinePrologue()
-{
-    /*
-     * The types of locals in inlined frames are left unsynced even if known.
-     * (Note that we mark as uninlineable all scripts containing locals with
-     * uses before defs). We will treat these locals as synced while compiling
-     * the script, and need to actually do the syncing if we ever expand the
-     * inline frame (which happens on recompilation, and recompilation is the
-     * only way we will ever need to observe the known type of the local).
-     */
-    for (uint32 i = 0; i < script->nfixed; i++) {
-        JS_ASSERT(!a->analysis.localHasUseBeforeDef(i));
-        JSValueType type = knownLocalType(i);
-        if (type != JSVAL_TYPE_UNKNOWN && type != JSVAL_TYPE_DOUBLE) {
-            FrameEntry *fe = frame.getLocal(i);
-            UnsyncedEntry entry;
-            PodZero(&entry);
-            entry.offset = frame.frameOffset(fe);
-            entry.knownType = true;
-            entry.u.type = type;
-            a->unsyncedEntries.append(entry);
-        }
-    }
-}
-
 CompileStatus
 mjit::Compiler::generateEpilogue()
 {
     return Compile_Okay;
 }
 
 CompileStatus
 mjit::Compiler::finishThisUp(JITScript **jitp)
@@ -738,20 +694,20 @@ mjit::Compiler::finishThisUp(JITScript *
     masm.executableCopy(result);
     stubcc.masm.executableCopy(result + masm.size());
     
     JSC::LinkBuffer fullCode(result, totalSize);
     JSC::LinkBuffer stubCode(result + masm.size(), stubcc.size());
 
     size_t nNmapLive = loopEntries.length();
     for (size_t i = 0; i < script->length; i++) {
-        analyze::Bytecode *opinfo = a->analysis.maybeCode(i);
+        analyze::Bytecode *opinfo = analysis->maybeCode(i);
         if (opinfo && opinfo->safePoint) {
             /* loopEntries cover any safe points which are at loop heads. */
-            if (!cx->typeInferenceEnabled() || !a->liveness.getCode(i).loop)
+            if (!cx->typeInferenceEnabled() || !opinfo->loopHead)
                 nNmapLive++;
         }
     }
 
     size_t nUnsyncedEntries = 0;
     for (size_t i = 0; i < inlineFrames.length(); i++)
         nUnsyncedEntries += inlineFrames[i]->unsyncedEntries.length();
 
@@ -807,17 +763,17 @@ mjit::Compiler::finishThisUp(JITScript *
 
     /* Build the pc -> ncode mapping. */
     NativeMapEntry *jitNmap = (NativeMapEntry *)cursor;
     jit->nNmapPairs = nNmapLive;
     cursor += sizeof(NativeMapEntry) * jit->nNmapPairs;
     size_t ix = 0;
     if (jit->nNmapPairs > 0) {
         for (size_t i = 0; i < script->length; i++) {
-            analyze::Bytecode *opinfo = a->analysis.maybeCode(i);
+            analyze::Bytecode *opinfo = analysis->maybeCode(i);
             if (opinfo && opinfo->safePoint) {
                 Label L = jumpMap[i];
                 JS_ASSERT(L.isValid());
                 jitNmap[ix].bcOff = i;
                 jitNmap[ix].ncode = (uint8 *)(result + masm.distanceOf(L));
                 ix++;
             }
         }
@@ -1336,17 +1292,17 @@ mjit::Compiler::generateMethod()
                 return Compile_Error;
             op = JSOp(*PC);
             trap |= stubs::JSTRAP_TRAP;
         }
         if (script->singleStepMode && scanner.firstOpInLine(PC - script->code))
             trap |= stubs::JSTRAP_SINGLESTEP;
         variadicRejoin = false;
 
-        analyze::Bytecode *opinfo = a->analysis.maybeCode(PC);
+        analyze::Bytecode *opinfo = analysis->maybeCode(PC);
 
         if (!opinfo) {
             if (op == JSOP_STOP)
                 break;
             if (js_CodeSpec[op].length != -1)
                 PC += js_CodeSpec[op].length;
             else
                 PC += js_GetVariableBytecodeLength(PC);
@@ -1355,39 +1311,39 @@ mjit::Compiler::generateMethod()
 
         if (loop)
             loop->PC = PC;
 
         frame.setPC(PC);
         frame.setInTryBlock(opinfo->inTryBlock);
         if (opinfo->jumpTarget || trap) {
             if (fallthrough) {
-                fixDoubleTypes();
+                fixDoubleTypes(PC);
 
                 /*
                  * Watch for fallthrough to the head of a 'do while' loop.
                  * We don't know what register state we will be using at the head
                  * of the loop so sync, branch, and fix it up after the loop
                  * has been processed.
                  */
-                if (cx->typeInferenceEnabled() && a->liveness.getCode(PC).loop) {
+                if (cx->typeInferenceEnabled() && analysis->getCode(PC).loopHead) {
                     frame.syncAndForgetEverything();
                     Jump j = masm.jump();
                     if (!startLoop(PC, j, PC))
                         return Compile_Error;
                 } else {
                     if (!frame.syncForBranch(PC, Uses(0)))
                         return Compile_Error;
                     JS_ASSERT(frame.consistentRegisters(PC));
                 }
             }
 
             if (!frame.discardForJoin(PC, opinfo->stackDepth))
                 return Compile_Error;
-            restoreAnalysisTypes(opinfo->stackDepth);
+            restoreAnalysisTypes();
             fallthrough = true;
 
             if (!cx->typeInferenceEnabled()) {
                 /* All join points have synced state if we aren't doing cross-branch regalloc. */
                 opinfo->safePoint = true;
             }
         }
 
@@ -1477,43 +1433,29 @@ mjit::Compiler::generateMethod()
           BEGIN_CASE(JSOP_RETURN)
             emitReturn(frame.peek(-1));
             fallthrough = false;
           END_CASE(JSOP_RETURN)
 
           BEGIN_CASE(JSOP_GOTO)
           BEGIN_CASE(JSOP_DEFAULT)
           {
-            jsbytecode *target = PC + GET_JUMP_OFFSET(PC);
-            fixDoubleTypes();
-
-            /*
-             * Watch out for backward jumps linking 'continue' statements
-             * together. These are jumping to another GOTO at the head of the
-             * loop, which should be short circuited so we don't mistake this
-             * for an actual loop back edge. :XXX: could there be a trap at
-             * the target?
-             */
-            if (target < PC) {
-                if (JSOp(*target) == JSOP_GOTO) {
-                    target = target + GET_JUMP_OFFSET(target);
-                    JS_ASSERT(target >= PC);
-                } else if (JSOp(*target) == JSOP_GOTOX) {
-                    return Compile_Abort;
-                }
-            }
+            unsigned targetOffset = analyze::FollowBranch(script, PC - script->code);
+            jsbytecode *target = script->code + targetOffset;
+
+            fixDoubleTypes(target);
 
             /*
              * Watch for gotos which are entering a 'for' or 'while' loop.
              * These jump to the loop condition test and are immediately
              * followed by the head of the loop.
              */
             jsbytecode *next = PC + JSOP_GOTO_LENGTH;
-            if (cx->typeInferenceEnabled() && a->analysis.maybeCode(next) &&
-                a->liveness.getCode(next).loop) {
+            if (cx->typeInferenceEnabled() && analysis->maybeCode(next) &&
+                analysis->getCode(next).loopHead) {
                 frame.syncAndForgetEverything();
                 Jump j = masm.jump();
                 if (!startLoop(next, j, target))
                     return Compile_Error;
             } else {
                 if (!frame.syncForBranch(target, Uses(0)))
                     return Compile_Error;
                 Jump j = masm.jump();
@@ -1521,17 +1463,16 @@ mjit::Compiler::generateMethod()
                     return Compile_Error;
             }
             fallthrough = false;
           }
           END_CASE(JSOP_GOTO)
 
           BEGIN_CASE(JSOP_IFEQ)
           BEGIN_CASE(JSOP_IFNE)
-            fixDoubleTypes();
             if (!jsop_ifneq(op, PC + GET_JUMP_OFFSET(PC)))
                 return Compile_Error;
           END_CASE(JSOP_IFNE)
 
           BEGIN_CASE(JSOP_ARGUMENTS)
             /*
              * For calls of the form 'f.apply(x, arguments)' we can avoid
              * creating an args object by having ic::SplatApplyArgs pull
@@ -1543,28 +1484,30 @@ mjit::Compiler::generateMethod()
                 applyTricks = LazyArgsObj;
             else
                 jsop_arguments();
             pushSyncedEntry(0);
           END_CASE(JSOP_ARGUMENTS)
 
           BEGIN_CASE(JSOP_FORARG)
           {
+            updateVarType();
             uint32 arg = GET_SLOTNO(PC);
             iterNext();
-            frame.storeArg(arg, knownArgumentType(arg), true);
+            frame.storeArg(arg, true);
             frame.pop();
           }
           END_CASE(JSOP_FORARG)
 
           BEGIN_CASE(JSOP_FORLOCAL)
           {
+            updateVarType();
             uint32 slot = GET_SLOTNO(PC);
             iterNext();
-            frame.storeLocal(slot, knownLocalType(slot), true, true);
+            frame.storeLocal(slot, true, true);
             frame.pop();
           }
           END_CASE(JSOP_FORLOCAL)
 
           BEGIN_CASE(JSOP_DUP)
             frame.dup();
           END_CASE(JSOP_DUP)
 
@@ -1583,24 +1526,24 @@ mjit::Compiler::generateMethod()
           BEGIN_CASE(JSOP_GT)
           BEGIN_CASE(JSOP_GE)
           BEGIN_CASE(JSOP_EQ)
           BEGIN_CASE(JSOP_NE)
           {
             /* Detect fusions. */
             jsbytecode *next = &PC[JSOP_GE_LENGTH];
             JSOp fused = JSOp(*next);
-            if ((fused != JSOP_IFEQ && fused != JSOP_IFNE) || a->analysis.jumpTarget(next))
+            if ((fused != JSOP_IFEQ && fused != JSOP_IFNE) || analysis->jumpTarget(next))
                 fused = JSOP_NOP;
 
             /* Get jump target, if any. */
             jsbytecode *target = NULL;
             if (fused != JSOP_NOP) {
                 target = next + GET_JUMP_OFFSET(next);
-                fixDoubleTypes();
+                fixDoubleTypes(target);
             }
 
             BoolStub stub = NULL;
             switch (op) {
               case JSOP_LT:
                 stub = stubs::LessThan;
                 break;
               case JSOP_LE:
@@ -1648,17 +1591,17 @@ mjit::Compiler::generateMethod()
 
                     if (!target) {
                         frame.push(Value(BooleanValue(result)));
                     } else {
                         if (fused == JSOP_IFEQ)
                             result = !result;
 
                         if (result) {
-                            fixDoubleTypes();
+                            fixDoubleTypes(target);
                             if (!frame.syncForBranch(target, Uses(0)))
                                 return Compile_Error;
                             if (needRejoins(PC)) {
                                 autoRejoin.oolRejoin(stubcc.masm.label());
                                 stubcc.rejoin(Changes(0));
                             }
                             Jump j = masm.jump();
                             if (!jumpAndTrace(j, target))
@@ -1953,17 +1896,17 @@ mjit::Compiler::generateMethod()
             if (!jsop_getelem(false))
                 return Compile_Error;
           END_CASE(JSOP_GETELEM)
 
           BEGIN_CASE(JSOP_SETELEM)
           BEGIN_CASE(JSOP_SETHOLE)
           {
             jsbytecode *next = &PC[JSOP_SETELEM_LENGTH];
-            bool pop = (JSOp(*next) == JSOP_POP && !a->analysis.jumpTarget(next));
+            bool pop = (JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next));
             if (!jsop_setelem(pop))
                 return Compile_Error;
           }
           END_CASE(JSOP_SETELEM);
 
           BEGIN_CASE(JSOP_CALLNAME)
           {
             REJOIN_SITE_ANY();
@@ -2141,28 +2084,28 @@ mjit::Compiler::generateMethod()
           END_CASE(JSOP_FALSE)
 
           BEGIN_CASE(JSOP_TRUE)
             frame.push(Value(BooleanValue(true)));
           END_CASE(JSOP_TRUE)
 
           BEGIN_CASE(JSOP_OR)
           BEGIN_CASE(JSOP_AND)
-            fixDoubleTypes();
             if (!jsop_andor(op, PC + GET_JUMP_OFFSET(PC)))
                 return Compile_Error;
           END_CASE(JSOP_AND)
 
           BEGIN_CASE(JSOP_TABLESWITCH)
             /*
              * Note: there is no need to syncForBranch for the various targets of
-             * switch statement.  The liveness analysis has already marked these as
-             * allocated with no registers in use.
+             * switch statement. The liveness analysis has already marked these as
+             * allocated with no registers in use. There is also no need to fix
+             * double types, as we don't track types of slots in scripts with
+             * switch statements (could be fixed).
              */
-            fixDoubleTypes();
 #if defined JS_CPU_ARM /* Need to implement jump(BaseIndex) for ARM */
             frame.syncAndKillEverything();
             masm.move(ImmPtr(PC), Registers::ArgReg1);
 
             /* prepareStubCall() is not needed due to syncAndForgetEverything() */
             INLINE_STUBCALL_NO_REJOIN(stubs::TableSwitch);
             frame.pop();
 
@@ -2171,17 +2114,16 @@ mjit::Compiler::generateMethod()
             if (!jsop_tableswitch(PC))
                 return Compile_Error;
 #endif
             PC += js_GetVariableBytecodeLength(PC);
             break;
           END_CASE(JSOP_TABLESWITCH)
 
           BEGIN_CASE(JSOP_LOOKUPSWITCH)
-            fixDoubleTypes();
             frame.syncAndForgetEverything();
             masm.move(ImmPtr(PC), Registers::ArgReg1);
 
             /* prepareStubCall() is not needed due to syncAndForgetEverything() */
             INLINE_STUBCALL_NO_REJOIN(stubs::LookupSwitch);
             frame.pop();
 
             masm.jump(Registers::ReturnReg);
@@ -2193,17 +2135,16 @@ mjit::Compiler::generateMethod()
             // X Y
 
             frame.dupAt(-2);
             // X Y X
 
             jsop_stricteq(JSOP_STRICTEQ);
             // X cond
 
-            fixDoubleTypes();
             if (!jsop_ifneq(JSOP_IFNE, PC + GET_JUMP_OFFSET(PC)))
                 return Compile_Error;
           END_CASE(JSOP_CASE)
 
           BEGIN_CASE(JSOP_STRICTEQ)
             jsop_stricteq(op);
           END_CASE(JSOP_STRICTEQ)
 
@@ -2234,74 +2175,75 @@ mjit::Compiler::generateMethod()
 
           BEGIN_CASE(JSOP_POP)
             frame.pop();
           END_CASE(JSOP_POP)
 
           BEGIN_CASE(JSOP_GETARG)
           {
             uint32 arg = GET_SLOTNO(PC);
-            frame.pushArg(arg, knownArgumentType(arg));
+            frame.pushArg(arg);
           }
           END_CASE(JSOP_GETARG)
 
           BEGIN_CASE(JSOP_CALLARG)
           {
             uint32 arg = GET_SLOTNO(PC);
             if (JSObject *singleton = pushedSingleton(0))
                 frame.push(ObjectValue(*singleton));
             else
-                frame.pushArg(arg, knownArgumentType(arg));
+                frame.pushArg(arg);
             frame.push(UndefinedValue());
           }
           END_CASE(JSOP_GETARG)
 
           BEGIN_CASE(JSOP_BINDGNAME)
             jsop_bindgname();
           END_CASE(JSOP_BINDGNAME)
 
           BEGIN_CASE(JSOP_SETARG)
           {
-            uint32 arg = GET_SLOTNO(PC);
+            updateVarType();
             jsbytecode *next = &PC[JSOP_SETLOCAL_LENGTH];
-            bool pop = JSOp(*next) == JSOP_POP && !a->analysis.jumpTarget(next);
-            frame.storeArg(arg, knownArgumentType(arg), pop);
+            bool pop = JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next);
+            frame.storeArg(GET_SLOTNO(PC), pop);
             if (pop) {
                 frame.pop();
                 PC += JSOP_SETARG_LENGTH + JSOP_POP_LENGTH;
                 break;
             }
           }
           END_CASE(JSOP_SETARG)
 
           BEGIN_CASE(JSOP_GETLOCAL)
           {
             uint32 slot = GET_SLOTNO(PC);
-            frame.pushLocal(slot, knownPushedType(0));
+            frame.pushLocal(slot);
           }
           END_CASE(JSOP_GETLOCAL)
 
           BEGIN_CASE(JSOP_SETLOCAL)
           {
-            uint32 slot = GET_SLOTNO(PC);
+            updateVarType();
             jsbytecode *next = &PC[JSOP_SETLOCAL_LENGTH];
-            bool pop = JSOp(*next) == JSOP_POP && !a->analysis.jumpTarget(next);
-            frame.storeLocal(slot, knownLocalType(slot), pop, true);
+            bool pop = JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next);
+            frame.storeLocal(GET_SLOTNO(PC), pop, true);
             if (pop) {
                 frame.pop();
                 PC += JSOP_SETLOCAL_LENGTH + JSOP_POP_LENGTH;
                 break;
             }
           }
           END_CASE(JSOP_SETLOCAL)
 
           BEGIN_CASE(JSOP_SETLOCALPOP)
           {
+            updateVarType();
             uint32 slot = GET_SLOTNO(PC);
-            frame.storeLocal(slot, knownLocalType(slot), true, true);
+            frame.storeLocal(slot, true, true);
             frame.pop();
           }
           END_CASE(JSOP_SETLOCALPOP)
 
           BEGIN_CASE(JSOP_UINT16)
             frame.push(Value(Int32Value((int32_t) GET_UINT16(PC))));
           END_CASE(JSOP_UINT16)
 
@@ -2340,17 +2282,17 @@ mjit::Compiler::generateMethod()
 
           BEGIN_CASE(JSOP_INCARG)
           BEGIN_CASE(JSOP_DECARG)
           BEGIN_CASE(JSOP_ARGINC)
           BEGIN_CASE(JSOP_ARGDEC)
           {
             jsbytecode *next = &PC[JSOP_ARGINC_LENGTH];
             bool popped = false;
-            if (JSOp(*next) == JSOP_POP && !a->analysis.jumpTarget(next))
+            if (JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next))
                 popped = true;
             if (!jsop_arginc(op, GET_SLOTNO(PC), popped))
                 return Compile_Retry;
             PC += JSOP_ARGINC_LENGTH;
             if (popped)
                 PC += JSOP_POP_LENGTH;
             break;
           }
@@ -2358,17 +2300,17 @@ mjit::Compiler::generateMethod()
 
           BEGIN_CASE(JSOP_INCLOCAL)
           BEGIN_CASE(JSOP_DECLOCAL)
           BEGIN_CASE(JSOP_LOCALINC)
           BEGIN_CASE(JSOP_LOCALDEC)
           {
             jsbytecode *next = &PC[JSOP_LOCALINC_LENGTH];
             bool popped = false;
-            if (JSOp(*next) == JSOP_POP && !a->analysis.jumpTarget(next))
+            if (JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next))
                 popped = true;
             /* These manually advance the PC. */
             if (!jsop_localinc(op, GET_SLOTNO(PC), popped))
                 return Compile_Retry;
             PC += JSOP_LOCALINC_LENGTH;
             if (popped)
                 PC += JSOP_POP_LENGTH;
             break;
@@ -2395,27 +2337,27 @@ mjit::Compiler::generateMethod()
 
           BEGIN_CASE(JSOP_BINDNAME)
             jsop_bindname(script->getAtom(fullAtomIndex(PC)), true);
           END_CASE(JSOP_BINDNAME)
 
           BEGIN_CASE(JSOP_SETPROP)
           {
             jsbytecode *next = &PC[JSOP_SETLOCAL_LENGTH];
-            bool pop = JSOp(*next) == JSOP_POP && !a->analysis.jumpTarget(next);
+            bool pop = JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next);
             if (!jsop_setprop(script->getAtom(fullAtomIndex(PC)), true, pop))
                 return Compile_Error;
           }
           END_CASE(JSOP_SETPROP)
 
           BEGIN_CASE(JSOP_SETNAME)
           BEGIN_CASE(JSOP_SETMETHOD)
           {
             jsbytecode *next = &PC[JSOP_SETLOCAL_LENGTH];
-            bool pop = JSOp(*next) == JSOP_POP && !a->analysis.jumpTarget(next);
+            bool pop = JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next);
             if (!jsop_setprop(script->getAtom(fullAtomIndex(PC)), true, pop))
                 return Compile_Error;
           }
           END_CASE(JSOP_SETNAME)
 
           BEGIN_CASE(JSOP_THROW)
             prepareStubCall(Uses(1));
             INLINE_STUBCALL_NO_REJOIN(stubs::Throw);
@@ -2525,16 +2467,17 @@ mjit::Compiler::generateMethod()
             masm.move(ImmPtr(atom), Registers::ArgReg1);
             INLINE_STUBCALL(stubs::SetConst);
           }
           END_CASE(JSOP_SETCONST)
 
           BEGIN_CASE(JSOP_DEFLOCALFUN_FC)
           {
             REJOIN_SITE_ANY();
+            updateVarType();
             uint32 slot = GET_SLOTNO(PC);
             JSFunction *fun = script->getFunction(fullAtomIndex(&PC[SLOTNO_LEN]));
             prepareStubCall(Uses(frame.frameSlots()));
             masm.move(ImmPtr(fun), Registers::ArgReg1);
             INLINE_STUBCALL(stubs::DefLocalFun_FC);
             frame.takeReg(Registers::ReturnReg);
             frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
             frame.storeLocal(slot, JSVAL_TYPE_OBJECT, true);
@@ -2627,16 +2570,17 @@ mjit::Compiler::generateMethod()
             INLINE_STUBCALL(stubs::ArgCnt);
             pushSyncedEntry(0);
           }
           END_CASE(JSOP_ARGCNT)
 
           BEGIN_CASE(JSOP_DEFLOCALFUN)
           {
             REJOIN_SITE_ANY();
+            updateVarType();
             uint32 slot = GET_SLOTNO(PC);
             JSFunction *fun = script->getFunction(fullAtomIndex(&PC[SLOTNO_LEN]));
             prepareStubCall(Uses(0));
             masm.move(ImmPtr(fun), Registers::ArgReg1);
             INLINE_STUBCALL(stubs::DefLocalFun);
             frame.takeReg(Registers::ReturnReg);
             frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
             frame.storeLocal(slot, JSVAL_TYPE_OBJECT, true);
@@ -2657,17 +2601,17 @@ mjit::Compiler::generateMethod()
             if (op == JSOP_CALLGNAME)
                 jsop_callgname_epilogue();
           }
           END_CASE(JSOP_GETGNAME)
 
           BEGIN_CASE(JSOP_SETGNAME)
           {
             jsbytecode *next = &PC[JSOP_SETLOCAL_LENGTH];
-            bool pop = JSOp(*next) == JSOP_POP && !a->analysis.jumpTarget(next);
+            bool pop = JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next);
             jsop_setgname(script->getAtom(fullAtomIndex(PC)), true, pop);
           }
           END_CASE(JSOP_SETGNAME)
 
           BEGIN_CASE(JSOP_REGEXP)
           {
             JSObject *regex = script->getRegExp(fullAtomIndex(PC));
             prepareStubCall(Uses(0));
@@ -2736,17 +2680,17 @@ mjit::Compiler::generateMethod()
           END_CASE(JSOP_LEAVEBLOCK)
 
           BEGIN_CASE(JSOP_CALLLOCAL)
           {
             uint32 slot = GET_SLOTNO(PC);
             if (JSObject *singleton = pushedSingleton(0))
                 frame.push(ObjectValue(*singleton));
             else
-                frame.pushLocal(slot, knownPushedType(0));
+                frame.pushLocal(slot);
             frame.push(UndefinedValue());
           }
           END_CASE(JSOP_CALLLOCAL)
 
           BEGIN_CASE(JSOP_INT8)
             frame.push(Value(Int32Value(GET_INT8(PC))));
           END_CASE(JSOP_INT8)
 
@@ -2770,17 +2714,17 @@ mjit::Compiler::generateMethod()
             frame.takeReg(Registers::ReturnReg);
             frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
           }
           END_CASE(JSOP_LAMBDA_FC)
 
           BEGIN_CASE(JSOP_TRACE)
           BEGIN_CASE(JSOP_NOTRACE)
           {
-            if (a->analysis.jumpTarget(PC)) {
+            if (analysis->jumpTarget(PC)) {
                 interruptCheckHelper();
                 recompileCheckHelper();
             }
           }
           END_CASE(JSOP_TRACE)
 
           BEGIN_CASE(JSOP_DEBUGGER)
           {
@@ -2828,17 +2772,17 @@ mjit::Compiler::generateMethod()
              * associated type sets in such cases.
              */
             unsigned nuses = analyze::GetUseCount(script, oldPC - script->code);
             unsigned ndefs = analyze::GetDefCount(script, oldPC - script->code);
             for (unsigned i = 0; i < ndefs; i++) {
                 FrameEntry *fe = frame.getStack(opinfo->stackDepth - nuses + i);
                 if (fe) {
                     /* fe may be NULL for conditionally pushed entries, e.g. JSOP_AND */
-                    frame.extra(fe).types = script->types->pushed(oldPC - script->code, i);
+                    frame.extra(fe).types = analysis->pushedTypes(oldPC - script->code, i);
                 }
             }
         }
 
 #ifdef DEBUG
         frame.assertValidRegisterState();
 #endif
     }
@@ -2978,17 +2922,17 @@ mjit::Compiler::loadReturnValue(Assemble
             }
         } else {
             frame.loadForReturn(fe, typeReg, dataReg, Registers::ReturnReg);
         }
     } else {
          // Load a return value from POPV or SETRVAL into the return registers,
          // otherwise return undefined.
         masm->loadValueAsComponents(UndefinedValue(), typeReg, dataReg);
-        if (a->analysis.usesReturnValue()) {
+        if (analysis->usesReturnValue()) {
             Jump rvalClear = masm->branchTest32(Assembler::Zero,
                                                FrameFlagsAddress(),
                                                Imm32(JSFRAME_HAS_RVAL));
             Address rvalAddress(JSFrameReg, JSStackFrame::offsetOfReturnValue());
             masm->loadValueAsComponents(rvalAddress, typeReg, dataReg);
             rvalClear.linkTo(masm->label(), masm);
         }
     }
@@ -3004,17 +2948,17 @@ mjit::Compiler::fixPrimitiveReturn(Assem
     JS_ASSERT(isConstructing);
 
     bool ool = (masm != &this->masm);
     Address thisv(JSFrameReg, JSStackFrame::offsetOfThis(script->fun));
 
     // We can just load |thisv| if either of the following is true:
     //  (1) There is no explicit return value, AND fp->rval is not used.
     //  (2) There is an explicit return value, and it's known to be primitive.
-    if ((!fe && !a->analysis.usesReturnValue()) ||
+    if ((!fe && !analysis->usesReturnValue()) ||
         (fe && fe->isTypeKnown() && fe->getKnownType() != JSVAL_TYPE_OBJECT))
     {
         if (ool)
             masm->loadValueAsComponents(thisv, JSReturnReg_Type, JSReturnReg_Data);
         else
             frame.loadThisForReturn(JSReturnReg_Type, JSReturnReg_Data, Registers::ReturnReg);
         return;
     }
@@ -3137,17 +3081,17 @@ mjit::Compiler::emitReturn(FrameEntry *f
         /*
          * Simple tests to see if we are at the end of the script and will
          * fallthrough after the script body finishes, thus won't need to jump.
          */
         bool endOfScript =
             (JSOp(*PC) == JSOP_STOP) ||
             (JSOp(*PC) == JSOP_RETURN &&
              (JSOp(*(PC + JSOP_RETURN_LENGTH)) == JSOP_STOP &&
-              !a->analysis.maybeCode(PC + JSOP_RETURN_LENGTH)));
+              !analysis->maybeCode(PC + JSOP_RETURN_LENGTH)));
         if (!endOfScript)
             a->returnJumps->append(masm.jump());
 
         frame.discardFrame();
         return;
     }
 
     /*
@@ -3244,17 +3188,17 @@ mjit::Compiler::interruptCheckHelper()
     stubcc.rejoin(Changes(0));
 }
 
 void
 mjit::Compiler::recompileCheckHelper()
 {
     REJOIN_SITE(stubs::RecompileForInline);
 
-    if (!a->analysis.hasFunctionCalls() || !cx->typeInferenceEnabled() ||
+    if (!analysis->hasFunctionCalls() || !cx->typeInferenceEnabled() ||
         script->callCount() >= CALLS_BACKEDGES_BEFORE_INLINING) {
         return;
     }
 
     size_t *addr = script->addressOfCallCount();
     masm.add32(Imm32(1), AbsoluteAddress(addr));
 #if defined(JS_CPU_X86) || defined(JS_CPU_ARM)
     Jump jump = masm.branch32(Assembler::GreaterThanOrEqual, AbsoluteAddress(addr),
@@ -3408,17 +3352,17 @@ mjit::Compiler::checkCallApplySpeculatio
 /* This predicate must be called before the current op mutates the FrameState. */
 bool
 mjit::Compiler::canUseApplyTricks()
 {
     JS_ASSERT(*PC == JSOP_ARGUMENTS);
     jsbytecode *nextpc = PC + JSOP_ARGUMENTS_LENGTH;
     return *nextpc == JSOP_FUNAPPLY &&
            IsLowerableFunCallOrApply(nextpc) &&
-           !a->analysis.jumpTarget(nextpc) &&
+           !analysis->jumpTarget(nextpc) &&
            !debugMode() && !a->parent;
 }
 
 /* See MonoIC.cpp, CallCompiler for more information on call ICs. */
 bool
 mjit::Compiler::inlineCallHelper(uint32 callImmArgc, bool callingNew, FrameSize &callFrameSize)
 {
     /* Check for interrupts on function call */
@@ -3951,28 +3895,28 @@ mjit::Compiler::inlineScriptedFunction(u
                 return Compile_InlineAbort;
             checka = checka->parent;
         }
 
         /* Watch for excessively deep nesting of inlined frames. */
         if (frame.totalDepth() + VALUES_PER_STACK_FRAME + fun->script()->nslots >= stackLimit)
             return Compile_InlineAbort;
 
-        analyze::Script analysis;
-        analysis.analyze(cx, script);
-
-        if (analysis.OOM())
+        analyze::ScriptAnalysis *analysis = script->analysis(cx);
+        if (analysis && !analysis->failed() && !analysis->ranBytecode())
+            analysis->analyzeBytecode(cx);
+        if (!analysis || analysis->OOM())
             return Compile_Error;
-        if (analysis.failed())
+        if (analysis->failed())
             return Compile_Abort;
 
-        if (!analysis.inlineable(argc))
+        if (!analysis->inlineable(argc))
             return Compile_InlineAbort;
 
-        if (analysis.usesThisValue() && origThis->isNotType(JSVAL_TYPE_OBJECT))
+        if (analysis->usesThisValue() && origThis->isNotType(JSVAL_TYPE_OBJECT))
             return Compile_InlineAbort;
     }
 
     types->addFreeze(cx);
 
     /*
      * For 'this' and arguments which are copies of other entries still in
      * memory, try to get registers now. This will let us carry these entries
@@ -4046,17 +3990,16 @@ mjit::Compiler::inlineScriptedFunction(u
         a->returnValueDouble = returnType == JSVAL_TYPE_DOUBLE;
         if (returnSet) {
             a->returnSet = true;
             a->returnRegister = returnRegister;
             a->returnParentRegs = returnParentRegs;
         }
         a->temporaryParentRegs = temporaryParentRegs;
 
-        generateInlinePrologue();
         status = generateMethod();
         if (status != Compile_Okay) {
             popActiveFrame();
             if (status == Compile_Abort) {
                 /* The callee is uncompileable, mark it as uninlineable and retry. */
                 if (!cx->markTypeFunctionUninlineable(fun->getType()))
                     return Compile_Error;
                 return Compile_Retry;
@@ -4252,21 +4195,22 @@ mjit::Compiler::compareTwoValues(JSConte
 
     JS_NOT_REACHED("NYI");
     return false;
 }
 
 bool
 mjit::Compiler::emitStubCmpOp(BoolStub stub, AutoRejoinSite &autoRejoin, jsbytecode *target, JSOp fused)
 {
-    fixDoubleTypes();
-    if (target)
+    if (target) {
+        fixDoubleTypes(target);
         frame.syncAndKillEverything();
-    else
+    } else {
         frame.syncAndKill(Uses(2));
+    }
 
     prepareStubCall(Uses(2));
     INLINE_STUBCALL(stub);
     frame.pop();
     frame.pop();
 
     if (!target) {
         frame.takeReg(Registers::ReturnReg);
@@ -4371,23 +4315,23 @@ mjit::Compiler::jsop_getprop(JSAtom *ato
     /* If the incoming type will never PIC, take slow path. */
     if (top->isNotType(JSVAL_TYPE_OBJECT)) {
         jsop_getprop_slow(atom, usePropCache);
         return true;
     }
 
     frame.forgetMismatchedObject(top);
 
-    if (atom == cx->runtime->atomState.lengthAtom) {
+    if (JSOp(*PC) == JSOP_LENGTH) {
         /*
          * Check if this is an array we can make a loop invariant entry for.
          * This will fail for objects which are not definitely dense arrays.
          */
         if (loop && loop->generatingInvariants()) {
-            FrameEntry *fe = loop->invariantLength(top);
+            FrameEntry *fe = loop->invariantLength(top, frame.extra(top).types);
             if (fe) {
                 frame.pop();
                 frame.pushTemporary(fe);
                 return true;
             }
         }
 
         /*
@@ -5260,17 +5204,17 @@ mjit::Compiler::jsop_bindname(JSAtom *at
 {
     REJOIN_SITE(ic::BindName);
     PICGenInfo pic(ic::PICInfo::BIND, JSOp(*PC), usePropCache);
 
     // This code does not check the frame flags to see if scopeChain has been
     // set. Rather, it relies on the up-front analysis statically determining
     // whether BINDNAME can be used, which reifies the scope chain at the
     // prologue.
-    JS_ASSERT(a->analysis.usesScopeChain());
+    JS_ASSERT(analysis->usesScopeChain());
 
     pic.shapeReg = frame.allocReg();
     pic.objReg = frame.allocReg();
     pic.typeReg = Registers::ReturnReg;
     pic.atom = atom;
     pic.hasTypeCheck = false;
 
     RESERVE_IC_SPACE(masm);
@@ -5385,17 +5329,19 @@ mjit::Compiler::jsop_this()
     /* 
      * In strict mode code, we don't wrap 'this'.
      * In direct-call eval code, we wrapped 'this' before entering the eval.
      * In global code, 'this' is always an object.
      */
     if (script->fun && !script->strictModeCode) {
         FrameEntry *thisFe = frame.peek(-1);
         if (!thisFe->isTypeKnown()) {
-            JSValueType type = knownThisType();
+            JSValueType type = cx->typeInferenceEnabled()
+                ? script->thisTypes()->getKnownTypeTag(cx)
+                : 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);
                 stubcc.rejoin(Changes(1));
             }
 
@@ -5411,17 +5357,17 @@ mjit::Compiler::jsop_this()
 
 bool
 mjit::Compiler::jsop_gnameinc(JSOp op, VoidStubAtom stub, uint32 index)
 {
     JSAtom *atom = script->getAtom(index);
 
 #if defined JS_MONOIC
     jsbytecode *next = &PC[JSOP_GNAMEINC_LENGTH];
-    bool pop = (JSOp(*next) == JSOP_POP) && !a->analysis.jumpTarget(next);
+    bool pop = (JSOp(*next) == JSOP_POP) && !analysis->jumpTarget(next);
     int amt = (op == JSOP_GNAMEINC || op == JSOP_INCGNAME) ? -1 : 1;
 
     if (pop || (op == JSOP_INCGNAME || op == JSOP_DECGNAME)) {
         /* These cases are easy, the original value is not observed. */
 
         jsop_getgname(index);
         // V
 
@@ -5502,17 +5448,17 @@ mjit::Compiler::jsop_gnameinc(JSOp op, V
 }
 
 CompileStatus
 mjit::Compiler::jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index)
 {
     JSAtom *atom = script->getAtom(index);
 #if defined JS_POLYIC
     jsbytecode *next = &PC[JSOP_NAMEINC_LENGTH];
-    bool pop = (JSOp(*next) == JSOP_POP) && !a->analysis.jumpTarget(next);
+    bool pop = (JSOp(*next) == JSOP_POP) && !analysis->jumpTarget(next);
     int amt = (op == JSOP_NAMEINC || op == JSOP_INCNAME) ? -1 : 1;
 
     if (pop || (op == JSOP_INCNAME || op == JSOP_DECNAME)) {
         /* These cases are easy, the original value is not observed. */
 
         jsop_bindname(atom, false);
         // OBJ
 
@@ -5579,17 +5525,17 @@ mjit::Compiler::jsop_nameinc(JSOp op, Vo
 }
 
 CompileStatus
 mjit::Compiler::jsop_propinc(JSOp op, VoidStubAtom stub, uint32 index)
 {
     JSAtom *atom = script->getAtom(index);
 #if defined JS_POLYIC
     jsbytecode *next = &PC[JSOP_PROPINC_LENGTH];
-    bool pop = (JSOp(*next) == JSOP_POP) && !a->analysis.jumpTarget(next);
+    bool pop = (JSOp(*next) == JSOP_POP) && !analysis->jumpTarget(next);
     int amt = (op == JSOP_PROPINC || op == JSOP_INCPROP) ? -1 : 1;
 
     if (pop || (op == JSOP_INCPROP || op == JSOP_DECPROP)) {
         /*
          * These cases are easier, the original value is not observed.
          * Use a consistent stack layout for the value as the observed case,
          * so that if the operation overflows the stub will be able to find
          * the modified object.
@@ -5858,17 +5804,17 @@ mjit::Compiler::iterMore()
     jsbytecode *target = &PC[JSOP_MOREITER_LENGTH];
     JSOp next = JSOp(*target);
     JS_ASSERT(next == JSOP_IFNE || next == JSOP_IFNEX);
 
     target += (next == JSOP_IFNE)
               ? GET_JUMP_OFFSET(target)
               : GET_JUMPX_OFFSET(target);
 
-    fixDoubleTypes();
+    fixDoubleTypes(target);
     if (!frame.syncForBranch(target, Uses(1)))
         return false;
 
     FrameEntry *fe = frame.peek(-1);
     RegisterID reg = frame.tempRegForData(fe);
     RegisterID tempreg = frame.allocReg();
 
     /* Test clasp */
@@ -6524,17 +6470,17 @@ mjit::Compiler::startLoop(jsbytecode *he
          * Convert all loop registers in the outer loop into unassigned registers.
          * We don't keep track of which registers the inner loop uses, so the only
          * registers that can be carried in the outer loop must be mentioned before
          * the inner loop starts.
          */
         loop->clearLoopRegisters();
     }
 
-    LoopState *nloop = cx->new_<LoopState>(cx, script, this, &frame, &a->analysis, &a->liveness);
+    LoopState *nloop = cx->new_<LoopState>(cx, script, this, &frame);
     if (!nloop || !nloop->init(head, entry, entryTarget))
         return false;
 
     nloop->outer = loop;
     loop = nloop;
     frame.setLoop(loop);
 
     return true;
@@ -6559,17 +6505,17 @@ mjit::Compiler::finishLoop(jsbytecode *h
      * Fix up the jump entering the loop. We are doing this after all code has
      * been emitted for the backedge, so that we are now in the loop's fallthrough
      * (where we will emit the entry code).
      */
     Jump fallthrough = masm.jump();
 
 #ifdef DEBUG
     if (IsJaegerSpewChannelActive(JSpew_Regalloc)) {
-        RegisterAllocation *alloc = a->liveness.getCode(head).allocation;
+        RegisterAllocation *alloc = analysis->getAllocation(head);
         JaegerSpew(JSpew_Regalloc, "loop allocation at %u:", head - script->code);
         frame.dumpAllocation(alloc);
     }
 #endif
 
     loop->entryJump().linkTo(masm.label(), &masm);
 
     jsbytecode *oldPC = PC;
@@ -6594,17 +6540,17 @@ mjit::Compiler::finishLoop(jsbytecode *h
     PC = oldPC;
 
     frame.prepareForJump(entryTarget, masm, true);
 
     if (!jumpInScript(masm.jump(), entryTarget))
         return false;
 
     PC = head;
-    if (!a->analysis.getCode(head).safePoint) {
+    if (!analysis->getCode(head).safePoint) {
         /*
          * Emit a stub into the OOL path which loads registers from a synced state
          * and jumps to the loop head, for rejoining from the interpreter.
          */
         LoopEntry entry;
         entry.pcOffset = head - script->code;
 
         AutoRejoinSite autoRejoinHead(this, JS_FUNC_TO_DATA_PTR(void *, stubs::MissedBoundsCheckHead));
@@ -6613,16 +6559,30 @@ mjit::Compiler::finishLoop(jsbytecode *h
         if (loop->generatingInvariants()) {
             if (oomInVector)
                 return false;
             entry.label = callSites[callSites.length() - 1].loopJumpLabel;
         } else {
             entry.label = stubcc.masm.label();
         }
 
+        /*
+         * 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 slot = analyze::ArgSlot(0); slot < analyze::TotalSlots(script); slot++) {
+            if (a->varTypes[slot].type == JSVAL_TYPE_DOUBLE) {
+                FrameEntry *fe = frame.getOrTrack(slot);
+                stubcc.masm.ensureInMemoryDouble(frame.addressOf(fe));
+            }
+        }
+
         autoRejoinHead.oolRejoin(stubcc.masm.label());
         frame.prepareForJump(head, stubcc.masm, true);
         if (!stubcc.jumpInScript(stubcc.masm.jump(), head))
             return false;
 
         loopEntries.append(entry);
     }
     PC = oldPC;
@@ -6669,19 +6629,19 @@ mjit::Compiler::jumpAndTrace(Jump j, jsb
 
     /*
      * 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 = a->liveness.getCode(target).allocation;
+        RegisterAllocation *&alloc = analysis->getAllocation(target);
         if (!alloc) {
-            alloc = ArenaNew<RegisterAllocation>(a->liveness.pool, false);
+            alloc = ArenaNew<RegisterAllocation>(cx->compartment->pool, false);
             if (!alloc)
                 return false;
         }
         lvtarget = alloc;
         consistent = frame.consistentRegisters(target);
     }
 
     if (!addTraceHints || target >= PC ||
@@ -6824,17 +6784,17 @@ void
 mjit::Compiler::enterBlock(JSObject *obj)
 {
     // If this is an exception entry point, then jsl_InternalThrow has set
     // VMFrame::fp to the correct fp for the entry point. We need to copy
     // that value here to FpReg so that FpReg also has the correct sp.
     // Otherwise, we would simply be using a stale FpReg value.
     // Additionally, we check the interrupt flag to allow interrupting
     // deeply nested exception handling.
-    if (a->analysis.getCode(PC).exceptionEntry) {
+    if (analysis->getCode(PC).exceptionEntry) {
         masm.loadPtr(FrameAddress(offsetof(VMFrame, regs.fp)), JSFrameReg);
         interruptCheckHelper();
     }
 
     /* For now, don't bother doing anything for this opcode. */
     frame.syncAndForgetEverything();
     masm.move(ImmPtr(obj), Registers::ArgReg1);
     uint32 n = js_GetEnterBlockStackDefs(cx, script, PC);
@@ -7066,78 +7026,106 @@ mjit::Compiler::jsop_forgname(JSAtom *at
  * doubles at control flow join points.  This function must be called before branching
  * to another opcode.
  */
 
 /*
  * Whether to ensure that locals/args known to be ints or doubles should be
  * preserved as doubles across control flow edges.
  */
-
 inline bool
-mjit::Compiler::preserveLocalType(unsigned i)
+mjit::Compiler::fixDoubleSlot(uint32 slot)
 {
-    return !a->analysis.localEscapes(i);
-}
-
-inline bool
-mjit::Compiler::preserveArgType(unsigned i)
-{
+    if (!analysis->trackSlot(slot))
+        return false;
+
     /*
      * Don't preserve double arguments in inline calls across branches, as we
      * can't mutate them when inlining. :XXX: could be more precise here.
      */
-    return !a->analysis.argEscapes(i) && !a->parent;
+    if (slot < analyze::LocalSlot(script, 0) && a->parent)
+        return false;
+
+    return true;
 }
 
 void
-mjit::Compiler::fixDoubleTypes()
+mjit::Compiler::fixDoubleTypes(jsbytecode *target)
 {
     if (!cx->typeInferenceEnabled())
         return;
 
-    for (uint32 i = 0; !a->parent && script->fun && i < script->fun->nargs; i++) {
-        JSValueType type = knownArgumentType(i);
-        if (type == JSVAL_TYPE_DOUBLE && preserveArgType(i)) {
-            FrameEntry *fe = frame.getArg(i);
+    /*
+     * Temporarily fix up the variable types to reflect state at the branch
+     * target. As described in prepareInferenceTypes, the target state consists
+     * of the current state plus any phi nodes introduced at the target.
+     */
+    Vector<SlotType, 8, CompilerAllocPolicy> restoreTypes(CompilerAllocPolicy(cx, *this));
+    const analyze::SlotValue *newv = analysis->newValues(target);
+    if (newv) {
+        while (newv->slot) {
+            if (newv->value.kind() != analyze::SSAValue::PHI ||
+                newv->value.phiOffset() != uint32(target - script->code)) {
+                newv++;
+                continue;
+            }
+            if (newv->slot < analyze::TotalSlots(script)) {
+                VarType &vt = a->varTypes[newv->slot];
+                restoreTypes.append(SlotType(newv->slot, vt));
+                vt.types = analysis->getValueTypes(newv->value);
+                vt.type = vt.types->getKnownTypeTag(cx);
+            }
+            newv++;
+        }
+    }
+
+    for (uint32 slot = analyze::ArgSlot(0);
+         slot < analyze::LocalSlot(script, script->nfixed);
+         slot++) {
+        if (!fixDoubleSlot(slot))
+            continue;
+        if (a->varTypes[slot].type == JSVAL_TYPE_DOUBLE) {
+            FrameEntry *fe = frame.getOrTrack(slot);
             if (!fe->isType(JSVAL_TYPE_DOUBLE))
                 frame.ensureDouble(fe);
         }
     }
 
-    for (uint32 i = 0; i < script->nfixed; i++) {
-        JSValueType type = knownLocalType(i);
-        if (type == JSVAL_TYPE_DOUBLE && preserveLocalType(i)) {
-            FrameEntry *fe = frame.getLocal(i);
-            if (!fe->isType(JSVAL_TYPE_DOUBLE))
-                frame.ensureDouble(fe);
-        }
+    for (unsigned i = 0; i < restoreTypes.length(); i++) {
+        const SlotType &rt = restoreTypes[i];
+        a->varTypes[rt.slot] = rt.vt;
     }
 }
 
 void
-mjit::Compiler::restoreAnalysisTypes(uint32 stackDepth)
+mjit::Compiler::restoreAnalysisTypes()
 {
     if (!cx->typeInferenceEnabled())
         return;
 
-    /* Restore known types of locals/args, for join points or after forgetting everything. */
-    for (uint32 i = 0; i < script->nfixed; i++) {
-        JSValueType type = knownLocalType(i);
-        if (type != JSVAL_TYPE_UNKNOWN && (type != JSVAL_TYPE_DOUBLE || preserveLocalType(i))) {
-            FrameEntry *fe = frame.getLocal(i);
-            JS_ASSERT_IF(fe->isTypeKnown(), fe->isType(type));
-            if (!fe->isTypeKnown())
-                frame.learnType(fe, type, false);
+    /* Update variable types for all new values at this bytecode. */
+    const analyze::SlotValue *newv = analysis->newValues(PC);
+    if (newv) {
+        while (newv->slot) {
+            if (newv->slot < analyze::TotalSlots(script)) {
+                VarType &vt = a->varTypes[newv->slot];
+                vt.types = analysis->getValueTypes(newv->value);
+                vt.type = vt.types->getKnownTypeTag(cx);
+            }
+            newv++;
         }
     }
-    for (uint32 i = 0; script->fun && i < script->fun->nargs; i++) {
-        JSValueType type = knownArgumentType(i);
-        if (type != JSVAL_TYPE_UNKNOWN && (type != JSVAL_TYPE_DOUBLE || preserveArgType(i))) {
-            FrameEntry *fe = frame.getArg(i);
+
+    /* Restore known types of locals/args. */
+    for (uint32 slot = analyze::ArgSlot(0);
+         slot < analyze::LocalSlot(script, script->nfixed);
+         slot++) {
+        JSValueType type = a->varTypes[slot].type;
+        if (type != JSVAL_TYPE_UNKNOWN && (type != JSVAL_TYPE_DOUBLE || fixDoubleSlot(slot))) {
+            FrameEntry *fe = frame.getOrTrack(slot);
             JS_ASSERT_IF(fe->isTypeKnown(), fe->isType(type));
             if (!fe->isTypeKnown())
                 frame.learnType(fe, type, false);
         }
     }
 }
 
 void
@@ -7145,134 +7133,114 @@ mjit::Compiler::watchGlobalReallocation(
 {
     JS_ASSERT(cx->typeInferenceEnabled());
     if (hasGlobalReallocation)
         return;
     types::TypeSet::WatchObjectReallocation(cx, globalObj);
     hasGlobalReallocation = true;
 }
 
-JSValueType
-mjit::Compiler::knownThisType()
+void
+mjit::Compiler::updateVarType()
 {
     if (!cx->typeInferenceEnabled())
-        return JSVAL_TYPE_UNKNOWN;
-    if (a->hasThisType)
-        return a->thisType;
-    a->hasThisType = true;
-    a->thisType = script->thisTypes()->getKnownTypeTag(cx);
-    return a->thisType;
-}
-
-JSValueType
-mjit::Compiler::knownArgumentType(uint32 arg)
-{
-    if (!cx->typeInferenceEnabled())
-        return JSVAL_TYPE_UNKNOWN;
-    JS_ASSERT(script->fun && arg < script->fun->nargs);
-    return a->argumentTypes[arg];
-}
-
-JSValueType
-mjit::Compiler::knownLocalType(uint32 local)
-{
-    if (!cx->typeInferenceEnabled() || local >= script->nfixed)
-        return JSVAL_TYPE_UNKNOWN;
-    return a->localTypes[local];
+        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 = NULL;
+    switch (JSOp(*PC)) {
+      case JSOP_SETARG:
+      case JSOP_SETLOCAL:
+      case JSOP_SETLOCALPOP:
+      case JSOP_DEFLOCALFUN:
+      case JSOP_DEFLOCALFUN_FC:
+      case JSOP_INCARG:
+      case JSOP_DECARG:
+      case JSOP_ARGINC:
+      case JSOP_ARGDEC:
+      case JSOP_INCLOCAL:
+      case JSOP_DECLOCAL:
+      case JSOP_LOCALINC:
+      case JSOP_LOCALDEC:
+        types = pushedTypeSet(0);
+        break;
+      case JSOP_FORARG:
+      case JSOP_FORLOCAL:
+        types = pushedTypeSet(1);
+        break;
+      default:
+        JS_NOT_REACHED("Bad op");
+    }
+
+    uint32 slot = analyze::GetBytecodeSlot(script, PC);
+
+    if (analysis->trackSlot(slot)) {
+        VarType &vt = a->varTypes[slot];
+        vt.types = types;
+        vt.type = types->getKnownTypeTag(cx);
+    }
 }
 
 JSValueType
 mjit::Compiler::knownPushedType(uint32 pushed)
 {
     if (!cx->typeInferenceEnabled())
         return JSVAL_TYPE_UNKNOWN;
-    types::TypeSet *types = script->types->pushed(PC - script->code, pushed);
+    types::TypeSet *types = analysis->pushedTypes(PC, pushed);
     return types->getKnownTypeTag(cx);
 }
 
 bool
 mjit::Compiler::mayPushUndefined(uint32 pushed)
 {
     JS_ASSERT(cx->typeInferenceEnabled());
 
     /*
      * This should only be used when the compiler is checking if it is OK to push
      * 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 = script->types->pushed(PC - script->code, pushed);
+    types::TypeSet *types = analysis->pushedTypes(PC, pushed);
     return types->hasType(types::TYPE_UNDEFINED);
 }
 
 types::TypeSet *
-mjit::Compiler::argTypeSet(uint32 arg)
-{
-    return cx->typeInferenceEnabled() ? script->argTypes(arg) : NULL;
-}
-
-types::TypeSet *
-mjit::Compiler::localTypeSet(uint32 local)
-{
-    if (!cx->typeInferenceEnabled() || local >= script->nfixed)
-        return NULL;
-    return script->localTypes(local);
-}
-
-types::TypeSet *
 mjit::Compiler::pushedTypeSet(uint32 pushed)
 {
     if (!cx->typeInferenceEnabled())
         return NULL;
-    return script->types->pushed(PC - script->code, pushed);
-}
-
-types::TypeSet *
-mjit::Compiler::getTypeSet(uint32 slot)
-{
-    if (!cx->typeInferenceEnabled())
-        return NULL;
-
-    if (slot == 0) /* callee */
-        return NULL;
-    if (slot == 1) /* this */
-        return script->thisTypes();
-    slot -= 2;
-
-    unsigned nargs = script->fun ? script->fun->nargs : 0;
-
-    if (slot < nargs)
-        return script->argTypes(slot);
-    slot -= nargs;
-
-    if (slot < script->nfixed)
-        return script->localTypes(slot);
-
-    return frame.extra(2 + nargs + slot).types;
+    return analysis->pushedTypes(PC, pushed);
 }
 
 bool
 mjit::Compiler::monitored(jsbytecode *pc)
 {
-    return cx->typeInferenceEnabled() && script->types->monitored(pc - script->code);
+    return cx->typeInferenceEnabled() && analysis->monitoredTypes(pc - script->code);
 }
 
 void
 mjit::Compiler::pushSyncedEntry(uint32 pushed)
 {
     frame.pushSynced(knownPushedType(pushed));
 }
 
 JSObject *
 mjit::Compiler::pushedSingleton(unsigned pushed)
 {
     if (!cx->typeInferenceEnabled())
         return NULL;
 
-    types::TypeSet *types = script->types->pushed(PC - script->code, pushed);
+    types::TypeSet *types = analysis->pushedTypes(PC, pushed);
     return types->getSingleton(cx);
 }
 
 bool
 mjit::Compiler::arrayPrototypeHasIndexedProperty()
 {
     if (!cx->typeInferenceEnabled())
         return true;
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -432,16 +432,28 @@ class Compiler : public BaseCompiler
         size_t offsetIndex;
     };
 
     struct LoopEntry {
         uint32 pcOffset;
         Label label;
     };
 
+    struct VarType {
+        JSValueType type;
+        types::TypeSet *types;
+    };
+
+    struct SlotType
+    {
+        uint32 slot;
+        VarType vt;
+        SlotType(uint32 slot, VarType vt) : slot(slot), vt(vt) {}
+    };
+
     JSScript *outerScript;
     bool isConstructing;
 
     JSObject *globalObj;
     Value *globalSlots;
 
     /* Existing frames on the stack whose slots may need to be updated. */
     const Vector<PatchableFrame> *patchFrames;
@@ -459,26 +471,23 @@ class Compiler : public BaseCompiler
      * the frame (reflected by the frame's active register state).
      */
 
     struct ActiveFrame {
         ActiveFrame *parent;
         jsbytecode *parentPC;
         JSScript *script;
         uint32 inlineIndex;
-        analyze::Script analysis;
-        analyze::LifetimeScript liveness;
         Label *jumpMap;
-        bool hasThisType;
-        JSValueType thisType;
-        JSValueType *argumentTypes;
-        JSValueType *localTypes;
         uint32 depth;
         Vector<UnsyncedEntry> unsyncedEntries; // :XXX: handle OOM
 
+        /* Current types for non-escaping vars in the script. */
+        VarType *varTypes;
+
         /* State for managing return from inlined frames. */
         bool needReturnValue;
         bool syncReturnValue;
         bool returnValueDouble;
         bool returnSet;
         AnyRegisterID returnRegister;
         Registers returnParentRegs;
         Registers temporaryParentRegs;
@@ -486,16 +495,17 @@ class Compiler : public BaseCompiler
 
         ActiveFrame(JSContext *cx);
         ~ActiveFrame();
     };
     ActiveFrame *a;
     ActiveFrame *outer;
 
     JSScript *script;
+    analyze::ScriptAnalysis *analysis;
     jsbytecode *PC;
     bool variadicRejoin;  /* There is a variadic rejoin for PC. */
 
     LoopState *loop;
 
     /* State spanning all stack frames. */
 
     js::Vector<ActiveFrame*, 4, CompilerAllocPolicy> inlineFrames;
@@ -584,19 +594,16 @@ class Compiler : public BaseCompiler
         while (scan && scan->parent != outer)
             scan = scan->parent;
         return scan->parentPC;
     }
 
     jsbytecode *inlinePC() { return PC; }
     uint32 inlineIndex() { return a->inlineIndex; }
 
-    types::TypeSet *getTypeSet(uint32 slot);
-    types::TypeSet *getTypeSet(const FrameEntry *fe) { return getTypeSet(frame.indexOfFe(fe)); }
-
     Assembler &getAssembler(bool ool) { return ool ? stubcc.masm : masm; }
 
     InvariantCodePatch *getInvariantPatch(unsigned index, bool call) {
         return call ? &callSites[index].loopPatch : &rejoinSites[index].loopPatch;
     }
     jsbytecode *getInvariantPC(unsigned index, bool call) {
         return call ? callSites[index].inlinepc : rejoinSites[index].pc;
     }
@@ -604,33 +611,27 @@ class Compiler : public BaseCompiler
   private:
     CompileStatus performCompilation(JITScript **jitp);
     CompileStatus generatePrologue();
     CompileStatus generateMethod();
     CompileStatus generateEpilogue();
     CompileStatus finishThisUp(JITScript **jitp);
     CompileStatus pushActiveFrame(JSScript *script, uint32 argc);
     void popActiveFrame();
-    void generateInlinePrologue();
 
     /* Analysis helpers. */
     CompileStatus prepareInferenceTypes(JSScript *script, ActiveFrame *a);
-    inline bool preserveLocalType(unsigned i);
-    inline bool preserveArgType(unsigned i);
-    void fixDoubleTypes();
-    void restoreAnalysisTypes(uint32 stackDepth);
+    inline bool fixDoubleSlot(uint32 slot);
+    void fixDoubleTypes(jsbytecode *target);
+    void restoreAnalysisTypes();
     void watchGlobalReallocation();
-    JSValueType knownThisType();
-    JSValueType knownArgumentType(uint32 arg);
-    JSValueType knownLocalType(uint32 local);
+    void updateVarType();
     JSValueType knownPushedType(uint32 pushed);
     bool arrayPrototypeHasIndexedProperty();
     bool mayPushUndefined(uint32 pushed);
-    types::TypeSet *argTypeSet(uint32 arg);
-    types::TypeSet *localTypeSet(uint32 local);
     types::TypeSet *pushedTypeSet(uint32 which);
     bool monitored(jsbytecode *pc);
     bool testSingletonProperty(JSObject *obj, jsid id);
     bool testSingletonPropertyTypes(FrameEntry *top, jsid id, bool *testObject);
 
     /* Non-emitting helpers. */
     void pushSyncedEntry(uint32 pushed);
     uint32 fullAtomIndex(jsbytecode *pc);
--- a/js/src/methodjit/FastArithmetic.cpp
+++ b/js/src/methodjit/FastArithmetic.cpp
@@ -1109,17 +1109,17 @@ mjit::Compiler::jsop_equality_int_string
         ValueRemat lvr, rvr;
         frame.pinEntry(lhs, lvr);
         frame.pinEntry(rhs, rvr);
 
         /*
          * Sync everything except the top two entries.
          * We will handle the lhs/rhs in the stub call path.
          */
-        fixDoubleTypes();
+        fixDoubleTypes(target);
         frame.syncAndKill(Registers(Registers::AvailRegs), Uses(frame.frameSlots()), Uses(2));
 
         RegisterID tempReg = frame.allocReg();
 
         JaegerSpew(JSpew_Insns, " ---- BEGIN STUB CALL CODE ---- \n");
 
         RESERVE_OOL_SPACE(stubcc.masm);
 
@@ -1384,17 +1384,17 @@ DoubleCondForOp(JSOp op, JSOp fused)
 
 bool
 mjit::Compiler::jsop_relational_double(JSOp op, BoolStub stub, AutoRejoinSite &autoRejoin, jsbytecode *target, JSOp fused)
 {
     FrameEntry *rhs = frame.peek(-1);
     FrameEntry *lhs = frame.peek(-2);
 
     if (target)
-        fixDoubleTypes();
+        fixDoubleTypes(target);
 
     JS_ASSERT_IF(!target, fused != JSOP_IFEQ);
 
     FPRegisterID fpLeft, fpRight;
     bool allocateLeft, allocateRight;
 
     MaybeJump lhsNotNumber = loadDouble(lhs, &fpLeft, &allocateLeft);
     if (!allocateLeft)
@@ -1482,17 +1482,17 @@ mjit::Compiler::jsop_relational_int(JSOp
         rhs = tmp;
         op = analyze::ReverseCompareOp(op);
     }
 
     JS_ASSERT_IF(!target, fused != JSOP_IFEQ);
     Assembler::Condition cond = GetCompareCondition(op, fused);
 
     if (target) {
-        fixDoubleTypes();
+        fixDoubleTypes(target);
         if (!frame.syncForBranch(target, Uses(2)))
             return false;
 
         RegisterID lreg = frame.tempRegForData(lhs);
         Jump fast;
         if (rhs->isConstant()) {
             fast = masm.branch32(cond, lreg, Imm32(rhs->getValue().toInt32()));
         } else {
@@ -1533,17 +1533,17 @@ bool
 mjit::Compiler::jsop_relational_full(JSOp op, BoolStub stub, AutoRejoinSite &rejoin, jsbytecode *target, JSOp fused)
 {
     AutoRejoinSite autoRejoin(this, JS_FUNC_TO_DATA_PTR(void *, stub));
 
     FrameEntry *rhs = frame.peek(-1);
     FrameEntry *lhs = frame.peek(-2);
 
     if (target)
-        fixDoubleTypes();
+        fixDoubleTypes(target);
 
     /* Allocate all registers up-front. */
     FrameState::BinaryAlloc regs;
     frame.allocForBinary(lhs, rhs, op, regs, !target);
 
     MaybeJump lhsNotDouble, rhsNotNumber, lhsUnknownDone;
     if (!lhs->isTypeKnown())
         emitLeftDoublePath(lhs, rhs, regs, lhsNotDouble, rhsNotNumber, lhsUnknownDone);
--- a/js/src/methodjit/FastOps.cpp
+++ b/js/src/methodjit/FastOps.cpp
@@ -425,17 +425,17 @@ mjit::Compiler::jsop_equality(JSOp op, B
         frame.pop();
 
         /*
          * :FIXME: Easier test for undefined || null?
          * Maybe put them next to each other, subtract, do a single compare?
          */
 
         if (target) {
-            fixDoubleTypes();
+            fixDoubleTypes(target);
             frame.syncAndKillEverything();
             frame.freeReg(reg);
 
             autoRejoin.oolRejoin(stubcc.masm.label());
             Jump sj = stubcc.masm.branchTest32(GetStubCompareCondition(fused),
                                                Registers::ReturnReg, Registers::ReturnReg);
 
             if ((op == JSOP_EQ && fused == JSOP_IFNE) ||
@@ -493,17 +493,17 @@ mjit::Compiler::jsop_equality(JSOp op, B
 
         if (lhsKind != types::OBJECT_UNKNOWN && rhsKind != types::OBJECT_UNKNOWN) {
             /* :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) {
-                fixDoubleTypes();
+                fixDoubleTypes(target);
                 autoRejoin.oolRejoin(stubcc.masm.label());
                 Jump sj = stubcc.masm.branchTest32(GetStubCompareCondition(fused),
                                                    Registers::ReturnReg, Registers::ReturnReg);
                 if (!frame.syncForBranch(target, Uses(2)))
                     return false;
                 RegisterID lreg = frame.tempRegForData(lhs);
                 frame.pinReg(lreg);
                 RegisterID rreg = frame.tempRegForData(rhs);
@@ -868,16 +868,17 @@ mjit::Compiler::booleanJumpScript(JSOp o
     frame.pop();
 
     return jumpAndTrace(j, target);
 }
 
 bool
 mjit::Compiler::jsop_ifneq(JSOp op, jsbytecode *target)
 {
+    fixDoubleTypes(target);
     FrameEntry *fe = frame.peek(-1);
 
     if (fe->isConstant()) {
         JSBool b = js_ValueToBoolean(fe->getValue());
 
         frame.pop();
 
         if (op == JSOP_IFEQ)
@@ -895,157 +896,163 @@ mjit::Compiler::jsop_ifneq(JSOp op, jsby
     }
 
     return booleanJumpScript(op, target);
 }
 
 bool
 mjit::Compiler::jsop_andor(JSOp op, jsbytecode *target)
 {
+    fixDoubleTypes(target);
     FrameEntry *fe = frame.peek(-1);
 
     if (fe->isConstant()) {
         JSBool b = js_ValueToBoolean(fe->getValue());
         
         /* Short-circuit. */
         if ((op == JSOP_OR && b == JS_TRUE) ||
             (op == JSOP_AND && b == JS_FALSE)) {
-            fixDoubleTypes();
             if (!frame.syncForBranch(target, Uses(0)))
                 return false;
             if (!jumpAndTrace(masm.jump(), target))
                 return false;
         }
 
         frame.pop();
         return true;
     }
 
     return booleanJumpScript(op, target);
 }
 
 bool
 mjit::Compiler::jsop_localinc(JSOp op, uint32 slot, bool popped)
 {
-    JSValueType type = knownLocalType(slot);
+    updateVarType();
+
+    types::TypeSet *types = pushedTypeSet(0);
+    JSValueType type = types ? types->getKnownTypeTag(cx) : JSVAL_TYPE_UNKNOWN;
 
     if (popped || (op == JSOP_INCLOCAL || op == JSOP_DECLOCAL)) {
         int amt = (op == JSOP_LOCALINC || op == JSOP_INCLOCAL) ? -1 : 1;
 
         // Before: 
         // After:  V
-        frame.pushLocal(slot, type);
+        frame.pushLocal(slot);
 
         // Before: V
         // After:  V 1
         frame.push(Int32Value(amt));
 
         // Note, SUB will perform integer conversion for us.
         // Before: V 1
         // After:  N+1
-        if (!jsop_binary(JSOP_SUB, stubs::Sub, type, localTypeSet(slot)))
+        if (!jsop_binary(JSOP_SUB, stubs::Sub, type, types))
             return false;
 
         // Before: N+1
         // After:  N+1
-        frame.storeLocal(slot, type, popped, true);
+        frame.storeLocal(slot, popped, true);
 
         if (popped)
             frame.pop();
     } else {
         int amt = (op == JSOP_LOCALINC || op == JSOP_INCLOCAL) ? 1 : -1;
 
         // Before:
         // After: V
-        frame.pushLocal(slot, type);
+        frame.pushLocal(slot);
 
         // Before: V
         // After:  N
         jsop_pos();
 
         // Before: N
         // After:  N N
         frame.dup();
 
         // Before: N N
         // After:  N N 1
         frame.push(Int32Value(amt));
 
         // Before: N N 1
         // After:  N N+1
-        if (!jsop_binary(JSOP_ADD, stubs::Add, type, localTypeSet(slot)))
+        if (!jsop_binary(JSOP_ADD, stubs::Add, type, types))
             return false;
 
         // Before: N N+1
         // After:  N N+1
-        frame.storeLocal(slot, type, true, true);
+        frame.storeLocal(slot, true, true);
 
         // Before: N N+1
         // After:  N
         frame.pop();
     }
 
     return true;
 }
 
 bool
 mjit::Compiler::jsop_arginc(JSOp op, uint32 slot, bool popped)
 {
-    JSValueType type = knownArgumentType(slot);
+    updateVarType();
+
+    types::TypeSet *types = pushedTypeSet(0);
+    JSValueType type = types ? types->getKnownTypeTag(cx) : JSVAL_TYPE_UNKNOWN;
 
     if (popped || (op == JSOP_INCARG || op == JSOP_DECARG)) {
         int amt = (op == JSOP_ARGINC || op == JSOP_INCARG) ? -1 : 1;
 
         // Before: 
         // After:  V
-        frame.pushArg(slot, type);
+        frame.pushArg(slot);
 
         // Before: V
         // After:  V 1
         frame.push(Int32Value(amt));
 
         // Note, SUB will perform integer conversion for us.
         // Before: V 1
         // After:  N+1
-        if (!jsop_binary(JSOP_SUB, stubs::Sub, type, argTypeSet(slot)))
+        if (!jsop_binary(JSOP_SUB, stubs::Sub, type, types))
             return false;
 
         // Before: N+1
         // After:  N+1
-        frame.storeArg(slot, type, popped);
+        frame.storeArg(slot, popped);
 
         if (popped)
             frame.pop();
     } else {
         int amt = (op == JSOP_ARGINC || op == JSOP_INCARG) ? 1 : -1;
 
         // Before:
         // After: V
-        frame.pushArg(slot, type);
+        frame.pushArg(slot);
 
         // Before: V
         // After:  N
         jsop_pos();
 
         // Before: N
         // After:  N N
         frame.dup();
 
         // Before: N N
         // After:  N N 1
         frame.push(Int32Value(amt));
 
         // Before: N N 1
         // After:  N N+1
-        if (!jsop_binary(JSOP_ADD, stubs::Add, type, argTypeSet(slot)))
+        if (!jsop_binary(JSOP_ADD, stubs::Add, type, types))
             return false;
 
         // Before: N N+1
         // After:  N N+1
-        frame.storeArg(slot, type, true);
+        frame.storeArg(slot, true);
 
         // Before: N N+1
         // After:  N
         frame.pop();
     }
 
     return true;
 }
@@ -1104,17 +1111,18 @@ mjit::Compiler::jsop_setelem_dense()
     bool pinKey = !key.isConstant() && !frame.haveSameBacking(id, value);
     if (pinKey)
         frame.pinReg(key.reg());
 
     // Register to hold the computed slots pointer for the object. If we can
     // hoist the initialized length check, we make the slots pointer loop
     // invariant and never access the object itself.
     RegisterID slotsReg;
-    bool hoisted = loop && !a->parent && loop->hoistArrayLengthCheck(obj, 1);
+    bool hoisted = loop && !a->parent &&
+        loop->hoistArrayLengthCheck(obj, frame.extra(obj).types, 1);
 
     if (hoisted) {
         FrameEntry *slotsFe = loop->invariantSlots(obj);
         slotsReg = frame.tempRegForData(slotsFe);
 
         frame.unpinEntry(vr);
         if (pinKey)
             frame.unpinReg(key.reg());
@@ -1428,17 +1436,18 @@ mjit::Compiler::jsop_getelem_dense(bool 
 
     // Allocate registers.
 
     // If we know the result of the GETELEM may be undefined, then misses on the
     // initialized length or hole checks can just produce an undefined value.
     // We checked in the caller that prototypes do not have indexed properties.
     bool allowUndefined = mayPushUndefined(0);
 
-    bool hoisted = loop && !a->parent && loop->hoistArrayLengthCheck(obj, 0);
+    bool hoisted = loop && !a->parent &&
+        loop->hoistArrayLengthCheck(obj, frame.extra(obj).types, 0);
 
     // Get a register with either the object or its slots, depending on whether
     // we are hoisting the bounds check.
     RegisterID baseReg;
     if (hoisted) {
         FrameEntry *slotsFe = loop->invariantSlots(obj);
         baseReg = frame.tempRegForData(slotsFe);
     } else {
--- a/js/src/methodjit/FrameState-inl.h
+++ b/js/src/methodjit/FrameState-inl.h
@@ -1182,51 +1182,49 @@ inline void
 FrameState::syncAt(int32 n)
 {
     JS_ASSERT(n < 0);
     FrameEntry *fe = peek(n);
     syncFe(fe);
 }
 
 inline void
-FrameState::pushLocal(uint32 n, JSValueType knownType)
+FrameState::pushLocal(uint32 n)
 {
     FrameEntry *fe = getLocal(n);
-    if (!a->analysis->localEscapes(n)) {
+    if (!analysis->slotEscapes(analyze::LocalSlot(script, n))) {
         pushCopyOf(indexOfFe(fe));
     } else {
 #ifdef DEBUG
         /*
          * We really want to assert on local variables, but in the presence of
          * SETLOCAL equivocation of stack slots, and let expressions, just
          * weakly assert on the fixed local vars.
          */
-        FrameEntry *fe = &locals[n];
-        if (fe->isTracked() && n < script->nfixed) {
-            JS_ASSERT(fe->type.inMemory());
+        if (fe->isTracked() && n < script->nfixed)
             JS_ASSERT(fe->data.inMemory());
-        }
 #endif
-        push(addressOf(fe), knownType);
+        JSValueType type = fe->isTypeKnown() ? fe->getKnownType() : JSVAL_TYPE_UNKNOWN;
+        push(addressOf(fe), type);
     }
 }
 
 inline void
-FrameState::pushArg(uint32 n, JSValueType knownType)
+FrameState::pushArg(uint32 n)
 {
     FrameEntry *fe = getArg(n);
-    if (!a->analysis->argEscapes(n)) {
+    if (!analysis->slotEscapes(analyze::ArgSlot(n))) {
         pushCopyOf(indexOfFe(fe));
     } else {
 #ifdef DEBUG
-        FrameEntry *fe = &args[n];
         if (fe->isTracked())
             JS_ASSERT(fe->data.inMemory());
 #endif
-        push(addressOf(fe), knownType);
+        JSValueType type = fe->isTypeKnown() ? fe->getKnownType() : JSVAL_TYPE_UNKNOWN;
+        push(addressOf(fe), type);
     }
 }
 
 inline void
 FrameState::pushCallee()
 {
     FrameEntry *fe = getCallee();
     pushCopyOf(indexOfFe(fe));
--- a/js/src/methodjit/FrameState.cpp
+++ b/js/src/methodjit/FrameState.cpp
@@ -58,16 +58,17 @@ FrameState::FrameState(JSContext *cx, mj
     loop(NULL), inTryBlock(false)
 {
 }
 
 FrameState::~FrameState()
 {
     while (a) {
         ActiveFrame *parent = a->parent;
+        a->script->analysis(cx)->clearAllocations();
 #if defined JS_NUNBOX32
         a->reifier.~ImmutableSync();
 #endif
         cx->free_(a);
         a = parent;
     }
 }
 
@@ -110,18 +111,17 @@ FrameState::getUnsyncedEntries(uint32 *p
             continue;
         }
 
         unsyncedEntries->append(entry);
     }
 }
 
 bool
-FrameState::pushActiveFrame(JSScript *script, uint32 argc,
-                            analyze::Script *analysis, analyze::LifetimeScript *liveness)
+FrameState::pushActiveFrame(JSScript *script, uint32 argc)
 {
     uint32 depth = a ? totalDepth() : 0;
     uint32 nentries = feLimit(script);
 
     size_t totalBytes = sizeof(ActiveFrame) +
                         sizeof(FrameEntry) * nentries +              // entries[]
                         sizeof(FrameEntry *) * nentries +            // tracker.entries
                         sizeof(StackEntryExtra) * script->nslots;    // extraArray
@@ -142,19 +142,16 @@ FrameState::pushActiveFrame(JSScript *sc
 
     newa->parent = a;
     newa->parentPC = PC;
     newa->parentSP = sp;
     newa->parentArgc = argc;
     newa->script = script;
     newa->freeRegs = Registers(Registers::AvailAnyRegs);
 
-    newa->analysis = analysis;
-    newa->liveness = liveness;
-
     newa->entries = (FrameEntry *)cursor;
     cursor += sizeof(FrameEntry) * nentries;
 
     newa->callee_ = newa->entries;
     newa->this_ = newa->entries + 1;
     newa->args = newa->entries + 2;
     newa->locals = newa->args + (script->fun ? script->fun->nargs : 0);
 
@@ -164,17 +161,17 @@ FrameState::pushActiveFrame(JSScript *sc
     newa->extraArray = (StackEntryExtra *)cursor;
     cursor += sizeof(StackEntryExtra) * script->nslots;
 
     JS_ASSERT(reinterpret_cast<uint8 *>(newa) + totalBytes == cursor);
 
     this->a = newa;
     updateActiveFrame();
 
-    if (a->parent && a->analysis->inlineable(argc)) {
+    if (a->parent && script->analysis(cx)->inlineable(argc)) {
         a->depth = depth + VALUES_PER_STACK_FRAME;
 
         /* Mark all registers which are in use by the parent or its own parent. */
         a->parentRegs = 0;
         Registers regs(Registers::AvailAnyRegs);
         while (!regs.empty()) {
             AnyRegisterID reg = regs.takeAnyReg();
             if (a->parent->parentRegs.hasReg(reg) || !a->parent->freeRegs.hasReg(reg))
@@ -252,31 +249,34 @@ FrameState::associateReg(FrameEntry *fe,
 
 void
 FrameState::popActiveFrame()
 {
     jsbytecode *parentPC = a->parentPC;
     FrameEntry *parentSP = a->parentSP;
     ActiveFrame *parent = a->parent;
 
+    analysis->clearAllocations();
+
 #if defined JS_NUNBOX32
     a->reifier.~ImmutableSync();
 #endif
     cx->free_(a);
 
     a = parent;
     updateActiveFrame();
     PC = parentPC;
     sp = parentSP;
 }
 
 void
 FrameState::updateActiveFrame()
 {
     script = a->script;
+    analysis = script->analysis(cx);
     entries = a->entries;
     callee_ = a->callee_;
     this_ = a->this_;
     args = a->args;
     locals = a->locals;
     spBase = locals + script->nfixed;
     sp = spBase;
     temporaries = locals + script->nslots;
@@ -459,17 +459,17 @@ FrameState::evictReg(AnyRegisterID reg)
 
 inline Lifetime *
 FrameState::variableLive(FrameEntry *fe, jsbytecode *pc) const
 {
     JS_ASSERT(cx->typeInferenceEnabled());
     JS_ASSERT(fe < spBase && fe != callee_);
 
     uint32 offset = pc - script->code;
-    return a->liveness->live(indexOfFe(fe), offset);
+    return analysis->liveness(indexOfFe(fe)).live(offset);
 }
 
 bool
 FrameState::isEntryCopied(FrameEntry *fe) const
 {
     /*
      * :TODO: It would be better for fe->isCopied() to mean 'is actually copied'
      * rather than 'might have copies', removing the need for this walk.
@@ -708,21 +708,21 @@ FrameState::dumpAllocation(RegisterAlloc
     printf("\n");
 }
 #endif
 
 RegisterAllocation *
 FrameState::computeAllocation(jsbytecode *target)
 {
     JS_ASSERT(cx->typeInferenceEnabled());
-    RegisterAllocation *alloc = ArenaNew<RegisterAllocation>(a->liveness->pool, false);
+    RegisterAllocation *alloc = ArenaNew<RegisterAllocation>(cx->compartment->pool, false);
     if (!alloc)
         return NULL;
 
-    if (a->analysis->getCode(target).exceptionEntry || a->analysis->getCode(target).switchTarget ||
+    if (analysis->getCode(target).exceptionEntry || analysis->getCode(target).switchTarget ||
         JSOp(*target) == JSOP_TRAP) {
         /* State must be synced at exception and switch targets, and at traps. */
 #ifdef DEBUG
         if (IsJaegerSpewChannelActive(JSpew_Regalloc)) {
             JaegerSpew(JSpew_Regalloc, "allocation at %u:", target - script->code);
             dumpAllocation(alloc);
         }
 #endif
@@ -816,17 +816,17 @@ FrameState::syncForBranch(jsbytecode *ta
 
     if (!cx->typeInferenceEnabled()) {
         syncAndForgetEverything();
         return true;
     }
 
     Registers regs = 0;
 
-    RegisterAllocation *&alloc = a->liveness->getCode(target).allocation;
+    RegisterAllocation *&alloc = analysis->getAllocation(target);
     if (!alloc) {
         alloc = computeAllocation(target);
         if (!alloc)
             return false;
     }
 
     /*
      * First pass. Sync all entries which will not be carried in a register,
@@ -950,24 +950,24 @@ FrameState::discardForJoin(jsbytecode *t
 {
     if (!cx->typeInferenceEnabled()) {
         resetInternalState();
         PodArrayZero(a->regstate_);
         sp = spBase + stackDepth;
         return true;
     }
 
-    RegisterAllocation *&alloc = a->liveness->getCode(target).allocation;
+    RegisterAllocation *&alloc = analysis->getAllocation(target);
 
     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 = ArenaNew<RegisterAllocation>(a->liveness->pool, false);
+        alloc = ArenaNew<RegisterAllocation>(cx->compartment->pool, false);
         if (!alloc)
             return false;
     }
 
     resetInternalState();
     PodArrayZero(a->regstate_);
 
     a->parentRegs = alloc->getParentRegs();
@@ -1014,17 +1014,17 @@ FrameState::consistentRegisters(jsbyteco
     }
 
     /*
      * Before calling this, either the entire state should have been synced or
      * syncForBranch should have been called. These will ensure that any FE
      * which is not consistent with the target's register state has already
      * been synced, and no stores will need to be issued by prepareForJump.
      */
-    RegisterAllocation *alloc = a->liveness->getCode(target).allocation;
+    RegisterAllocation *alloc = analysis->getAllocation(target);
     JS_ASSERT(alloc);
 
     Registers regs(Registers::AvailAnyRegs);
     while (!regs.empty()) {
         AnyRegisterID reg = regs.takeAnyReg();
         if (alloc->assigned(reg)) {
             FrameEntry *needed = getOrTrack(alloc->slot(reg));
             if (!a->freeRegs.hasReg(reg)) {
@@ -1046,17 +1046,17 @@ FrameState::consistentRegisters(jsbyteco
 void
 FrameState::prepareForJump(jsbytecode *target, Assembler &masm, bool synced)
 {
     if (!cx->typeInferenceEnabled())
         return;
 
     JS_ASSERT_IF(!synced, !consistentRegisters(target));
 
-    RegisterAllocation *alloc = a->liveness->getCode(target).allocation;
+    RegisterAllocation *alloc = analysis->getAllocation(target);
     JS_ASSERT(alloc);
 
     Registers regs = 0;
 
     regs = Registers(Registers::AvailAnyRegs);
     while (!regs.empty()) {
         AnyRegisterID reg = regs.takeAnyReg();
         if (!alloc->assigned(reg))
@@ -2301,68 +2301,53 @@ FrameState::separateBinaryEntries(FrameE
     if (rhs->isCopy() && rhs->copyOf() == lhs) {
         syncAndForgetFe(rhs);
         syncAndForgetFe(lhs);
         uncopy(lhs);
     }
 }
 
 void
-FrameState::storeLocal(uint32 n, JSValueType type, bool popGuaranteed, bool fixedType)
+FrameState::storeLocal(uint32 n, bool popGuaranteed, bool fixedType)
 {
     FrameEntry *local = getLocal(n);
 
-    if (a->analysis->localEscapes(n)) {
+    if (analysis->slotEscapes(indexOfFe(local))) {
         JS_ASSERT(local->data.inMemory());
         storeTo(peek(-1), addressOf(local), popGuaranteed);
         return;
     }
 
-    storeTop(local, type, popGuaranteed);
+    storeTop(local, popGuaranteed);
 
     if (loop)
         local->lastLoop = loop->headOffset();
 
-    if (type != JSVAL_TYPE_UNKNOWN && type != JSVAL_TYPE_DOUBLE &&
-        fixedType && !local->type.synced()) {
-        /*
-         * Except when inlining, known types are always in sync for locals.
-         * If we are inlining, the known type is filled in when the frame is
-         * expanded (which happens upon any recompilation activity).
-         */
-        local->type.sync();
-    }
-
     if (inTryBlock)
         syncFe(local);
 }
 
 void
-FrameState::storeArg(uint32 n, JSValueType type, bool popGuaranteed)
+FrameState::storeArg(uint32 n, bool popGuaranteed)
 {
     // Note that args are always immediately synced, because they can be
     // aliased (but not written to) via f.arguments.
     FrameEntry *arg = getArg(n);
 
-    if (a->analysis->argEscapes(n)) {
+    if (analysis->slotEscapes(indexOfFe(arg))) {
         JS_ASSERT(arg->data.inMemory());
         storeTo(peek(-1), addressOf(arg), popGuaranteed);
         return;
     }
 
-    storeTop(arg, type, popGuaranteed);
+    storeTop(arg, popGuaranteed);
 
     if (loop)
         arg->lastLoop = loop->headOffset();
 
-    if (type != JSVAL_TYPE_UNKNOWN && type != JSVAL_TYPE_DOUBLE && !arg->type.synced()) {
-        /* Known types are always in sync for args. (Frames which update args are not inlined). */
-        arg->type.sync();
-    }
-
     syncFe(arg);
 }
 
 void
 FrameState::forgetEntry(FrameEntry *fe)
 {
     if (fe->isCopied()) {
         uncopy(fe);
@@ -2372,36 +2357,47 @@ FrameState::forgetEntry(FrameEntry *fe)
         forgetAllRegs(fe);
     }
 
     if (fe >= spBase && fe < sp)
         a->extraArray[fe - spBase].reset();
 }
 
 void
-FrameState::storeTop(FrameEntry *target, JSValueType type, bool popGuaranteed)
+FrameState::storeTop(FrameEntry *target, bool popGuaranteed)
 {
     JS_ASSERT(!isTemporary(target));
 
     /* Detect something like (x = x) which is a no-op. */
     FrameEntry *top = peek(-1);
     if (top->isCopy() && top->copyOf() == target) {
         JS_ASSERT(target->isCopied());
         return;
     }
 
+    /*
+     * If this is overwriting a known non-double type with another value of the
+     * same type, then make sure we keep the type marked as synced after doing
+     * the copy.
+     */
+    bool wasSynced = target->type.synced();
+    JSValueType oldType = target->isTypeKnown() ? target->getKnownType() : JSVAL_TYPE_UNKNOWN;
+    bool trySyncType = wasSynced && oldType != JSVAL_TYPE_UNKNOWN && oldType != JSVAL_TYPE_DOUBLE;
+
     /* Completely invalidate the local variable. */
     forgetEntry(target);
     target->resetUnsynced();
 
     /* Constants are easy to propagate. */
     if (top->isConstant()) {
         target->setCopyOf(NULL);
         target->setNotCopied();
         target->setConstant(Jsvalify(top->getValue()));
+        if (trySyncType && target->isType(oldType))
+            target->type.sync();
         return;
     }
 
     /*
      * When dealing with copies, there are three important invariants:
      *
      * 1) The backing store precedes all copies in the tracker.
      * 2) The backing store precedes all copies in the FrameState.
@@ -2420,16 +2416,18 @@ FrameState::storeTop(FrameEntry *target,
         JS_ASSERT(backing->trackerIndex() < top->trackerIndex());
 
         if (backing < target) {
             /* local.idx < backing.idx means local cannot be a copy yet */
             if (target->trackerIndex() < backing->trackerIndex())
                 swapInTracker(backing, target);
             target->setNotCopied();
             target->setCopyOf(backing);
+            if (trySyncType && target->isType(oldType))
+                target->type.sync();
             return;
         }
 
         /*
          * If control flow lands here, then there was a bytecode sequence like
          *
          *  ENTERBLOCK 2
          *  GETLOCAL 1
@@ -2463,81 +2461,51 @@ FrameState::storeTop(FrameEntry *target,
      * consistent ordering - all copies of |backing| are tracked after 
      * |backing|. Transitively, only one swap is needed.
      */
     if (backing->trackerIndex() < target->trackerIndex())
         swapInTracker(backing, target);
 
     if (backing->isType(JSVAL_TYPE_DOUBLE)) {
         FPRegisterID fpreg = tempFPRegForData(backing);
-        if (type != JSVAL_TYPE_DOUBLE) {
-            masm.storeDouble(fpreg, addressOf(target));
-            target->resetSynced();
-
-            /* We're about to invalidate the backing, so forget the FP register. */
-            forgetReg(fpreg);
-        } else {
-            target->data.setFPRegister(fpreg);
-            regstate(fpreg).reassociate(target);
-        }
-
         target->setType(JSVAL_TYPE_DOUBLE);
+        target->data.setFPRegister(fpreg);
+        regstate(fpreg).reassociate(target);
     } else {
         /*
          * Move the backing store down - we spill registers here, but we could be
          * smarter and re-use the type reg. If we need registers for both the type
          * and data in the backing, make sure we keep the other components pinned.
          * There is nothing else to keep us from evicting the backing's registers.
          */
         if (backing->type.inRegister())
             pinReg(backing->type.reg());
         RegisterID reg = tempRegForData(backing);
         if (backing->type.inRegister())
             unpinReg(backing->type.reg());
         target->data.setRegister(reg);
         regstate(reg).reassociate(target);
 
-        if (type == JSVAL_TYPE_UNKNOWN) {
-            if (backing->isTypeKnown()) {
-                target->setType(backing->getKnownType());
-            } else {
-                pinReg(reg);
-                RegisterID typeReg = tempRegForType(backing);
-                unpinReg(reg);
-                target->type.setRegister(typeReg);
-                regstate(typeReg).reassociate(target);
-            }
-        } else if (type != JSVAL_TYPE_DOUBLE || backing->isType(JSVAL_TYPE_INT32)) {
-            /*
-             * Treat the stored entry as an int even if inference has marked it
-             * as a float (we will fixDoubles on it before branching), to avoid
-             * demoting the backing.
-             */
-            if (type == JSVAL_TYPE_DOUBLE)
-                type = JSVAL_TYPE_INT32;
-            JS_ASSERT_IF(backing->isTypeKnown(), backing->isType(type));
-            if (!backing->isTypeKnown())
-                learnType(backing, type);
-            target->setType(type);
+        if (backing->isTypeKnown()) {
+            target->setType(backing->getKnownType());
         } else {
-            FPRegisterID fpreg = allocFPReg();
-            syncFe(backing);
-            masm.moveInt32OrDouble(addressOf(backing), fpreg);
-
-            forgetAllRegs(backing);
-            backing->setType(JSVAL_TYPE_DOUBLE);
-            target->setType(JSVAL_TYPE_DOUBLE);
-            target->data.setFPRegister(fpreg);
-            regstate(fpreg).associate(target, RematInfo::DATA);
+            pinReg(reg);
+            RegisterID typeReg = tempRegForType(backing);
+            unpinReg(reg);
+            target->type.setRegister(typeReg);
+            regstate(typeReg).reassociate(target);
         }
     }
 
     backing->setCopyOf(target);
     JS_ASSERT(top->copyOf() == target);
 
+    if (trySyncType && target->isType(oldType))
+        target->type.sync();
+
     /*
      * Right now, |backing| is a copy of |target| (note the reversal), but
      * |target| is not marked as copied. This is an optimization so uncopy()
      * may avoid frame traversal.
      *
      * There are two cases where we must set the copy bit, however:
      *  - The fixup phase redirected more copies to |target|.
      *  - An immediate pop is not guaranteed.
@@ -2546,26 +2514,26 @@ FrameState::storeTop(FrameEntry *target,
         target->setCopied();
 }
 
 void
 FrameState::shimmy(uint32 n)
 {
     JS_ASSERT(sp - n >= spBase);
     int32 depth = 0 - int32(n);
-    storeTop(peek(depth - 1), JSVAL_TYPE_UNKNOWN, true);
+    storeTop(peek(depth - 1), true);
     popn(n);
 }
 
 void
 FrameState::shift(int32 n)
 {
     JS_ASSERT(n < 0);
     JS_ASSERT(sp + n - 1 >= spBase);
-    storeTop(peek(n - 1), JSVAL_TYPE_UNKNOWN, true);
+    storeTop(peek(n - 1), true);
     pop();
 }
 
 void
 FrameState::forgetKnownDouble(FrameEntry *fe)
 {
     /*
      * Forget all information indicating fe is a double, so we can use GPRs for its
--- a/js/src/methodjit/FrameState.h
+++ b/js/src/methodjit/FrameState.h
@@ -363,18 +363,18 @@ class FrameState
     /*
      * Temporarily increase and decrease local variable depth.
      */
     inline void enterBlock(uint32 n);
     inline void leaveBlock(uint32 n);
 
     // Pushes a copy of a slot (formal argument, local variable, or stack slot)
     // onto the operation stack.
-    void pushLocal(uint32 n, JSValueType knownType);
-    void pushArg(uint32 n, JSValueType knownType);
+    void pushLocal(uint32 n);
+    void pushArg(uint32 n);
     void pushCallee();
     void pushThis();
     void pushTemporary(FrameEntry *fe);
     inline void learnThisIsObject(bool unsync = true);
 
     inline FrameEntry *getStack(uint32 slot);
     inline FrameEntry *getLocal(uint32 slot);
     inline FrameEntry *getArg(uint32 slot);
@@ -598,23 +598,20 @@ class FrameState
 
     /*
      * Fully stores a FrameEntry into two arbitrary registers. tempReg may be
      * used as a temporary.
      */
     void loadForReturn(FrameEntry *fe, RegisterID typeReg, RegisterID dataReg, RegisterID tempReg);
     void loadThisForReturn(RegisterID typeReg, RegisterID dataReg, RegisterID tempReg);
 
-    /*
-     * Stores the top stack slot back to a local or slot.  type indicates any known
-     * type for the local/slot.
-     */
-    void storeLocal(uint32 n, JSValueType type, bool popGuaranteed = false, bool fixedType = false);
-    void storeArg(uint32 n, JSValueType type, bool popGuaranteed = false);
-    void storeTop(FrameEntry *target, JSValueType type, bool popGuaranteed);
+    /* Stores the top stack slot back to a local or slot. */
+    void storeLocal(uint32 n, bool popGuaranteed = false, bool fixedType = false);
+    void storeArg(uint32 n, bool popGuaranteed = false);
+    void storeTop(FrameEntry *target, bool popGuaranteed);
 
     /*
      * Restores state from a slow path.
      */
     void merge(Assembler &masm, Changes changes) const;
 
     /*
      * Writes unsynced stores to an arbitrary buffer.
@@ -867,18 +864,17 @@ class FrameState
 
     inline uint32 regsInUse() const { return Registers::AvailRegs & ~a->freeRegs.freeMask; }
 
     void setPC(jsbytecode *PC) { this->PC = PC; }
     void setLoop(LoopState *loop) { this->loop = loop; }
 
     void getUnsyncedEntries(uint32 *pdepth, Vector<UnsyncedEntry> *unsyncedEntries);
 
-    bool pushActiveFrame(JSScript *script, uint32 argc,
-                         analyze::Script *analysis, analyze::LifetimeScript *liveness);
+    bool pushActiveFrame(JSScript *script, uint32 argc);
     void popActiveFrame();
 
     void discardLocalRegisters();
     void evictInlineModifiedRegisters(Registers regs);
     void syncParentRegistersInMask(Assembler &masm, uint32 mask, bool update) const;
     void restoreParentRegistersInMask(Assembler &masm, uint32 mask, bool update) const;
     Registers getParentRegs() const { return a->parentRegs; }
 
@@ -1072,25 +1068,23 @@ class FrameState
         const RegisterState & regstate(AnyRegisterID reg) const {
             JS_ASSERT(reg.reg_ < Registers::TotalAnyRegisters);
             return regstate_[reg.reg_];
         }
 
 #if defined JS_NUNBOX32
         mutable ImmutableSync reifier;
 #endif
-
-        analyze::Script *analysis;
-        analyze::LifetimeScript *liveness;
     };
     ActiveFrame *a;
 
     /* State derived/copied from the active frame. :XXX: remove? */
 
     JSScript *script;
+    analyze::ScriptAnalysis *analysis;
 
     FrameEntry *entries;
     FrameEntry *callee_;
     FrameEntry *this_;
 
     /* Base pointer for arguments. */
     FrameEntry *args;
 
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -376,18 +376,16 @@ UncachedInlineCall(VMFrame &f, uint32 fl
         } else {
             if (!newscript->typeSetThis(cx, &argTypes[0]))
                 return false;
         }
         for (unsigned i = 0; i < argc; i++) {
             if (!newscript->typeSetArgument(cx, i, &argTypes[1 + i]))
                 return false;
         }
-        if (!cx->compartment->types.checkPendingRecompiles(cx))
-            return false;
     } else {
         CallArgs args = CallArgsFromVp(argc, vp);
         if (!cx->typeMonitorCall(args, flags & JSFRAME_CONSTRUCTING))
             return false;
     }
 
     /* Get pointer to new frame/slots, prepare arguments. */
     StackSpace &stack = cx->stack();
--- a/js/src/methodjit/LoopState.cpp
+++ b/js/src/methodjit/LoopState.cpp
@@ -41,19 +41,18 @@
 #include "methodjit/FrameState-inl.h"
 #include "methodjit/StubCalls.h"
 
 using namespace js;
 using namespace js::mjit;
 using namespace js::analyze;
 
 LoopState::LoopState(JSContext *cx, JSScript *script,
-                     mjit::Compiler *cc, FrameState *frame,
-                     Script *analysis, LifetimeScript *liveness)
-    : cx(cx), script(script), cc(*cc), frame(*frame), analysis(analysis), liveness(liveness),
+                     mjit::Compiler *cc, FrameState *frame)
+    : cx(cx), script(script), analysis(script->analysis(cx)), cc(*cc), frame(*frame),
       lifetime(NULL), alloc(NULL), loopRegs(0), skipAnalysis(false),
       loopJoins(CompilerAllocPolicy(cx, *cc)),
       loopPatches(CompilerAllocPolicy(cx, *cc)),
       restoreInvariantCalls(CompilerAllocPolicy(cx, *cc)),
       invariantEntries(CompilerAllocPolicy(cx, *cc)),
       outer(NULL), PC(NULL),
       testLHS(UNASSIGNED), testRHS(UNASSIGNED),
       testConstant(0), testLessEqual(false), testLength(false),
@@ -62,28 +61,23 @@ LoopState::LoopState(JSContext *cx, JSSc
       modifiedProperties(CompilerAllocPolicy(cx, *cc))
 {
     JS_ASSERT(cx->typeInferenceEnabled());
 }
 
 bool
 LoopState::init(jsbytecode *head, Jump entry, jsbytecode *entryTarget)
 {
-    this->lifetime = liveness->getCode(head).loop;
+    this->lifetime = analysis->getLoop(head);
     JS_ASSERT(lifetime &&
               lifetime->head == uint32(head - script->code) &&
               lifetime->entry == uint32(entryTarget - script->code));
 
     this->entry = entry;
 
-    if (!stack.analyze(liveness->pool, script, lifetime->head,
-                       lifetime->backedge - lifetime->head + 1, analysis)) {
-        return false;
-    }
-
     analyzeLoopTest();
     analyzeLoopIncrements();
     analyzeModset();
 
     if (testLHS != UNASSIGNED) {
         JaegerSpew(JSpew_Analysis, "loop test at %u: %s %s%s %s + %d\n", lifetime->head,
                    frame.entryName(testLHS),
                    testLessEqual ? "<=" : ">=",
@@ -104,20 +98,20 @@ LoopState::init(jsbytecode *head, Jump e
     }
 
     for (unsigned i = 0; i < modifiedProperties.length(); i++) {
         JaegerSpew(JSpew_Analysis, "loop modified property at %u: %s %s\n", lifetime->head,
                    modifiedProperties[i].object->name(),
                    types::TypeIdString(modifiedProperties[i].id));
     }
 
-    RegisterAllocation *&alloc = liveness->getCode(head).allocation;
+    RegisterAllocation *&alloc = analysis->getAllocation(head);
     JS_ASSERT(!alloc);
 
-    alloc = ArenaNew<RegisterAllocation>(liveness->pool, true);
+    alloc = ArenaNew<RegisterAllocation>(cx->compartment->pool, true);
     if (!alloc)
         return false;
 
     this->alloc = alloc;
     this->loopRegs = Registers::AvailAnyRegs;
     this->PC = head;
 
     /*
@@ -237,36 +231,19 @@ LoopState::clearLoopRegisters()
 {
     alloc->clearLoops();
     loopRegs = 0;
 }
 
 bool
 LoopState::loopInvariantEntry(uint32 slot)
 {
-    unsigned nargs = script->fun ? script->fun->nargs : 0;
-
-    if (slot >= 2 + nargs + script->nfixed)
-        return false;
-
-    if (liveness->firstWrite(slot, lifetime) != uint32(-1))
-        return false;
-
-    if (slot == 0) /* callee */
+    if (slot == analyze::CalleeSlot() || analysis->slotEscapes(slot))
         return false;
-    if (slot == 1) /* this */
-        return true;
-    slot -= 2;
-
-    if (slot < nargs && !analysis->argEscapes(slot))
-        return true;
-    if (script->fun)
-        slot -= script->fun->nargs;
-
-    return !analysis->localEscapes(slot);
+    return analysis->liveness(slot).firstWrite(lifetime) == uint32(-1);
 }
 
 bool
 LoopState::addHoistedCheck(uint32 arraySlot, uint32 valueSlot1, uint32 valueSlot2, int32 constant)
 {
 #ifdef DEBUG
     JS_ASSERT_IF(valueSlot1 == UNASSIGNED, valueSlot2 == UNASSIGNED);
     if (valueSlot1 == UNASSIGNED) {
@@ -389,17 +366,17 @@ LoopState::setLoopReg(AnyRegisterID reg,
     }
 
     if (lifetime->entry != lifetime->head && PC >= script->code + lifetime->entry) {
         /*
          * We've advanced past the entry point of the loop (we're analyzing the condition),
          * so need to update the register state at that entry point so that the right
          * things get loaded when we enter the loop.
          */
-        RegisterAllocation *entry = liveness->getCode(lifetime->entry).allocation;
+        RegisterAllocation *entry = analysis->getAllocation(lifetime->entry);
         JS_ASSERT(entry && !entry->assigned(reg));
         entry->set(reg, slot, true);
     }
 }
 
 inline bool
 SafeAdd(int32 one, int32 two, int32 *res)
 {
@@ -418,43 +395,38 @@ SafeSub(int32 one, int32 two, int32 *res
     int64 ores = (int64)one - (int64)two;
     if (ores == (int64)*res)
         return true;
     JaegerSpew(JSpew_Analysis, "Overflow computing %d - %d\n", one, two);
     return false;
 }
 
 bool
-LoopState::hoistArrayLengthCheck(const FrameEntry *obj, unsigned indexPopped)
+LoopState::hoistArrayLengthCheck(const FrameEntry *obj, types::TypeSet *objTypes,
+                                 unsigned indexPopped)
 {
     if (skipAnalysis || script->failedBoundsCheck)
         return false;
 
-    /*
-     * Note: this should only be used when the object is known to be a dense
-     * array (if it is an object at all).
-     */
-
     obj = obj->backing();
 
     JaegerSpew(JSpew_Analysis, "Trying to hoist bounds check on %s\n",
                frame.entryName(obj));
 
     if (!loopInvariantEntry(frame.indexOfFe(obj))) {
         JaegerSpew(JSpew_Analysis, "Object is not loop invariant\n");
         return false;
     }
 
     /*
      * Check for an overlap with the arrays we think might grow in this loop.
      * This information is only a guess; if we don't think the array can grow
      * but it actually can, we will probably recompile after the hoisted
      * bounds check fails.
      */
-    types::TypeSet *objTypes = cc.getTypeSet(obj);
     JS_ASSERT(objTypes && !objTypes->unknown());
     if (!growArrays.empty()) {
         unsigned count = objTypes->getObjectCount();
         for (unsigned i = 0; i < count; i++) {
             types::TypeObject *object = objTypes->getObject(i);
             if (object) {
                 for (unsigned j = 0; j < growArrays.length(); j++) {
                     if (object == growArrays[j]) {
@@ -487,48 +459,44 @@ LoopState::hoistArrayLengthCheck(const F
         return addHoistedCheck(frame.indexOfFe(obj), index, UNASSIGNED, indexConstant);
     }
 
     /*
      * If the LHS can decrease in the loop, it could become negative and
      * underflow the array. We currently only hoist bounds checks for loops
      * which walk arrays going forward.
      */
-    if (!liveness->nonDecreasing(index, lifetime)) {
+    if (!analysis->liveness(index).nonDecreasing(script, lifetime)) {
         JaegerSpew(JSpew_Analysis, "Index may decrease in future iterations\n");
         return false;
     }
 
     /*
      * If the access is of the form x[y + a] where we know that y <= z + b
      * (both in terms of state at the head of the loop), hoist as follows:
      *
      * y + a < initlen(x)
      * y < initlen(x) - a
      * z + b < initlen(x) - a
      * z + b - a < initlen(x)
      */
     if (index == testLHS && testLessEqual) {
         uint32 rhs = testRHS;
 
-        if (rhs != UNASSIGNED) {
-            types::TypeSet *types = cc.getTypeSet(rhs);
-            if (!types) {
-                JaegerSpew(JSpew_Analysis, "Unknown type of branch test\n");
-                return false;
-            }
-            if (testLength) {
-                FrameEntry *rhsFE = frame.getOrTrack(rhs);
-                FrameEntry *lengthEntry = invariantLength(rhsFE);
-                if (!lengthEntry) {
-                    JaegerSpew(JSpew_Analysis, "Could not get invariant entry for length\n");
-                    return false;
-                }
-                rhs = frame.indexOfFe(lengthEntry);
-            }
+        if (testLength) {
+            FrameEntry *rhsFE = frame.getOrTrack(rhs);
+            FrameEntry *lengthEntry = invariantLength(rhsFE, NULL);
+
+            /*
+             * An entry for the length should have been constructed while
+             * processing the test.
+             */
+            JS_ASSERT(lengthEntry);
+
+            rhs = frame.indexOfFe(lengthEntry);
         }
 
         int32 constant;
         if (!SafeSub(testConstant, indexConstant, &constant))
             return false;
 
         /*
          * Check that the LHS is nonnegative every time we rejoin the loop.
@@ -607,17 +575,17 @@ LoopState::invariantSlots(const FrameEnt
     }
 
     /* addHoistedCheck should have ensured there is an entry for the slots. */
     JS_NOT_REACHED("Missing invariant slots");
     return NULL;
 }
 
 FrameEntry *
-LoopState::invariantLength(const FrameEntry *obj)
+LoopState::invariantLength(const FrameEntry *obj, types::TypeSet *objTypes)
 {
     if (skipAnalysis || script->failedBoundsCheck)
         return NULL;
 
     obj = obj->backing();
     uint32 slot = frame.indexOfFe(obj);
 
     for (unsigned i = 0; i < invariantEntries.length(); i++) {
@@ -625,38 +593,39 @@ LoopState::invariantLength(const FrameEn
         if (entry.kind == InvariantEntry::INVARIANT_LENGTH &&
             entry.u.array.arraySlot == slot) {
             FrameEntry *fe = frame.getTemporary(entry.u.array.temporary);
             frame.learnType(fe, JSVAL_TYPE_INT32, false);
             return fe;
         }
     }
 
+    if (!objTypes)
+        return NULL;
+
     if (!loopInvariantEntry(frame.indexOfFe(obj)))
         return NULL;
 
-    /* Make sure this is a dense array whose length fits in an int32. */
-    types::TypeSet *types = cc.getTypeSet(slot);
-    types::ObjectKind kind = types ? types->getKnownObjectKind(cx) : types::OBJECT_UNKNOWN;
+    types::ObjectKind kind = objTypes->getKnownObjectKind(cx);
     if (kind != types::OBJECT_DENSE_ARRAY && kind != types::OBJECT_PACKED_ARRAY)
         return NULL;
 
     /*
      * Don't make 'length' loop invariant if the loop might directly write
      * to the elements of any of the accessed arrays. This could invoke an
      * inline path which updates the length.
      */
-    for (unsigned i = 0; i < types->getObjectCount(); i++) {
-        types::TypeObject *object = types->getObject(i);
+    for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
+        types::TypeObject *object = objTypes->getObject(i);
         if (!object)
             continue;
         if (object->unknownProperties() || hasModifiedProperty(object, JSID_VOID))
             return NULL;
     }
-    types->addFreeze(cx);
+    objTypes->addFreeze(cx);
 
     uint32 which = frame.allocTemporary();
     if (which == uint32(-1))
         return NULL;
     FrameEntry *fe = frame.getTemporary(which);
     frame.learnType(fe, JSVAL_TYPE_INT32, false);
 
     JaegerSpew(JSpew_Analysis, "Using %s for loop invariant length of %s\n",
@@ -671,19 +640,19 @@ LoopState::invariantLength(const FrameEn
     return fe;
 }
 
 void
 LoopState::restoreInvariants(jsbytecode *pc, Assembler &masm, Vector<Jump> *jumps)
 {
     /*
      * Restore all invariants in memory when entering the loop or after any
-     * scripted or C++ call, and check that all hoisted conditions. Care should
-     * be taken not to clobber the return register or callee-saved registers,
-     * which may still be live after some calls.
+     * scripted or C++ call, and check that all hoisted conditions still hold.
+     * Care should be taken not to clobber the return register or callee-saved
+     * registers, which may still be live after some calls.
      */
 
     Registers regs(Registers::TempRegs);
     regs.takeReg(Registers::ReturnReg);
 
     RegisterID T0 = regs.takeAnyReg().reg();
     RegisterID T1 = regs.takeAnyReg().reg();
 
@@ -734,22 +703,19 @@ LoopState::restoreInvariants(jsbytecode 
             }
             Jump j = masm.branch32(Assembler::LessThan, T0, Imm32(0));
             jumps->append(j);
             break;
           }
 
           case InvariantEntry::INVARIANT_SLOTS:
           case InvariantEntry::INVARIANT_LENGTH: {
-            /* Make sure this is an object before trying to access its slots or length. */
             uint32 array = entry.u.array.arraySlot;
-            if (cc.getTypeSet(array)->getKnownTypeTag(cx) != JSVAL_TYPE_OBJECT) {
-                Jump notObject = masm.testObject(Assembler::NotEqual, frame.addressOf(array));
-                jumps->append(notObject);
-            }
+            Jump notObject = masm.testObject(Assembler::NotEqual, frame.addressOf(array));
+            jumps->append(notObject);
             masm.loadPayload(frame.addressOf(array), T0);
 
             uint32 offset = (entry.kind == InvariantEntry::INVARIANT_SLOTS)
                 ? JSObject::offsetOfSlots()
                 : offsetof(JSObject, privateData);
 
             masm.loadPtr(Address(T0, offset), T0);
             masm.storePtr(T0, frame.addressOf(frame.getTemporary(entry.u.array.temporary)));
@@ -759,82 +725,41 @@ LoopState::restoreInvariants(jsbytecode 
           default:
             JS_NOT_REACHED("Bad invariant kind");
         }
     }
 }
 
 /* Loop analysis methods. */
 
-/* :XXX: factor out into more common code. */
-static inline uint32 localSlot(JSScript *script, uint32 local) {
-    return 2 + (script->fun ? script->fun->nargs : 0) + local;
-}
-static inline uint32 argSlot(uint32 arg) {
-    return 2 + arg;
-}
-static inline uint32 thisSlot() {
-    return 1;
-}
-
 /* Whether pc is a loop test operand accessing a variable modified by the loop. */
 bool
 LoopState::loopVariableAccess(jsbytecode *pc)
 {
     switch (JSOp(*pc)) {
       case JSOP_GETLOCAL:
       case JSOP_INCLOCAL:
       case JSOP_DECLOCAL:
       case JSOP_LOCALINC:
       case JSOP_LOCALDEC:
-        if (analysis->localEscapes(GET_SLOTNO(pc)))
-            return false;
-        return liveness->firstWrite(localSlot(script, GET_SLOTNO(pc)), lifetime) != uint32(-1);
       case JSOP_GETARG:
       case JSOP_INCARG:
       case JSOP_DECARG:
       case JSOP_ARGINC:
-      case JSOP_ARGDEC:
-        if (analysis->argEscapes(GET_SLOTNO(pc)))
+      case JSOP_ARGDEC: {
+        uint32 slot = GetBytecodeSlot(script, pc);
+        if (analysis->slotEscapes(slot))
             return false;
-        return liveness->firstWrite(argSlot(GET_SLOTNO(pc)), lifetime) != uint32(-1);
+        return analysis->liveness(slot).firstWrite(lifetime) != uint32(-1);
+      }
       default:
         return false;
     }
 }
 
-static inline int32
-GetBytecodeInteger(jsbytecode *pc)
-{
-    switch (JSOp(*pc)) {
-
-      case JSOP_ZERO:
-        return 0;
-
-      case JSOP_ONE:
-        return 1;
-
-      case JSOP_UINT16:
-        return (int32_t) GET_UINT16(pc);
-
-      case JSOP_UINT24:
-        return (int32_t) GET_UINT24(pc);
-
-      case JSOP_INT8:
-        return GET_INT8(pc);
-
-      case JSOP_INT32:
-        return GET_INT32(pc);
-
-      default:
-        JS_NOT_REACHED("Bad op");
-        return 0;
-    }
-}
-
 /*
  * Get any slot/constant accessed by a loop test operand, in terms of its value
  * at the start of the next loop iteration.
  */
 bool
 LoopState::getLoopTestAccess(jsbytecode *pc, uint32 *pslot, int32 *pconstant)
 {
     *pslot = UNASSIGNED;
@@ -845,47 +770,46 @@ LoopState::getLoopTestAccess(jsbytecode 
      * (e.g. 'x++ < n'), we need to account for the modification --- at the start
      * of the next iteration, the value compared will have been 'x - 1'.
      * Note that we don't need to worry about other accesses to the variable
      * in the condition like 'x++ < x', as loop tests where both operands are
      * modified by the loop are rejected.
      */
 
     JSOp op = JSOp(*pc);
+    const JSCodeSpec *cs = &js_CodeSpec[op];
+
     switch (op) {
 
       case JSOP_GETLOCAL:
       case JSOP_INCLOCAL:
       case JSOP_DECLOCAL:
       case JSOP_LOCALINC:
-      case JSOP_LOCALDEC: {
-        uint32 local = GET_SLOTNO(pc);
-        if (analysis->localEscapes(local))
-            return false;
-        *pslot = localSlot(script, local);
-        if (op == JSOP_LOCALINC)
-            *pconstant = -1;
-        else if (op == JSOP_LOCALDEC)
-            *pconstant = 1;
-        return true;
-      }
-
+      case JSOP_LOCALDEC:
       case JSOP_GETARG:
       case JSOP_INCARG:
       case JSOP_DECARG:
       case JSOP_ARGINC:
       case JSOP_ARGDEC: {
-        uint32 arg = GET_SLOTNO(pc);
-        if (analysis->argEscapes(arg))
+        uint32 slot = GetBytecodeSlot(script, pc);
+        if (analysis->slotEscapes(slot))
+            return false;
+
+        /* Only consider tests on known integers. */
+        types::TypeSet *types = analysis->pushedTypes(pc, 0);
+        if (types->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
             return false;
-        *pslot = argSlot(arg);
-        if (op == JSOP_ARGINC)
-            *pconstant = -1;
-        else if (op == JSOP_ARGDEC)
-            *pconstant = 1;
+
+        *pslot = slot;
+        if (cs->format & JOF_POST) {
+            if (cs->format & JOF_INC)
+                *pconstant = -1;
+            else
+                *pconstant = 1;
+        }
         return true;
       }
 
       case JSOP_ZERO:
       case JSOP_ONE:
       case JSOP_UINT16:
       case JSOP_UINT24:
       case JSOP_INT8:
@@ -908,40 +832,38 @@ LoopState::analyzeLoopTest()
     /* Don't handle loops with branching inside their condition. */
     if (lifetime->entry < lifetime->lastBlock)
         return;
 
     /* Get the test performed before branching. */
     jsbytecode *backedge = script->code + lifetime->backedge;
     if (JSOp(*backedge) != JSOP_IFNE)
         return;
-    StackAnalysis::PoppedValue test = stack.popped(backedge, 0);
-    if (test.offset == StackAnalysis::UNKNOWN_PUSHED)
+    const SSAValue &test = analysis->poppedValue(backedge, 0);
+    if (test.kind() != SSAValue::PUSHED)
         return;
-    JSOp cmpop = JSOp(script->code[test.offset]);
+    JSOp cmpop = JSOp(script->code[test.pushedOffset()]);
     switch (cmpop) {
       case JSOP_GT:
       case JSOP_GE:
       case JSOP_LT:
       case JSOP_LE:
         break;
       default:
         return;
     }
 
-    StackAnalysis::PoppedValue poppedOne = stack.popped(test.offset, 1);
-    StackAnalysis::PoppedValue poppedTwo = stack.popped(test.offset, 0);
+    const SSAValue &poppedOne = analysis->poppedValue(test.pushedOffset(), 1);
+    const SSAValue &poppedTwo = analysis->poppedValue(test.pushedOffset(), 0);
 
-    if (poppedOne.offset == StackAnalysis::UNKNOWN_PUSHED ||
-        poppedTwo.offset == StackAnalysis::UNKNOWN_PUSHED) {
+    if (poppedOne.kind() != SSAValue::PUSHED || poppedTwo.kind() != SSAValue::PUSHED)
         return;
-    }
 
-    jsbytecode *one = script->code + poppedOne.offset;
-    jsbytecode *two = script->code + poppedTwo.offset;
+    jsbytecode *one = script->code + poppedOne.pushedOffset();
+    jsbytecode *two = script->code + poppedTwo.pushedOffset();
 
     /* Reverse the condition if the RHS is modified by the loop. */
     if (loopVariableAccess(two)) {
         jsbytecode *tmp = one;
         one = two;
         two = tmp;
         cmpop = ReverseCompareOp(cmpop);
     }
@@ -956,62 +878,43 @@ LoopState::analyzeLoopTest()
         return;
 
     uint32 rhs = UNASSIGNED;
     int32 rhsConstant = 0;
     bool rhsLength = false;
 
     if (JSOp(*two) == JSOP_LENGTH) {
         /* Handle 'this.length' or 'x.length' for loop invariant 'x'. */
-        StackAnalysis::PoppedValue array = stack.popped(two, 0);
-        if (array.offset == StackAnalysis::UNKNOWN_PUSHED)
+        const SSAValue &array = analysis->poppedValue(two, 0);
+        if (array.kind() != SSAValue::PUSHED)
             return;
-        jsbytecode *arraypc = script->code + array.offset;
+        jsbytecode *arraypc = script->code + array.pushedOffset();
         if (loopVariableAccess(arraypc))
             return;
         switch (JSOp(*arraypc)) {
-          case JSOP_GETLOCAL: {
-            uint32 local = GET_SLOTNO(arraypc);
-            if (analysis->localEscapes(local))
-                return;
-            rhs = localSlot(script, local);
+          case JSOP_GETLOCAL:
+          case JSOP_GETARG:
+          case JSOP_THIS: {
+            rhs = GetBytecodeSlot(script, arraypc);
             break;
           }
-          case JSOP_GETARG: {
-            uint32 arg = GET_SLOTNO(arraypc);
-            if (analysis->argEscapes(arg))
-                return;
-            rhs = argSlot(arg);
-            break;
-          }
-          case JSOP_THIS:
-            rhs = thisSlot();
-            break;
           default:
             return;
         }
+        if (!invariantLength(frame.getOrTrack(rhs), analysis->getValueTypes(array)))
+            return;
         rhsLength = true;
     } else {
         if (!getLoopTestAccess(two, &rhs, &rhsConstant))
             return;
     }
 
     if (lhs == UNASSIGNED)
         return;
 
-    /* Only consider comparisons on known integers. */
-    types::TypeSet *lhsTypes = cc.getTypeSet(lhs);
-    if (!lhsTypes || lhsTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
-        return;
-    if (rhs != UNASSIGNED && !rhsLength) {
-        types::TypeSet *rhsTypes = cc.getTypeSet(rhs);
-        if (!rhsTypes || rhsTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
-            return;
-    }
-
     int32 constant;
     if (!SafeSub(rhsConstant, lhsConstant, &constant))
         return;
 
     /* x > y ==> x >= y + 1 */
     if (cmpop == JSOP_GT && !SafeAdd(constant, 1, &constant))
         return;
 
@@ -1032,59 +935,36 @@ void
 LoopState::analyzeLoopIncrements()
 {
     /*
      * Find locals and arguments which are used in exactly one inc/dec operation in every
      * iteration of the loop (we only match against the last basic block, but could
      * also handle the first basic block).
      */
 
-    unsigned nargs = script->fun ? script->fun->nargs : 0;
-    for (unsigned i = 0; i < nargs; i++) {
-        if (analysis->argEscapes(i))
+    for (uint32 slot = ArgSlot(0); slot < LocalSlot(script, script->nfixed); slot++) {
+        if (analysis->slotEscapes(slot))
             continue;
 
-        uint32 offset = liveness->onlyWrite(argSlot(i), lifetime);
+        uint32 offset = analysis->liveness(slot).onlyWrite(lifetime);
         if (offset == uint32(-1) || offset < lifetime->lastBlock)
             continue;
 
         JSOp op = JSOp(script->code[offset]);
-        if (op == JSOP_SETARG)
-            continue;
-
-        types::TypeSet *types = cc.getTypeSet(argSlot(i));
-        if (!types || types->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
-            continue;
-
-        Increment inc;
-        inc.slot = argSlot(i);
-        inc.offset = offset;
-        increments.append(inc);
-    }
-
-    for (unsigned i = 0; i < script->nfixed; i++) {
-        if (analysis->localEscapes(i))
-            continue;
+        const JSCodeSpec *cs = &js_CodeSpec[op];
+        if (cs->format & (JOF_INC | JOF_DEC)) {
+            types::TypeSet *types = analysis->pushedTypes(offset);
+            if (types->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
+                continue;
 
-        uint32 offset = liveness->onlyWrite(localSlot(script, i), lifetime);
-        if (offset == uint32(-1) || offset < lifetime->lastBlock)
-            continue;
-
-        JSOp op = JSOp(script->code[offset]);
-        if (op == JSOP_SETLOCAL || op == JSOP_SETLOCALPOP)
-            continue;
-
-        types::TypeSet *types = cc.getTypeSet(localSlot(script, i));
-        if (!types || types->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
-            continue;
-
-        Increment inc;
-        inc.slot = localSlot(script, i);
-        inc.offset = offset;
-        increments.append(inc);
+            Increment inc;
+            inc.slot = slot;
+            inc.offset = offset;
+            increments.append(inc);
+        }
     }
 }
 
 void
 LoopState::analyzeModset()
 {
     /* :XXX: Currently only doing this for arrays modified in the loop. */
 
@@ -1099,18 +979,18 @@ LoopState::analyzeModset()
             continue;
         }
 
         JSOp op = JSOp(*pc);
         switch (op) {
 
           case JSOP_SETHOLE:
           case JSOP_SETELEM: {
-            types::TypeSet *objTypes = poppedTypes(pc, 2);
-            types::TypeSet *elemTypes = poppedTypes(pc, 1);
+            types::TypeSet *objTypes = analysis->poppedTypes(pc, 2);
+            types::TypeSet *elemTypes = analysis->poppedTypes(pc, 1);
 
             /*
              * Mark the modset as unknown if the index might be non-integer,
              * we don't want to consider the SETELEM PIC here.
              */
             if (!objTypes || objTypes->unknown() || !elemTypes ||
                 elemTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) {
                 unknownModset = true;
@@ -1246,69 +1126,49 @@ LoopState::adjustConstantForIncrement(js
       case JSOP_ARGDEC:
         return -1;
       default:
         JS_NOT_REACHED("Bad op");
         return 0;
     }
 }
 
-inline types::TypeSet *
-LoopState::poppedTypes(jsbytecode *pc, unsigned which)
-{
-    StackAnalysis::PoppedValue value = stack.popped(pc, which);
-    if (value.offset == StackAnalysis::UNKNOWN_PUSHED)
-        return NULL;
-    return script->types->pushed(value.offset, value.which);
-}
-
 bool
 LoopState::getEntryValue(uint32 offset, uint32 popped, uint32 *pslot, int32 *pconstant)
 {
     /*
      * For a stack value popped by the bytecode at offset, try to get an
      * expression 'slot + constant' with the same value as the stack value
      * and expressed in terms of the state at loop entry.
      */
-    StackAnalysis::PoppedValue value = stack.popped(offset, popped);
-    if (value.offset == StackAnalysis::UNKNOWN_PUSHED)
+    const SSAValue &value = analysis->poppedValue(offset, popped);
+    if (value.kind() != SSAValue::PUSHED)
         return false;
 
-    jsbytecode *pc = script->code + value.offset;
+    jsbytecode *pc = script->code + value.pushedOffset();
     JSOp op = (JSOp)*pc;
 
     switch (op) {
 
       case JSOP_GETLOCAL:
       case JSOP_LOCALINC:
-      case JSOP_INCLOCAL: {
-        uint32 local = GET_SLOTNO(pc);
-        if (analysis->localEscapes(local))
+      case JSOP_INCLOCAL:
+      case JSOP_GETARG:
+      case JSOP_ARGINC:
+      case JSOP_INCARG: {
+        uint32 slot = GetBytecodeSlot(script, pc);
+        if (analysis->slotEscapes(slot))
             return false;
-        uint32 write = liveness->firstWrite(localSlot(script, local), lifetime);
-        if (write != uint32(-1) && write < value.offset) {
+        uint32 write = analysis->liveness(slot).firstWrite(lifetime);
+        if (write != uint32(-1) && write < value.pushedOffset()) {
             /* Variable has been modified since the start of the loop. */
             return false;
         }
-        *pslot = localSlot(script, local);
-        *pconstant = (op == JSOP_INCLOCAL) ? 1 : 0;
-        return true;
-      }
-
-      case JSOP_GETARG:
-      case JSOP_ARGINC:
-      case JSOP_INCARG: {
-        uint32 arg = GET_SLOTNO(pc);
-        if (analysis->argEscapes(arg))
-            return false;
-        uint32 write = liveness->firstWrite(argSlot(arg), lifetime);
-        if (write != uint32(-1) && write < value.offset)
-            return false;
-        *pslot = argSlot(arg);
-        *pconstant = (op == JSOP_INCARG) ? 1 : 0;
+        *pslot = slot;
+        *pconstant = (op == JSOP_INCLOCAL || op == JSOP_INCARG) ? 1 : 0;
         return true;
       }
 
       case JSOP_ZERO:
       case JSOP_ONE:
       case JSOP_UINT16:
       case JSOP_UINT24:
       case JSOP_INT8:
--- a/js/src/methodjit/LoopState.h
+++ b/js/src/methodjit/LoopState.h
@@ -84,23 +84,22 @@ namespace mjit {
  * involved in an 'invariant' then we will reload the invariant's new value
  * after the call finishes.
  */
 
 class LoopState : public MacroAssemblerTypedefs
 {
     JSContext *cx;
     JSScript *script;
+    analyze::ScriptAnalysis *analysis;
     Compiler &cc;
     FrameState &frame;
-    analyze::Script *analysis;
-    analyze::LifetimeScript *liveness;
 
     /* Basic information about this loop. */
-    analyze::LifetimeLoop *lifetime;
+    analyze::LoopAnalysis *lifetime;
 
     /* Allocation at the head of the loop, has all loop carried variables. */
     RegisterAllocation *alloc;
 
     /*
      * Jump which initially enters the loop. The state is synced when this jump
      * occurs, and needs a trampoline generated to load the right registers
      * before going to entryTarget.
@@ -174,33 +173,33 @@ class LoopState : public MacroAssemblerT
                 uint32 temporary;
             } array;
         } u;
         InvariantEntry() { PodZero(this); }
     };
     Vector<InvariantEntry, 4, CompilerAllocPolicy> invariantEntries;
 
     bool loopInvariantEntry(uint32 slot);
-    bool addHoistedCheck(uint32 arraySlot, uint32 valueSlot1, uint32 valueSlot2, int32 constant);
+    bool addHoistedCheck(uint32 arraySlot,
+                         uint32 valueSlot1, uint32 valueSlot2, int32 constant);
     void addNegativeCheck(uint32 valueSlot, int32 constant);
 
     bool hasInvariants() { return !invariantEntries.empty(); }
     void restoreInvariants(jsbytecode *pc, Assembler &masm, Vector<Jump> *jumps);
 
   public:
 
     /* Outer loop to this one, in case of loop nesting. */
     LoopState *outer;
 
     /* Current bytecode for compilation. */
     jsbytecode *PC;
 
     LoopState(JSContext *cx, JSScript *script,
-              Compiler *cc, FrameState *frame,
-              analyze::Script *analysis, analyze::LifetimeScript *liveness);
+              Compiler *cc, FrameState *frame);
     bool init(jsbytecode *head, Jump entry, jsbytecode *entryTarget);
 
     bool generatingInvariants() { return !skipAnalysis; }
 
     /* Add a call with trailing jump/label, after which invariants need to be restored. */
     void addInvariantCall(Jump jump, Label label, bool ool, unsigned patchIndex, bool patchCall);
 
     uint32 headOffset() { return lifetime->head; }
@@ -229,26 +228,28 @@ class LoopState : public MacroAssemblerT
         }
     }
 
     void addJoin(unsigned index, bool script);
     void clearLoopRegisters();
 
     void flushLoop(StubCompiler &stubcc);
 
-    bool hoistArrayLengthCheck(const FrameEntry *obj, unsigned indexPopped);
+    /*
+     * These should only be used for entries which are known to be dense arrays
+     * (if they are objects at all).
+     */
+    bool hoistArrayLengthCheck(const FrameEntry *obj, types::TypeSet *objTypes,
+                               unsigned indexPopped);
     FrameEntry *invariantSlots(const FrameEntry *obj);
-    FrameEntry *invariantLength(const FrameEntry *obj);
+    FrameEntry *invariantLength(const FrameEntry *obj, types::TypeSet *objTypes);
 
   private:
     /* Analysis information for the loop. */
 
-    /* Stack information at points within this loop. */
-    analyze::StackAnalysis stack;
-
     /*
      * Any inequality known to hold at the head of the loop. This has the
      * form 'lhs <= rhs + constant' or 'lhs >= rhs + constant', depending on
      * lessEqual. The lhs may be modified within the loop body (the test is
      * invalid afterwards), and the rhs is invariant. This information is only
      * valid if the LHS/RHS are known integers.
      */
     enum { UNASSIGNED = uint32(-1) };
@@ -257,16 +258,17 @@ class LoopState : public MacroAssemblerT
     int32 testConstant;
     bool testLessEqual;
 
     /*
      * The rhs in the test is testRHS.length; for the test to be valid, the
      * length must not be directly modified within the loop.
      */
     bool testLength;
+    bool testLengthKnownObject;
 
     /*
      * A variable which will be incremented or decremented exactly once in each
      * iteration of the loop. The offset of the operation is indicated, which
      * may or may not run after the initial entry into the loop.
      */
     struct Increment {
         uint32 slot;
@@ -301,17 +303,15 @@ class LoopState : public MacroAssemblerT
     bool addModifiedProperty(types::TypeObject *object, jsid id);
 
     bool hasGrowArray(types::TypeObject *object);
     bool hasModifiedProperty(types::TypeObject *object, jsid id);
 
     uint32 getIncrement(uint32 slot);
     int32 adjustConstantForIncrement(jsbytecode *pc, uint32 slot);
 
-    inline types::TypeSet *poppedTypes(jsbytecode *pc, unsigned which);
-
     bool getEntryValue(uint32 offset, uint32 popped, uint32 *pslot, int32 *pconstant);
 };
 
 } /* namespace mjit */
 } /* namespace js */
 
 #endif /* jsjaeger_loopstate_h__ */
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -739,45 +739,16 @@ mjit::JaegerShot(JSContext *cx)
 
 JSBool
 js::mjit::JaegerShotAtSafePoint(JSContext *cx, void *safePoint)
 {
 #ifdef JS_TRACER
     JS_ASSERT(!TRACE_RECORDER(cx));
 #endif
 
-    JSScript *script = cx->fp()->script();
-    if (cx->typeInferenceEnabled() && script->varTypes) {
-        /*
-         * Convert integer locals/args to doubles as required. The code we are
-         * jumping to may assume that non-escaping locals and args have double
-         * values if they were inferred as 'int or double'. The interpreter cannot
-         * guarantee this holds, so we check and fixup the args/locals here.
-         */
-
-        if (cx->fp()->hasArgs()) {
-            JSFunction *fun = cx->fp()->fun();
-            Value *formals = cx->fp()->formalArgs();
-            for (uint32 i = 0; i < fun->nargs; i++) {
-                if (formals[i].isInt32() &&
-                    script->argTypes(i)->getKnownTypeTag(cx) == JSVAL_TYPE_DOUBLE) {
-                    formals[i].setDouble((double)formals[i].toInt32());
-                }
-            }
-        }
-
-        Value *fixed = cx->fp()->slots();
-        for (uint32 i = 0; i < script->nfixed; i++) {
-            if (fixed[i].isInt32() &&
-                script->localTypes(i)->getKnownTypeTag(cx) == JSVAL_TYPE_DOUBLE) {
-                fixed[i].setDouble((double)fixed[i].toInt32());
-            }
-        }
-    }
-
     return CheckStackAndEnterMethodJIT(cx, cx->fp(), safePoint);
 }
 
 NativeMapEntry *
 JITScript::nmap() const
 {
     return (NativeMapEntry *)((char*)this + sizeof(JITScript));
 }
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -1541,34 +1541,34 @@ class ScopeNameCompiler : public PICStub
         /* Get the type set to use. We can stop early if we know the IC has been disabled. */
         types::TypeSet *types = NULL;
 
         if (getprop.obj->getClass() == &js_CallClass) {
             JS_ASSERT(shape->getterOp() == GetCallArg || shape->getterOp() == GetCallVar);
             JSScript *newscript = getprop.obj->getCallObjCalleeFunction()->script();
             uint16 slot = uint16(getprop.shape->shortid);
             if (!newscript->ensureVarTypes(cx))
-                return cx->compartment->types.checkPendingRecompiles(cx);
+                return false;
             if (shape->getterOp() == GetCallArg)
                 types = newscript->argTypes(slot);
             else if (shape->getterOp() == GetCallVar)
                 types = newscript->localTypes(slot);
         } else {
             JS_ASSERT(!getprop.obj->getParent());
             if (getprop.obj->getType()->unknownProperties()) {
                 f.script()->typeMonitorResult(cx, f.pc(), types::TYPE_UNKNOWN);
-                return cx->compartment->types.checkPendingRecompiles(cx);
+                return true;
             }
             types = getprop.obj->getType()->getProperty(cx, shape->id, false);
             if (!types)
-                return cx->compartment->types.checkPendingRecompiles(cx);
+                return false;
         }
 
         types->pushAllTypes(cx, f.script(), f.pc());
-        return cx->compartment->types.checkPendingRecompiles(cx);
+        return true;
     }
 };
  
 class BindNameCompiler : public PICStubCompiler
 {
     JSObject *scopeChain;
     JSAtom *atom;
 
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -554,20 +554,16 @@ Recompiler::recompile()
                                ctorFrames, ctorPatches, ctorSites, ctorNatives)) {
         return false;
     }
 
     JS_ASSERT_IF(keepNormal, script->jitNormal);
     JS_ASSERT_IF(keepCtor, script->jitCtor);
 
     cx->compartment->types.recompilations++;
-
-    if (!cx->compartment->types.checkPendingRecompiles(cx))
-        return Compile_Error;
-
     return true;
 }
 
 bool
 Recompiler::cleanup(JITScript *jit, Vector<CallSite> *sites)
 {
     while (!JS_CLIST_IS_EMPTY(&jit->callers)) {
         JaegerSpew(JSpew_Recompile, "Purging IC caller\n");
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -2835,32 +2835,31 @@ stubs::CallPropSwap(VMFrame &f)
 void JS_FASTCALL
 stubs::CheckArgumentTypes(VMFrame &f)
 {
     JSStackFrame *fp = f.fp();
     JSFunction *fun = fp->fun();
     JSScript *script = fun->script();
     RecompilationMonitor monitor(f.cx);
 
-    /* Postpone recompilations until all args have been updated. */
-    types::AutoEnterTypeInference enter(f.cx);
-
-    if (!f.fp()->isConstructing()) {
-        if (!script->typeSetThis(f.cx, fp->thisValue()))
-            THROW();
+    {
+        /* Postpone recompilations until all args have been updated. */
+        types::AutoEnterTypeInference enter(f.cx);
+
+        if (!f.fp()->isConstructing()) {
+            if (!script->typeSetThis(f.cx, fp->thisValue()))
+                THROW();
+        }
+
+        for (unsigned i = 0; i < fun->nargs; i++) {
<