Bug 804676 - Remove dependence of Ion compilation on ScriptAnalysis::analyzeTypes.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 11 Apr 2013 18:39:32 -0600
changeset 129593 ee14945b452c669ded1d6cabe7b51c12ec722ec1
parent 128511 d989eab66df4312452c5c53f2445d9f807d9570e
child 129594 79f78c194329eefadfffdd122e33007317a3e5e0
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
bugs804676
milestone23.0a1
Bug 804676 - Remove dependence of Ion compilation on ScriptAnalysis::analyzeTypes.
js/src/Makefile.in
js/src/ion/BaselineInspector.cpp
js/src/ion/BaselineInspector.h
js/src/ion/CodeGenerator.cpp
js/src/ion/CodeGenerator.h
js/src/ion/Ion.cpp
js/src/ion/IonAnalysis.cpp
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
js/src/ion/IonCaches.h
js/src/ion/IonMacroAssembler.h
js/src/ion/IonTypes.h
js/src/ion/JSONSpewer.cpp
js/src/ion/LIR-Common.h
js/src/ion/LIR.h
js/src/ion/LOpcodes.h
js/src/ion/Lowering.cpp
js/src/ion/Lowering.h
js/src/ion/MCallOptimize.cpp
js/src/ion/MIR.cpp
js/src/ion/MIR.h
js/src/ion/MIRGraph.cpp
js/src/ion/MIRGraph.h
js/src/ion/MOpcodes.h
js/src/ion/ParallelArrayAnalysis.cpp
js/src/ion/RegisterSets.h
js/src/ion/TypeOracle.cpp
js/src/ion/TypeOracle.h
js/src/ion/TypePolicy.h
js/src/ion/arm/LIR-arm.h
js/src/ion/x64/LIR-x64.h
js/src/ion/x86/LIR-x86.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsinterpinlines.h
js/src/jsopcode.cpp
js/src/methodjit/Compiler.cpp
js/src/vm/Stack.h
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -288,17 +288,16 @@ CPPSRCS +=	MIR.cpp \
 		MCallOptimize.cpp \
 		MIRGraph.cpp \
 		MoveResolver.cpp \
 		EdgeCaseAnalysis.cpp \
 		RegisterAllocator.cpp \
 		Snapshots.cpp \
 		Safepoints.cpp \
 		StupidAllocator.cpp \
-		TypeOracle.cpp \
 		TypePolicy.cpp \
 		ValueNumbering.cpp \
 		RangeAnalysis.cpp \
 		VMFunctions.cpp \
 		ParallelFunctions.cpp \
 		AliasAnalysis.cpp \
 		ParallelArrayAnalysis.cpp \
 		UnreachableCodeElimination.cpp \
--- a/js/src/ion/BaselineInspector.cpp
+++ b/js/src/ion/BaselineInspector.cpp
@@ -53,8 +53,38 @@ BaselineInspector::maybeMonomorphicShape
         JS_ASSERT(next->isSetProp_Fallback());
         if (next->toSetProp_Fallback()->hadUnoptimizableAccess())
             return NULL;
         return stub->toSetProp_Native()->shape();
     }
 
     return NULL;
 }
+
+MIRType
+BaselineInspector::expectedResultType(jsbytecode *pc)
+{
+    // Look at the IC entries for this op to guess what type it will produce,
+    // returning MIRType_None otherwise.
+    const ICEntry &entry = icEntryFromPC(pc);
+
+    ICStub *stub = entry.firstStub();
+    ICStub *next = stub->next();
+
+    if (!next || !next->isFallback())
+        return MIRType_None;
+
+    switch (stub->kind()) {
+      case ICStub::BinaryArith_Int32:
+      case ICStub::BinaryArith_BooleanWithInt32:
+      case ICStub::UnaryArith_Int32:
+        return MIRType_Int32;
+      case ICStub::BinaryArith_Double:
+      case ICStub::BinaryArith_DoubleWithInt32:
+      case ICStub::UnaryArith_Double:
+        return MIRType_Double;
+      case ICStub::BinaryArith_StringConcat:
+      case ICStub::BinaryArith_StringObjectConcat:
+        return MIRType_String;
+      default:
+        return MIRType_None;
+    }
+}
--- a/js/src/ion/BaselineInspector.h
+++ b/js/src/ion/BaselineInspector.h
@@ -90,15 +90,17 @@ class BaselineInspector
     }
 
   public:
     RawShape maybeMonomorphicShapeForPropertyOp(jsbytecode *pc);
 
     SetElemICInspector setElemICInspector(jsbytecode *pc) {
         return makeICInspector<SetElemICInspector>(pc, ICStub::SetElem_Fallback);
     }
+
+    MIRType expectedResultType(jsbytecode *pc);
 };
 
 } // namespace ion
 } // namespace js
 
 #endif
 
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -986,17 +986,17 @@ CodeGenerator::visitParDump(LParDump *li
 
 bool
 CodeGenerator::visitTypeBarrier(LTypeBarrier *lir)
 {
     ValueOperand operand = ToValue(lir, LTypeBarrier::Input);
     Register scratch = ToRegister(lir->temp());
 
     Label matched, miss;
-    masm.guardTypeSet(operand, lir->mir()->typeSet(), scratch, &matched, &miss);
+    masm.guardTypeSet(operand, lir->mir()->resultTypeSet(), scratch, &matched, &miss);
     masm.jump(&miss);
     if (!bailoutFrom(&miss, lir->snapshot()))
         return false;
     masm.bind(&matched);
     return true;
 }
 
 bool
@@ -1811,17 +1811,17 @@ CodeGenerator::generateArgumentsChecks()
     // Indexes need to be shifted by one, to skip the scope chain slot.
     JS_ASSERT(info.scopeChainSlot() == 0);
     static const uint32_t START_SLOT = 1;
 
     Label miss;
     for (uint32_t i = START_SLOT; i < CountArgSlots(info.fun()); i++) {
         // All initial parameters are guaranteed to be MParameters.
         MParameter *param = rp->getOperand(i)->toParameter();
-        const types::TypeSet *types = param->typeSet();
+        const types::TypeSet *types = param->resultTypeSet();
         if (!types || types->unknown())
             continue;
 
         // Use ReturnReg as a scratch register here, since not all platforms
         // have an actual ScratchReg.
         int32_t offset = ArgToStackOffset((i - START_SLOT) * sizeof(Value));
         Label matched;
         masm.guardTypeSet(Address(StackPointer, offset), types, temp, &matched, &miss);
@@ -4686,16 +4686,29 @@ CodeGenerator::visitCallSetElement(LCall
 {
     pushArg(Imm32(current->mir()->strict()));
     pushArg(ToValue(lir, LCallSetElement::Value));
     pushArg(ToValue(lir, LCallSetElement::Index));
     pushArg(ToRegister(lir->getOperand(0)));
     return callVM(SetObjectElementInfo, lir);
 }
 
+typedef bool (*InitElementArrayFn)(JSContext *, jsbytecode *, HandleObject, uint32_t, HandleValue);
+static const VMFunction InitElementArrayInfo = FunctionInfo<InitElementArrayFn>(js::InitElementArray);
+
+bool
+CodeGenerator::visitCallInitElementArray(LCallInitElementArray *lir)
+{
+    pushArg(ToValue(lir, LCallInitElementArray::Value));
+    pushArg(Imm32(lir->mir()->index()));
+    pushArg(ToRegister(lir->getOperand(0)));
+    pushArg(ImmWord(lir->mir()->resumePoint()->pc()));
+    return callVM(InitElementArrayInfo, lir);
+}
+
 bool
 CodeGenerator::visitLoadFixedSlotV(LLoadFixedSlotV *ins)
 {
     const Register obj = ToRegister(ins->getOperand(0));
     size_t slot = ins->mir()->slot();
     ValueOperand result = GetValueOutput(ins);
 
     masm.loadValue(Address(obj, JSObject::getFixedSlotOffset(slot)), result);
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -160,16 +160,17 @@ class CodeGenerator : public CodeGenerat
     bool visitFromCharCode(LFromCharCode *lir);
     bool visitFunctionEnvironment(LFunctionEnvironment *lir);
     bool visitParSlice(LParSlice *lir);
     bool visitParWriteGuard(LParWriteGuard *lir);
     bool visitParDump(LParDump *lir);
     bool visitCallGetProperty(LCallGetProperty *lir);
     bool visitCallGetElement(LCallGetElement *lir);
     bool visitCallSetElement(LCallSetElement *lir);
+    bool visitCallInitElementArray(LCallInitElementArray *lir);
     bool visitThrow(LThrow *lir);
     bool visitTypeOfV(LTypeOfV *lir);
     bool visitOutOfLineTypeOfV(OutOfLineTypeOfV *ool);
     bool visitToIdV(LToIdV *lir);
     bool visitLoadElementV(LLoadElementV *load);
     bool visitLoadElementHole(LLoadElementHole *lir);
     bool visitStoreElementT(LStoreElementT *lir);
     bool visitStoreElementV(LStoreElementV *lir);
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -1282,26 +1282,30 @@ AttachFinishedCompilations(JSContext *cx
     compilations.clear();
 #endif
 }
 
 static const size_t BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
 
 template <typename CompileContext>
 static AbortReason
-IonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing,
+IonCompile(JSContext *cx, JSScript *script,
+           AbstractFramePtr fp, jsbytecode *osrPc, bool constructing,
            CompileContext &compileContext)
 {
 #if JS_TRACE_LOGGING
     AutoTraceLog logger(TraceLogging::defaultLogger(),
                         TraceLogging::ION_COMPILE_START,
                         TraceLogging::ION_COMPILE_STOP,
                         script);
 #endif
 
+    if (!script->ensureRanAnalysis(cx))
+        return AbortReason_Alloc;
+
     LifoAlloc *alloc = cx->new_<LifoAlloc>(BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
     if (!alloc)
         return AbortReason_Alloc;
 
     ScopedJSDeletePtr<LifoAlloc> autoDelete(alloc);
 
     TempAllocator *temp = alloc->new_<TempAllocator>(alloc);
     if (!temp)
@@ -1311,36 +1315,32 @@ IonCompile(JSContext *cx, JSScript *scri
 
     types::AutoEnterAnalysis enter(cx);
 
     if (!cx->compartment->ensureIonCompartmentExists(cx))
         return AbortReason_Alloc;
 
     MIRGraph *graph = alloc->new_<MIRGraph>(temp);
     ExecutionMode executionMode = compileContext.executionMode();
-    CompileInfo *info = alloc->new_<CompileInfo>(script, fun, osrPc, constructing,
+    CompileInfo *info = alloc->new_<CompileInfo>(script, script->function(), osrPc, constructing,
                                                  executionMode);
     if (!info)
         return AbortReason_Alloc;
 
-    TypeInferenceOracle oracle;
-    if (!oracle.init(cx, script, /* inlinedCall = */ false))
-        return AbortReason_Disable;
-
     BaselineInspector inspector(cx, script);
 
     AutoFlushCache afc("IonCompile");
 
     types::AutoEnterCompilation enterCompiler(cx, CompilerOutputKind(executionMode));
     if (!enterCompiler.init(script, false, 0))
         return AbortReason_Disable;
 
     AutoTempAllocatorRooter root(cx, temp);
 
-    IonBuilder *builder = alloc->new_<IonBuilder>(cx, temp, graph, &oracle, &inspector, info);
+    IonBuilder *builder = alloc->new_<IonBuilder>(cx, temp, graph, &inspector, info, fp);
     if (!builder)
         return AbortReason_Alloc;
 
     AbortReason abortReason  = compileContext.compile(builder, graph, autoDelete);
     if (abortReason != AbortReason_NoAbort)
         IonSpew(IonSpew_Abort, "IM Compilation failed.");
 
     return abortReason;
@@ -1514,17 +1514,18 @@ CanIonCompileScript(JSContext *cx, Handl
         return false;
 
     SequentialCompileContext compileContext;
     return compileContext.checkScriptSize(cx, script) == Method_Compiled;
 }
 
 template <typename CompileContext>
 static MethodStatus
-Compile(JSContext *cx, HandleScript script, HandleFunction fun, jsbytecode *osrPc, bool constructing,
+Compile(JSContext *cx, HandleScript script,
+        AbstractFramePtr fp, jsbytecode *osrPc, bool constructing,
         CompileContext &compileContext)
 {
     JS_ASSERT(ion::IsEnabled(cx));
     JS_ASSERT_IF(osrPc != NULL, (JSOp)*osrPc == JSOP_LOOPENTRY);
 
     ExecutionMode executionMode = compileContext.executionMode();
 
     if (executionMode == SequentialExecution &&
@@ -1565,17 +1566,17 @@ Compile(JSContext *cx, HandleScript scri
             if (script->getUseCount() < js_IonOptions.usesBeforeCompile)
                 return Method_Skipped;
         } else {
             if (script->incUseCount() < js_IonOptions.usesBeforeCompileNoJaeger)
                 return Method_Skipped;
         }
     }
 
-    AbortReason reason = IonCompile(cx, script, fun, osrPc, constructing, compileContext);
+    AbortReason reason = IonCompile(cx, script, fp, osrPc, constructing, compileContext);
     if (reason == AbortReason_Disable)
         return Method_CantCompile;
 
     // Compilation succeeded or we invalidated right away or an inlining/alloc abort
     return HasIonScript(script, executionMode) ? Method_Compiled : Method_Skipped;
 }
 
 } // namespace ion
@@ -1608,20 +1609,19 @@ ion::CanEnterAtBranch(JSContext *cx, JSS
 
     // Mark as forbidden if frame can't be handled.
     if (!CheckFrame(fp)) {
         ForbidCompilation(cx, script);
         return Method_CantCompile;
     }
 
     // Attempt compilation. Returns Method_Compiled if already compiled.
-    RootedFunction fun(cx, fp.isFunctionFrame() ? fp.fun() : NULL);
     SequentialCompileContext compileContext;
     RootedScript rscript(cx, script);
-    MethodStatus status = Compile(cx, rscript, fun, pc, isConstructing, compileContext);
+    MethodStatus status = Compile(cx, rscript, fp, pc, isConstructing, compileContext);
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, script);
         return status;
     }
 
     if (script->hasIonScript() && script->ionScript()->osrPc() != pc)
         return Method_Skipped;
@@ -1661,20 +1661,19 @@ ion::CanEnter(JSContext *cx, JSScript *s
 
     // Mark as forbidden if frame can't be handled.
     if (!CheckFrame(fp)) {
         ForbidCompilation(cx, script);
         return Method_CantCompile;
     }
 
     // Attempt compilation. Returns Method_Compiled if already compiled.
-    RootedFunction fun(cx, fp.isFunctionFrame() ? fp.fun() : NULL);
     SequentialCompileContext compileContext;
     RootedScript rscript(cx, script);
-    MethodStatus status = Compile(cx, rscript, fun, NULL, isConstructing, compileContext);
+    MethodStatus status = Compile(cx, rscript, fp, NULL, isConstructing, compileContext);
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, script);
         return status;
     }
 
     return Method_Compiled;
 }
@@ -1692,18 +1691,17 @@ ion::CompileFunctionForBaseline(JSContex
     // Mark as forbidden if frame can't be handled.
     if (!CheckFrame(fp)) {
         ForbidCompilation(cx, script);
         return Method_CantCompile;
     }
 
     // Attempt compilation. Returns Method_Compiled if already compiled.
     SequentialCompileContext compileContext;
-    RootedFunction fun(cx, fp.fun());
-    MethodStatus status = Compile(cx, script, fun, NULL, isConstructing, compileContext);
+    MethodStatus status = Compile(cx, script, fp, NULL, isConstructing, compileContext);
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, script);
         return status;
     }
 
     return Method_Compiled;
 }
@@ -1784,17 +1782,17 @@ ParallelCompileContext::compileTransitiv
                                    "Adding previously invalidated function %p:%s:%u",
                                    fun.get(), target->filename(), target->lineno);
                     appendToWorklist(target);
                 }
             }
         }
 
         // Attempt compilation. Returns Method_Compiled if already compiled.
-        MethodStatus status = Compile(cx_, script, fun, NULL, false, *this);
+        MethodStatus status = Compile(cx_, script, AbstractFramePtr(), NULL, false, *this);
         if (status != Method_Compiled) {
             if (status == Method_CantCompile)
                 ForbidCompilation(cx_, script, ParallelExecution);
             return SpewEndCompile(status);
         }
 
         // This can GC, so afterward, script->parallelIon is not guaranteed to be valid.
         if (!cx_->compartment->ionCompartment()->enterJIT())
--- a/js/src/ion/IonAnalysis.cpp
+++ b/js/src/ion/IonAnalysis.cpp
@@ -524,17 +524,17 @@ TypeAnalyzer::adjustPhiInputs(MPhi *phi)
         return;
 
     // Box every typed input.
     for (size_t i = 0; i < phi->numOperands(); i++) {
         MDefinition *in = phi->getOperand(i);
         if (in->type() == MIRType_Value)
             continue;
 
-        if (in->isUnbox()) {
+        if (in->isUnbox() && phi->typeIncludes(in->toUnbox()->input())) {
             // The input is being explicitly unboxed, so sneak past and grab
             // the original box.
             phi->replaceOperand(i, in->toUnbox()->input());
         } else {
             MBox *box = MBox::New(in);
             in->block()->insertBefore(in->block()->lastIns(), box);
             phi->replaceOperand(i, box);
         }
@@ -1267,18 +1267,18 @@ TryEliminateTypeBarrierFromTest(MTypeBar
     barrier->replaceAllUsesWith(barrier->input());
 }
 
 static bool
 TryEliminateTypeBarrier(MTypeBarrier *barrier, bool *eliminated)
 {
     JS_ASSERT(!*eliminated);
 
-    const types::StackTypeSet *barrierTypes = barrier->typeSet();
-    const types::StackTypeSet *inputTypes = barrier->input()->typeSet();
+    const types::StackTypeSet *barrierTypes = barrier->resultTypeSet();
+    const types::StackTypeSet *inputTypes = barrier->input()->resultTypeSet();
 
     if (!barrierTypes || !inputTypes)
         return true;
 
     bool filtersNull = barrierTypes->filtersType(inputTypes, types::Type::NullType());
     bool filtersUndefined = barrierTypes->filtersType(inputTypes, types::Type::UndefinedType());
 
     if (!filtersNull && !filtersUndefined)
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -28,42 +28,48 @@
 #endif
 
 using namespace js;
 using namespace js::ion;
 
 using mozilla::DebugOnly;
 
 IonBuilder::IonBuilder(JSContext *cx, TempAllocator *temp, MIRGraph *graph,
-                       TypeOracle *oracle, BaselineInspector *inspector, CompileInfo *info,
+                       BaselineInspector *inspector, CompileInfo *info, AbstractFramePtr fp,
                        size_t inliningDepth, uint32_t loopDepth)
   : MIRGenerator(cx->compartment, temp, graph, info),
     backgroundCodegen_(NULL),
     recompileInfo(cx->compartment->types.compiledInfo),
     cx(cx),
+    fp(fp),
     abortReason_(AbortReason_Disable),
     loopDepth_(loopDepth),
     callerResumePoint_(NULL),
     callerBuilder_(NULL),
-    oracle(oracle),
     inspector(inspector),
     inliningDepth_(inliningDepth),
     failedBoundsCheck_(info->script()->failedBoundsCheck),
     failedShapeGuard_(info->script()->failedShapeGuard),
+    nonStringIteration_(false),
     lazyArguments_(NULL)
 {
     script_.init(info->script());
     pc = info->startPC();
+
+    if (!script_->hasFreezeConstraints) {
+        types::TypeScript::AddFreezeConstraints(cx, script_);
+        script_->hasFreezeConstraints = true;
+    }
 }
 
 void
 IonBuilder::clearForBackEnd()
 {
     cx = NULL;
-    oracle = NULL;
+    fp = AbstractFramePtr();
 }
 
 bool
 IonBuilder::abort(const char *message, ...)
 {
     // Don't call PCToLineNumber in release builds.
 #ifdef DEBUG
     va_list ap;
@@ -183,16 +189,45 @@ IonBuilder::getPolyCallTargets(types::St
         if (!targets.append(obj))
             return false;
     }
 
     return true;
 }
 
 bool
+IonBuilder::canEnterInlinedFunction(JSFunction *target)
+{
+    RootedScript targetScript(cx, target->nonLazyScript());
+
+    if (!targetScript->hasAnalysis())
+        return false;
+
+    if (!targetScript->analysis()->ionInlineable())
+        return false;
+
+    if (targetScript->needsArgsObj())
+        return false;
+
+    if (!targetScript->compileAndGo)
+        return false;
+
+    if (targetScript->analysis()->usesScopeChain())
+        return false;
+
+    types::TypeObject *targetType = target->getType(cx);
+    if (!targetType || targetType->unknownProperties())
+        return false;
+
+    // TI calls ObjectStateChange to trigger invalidation of the caller.
+    types::HeapTypeSet::WatchObjectStateChange(cx, targetType);
+    return true;
+}
+
+bool
 IonBuilder::canInlineTarget(JSFunction *target, CallInfo &callInfo)
 {
     if (!target->isInterpreted()) {
         IonSpew(IonSpew_Inlining, "Cannot inline due to non-interpreted");
         return false;
     }
 
     if (target->getParent() != &script()->global()) {
@@ -221,96 +256,181 @@ IonBuilder::canInlineTarget(JSFunction *
     while (builder) {
         if (builder->script() == inlineScript) {
             IonSpew(IonSpew_Inlining, "Not inlining recursive call");
             return false;
         }
         builder = builder->callerBuilder_;
     }
 
-    RootedScript callerScript(cx, script());
-
-    if (!oracle->canEnterInlinedFunction(target)) {
-        IonSpew(IonSpew_Inlining, "Cannot inline due to oracle veto %d", script()->lineno);
-        return false;
-    }
-
-    if (!oracle->callReturnTypeSetMatches(callerScript, pc, target)) {
-        IonSpew(IonSpew_Inlining, "Cannot inline due to return typeset mismatch");
+    if (!canEnterInlinedFunction(target)) {
+        IonSpew(IonSpew_Inlining, "Cannot inline due to analysis data %d", script()->lineno);
         return false;
     }
 
-    JS_ASSERT(callInfo.hasCallType());
-    if (callInfo.constructing()) {
-        // For constructing calls the typeset of caller should intersect the callee's typeset.
-        // Except for the |this| type, because that is created during execution depending on target.
-        if (!oracle->callArgsTypeSetIntersects(NULL, callInfo.argvType(), target)) {
-            IonSpew(IonSpew_Inlining, "Cannot inline due to arguments typeset mismatch");
-            return false;
-        }
-    } else if (JSOp(*pc) == JSOP_FUNAPPLY) {
-        // For fun.apply() the typeset of the caller should be a subset of the callee.
-        // Currently we can't loosen this, since there are no type barriers at the call boundary
-        // of fun.apply. Seeing a new type will only be noticed in the inlined call and
-        // result in missed types in TI.
-        if (!oracle->callArgsTypeSetMatches(callInfo.thisType(), callInfo.argvType(), target)) {
-            IonSpew(IonSpew_Inlining, "Cannot inline due to arguments typeset mismatch");
-            return false;
-        }
-    } else {
-        // For normal calls the typeset of caller should intersect the callee's typeset.
-        if (!oracle->callArgsTypeSetIntersects(callInfo.thisType(), callInfo.argvType(), target)) {
-            IonSpew(IonSpew_Inlining, "Cannot inline due to arguments typeset mismatch");
-            return false;
-        }
-    }
-
     IonSpew(IonSpew_Inlining, "Inlining good to go!");
     return true;
 }
 
 void
 IonBuilder::popCfgStack()
 {
     if (cfgStack_.back().isLoop())
         loops_.popBack();
     if (cfgStack_.back().state == CFGState::LABEL)
         labels_.popBack();
     cfgStack_.popBack();
 }
 
+void
+IonBuilder::analyzeNewLoopTypes(MBasicBlock *entry, jsbytecode *start, jsbytecode *end)
+{
+    // The phi inputs at the loop head only reflect types for variables that
+    // were present at the start of the loop. If the variable changes to a new
+    // type within the loop body, and that type is carried around to the loop
+    // head, then we need to know about the new type up front.
+    //
+    // Since SSA information hasn't been constructed for the loop body yet, we
+    // need a separate analysis to pick out the types that might flow around
+    // the loop header. This is a best-effort analysis that may either over-
+    // or under-approximate the set of such types.
+    //
+    // Over-approximating the types may lead to inefficient generated code, and
+    // under-approximating the types will cause the loop body to be analyzed
+    // multiple times as the correct types are deduced (see finishLoop).
+
+    jsbytecode *last = NULL;
+    for (jsbytecode *pc = start; pc != end; last = pc, pc += GetBytecodeLength(pc)) {
+        uint32_t slot;
+        if (*pc == JSOP_SETLOCAL)
+            slot = info().localSlot(GET_SLOTNO(pc));
+        else if (*pc == JSOP_SETARG)
+            slot = info().argSlot(GET_SLOTNO(pc));
+        else
+            continue;
+        if (slot >= info().firstStackSlot())
+            continue;
+        if (!script()->analysis()->maybeCode(pc))
+            continue;
+
+        MPhi *phi = entry->getSlot(slot)->toPhi();
+
+        if (js_CodeSpec[*last].format & JOF_TYPESET) {
+            types::StackTypeSet *typeSet = script()->analysis()->bytecodeTypes(last);
+            if (!typeSet->empty()) {
+                MIRType type = MIRTypeFromValueType(typeSet->getKnownTypeTag());
+                phi->addBackedgeType(type, typeSet);
+            }
+        } else if (*last == JSOP_GETLOCAL || *last == JSOP_GETARG) {
+            uint32_t slot = (*last == JSOP_GETLOCAL)
+                            ? info().localSlot(GET_SLOTNO(last))
+                            : info().argSlot(GET_SLOTNO(last));
+            MPhi *otherPhi = entry->getSlot(slot)->toPhi();
+            if (otherPhi->hasBackedgeType())
+                phi->addBackedgeType(otherPhi->type(), otherPhi->resultTypeSet());
+        } else {
+            MIRType type = MIRType_None;
+            switch (*last) {
+              case JSOP_VOID:
+              case JSOP_UNDEFINED:
+                type = MIRType_Undefined;
+                break;
+              case JSOP_NULL:
+                type = MIRType_Null;
+                break;
+              case JSOP_ZERO:
+              case JSOP_ONE:
+              case JSOP_INT8:
+              case JSOP_INT32:
+              case JSOP_UINT16:
+              case JSOP_UINT24:
+              case JSOP_BITAND:
+              case JSOP_BITOR:
+              case JSOP_BITXOR:
+              case JSOP_BITNOT:
+              case JSOP_RSH:
+              case JSOP_LSH:
+              case JSOP_URSH:
+                type = MIRType_Int32;
+                break;
+              case JSOP_FALSE:
+              case JSOP_TRUE:
+              case JSOP_EQ:
+              case JSOP_NE:
+              case JSOP_LT:
+              case JSOP_LE:
+              case JSOP_GT:
+              case JSOP_GE:
+              case JSOP_NOT:
+              case JSOP_STRICTEQ:
+              case JSOP_STRICTNE:
+              case JSOP_IN:
+              case JSOP_INSTANCEOF:
+                type = MIRType_Boolean;
+                break;
+              case JSOP_DOUBLE:
+                type = MIRType_Double;
+                break;
+              case JSOP_STRING:
+              case JSOP_TYPEOF:
+              case JSOP_TYPEOFEXPR:
+              case JSOP_ITERNEXT:
+                type = MIRType_String;
+                break;
+              case JSOP_ADD:
+              case JSOP_SUB:
+              case JSOP_MUL:
+              case JSOP_DIV:
+              case JSOP_MOD:
+              case JSOP_NEG:
+                type = inspector->expectedResultType(last);
+              default:
+                break;
+            }
+            if (type != MIRType_None)
+                phi->addBackedgeType(type, NULL);
+        }
+    }
+}
+
 bool
 IonBuilder::pushLoop(CFGState::State initial, jsbytecode *stopAt, MBasicBlock *entry,
+                     jsbytecode *loopHead, jsbytecode *initialPc,
                      jsbytecode *bodyStart, jsbytecode *bodyEnd, jsbytecode *exitpc,
                      jsbytecode *continuepc)
 {
     if (!continuepc)
         continuepc = entry->pc();
 
     ControlFlowInfo loop(cfgStack_.length(), continuepc);
     if (!loops_.append(loop))
         return false;
 
     CFGState state;
     state.state = initial;
     state.stopAt = stopAt;
     state.loop.bodyStart = bodyStart;
     state.loop.bodyEnd = bodyEnd;
     state.loop.exitpc = exitpc;
+    state.loop.continuepc = continuepc;
     state.loop.entry = entry;
     state.loop.successor = NULL;
     state.loop.breaks = NULL;
     state.loop.continues = NULL;
+    state.loop.initialState = initial;
+    state.loop.initialPc = initialPc;
+    state.loop.initialStopAt = stopAt;
+    state.loop.loopHead = loopHead;
     return cfgStack_.append(state);
 }
 
 bool
 IonBuilder::build()
 {
-    current = newBlock(pc);
+    setCurrent(newBlock(pc));
     if (!current)
         return false;
 
     IonSpew(IonSpew_Scripts, "Analyzing script %s:%d (%p) (usecount=%d) (maxloopcount=%d)",
             script()->filename(), script()->lineno, (void *)script(), (int)script()->getUseCount(),
             (int)script()->getMaxLoopCount());
 
     if (!graph().addScript(script()))
@@ -444,17 +564,17 @@ IonBuilder::buildInline(IonBuilder *call
 
     if (callerBuilder->failedBoundsCheck_)
         failedBoundsCheck_ = true;
 
     if (callerBuilder->failedShapeGuard_)
         failedShapeGuard_ = true;
 
     // Generate single entrance block.
-    current = newBlock(pc);
+    setCurrent(newBlock(pc));
     if (!current)
         return false;
 
     current->setCallerResumePoint(callerResumePoint);
 
     // Connect the entrance block to the last block in the caller's graph.
     MBasicBlock *predecessor = callerBuilder->current;
     JS_ASSERT(predecessor == callerResumePoint->block());
@@ -470,23 +590,20 @@ IonBuilder::buildInline(IonBuilder *call
 
     predecessor->end(MGoto::New(current));
     if (!current->addPredecessorWithoutPhis(predecessor))
         return false;
 
     // Save the actual arguments the caller used to call this inlined call,
     // to shortcut operations on "arguments" in the inlined call.
     JS_ASSERT(inlinedArguments_.length() == 0);
-    JS_ASSERT(inlinedArgumentTypes_.length() == 0);
     if (!inlinedArguments_.append(callInfo.argv().begin(), callInfo.argv().end()))
         return false;
-    if (!inlinedArgumentTypes_.append(callInfo.argvType().begin(), callInfo.argvType().end()))
-        return false;
-
-    // The Oracle ensures that the inlined script does not use the scope chain.
+
+    // canEnterInlinedFunction vetoes scripts which use the scope chain.
     JS_ASSERT(!script()->analysis()->usesScopeChain());
     MInstruction *scope = MConstant::New(UndefinedValue());
     current->add(scope);
     current->initSlot(info().scopeChainSlot(), scope);
     current->initSlot(info().thisSlot(), callInfo.thisArg());
 
     IonSpew(IonSpew_Inlining, "Initializing %u arg slots", info().nargs());
 
@@ -538,21 +655,19 @@ IonBuilder::rewriteParameters()
 
     for (uint32_t i = START_SLOT; i < CountArgSlots(info().fun()); i++) {
         MParameter *param = current->getSlot(i)->toParameter();
 
         // Find the original (not cloned) type set for the MParameter, as we
         // will be adding constraints to it.
         types::StackTypeSet *types;
         if (param->index() == MParameter::THIS_SLOT)
-            types = oracle->thisTypeSet(script());
+            types = types::TypeScript::ThisTypes(script());
         else
-            types = oracle->parameterTypeSet(script(), param->index());
-        if (!types)
-            continue;
+            types = types::TypeScript::ArgTypes(script(), param->index());
 
         JSValueType definiteType = types->getKnownTypeTag();
         if (definiteType == JSVAL_TYPE_UNKNOWN)
             continue;
 
         MInstruction *actual = NULL;
         switch (definiteType) {
           case JSVAL_TYPE_UNDEFINED:
@@ -586,22 +701,22 @@ IonBuilder::rewriteParameters()
 
 bool
 IonBuilder::initParameters()
 {
     if (!info().fun())
         return true;
 
     MParameter *param = MParameter::New(MParameter::THIS_SLOT,
-                                        cloneTypeSet(oracle->thisTypeSet(script())));
+                                        cloneTypeSet(types::TypeScript::ThisTypes(script())));
     current->add(param);
     current->initSlot(info().thisSlot(), param);
 
     for (uint32_t i = 0; i < info().nargs(); i++) {
-        param = MParameter::New(i, cloneTypeSet(oracle->parameterTypeSet(script(), i)));
+        param = MParameter::New(i, cloneTypeSet(types::TypeScript::ArgTypes(script(), i)));
         current->add(param);
         current->initSlot(info().argSlot(i), param);
     }
 
     return true;
 }
 
 bool
@@ -1259,32 +1374,32 @@ IonBuilder::processIfEnd(CFGState &state
         // current block to the false block. Note that a RETURN opcode
         // could have already ended the block.
         current->end(MGoto::New(state.branch.ifFalse));
 
         if (!state.branch.ifFalse->addPredecessor(current))
             return ControlStatus_Error;
     }
 
-    current = state.branch.ifFalse;
+    setCurrent(state.branch.ifFalse);
     graph().moveBlockToEnd(current);
     pc = current->pc();
     return ControlStatus_Joined;
 }
 
 IonBuilder::ControlStatus
 IonBuilder::processIfElseTrueEnd(CFGState &state)
 {
     // We've reached the end of the true branch of an if-else. Don't
     // create an edge yet, just transition to parsing the false branch.
     state.state = CFGState::IF_ELSE_FALSE;
     state.branch.ifTrue = current;
     state.stopAt = state.branch.falseEnd;
     pc = state.branch.ifFalse->pc();
-    current = state.branch.ifFalse;
+    setCurrent(state.branch.ifFalse);
     graph().moveBlockToEnd(current);
     return ControlStatus_Jumped;
 }
 
 IonBuilder::ControlStatus
 IonBuilder::processIfElseFalseEnd(CFGState &state)
 {
     // Update the state to have the latest block from the false path.
@@ -1310,17 +1425,17 @@ IonBuilder::processIfElseFalseEnd(CFGSta
 
     if (other) {
         other->end(MGoto::New(join));
         if (!join->addPredecessor(other))
             return ControlStatus_Error;
     }
 
     // Ignore unreachable remainder of false block if existent.
-    current = join;
+    setCurrent(join);
     pc = current->pc();
     return ControlStatus_Joined;
 }
 
 IonBuilder::ControlStatus
 IonBuilder::processBrokenLoop(CFGState &state)
 {
     JS_ASSERT(!current);
@@ -1333,17 +1448,17 @@ IonBuilder::processBrokenLoop(CFGState &
     for (MBasicBlockIterator i(graph().begin(state.loop.entry)); i != graph().end(); i++) {
         if (i->loopDepth() > loopDepth_)
             i->setLoopDepth(i->loopDepth() - 1);
     }
 
     // If the loop started with a condition (while/for) then even if the
     // structure never actually loops, the condition itself can still fail and
     // thus we must resume at the successor, if one exists.
-    current = state.loop.successor;
+    setCurrent(state.loop.successor);
     if (current) {
         JS_ASSERT(current->loopDepth() == loopDepth_);
         graph().moveBlockToEnd(current);
     }
 
     // Join the breaks together and continue parsing.
     if (state.loop.breaks) {
         MBasicBlock *block = createBreakCatchBlock(state.loop.breaks, state.loop.exitpc);
@@ -1351,17 +1466,17 @@ IonBuilder::processBrokenLoop(CFGState &
             return ControlStatus_Error;
 
         if (current) {
             current->end(MGoto::New(block));
             if (!block->addPredecessor(current))
                 return ControlStatus_Error;
         }
 
-        current = block;
+        setCurrent(block);
     }
 
     // If the loop is not gated on a condition, and has only returns, we'll
     // reach this case. For example:
     // do { ... return; } while ();
     if (!current)
         return ControlStatus_Ended;
 
@@ -1377,18 +1492,27 @@ IonBuilder::finishLoop(CFGState &state, 
     JS_ASSERT(current);
 
     JS_ASSERT(loopDepth_);
     loopDepth_--;
     JS_ASSERT_IF(successor, successor->loopDepth() == loopDepth_);
 
     // Compute phis in the loop header and propagate them throughout the loop,
     // including the successor.
-    if (!state.loop.entry->setBackedge(current))
+    AbortReason r = state.loop.entry->setBackedge(current);
+    if (r == AbortReason_Alloc)
         return ControlStatus_Error;
+    if (r == AbortReason_Disable) {
+        // If there are types for variables on the backedge that were not
+        // present at the original loop header, then uses of the variables'
+        // phis may have generated incorrect nodes. The new types have been
+        // incorporated into the header phis, so remove all blocks for the
+        // loop body and restart with the new types.
+        return restartLoop(state);
+    }
     if (successor) {
         graph().moveBlockToEnd(successor);
         successor->inheritPhis(state.loop.entry);
     }
 
     if (state.loop.breaks) {
         // Propagate phis placed in the header to individual break exit points.
         DeferredEdge *edge = state.loop.breaks;
@@ -1407,27 +1531,67 @@ IonBuilder::finishLoop(CFGState &state, 
             // catch block.
             successor->end(MGoto::New(block));
             if (!block->addPredecessor(successor))
                 return ControlStatus_Error;
         }
         successor = block;
     }
 
-    current = successor;
+    setCurrent(successor);
 
     // An infinite loop (for (;;) { }) will not have a successor.
     if (!current)
         return ControlStatus_Ended;
 
     pc = current->pc();
     return ControlStatus_Joined;
 }
 
 IonBuilder::ControlStatus
+IonBuilder::restartLoop(CFGState state)
+{
+    MBasicBlock *header = state.loop.entry;
+
+    // Remove all blocks in the loop body other than the header, which has phis
+    // of the appropriate type and incoming edges to preserve.
+    graph().removeBlocksAfter(header);
+
+    // Remove all instructions from the header itself.
+    header->discardAllInstructions();
+    header->setStackDepth(header->getPredecessor(0)->stackDepth());
+
+    popCfgStack();
+
+    loopDepth_++;
+
+    if (!pushLoop(state.loop.initialState, state.loop.initialStopAt, header,
+                  state.loop.loopHead, state.loop.initialPc,
+                  state.loop.bodyStart, state.loop.bodyEnd,
+                  state.loop.exitpc, state.loop.continuepc))
+        return ControlStatus_Error;
+
+    CFGState &nstate = cfgStack_.back();
+
+    nstate.loop.condpc = state.loop.condpc;
+    nstate.loop.updatepc = state.loop.updatepc;
+    nstate.loop.updateEnd = state.loop.updateEnd;
+
+    // Bypass specializePhis() in setCurrent(), as phis in the header already
+    // have the correct type.
+    current = header;
+
+    if (!jsop_loophead(nstate.loop.loopHead))
+        return ControlStatus_Error;
+
+    pc = nstate.loop.initialPc;
+    return ControlStatus_Jumped;
+}
+
+IonBuilder::ControlStatus
 IonBuilder::processDoWhileBodyEnd(CFGState &state)
 {
     if (!processDeferredContinues(state))
         return ControlStatus_Error;
 
     // No current means control flow cannot reach the condition, so this will
     // never loop.
     if (!current)
@@ -1436,17 +1600,17 @@ IonBuilder::processDoWhileBodyEnd(CFGSta
     MBasicBlock *header = newBlock(current, state.loop.updatepc);
     if (!header)
         return ControlStatus_Error;
     current->end(MGoto::New(header));
 
     state.state = CFGState::DO_WHILE_LOOP_COND;
     state.stopAt = state.loop.updateEnd;
     pc = state.loop.updatepc;
-    current = header;
+    setCurrent(header);
     return ControlStatus_Jumped;
 }
 
 IonBuilder::ControlStatus
 IonBuilder::processDoWhileCondEnd(CFGState &state)
 {
     JS_ASSERT(JSOp(*pc) == JSOP_IFNE);
 
@@ -1481,17 +1645,17 @@ IonBuilder::processWhileCondEnd(CFGState
         return ControlStatus_Error;
 
     MTest *test = MTest::New(ins, body, state.loop.successor);
     current->end(test);
 
     state.state = CFGState::WHILE_LOOP_BODY;
     state.stopAt = state.loop.bodyEnd;
     pc = state.loop.bodyStart;
-    current = body;
+    setCurrent(body);
     return ControlStatus_Jumped;
 }
 
 IonBuilder::ControlStatus
 IonBuilder::processWhileBodyEnd(CFGState &state)
 {
     if (!processDeferredContinues(state))
         return ControlStatus_Error;
@@ -1518,17 +1682,17 @@ IonBuilder::processForCondEnd(CFGState &
         return ControlStatus_Error;
 
     MTest *test = MTest::New(ins, body, state.loop.successor);
     current->end(test);
 
     state.state = CFGState::FOR_LOOP_BODY;
     state.stopAt = state.loop.bodyEnd;
     pc = state.loop.bodyStart;
-    current = body;
+    setCurrent(body);
     return ControlStatus_Jumped;
 }
 
 IonBuilder::ControlStatus
 IonBuilder::processForBodyEnd(CFGState &state)
 {
     if (!processDeferredContinues(state))
         return ControlStatus_Error;
@@ -1553,23 +1717,48 @@ IonBuilder::processForUpdateEnd(CFGState
     // update clause.
     if (!current)
         return processBrokenLoop(state);
 
     current->end(MGoto::New(state.loop.entry));
     return finishLoop(state, state.loop.successor);
 }
 
+IonBuilder::DeferredEdge *
+IonBuilder::filterDeadDeferredEdges(DeferredEdge *edge)
+{
+    DeferredEdge *head = edge, *prev = NULL;
+
+    while (edge) {
+        if (edge->block->isDead()) {
+            if (prev)
+                prev->next = edge->next;
+            else
+                head = edge->next;
+        } else {
+            prev = edge;
+        }
+        edge = edge->next;
+    }
+
+    // There must be at least one deferred edge from a block that was not
+    // deleted; blocks are deleted when restarting processing of a loop, and
+    // the final version of the loop body will have edges from live blocks.
+    JS_ASSERT(head);
+
+    return head;
+}
+
 bool
 IonBuilder::processDeferredContinues(CFGState &state)
 {
     // If there are any continues for this loop, and there is an update block,
     // then we need to create a new basic block to house the update.
     if (state.loop.continues) {
-        DeferredEdge *edge = state.loop.continues;
+        DeferredEdge *edge = filterDeadDeferredEdges(state.loop.continues);
 
         MBasicBlock *update = newBlock(edge->block, loops_.back().continuepc);
         if (!update)
             return false;
 
         if (current) {
             current->end(MGoto::New(update));
             if (!update->addPredecessor(current))
@@ -1585,25 +1774,27 @@ IonBuilder::processDeferredContinues(CFG
         while (edge) {
             edge->block->end(MGoto::New(update));
             if (!update->addPredecessor(edge->block))
                 return ControlStatus_Error;
             edge = edge->next;
         }
         state.loop.continues = NULL;
 
-        current = update;
+        setCurrent(update);
     }
 
     return true;
 }
 
 MBasicBlock *
 IonBuilder::createBreakCatchBlock(DeferredEdge *edge, jsbytecode *pc)
 {
+    edge = filterDeadDeferredEdges(edge);
+
     // Create block, using the first break statement as predecessor
     MBasicBlock *successor = newBlock(edge->block, pc);
     if (!successor)
         return NULL;
 
     // No need to use addPredecessor for first edge,
     // because it is already predecessor.
     edge->block->end(MGoto::New(successor));
@@ -1647,32 +1838,32 @@ IonBuilder::processNextTableSwitchCase(C
 
     // If this is the last successor the block should stop at the end of the tableswitch
     // Else it should stop at the start of the next successor
     if (state.tableswitch.currentBlock+1 < state.tableswitch.ins->numBlocks())
         state.stopAt = state.tableswitch.ins->getBlock(state.tableswitch.currentBlock+1)->pc();
     else
         state.stopAt = state.tableswitch.exitpc;
 
-    current = successor;
+    setCurrent(successor);
     pc = current->pc();
     return ControlStatus_Jumped;
 }
 
 IonBuilder::ControlStatus
 IonBuilder::processAndOrEnd(CFGState &state)
 {
     // We just processed the RHS of an && or || expression.
     // Now jump to the join point (the false block).
     current->end(MGoto::New(state.branch.ifFalse));
 
     if (!state.branch.ifFalse->addPredecessor(current))
         return ControlStatus_Error;
 
-    current = state.branch.ifFalse;
+    setCurrent(state.branch.ifFalse);
     graph().moveBlockToEnd(current);
     pc = current->pc();
     return ControlStatus_Joined;
 }
 
 IonBuilder::ControlStatus
 IonBuilder::processLabelEnd(CFGState &state)
 {
@@ -1691,17 +1882,17 @@ IonBuilder::processLabelEnd(CFGState &st
         return ControlStatus_Error;
 
     if (current) {
         current->end(MGoto::New(successor));
         successor->addPredecessor(current);
     }
 
     pc = state.stopAt;
-    current = successor;
+    setCurrent(successor);
     return ControlStatus_Joined;
 }
 
 IonBuilder::ControlStatus
 IonBuilder::processBreak(JSOp op, jssrcnote *sn)
 {
     JS_ASSERT(op == JSOP_GOTO);
 
@@ -1731,17 +1922,17 @@ IonBuilder::processBreak(JSOp op, jssrcn
                 found = true;
                 break;
             }
         }
     }
 
     JS_ASSERT(found);
 
-    current = NULL;
+    setCurrent(NULL);
     pc += js_CodeSpec[op].length;
     return processControlEnd();
 }
 
 static inline jsbytecode *
 EffectiveContinue(jsbytecode *pc)
 {
     if (JSOp(*pc) == JSOP_GOTO)
@@ -1768,17 +1959,17 @@ IonBuilder::processContinue(JSOp op)
 
     // There must always be a valid target loop structure. If not, there's
     // probably an off-by-something error in which pc we track.
     JS_ASSERT(found);
     CFGState &state = *found;
 
     state.loop.continues = new DeferredEdge(current, state.loop.continues);
 
-    current = NULL;
+    setCurrent(NULL);
     pc += js_CodeSpec[op].length;
     return processControlEnd();
 }
 
 IonBuilder::ControlStatus
 IonBuilder::processSwitchBreak(JSOp op)
 {
     JS_ASSERT(op == JSOP_GOTO);
@@ -1808,17 +1999,17 @@ IonBuilder::processSwitchBreak(JSOp op)
         break;
       default:
         JS_NOT_REACHED("Unexpected switch state.");
         return ControlStatus_Error;
     }
 
     *breaks = new DeferredEdge(current, *breaks);
 
-    current = NULL;
+    setCurrent(NULL);
     pc += js_CodeSpec[op].length;
     return processControlEnd();
 }
 
 IonBuilder::ControlStatus
 IonBuilder::processSwitchEnd(DeferredEdge *breaks, jsbytecode *exitpc)
 {
     // No break statements, no current.
@@ -1843,17 +2034,17 @@ IonBuilder::processSwitchEnd(DeferredEdg
     // So current is also a predecessor to this block
     if (current) {
         current->end(MGoto::New(successor));
         if (breaks)
             successor->addPredecessor(current);
     }
 
     pc = exitpc;
-    current = successor;
+    setCurrent(successor);
     return ControlStatus_Joined;
 }
 
 IonBuilder::ControlStatus
 IonBuilder::maybeLoop(JSOp op, jssrcnote *sn)
 {
     // This function looks at the opcode and source note and tries to
     // determine the structure of the loop. For some opcodes, like
@@ -1954,39 +2145,41 @@ IonBuilder::doWhileLoop(JSOp op, jssrcno
     JS_ASSERT(loopHead == ifne + GetJumpOffset(ifne));
 
     jsbytecode *loopEntry = GetNextPc(loopHead);
     if (info().hasOsrAt(loopEntry)) {
         MBasicBlock *preheader = newOsrPreheader(current, loopEntry);
         if (!preheader)
             return ControlStatus_Error;
         current->end(MGoto::New(preheader));
-        current = preheader;
+        setCurrent(preheader);
     }
 
     MBasicBlock *header = newPendingLoopHeader(current, pc);
     if (!header)
         return ControlStatus_Error;
     current->end(MGoto::New(header));
 
-    jsbytecode *bodyStart = GetNextPc(GetNextPc(pc));
+    jsbytecode *loophead = GetNextPc(pc);
+    jsbytecode *bodyStart = GetNextPc(loophead);
     jsbytecode *bodyEnd = conditionpc;
     jsbytecode *exitpc = GetNextPc(ifne);
+    analyzeNewLoopTypes(header, bodyStart, exitpc);
     if (!pushLoop(CFGState::DO_WHILE_LOOP_BODY, conditionpc, header,
-                  bodyStart, bodyEnd, exitpc, conditionpc))
+                  loopHead, bodyStart, bodyStart, bodyEnd, exitpc, conditionpc))
     {
         return ControlStatus_Error;
     }
 
     CFGState &state = cfgStack_.back();
     state.loop.updatepc = conditionpc;
     state.loop.updateEnd = ifne;
 
-    current = header;
-    if (!jsop_loophead(GetNextPc(pc)))
+    setCurrent(header);
+    if (!jsop_loophead(loophead))
         return ControlStatus_Error;
 
     pc = bodyStart;
     return ControlStatus_Jumped;
 }
 
 IonBuilder::ControlStatus
 IonBuilder::whileOrForInLoop(jssrcnote *sn)
@@ -2010,34 +2203,39 @@ IonBuilder::whileOrForInLoop(jssrcnote *
     JS_ASSERT(GetNextPc(pc) == ifne + GetJumpOffset(ifne));
 
     jsbytecode *loopEntry = pc + GetJumpOffset(pc);
     if (info().hasOsrAt(loopEntry)) {
         MBasicBlock *preheader = newOsrPreheader(current, loopEntry);
         if (!preheader)
             return ControlStatus_Error;
         current->end(MGoto::New(preheader));
-        current = preheader;
+        setCurrent(preheader);
     }
 
     MBasicBlock *header = newPendingLoopHeader(current, pc);
     if (!header)
         return ControlStatus_Error;
     current->end(MGoto::New(header));
 
     // Skip past the JSOP_LOOPHEAD for the body start.
-    jsbytecode *bodyStart = GetNextPc(GetNextPc(pc));
+    jsbytecode *loopHead = GetNextPc(pc);
+    jsbytecode *bodyStart = GetNextPc(loopHead);
     jsbytecode *bodyEnd = pc + GetJumpOffset(pc);
     jsbytecode *exitpc = GetNextPc(ifne);
-    if (!pushLoop(CFGState::WHILE_LOOP_COND, ifne, header, bodyStart, bodyEnd, exitpc))
+    analyzeNewLoopTypes(header, bodyStart, exitpc);
+    if (!pushLoop(CFGState::WHILE_LOOP_COND, ifne, header,
+                  loopHead, bodyEnd, bodyStart, bodyEnd, exitpc))
+    {
         return ControlStatus_Error;
+    }
 
     // Parse the condition first.
-    current = header;
-    if (!jsop_loophead(GetNextPc(pc)))
+    setCurrent(header);
+    if (!jsop_loophead(loopHead))
         return ControlStatus_Error;
 
     pc = bodyEnd;
     return ControlStatus_Jumped;
 }
 
 IonBuilder::ControlStatus
 IonBuilder::forLoop(JSOp op, jssrcnote *sn)
@@ -2087,17 +2285,17 @@ IonBuilder::forLoop(JSOp op, jssrcnote *
     JS_ASSERT(ifne + GetJumpOffset(ifne) == bodyStart);
     bodyStart = GetNextPc(bodyStart);
 
     if (info().hasOsrAt(loopEntry)) {
         MBasicBlock *preheader = newOsrPreheader(current, loopEntry);
         if (!preheader)
             return ControlStatus_Error;
         current->end(MGoto::New(preheader));
-        current = preheader;
+        setCurrent(preheader);
     }
 
     MBasicBlock *header = newPendingLoopHeader(current, pc);
     if (!header)
         return ControlStatus_Error;
     current->end(MGoto::New(header));
 
     // If there is no condition, we immediately parse the body. Otherwise, we
@@ -2109,26 +2307,27 @@ IonBuilder::forLoop(JSOp op, jssrcnote *
         stopAt = ifne;
         initial = CFGState::FOR_LOOP_COND;
     } else {
         pc = bodyStart;
         stopAt = bodyEnd;
         initial = CFGState::FOR_LOOP_BODY;
     }
 
-    if (!pushLoop(initial, stopAt, header, bodyStart, bodyEnd, exitpc, updatepc))
+    analyzeNewLoopTypes(header, bodyStart, exitpc);
+    if (!pushLoop(initial, stopAt, header, loopHead, pc, bodyStart, bodyEnd, exitpc, updatepc))
         return ControlStatus_Error;
 
     CFGState &state = cfgStack_.back();
     state.loop.condpc = (condpc != ifne) ? condpc : NULL;
     state.loop.updatepc = (updatepc != condpc) ? updatepc : NULL;
     if (state.loop.updatepc)
         state.loop.updateEnd = condpc;
 
-    current = header;
+    setCurrent(header);
     if (!jsop_loophead(loopHead))
         return ControlStatus_Error;
 
     return ControlStatus_Jumped;
 }
 
 int
 IonBuilder::CmpSuccessors(const void *a, const void *b)
@@ -2235,17 +2434,17 @@ IonBuilder::tableSwitch(JSOp op, jssrcno
 
     // Save the MIR instruction as last instruction of this block.
     current->end(tableswitch);
 
     // If there is only one successor the block should stop at the end of the switch
     // Else it should stop at the start of the next successor
     if (tableswitch->numBlocks() > 1)
         state.stopAt = tableswitch->getBlock(1)->pc();
-    current = tableswitch->getBlock(0);
+    setCurrent(tableswitch->getBlock(0));
 
     if (!cfgStack_.append(state))
         return ControlStatus_Error;
 
     pc = current->pc();
     return ControlStatus_Jumped;
 }
 
@@ -2464,18 +2663,17 @@ IonBuilder::processCondSwitchCase(CFGSta
         return ControlStatus_Error;
 
     // Terminate the last case condition block by emitting the code
     // corresponding to JSOP_CASE bytecode.
     if (bodyBlock != caseBlock) {
         MDefinition *caseOperand = current->pop();
         MDefinition *switchOperand = current->peek(-1);
         MCompare *cmpResult = MCompare::New(switchOperand, caseOperand, JSOP_STRICTEQ);
-        TypeOracle::BinaryTypes b = oracle->binaryTypes(script(), pc);
-        cmpResult->infer(b, cx);
+        cmpResult->infer(cx);
         JS_ASSERT(!cmpResult->isEffectful());
         current->add(cmpResult);
         current->end(MTest::New(cmpResult, bodyBlock, caseBlock));
 
         // Add last case as predecessor of the body if the body is aliasing
         // the previous case body.
         if (!bodyIsNew && !bodyBlock->addPredecessorPopN(current, 1))
             return ControlStatus_Error;
@@ -2508,23 +2706,23 @@ IonBuilder::processCondSwitchCase(CFGSta
         // Handle break statements in processSwitchBreak while processing
         // bodies.
         ControlFlowInfo breakInfo(cfgStack_.length() - 1, state.condswitch.exitpc);
         if (!switches_.append(breakInfo))
             return ControlStatus_Error;
 
         // Jump into the first body.
         currentIdx = 0;
-        current = NULL;
+        setCurrent(NULL);
         state.state = CFGState::COND_SWITCH_BODY;
         return processCondSwitchBody(state);
     }
 
     // Continue until the case condition.
-    current = caseBlock;
+    setCurrent(caseBlock);
     pc = current->pc();
     state.stopAt = casePc;
     return ControlStatus_Jumped;
 }
 
 IonBuilder::ControlStatus
 IonBuilder::processCondSwitchBody(CFGState &state)
 {
@@ -2548,17 +2746,17 @@ IonBuilder::processCondSwitchBody(CFGSta
 
     // The last body continue into the new one.
     if (current) {
         current->end(MGoto::New(nextBody));
         nextBody->addPredecessor(current);
     }
 
     // Continue in the next body.
-    current = nextBody;
+    setCurrent(nextBody);
     pc = current->pc();
 
     if (currentIdx < bodies.length())
         state.stopAt = bodies[currentIdx]->pc();
     else
         state.stopAt = state.condswitch.exitpc;
     return ControlStatus_Jumped;
 }
@@ -2578,24 +2776,23 @@ IonBuilder::jsop_andor(JSOp op)
     MBasicBlock *evalRhs = newBlock(current, rhsStart);
     MBasicBlock *join = newBlock(current, joinStart);
     if (!evalRhs || !join)
         return false;
 
     MTest *test = (op == JSOP_AND)
                   ? MTest::New(lhs, evalRhs, join)
                   : MTest::New(lhs, join, evalRhs);
-    TypeOracle::UnaryTypes types = oracle->unaryTypes(script(), pc);
-    test->infer(types, cx);
+    test->infer(cx);
     current->end(test);
 
     if (!cfgStack_.append(CFGState::AndOr(joinStart, join)))
         return false;
 
-    current = evalRhs;
+    setCurrent(evalRhs);
     return true;
 }
 
 bool
 IonBuilder::jsop_dup2()
 {
     uint32_t lhsSlot = current->stackDepth() - 2;
     uint32_t rhsSlot = current->stackDepth() - 1;
@@ -2684,17 +2881,17 @@ IonBuilder::jsop_ifeq(JSOp op)
 
       default:
         JS_NOT_REACHED("unexpected source note type");
         break;
     }
 
     // Switch to parsing the true branch. Note that no PC update is needed,
     // it's the next instruction.
-    current = ifTrue;
+    setCurrent(ifTrue);
 
     return true;
 }
 
 IonBuilder::ControlStatus
 IonBuilder::processReturn(JSOp op)
 {
     MDefinition *def;
@@ -2721,33 +2918,33 @@ IonBuilder::processReturn(JSOp op)
         current->add(MFunctionBoundary::New(script(), MFunctionBoundary::Exit));
     MReturn *ret = MReturn::New(def);
     current->end(ret);
 
     if (!graph().addExit(current))
         return ControlStatus_Error;
 
     // Make sure no one tries to use this block now.
-    current = NULL;
+    setCurrent(NULL);
     return processControlEnd();
 }
 
 IonBuilder::ControlStatus
 IonBuilder::processThrow()
 {
     MDefinition *def = current->pop();
 
     MThrow *ins = MThrow::New(def);
     current->end(ins);
 
     if (!graph().addExit(current))
         return ControlStatus_Error;
 
     // Make sure no one tries to use this block now.
-    current = NULL;
+    setCurrent(NULL);
     return processControlEnd();
 }
 
 bool
 IonBuilder::pushConstant(const Value &v)
 {
     MConstant *ins = MConstant::New(v);
     current->add(ins);
@@ -2757,17 +2954,17 @@ IonBuilder::pushConstant(const Value &v)
 
 bool
 IonBuilder::jsop_bitnot()
 {
     MDefinition *input = current->pop();
     MBitNot *ins = MBitNot::New(input);
 
     current->add(ins);
-    ins->infer(oracle->unaryTypes(script(), pc));
+    ins->infer();
 
     current->push(ins);
     if (ins->isEffectful() && !resumeAfter(ins))
         return false;
     return true;
 }
 bool
 IonBuilder::jsop_bitop(JSOp op)
@@ -2803,34 +3000,34 @@ IonBuilder::jsop_bitop(JSOp op)
         break;
 
       default:
         JS_NOT_REACHED("unexpected bitop");
         return false;
     }
 
     current->add(ins);
-    TypeOracle::BinaryTypes types = oracle->binaryTypes(script(), pc);
-    ins->infer(types);
+    ins->infer();
 
     current->push(ins);
     if (ins->isEffectful() && !resumeAfter(ins))
         return false;
 
     return true;
 }
 
 bool
 IonBuilder::jsop_binary(JSOp op, MDefinition *left, MDefinition *right)
 {
-    TypeOracle::Binary b = oracle->binaryOp(script(), pc);
-
-    if (op == JSOP_ADD && b.rval == MIRType_String &&
-        (b.lhs == MIRType_String || b.lhs == MIRType_Int32) &&
-        (b.rhs == MIRType_String || b.rhs == MIRType_Int32))
+    // Do a string concatenation if adding two inputs that are int or string
+    // and at least one is a string.
+    if (op == JSOP_ADD &&
+        (left->type() == MIRType_String || right->type() == MIRType_String) &&
+        (left->type() == MIRType_String || left->type() == MIRType_Int32) &&
+        (right->type() == MIRType_String || right->type() == MIRType_Int32))
     {
         MConcat *ins = MConcat::New(left, right);
         current->add(ins);
         current->push(ins);
         return maybeInsertResume();
     }
 
     MBinaryArithInstruction *ins;
@@ -2855,19 +3052,20 @@ IonBuilder::jsop_binary(JSOp op, MDefini
         ins = MMod::New(left, right);
         break;
 
       default:
         JS_NOT_REACHED("unexpected binary opcode");
         return false;
     }
 
-    TypeOracle::BinaryTypes types = oracle->binaryTypes(script(), pc);
+    bool overflowed = types::HasOperationOverflowed(script(), pc);
+
     current->add(ins);
-    ins->infer(types, cx);
+    ins->infer(overflowed);
     current->push(ins);
 
     if (ins->isEffectful())
         return resumeAfter(ins);
     return maybeInsertResume();
 }
 
 bool
@@ -2877,20 +3075,18 @@ IonBuilder::jsop_binary(JSOp op)
     MDefinition *left = current->pop();
 
     return jsop_binary(op, left, right);
 }
 
 bool
 IonBuilder::jsop_pos()
 {
-    TypeOracle::Unary types = oracle->unaryOp(script(), pc);
-    if (IsNumberType(types.ival)) {
+    if (IsNumberType(current->peek(-1)->type())) {
         // Already int32 or double.
-        JS_ASSERT(IsNumberType(types.rval));
         return true;
     }
 
     // Compile +x as x * 1.
     MDefinition *value = current->pop();
     MConstant *one = MConstant::New(Int32Value(1));
     current->add(one);
 
@@ -2941,17 +3137,16 @@ class AutoAccumulateExits
         graph_.setExitAccumulator(prev_);
     }
 };
 
 bool
 IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target)
 {
     JS_ASSERT(target->isInterpreted());
-    JS_ASSERT(callInfo.hasCallType());
     JS_ASSERT(types::IsInlinableCall(pc));
 
     // Remove any MPassArgs.
     if (callInfo.isWrapped())
         callInfo.unwrapArgs();
 
     // Ensure sufficient space in the slots: needed for inlining from FUNAPPLY.
     uint32_t depth = current->stackDepth() + callInfo.numFormals();
@@ -2977,53 +3172,33 @@ IonBuilder::inlineScriptedCall(CallInfo 
     if (!outerResumePoint)
         return false;
 
     // Pop formals again, except leave |fun| on stack for duration of call.
     callInfo.popFormals(current);
     current->push(callInfo.fun());
 
     RootedScript calleeScript(cx, target->nonLazyScript());
-    TypeInferenceOracle oracle;
-    if (!oracle.init(cx, calleeScript, /* inlinedCall = */ true))
-        return false;
-
     BaselineInspector inspector(cx, target->nonLazyScript());
 
-    // Copy the CallInfo as the addTypeBarrier is mutating it.
-    bool argsBarrier = callInfo.argsBarrier();
-    CallInfo clonedCallInfo(cx, callInfo.constructing());
-    CallInfo &thisCall = argsBarrier ? clonedCallInfo : callInfo;
-
-    // Add exclude type barriers.
-    if (argsBarrier) {
-        if (!thisCall.init(callInfo))
-            return false;
-
-        addTypeBarrier(0, thisCall, oracle.thisTypeSet(calleeScript));
-        int32_t max = (thisCall.argc() < target->nargs) ? thisCall.argc() : target->nargs;
-        for (int32_t i = 1; i <= max; i++)
-            addTypeBarrier(i, thisCall, oracle.parameterTypeSet(calleeScript, i - 1));
-    }
-
     // Start inlining.
     LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc();
     CompileInfo *info = alloc->new_<CompileInfo>(calleeScript.get(), target,
-                                                 (jsbytecode *)NULL, thisCall.constructing(),
+                                                 (jsbytecode *)NULL, callInfo.constructing(),
                                                  this->info().executionMode());
     if (!info)
         return false;
 
     MIRGraphExits saveExits;
     AutoAccumulateExits aae(graph(), saveExits);
 
     // Build the graph.
-    IonBuilder inlineBuilder(cx, &temp(), &graph(), &oracle, &inspector, info, inliningDepth_ + 1,
-                             loopDepth_);
-    if (!inlineBuilder.buildInline(this, outerResumePoint, thisCall)) {
+    IonBuilder inlineBuilder(cx, &temp(), &graph(), &inspector, info, AbstractFramePtr(),
+                             inliningDepth_ + 1, loopDepth_);
+    if (!inlineBuilder.buildInline(this, outerResumePoint, callInfo)) {
         JS_ASSERT(calleeScript->hasAnalysis());
 
         // Inlining the callee failed. Disable inlining the function
         if (inlineBuilder.abortReason_ == AbortReason_Disable)
             calleeScript->analysis()->setIonUninlineable();
 
         abortReason_ = AbortReason_Inlining;
         return false;
@@ -3041,106 +3216,29 @@ IonBuilder::inlineScriptedCall(CallInfo 
         returnBlock->add(MFunctionBoundary::New(NULL, MFunctionBoundary::Inline_Exit));
 
     // Inherit the slots from current and pop |fun|.
     returnBlock->inheritSlots(current);
     returnBlock->pop();
 
     // Accumulate return values.
     MIRGraphExits &exits = *inlineBuilder.graph().exitAccumulator();
-    MDefinition *retvalDefn = patchInlinedReturns(thisCall, exits, returnBlock);
+    MDefinition *retvalDefn = patchInlinedReturns(callInfo, exits, returnBlock);
     if (!retvalDefn)
         return false;
     returnBlock->push(retvalDefn);
 
     // Initialize entry slots now that the stack has been fixed up.
     if (!returnBlock->initEntrySlots())
         return false;
 
-    current = returnBlock;
+    setCurrent(returnBlock);
     return true;
 }
 
-void
-IonBuilder::addTypeBarrier(uint32_t i, CallInfo &callinfo, types::StackTypeSet *calleeObs)
-{
-    MDefinition *ins = NULL;
-    types::StackTypeSet *callerObs = NULL;
-    types::TypeBarrier *excluded = callinfo.argsBarrier();
-
-    if (i == 0) {
-        // The |this| type get's created during execution, no need to add type barrier.
-        if (callinfo.constructing())
-            return;
-        ins = callinfo.thisArg();
-        callerObs = callinfo.thisType();
-    } else {
-        ins = callinfo.getArg(i - 1);
-        callerObs = callinfo.getArgType(i - 1);
-    }
-
-    bool needsBarrier = false;
-
-    while (excluded) {
-        if (excluded->target == calleeObs && callerObs->hasType(excluded->type)) {
-            if (excluded->type == types::Type::DoubleType() &&
-                calleeObs->hasType(types::Type::Int32Type()))
-            {
-                // The double type also implies int32, so this implies that
-                // double should be coerced into int if possible, and other
-                // types should remain.
-
-                JSValueType callerType = callerObs->getKnownTypeTag();
-                if (callerType == JSVAL_TYPE_DOUBLE) {
-                    MInstruction *bailType = MToInt32::New(ins);
-                    current->add(bailType);
-                    ins = bailType;
-                } else {
-                    // We expect either an Int or a Value, this variant is not
-                    // optimized and favor the int variant by filtering out all
-                    // other inputs.
-                    JS_ASSERT(callerType == JSVAL_TYPE_UNKNOWN);
-                    // Bail if the input is not a number.
-                    MInstruction *toDouble = MUnbox::New(ins, MIRType_Double, MUnbox::Fallible);
-                    // Bail if the double does not fit in an int.
-                    MInstruction *toInt = MToInt32::New(ins);
-                    current->add(toDouble);
-                    current->add(toInt);
-                    ins = toInt;
-                }
-
-                needsBarrier = false;
-                break;
-            }
-
-            needsBarrier = true;
-        }
-        excluded = excluded->next;
-    }
-
-    if (needsBarrier) {
-        MTypeBarrier *barrier = MTypeBarrier::New(ins, cloneTypeSet(calleeObs), Bailout_Normal);
-        current->add(barrier);
-
-        // Make sure unknown inputs are always boxed.
-        if (callerObs->getKnownTypeTag() == JSVAL_TYPE_UNKNOWN &&
-            ins->type() != MIRType_Value)
-        {
-            MBox *box = MBox::New(ins);
-            current->add(box);
-            ins = box;
-        }
-    }
-
-    if (i == 0)
-        callinfo.setThis(ins);
-    else
-        callinfo.setArg(i - 1, ins);
-}
-
 MDefinition *
 IonBuilder::patchInlinedReturn(CallInfo &callInfo, MBasicBlock *exit, MBasicBlock *bottom)
 {
     // Replaces the MReturn in the exit block with an MGoto.
     MDefinition *rdef = exit->lastIns()->toReturn()->input();
     exit->discardLastIns();
 
     // Constructors must be patched by the caller to always return an object.
@@ -3210,26 +3308,18 @@ IonBuilder::makeInliningDecision(JSFunct
     // Native functions provide their own detection in inlineNativeCall().
     if (target->isNative())
         return true;
 
     // Determine whether inlining is possible at callee site
     if (!canInlineTarget(target, callInfo))
         return false;
 
-    // Determine whether inlining is possible at caller site
-    RootedScript scriptRoot(cx, script());
+    // Heuristics!
     JSScript *targetScript = target->nonLazyScript();
-    if (!oracle->canInlineCall(scriptRoot, pc)) {
-        IonSpew(IonSpew_Inlining, "%s:%d - Cannot inline due to uninlineable call site",
-                                  targetScript->filename(), targetScript->lineno);
-        return false;
-    }
-
-    // Heuristics!
 
     // Cap the inlining depth.
     if (IsSmallFunction(targetScript)) {
         if (inliningDepth_ >= js_IonOptions.smallFunctionMaxInlineDepth) {
             IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: exceeding allowed inline depth",
                                       targetScript->filename(), targetScript->lineno);
             return false;
         }
@@ -3461,31 +3551,29 @@ IonBuilder::makePolyInlineDispatch(JSCon
     // Finally create a fallbackEnd block to do the actual call.  The fallbackEnd block will
     // have the |pc| restored to the current PC.
     MBasicBlock *fallbackEndBlock = newBlock(fallbackBlock, pc, preCallResumePoint);
     if (!fallbackEndBlock)
         return NULL;
     fallbackBlock->end(MGoto::New(fallbackEndBlock));
 
     MBasicBlock *top = current;
-    current = fallbackEndBlock;
+    setCurrent(fallbackEndBlock);
 
     // Make the actual call.
     CallInfo realCallInfo(cx, callInfo.constructing());
     if (!realCallInfo.init(callInfo))
         return NULL;
     realCallInfo.popFormals(current);
     realCallInfo.wrapArgs(current);
 
     RootedFunction target(cx, NULL);
-    makeCallBarrier(target, realCallInfo,
-                    oracle->getCallTarget(script(), callInfo.argc(), pc),
-                    false);
-
-    current = top;
+    makeCall(target, realCallInfo, false);
+
+    setCurrent(top);
 
     // Create a new MPolyInlineDispatch containing the getprop and the fallback block
     return MPolyInlineDispatch::New(targetObject, inlinePropTable,
                                     fallbackPrepBlock, fallbackBlock,
                                     fallbackEndBlock);
 }
 
 IonBuilder::InliningStatus
@@ -3559,20 +3647,19 @@ IonBuilder::inlineGenericFallback(JSFunc
     // Create a new CallInfo to track modified state within this block.
     CallInfo fallbackInfo(cx, callInfo.constructing());
     if (!fallbackInfo.init(callInfo))
         return false;
     fallbackInfo.popFormals(fallbackBlock);
     fallbackInfo.wrapArgs(fallbackBlock);
 
     // Generate an MCall, which uses stateful |current|.
-    current = fallbackBlock;
+    setCurrent(fallbackBlock);
     RootedFunction targetRooted(cx, target);
-    types::StackTypeSet *calleeTypes = oracle->getCallTarget(script(), callInfo.argc(), pc);
-    if (!makeCallBarrier(targetRooted, fallbackInfo, calleeTypes, clonedAtCallsite))
+    if (!makeCall(targetRooted, fallbackInfo, clonedAtCallsite))
         return false;
 
     // Pass return block to caller as |current|.
     return true;
 }
 
 bool
 IonBuilder::inlineTypeObjectFallback(CallInfo &callInfo, MBasicBlock *dispatchBlock,
@@ -3684,17 +3771,16 @@ IonBuilder::inlineCalls(CallInfo &callIn
     JS_ASSERT(types::IsInlinableCall(pc));
     JS_ASSERT(choiceSet.length() == targets.length());
     JS_ASSERT_IF(!maybeCache, targets.length() >= 2);
     JS_ASSERT_IF(maybeCache, targets.length() >= 1);
 
     MBasicBlock *dispatchBlock = current;
 
     // Unwrap the arguments.
-    JS_ASSERT(callInfo.hasTypeInfo());
     JS_ASSERT(callInfo.isWrapped());
     callInfo.unwrapArgs();
     callInfo.pushFormals(dispatchBlock);
 
     // Patch any InlinePropertyTable to only contain functions that are inlineable.
     // Also guarantee that the table uses functions from |targets| instead of |originals|.
     // The InlinePropertyTable will also be patched at the end to exclude native functions
     // that vetoed inlining.
@@ -3736,16 +3822,22 @@ IonBuilder::inlineCalls(CallInfo &callIn
     // Note: this is an upperbound. Unreachable targets and uninlineable natives are also counted.
     uint32_t count = 1; // Possible fallback block.
     for (uint32_t i = 0; i < targets.length(); i++) {
         if (choiceSet[i])
             count++;
     }
     retPhi->reserveLength(count);
 
+    // During inlining the 'this' value is assigned a type set which is
+    // specialized to the type objects which can generate that inlining target.
+    // After inlining the original type set is restored.
+    types::StackTypeSet *cacheObjectTypeSet =
+        maybeCache ? maybeCache->object()->resultTypeSet() : NULL;
+
     // Inline each of the inlineable targets.
     JS_ASSERT(targets.length() == originals.length());
     for (uint32_t i = 0; i < targets.length(); i++) {
         JSFunction *target = targets[i]->toFunction();
 
         // Target must be inlineable.
         if (!choiceSet[i])
             continue;
@@ -3776,18 +3868,27 @@ IonBuilder::inlineCalls(CallInfo &callIn
         // Create a new CallInfo to track modified state within the inline block.
         CallInfo inlineInfo(cx, callInfo.constructing());
         if (!inlineInfo.init(callInfo))
             return false;
         inlineInfo.popFormals(inlineBlock);
         inlineInfo.setFun(funcDef);
         inlineInfo.wrapArgs(inlineBlock);
 
+        if (maybeCache) {
+            JS_ASSERT(callInfo.thisArg() == maybeCache->object());
+            types::StackTypeSet *targetThisTypes =
+                maybeCache->propTable()->buildTypeSetForFunction(target);
+            if (!targetThisTypes)
+                return false;
+            maybeCache->object()->setResultTypeSet(targetThisTypes);
+        }
+
         // Inline the call into the inlineBlock.
-        current = inlineBlock;
+        setCurrent(inlineBlock);
         InliningStatus status = inlineSingleCall(inlineInfo, target);
         if (status == InliningStatus_Error)
             return false;
 
         // Natives may veto inlining.
         if (status == InliningStatus_NotInlined) {
             JS_ASSERT(target->isNative());
             JS_ASSERT(current == inlineBlock);
@@ -3812,16 +3913,18 @@ IonBuilder::inlineCalls(CallInfo &callIn
         retPhi->addInput(retVal);
         inlineReturnBlock->end(MGoto::New(returnBlock));
         if (!returnBlock->addPredecessorWithoutPhis(inlineReturnBlock))
             return false;
     }
 
     // Patch the InlinePropertyTable to not dispatch to vetoed paths.
     if (maybeCache) {
+        maybeCache->object()->setResultTypeSet(cacheObjectTypeSet);
+
         InlinePropertyTable *propTable = maybeCache->propTable();
         propTable->trimTo(targets, choiceSet);
 
         // If all paths were vetoed, output only a generic fallback path.
         if (propTable->numEntries() == 0) {
             JS_ASSERT(dispatch->numCases() == 0);
             maybeCache = NULL;
         }
@@ -3874,17 +3977,17 @@ IonBuilder::inlineCalls(CallInfo &callIn
     // Finally add the dispatch instruction.
     // This must be done at the end so that add() may be called above.
     dispatchBlock->end(dispatch);
 
     // Check the depth change: +1 for retval
     JS_ASSERT(returnBlock->stackDepth() == dispatchBlock->stackDepth() - callInfo.numFormals() + 1);
 
     graph().moveBlockToEnd(returnBlock);
-    current = returnBlock;
+    setCurrent(returnBlock);
     return true;
 }
 
 MInstruction *
 IonBuilder::createDeclEnvObject(MDefinition *callee, MDefinition *scope)
 {
     // Create a template CallObject that we'll use to generate inline object
     // creation.
@@ -4094,33 +4197,34 @@ IonBuilder::jsop_funcall(uint32_t argc)
 {
     // Stack for JSOP_FUNCALL:
     // 1:      MPassArg(arg0)
     // ...
     // argc:   MPassArg(argN)
     // argc+1: MPassArg(JSFunction *), the 'f' in |f.call()|, in |this| position.
     // argc+2: The native 'call' function.
 
+    int calleeDepth = -((int)argc + 2);
+    int funcDepth = -((int)argc + 1);
+
     // If |Function.prototype.call| may be overridden, don't optimize callsite.
-    types::StackTypeSet *calleeTypes = oracle->getCallTarget(script(), argc, pc);
+    types::StackTypeSet *calleeTypes = current->peek(calleeDepth)->resultTypeSet();
     RootedFunction native(cx, getSingleCallTarget(calleeTypes));
     if (!native || !native->isNative() || native->native() != &js_fun_call) {
         CallInfo callInfo(cx, false);
         if (!callInfo.init(current, argc))
             return false;
-        return makeCall(native, callInfo, calleeTypes, false);
+        return makeCall(native, callInfo, false);
     }
 
     // Extract call target.
-    types::StackTypeSet *funTypes = oracle->getCallArg(script(), argc, 0, pc);
-    RootedObject funobj(cx, (funTypes) ? funTypes->getSingleton() : NULL);
-    RootedFunction target(cx, (funobj && funobj->isFunction()) ? funobj->toFunction() : NULL);
+    types::StackTypeSet *funTypes = current->peek(funcDepth)->resultTypeSet();
+    RootedFunction target(cx, getSingleCallTarget(funTypes));
 
     // Unwrap the (JSFunction *) parameter.
-    int funcDepth = -((int)argc + 1);
     MPassArg *passFunc = current->peek(funcDepth)->toPassArg();
     current->rewriteAtDepth(funcDepth, passFunc->getArgument());
 
     // Remove the MPassArg(JSFunction *).
     passFunc->replaceAllUsesWith(passFunc->getArgument());
     passFunc->block()->discard(passFunc);
 
     // Shimmy the slots down to remove the native 'call' function.
@@ -4138,44 +4242,49 @@ IonBuilder::jsop_funcall(uint32_t argc)
         // |this| becomes implicit in the call.
         argc -= 1;
     }
 
     // Call without inlining.
     CallInfo callInfo(cx, false);
     if (!callInfo.init(current, argc))
         return false;
-    return makeCall(target, callInfo, funTypes, false);
+    return makeCall(target, callInfo, false);
 }
 
 bool
 IonBuilder::jsop_funapply(uint32_t argc)
 {
-    types::StackTypeSet *calleeTypes = oracle->getCallTarget(script(), argc, pc);
+    int calleeDepth = -((int)argc + 2);
+
+    types::StackTypeSet *calleeTypes = current->peek(calleeDepth)->resultTypeSet();
     RootedFunction native(cx, getSingleCallTarget(calleeTypes));
     if (argc != 2) {
         CallInfo callInfo(cx, false);
         if (!callInfo.init(current, argc))
             return false;
-        return makeCall(native, callInfo, calleeTypes, false);
+        return makeCall(native, callInfo, false);
     }
 
     // Disable compilation if the second argument to |apply| cannot be guaranteed
     // to be either definitely |arguments| or definitely not |arguments|.
-    types::StackTypeSet *argObjTypes = oracle->getCallArg(script(), argc, 2, pc);
-    LazyArgumentsType isArgObj = oracle->isArgumentObject(argObjTypes);
-    if (isArgObj == MaybeArguments)
+    MDefinition *argument = current->peek(-1);
+    if (script()->argumentsHasVarBinding() &&
+        argument->mightBeType(MIRType_Magic) &&
+        argument->type() != MIRType_Magic)
+    {
         return abort("fun.apply with MaybeArguments");
+    }
 
     // Fallback to regular call if arg 2 is not definitely |arguments|.
-    if (isArgObj != DefinitelyArguments) {
+    if (argument->type() != MIRType_Magic) {
         CallInfo callInfo(cx, false);
         if (!callInfo.init(current, argc))
             return false;
-        return makeCall(native, callInfo, calleeTypes, false);
+        return makeCall(native, callInfo, false);
     }
 
     if (!native ||
         !native->isNative() ||
         native->native() != js_fun_apply)
     {
         return abort("fun.apply speculation failed");
     }
@@ -4188,20 +4297,21 @@ bool
 IonBuilder::jsop_funapplyarguments(uint32_t argc)
 {
     // Stack for JSOP_FUNAPPLY:
     // 1:      MPassArg(Vp)
     // 2:      MPassArg(This)
     // argc+1: MPassArg(JSFunction *), the 'f' in |f.call()|, in |this| position.
     // argc+2: The native 'apply' function.
 
+    int funcDepth = -((int)argc + 1);
+
     // Extract call target.
-    types::StackTypeSet *funTypes = oracle->getCallArg(script(), argc, 0, pc);
-    RootedObject funobj(cx, (funTypes) ? funTypes->getSingleton() : NULL);
-    RootedFunction target(cx, (funobj && funobj->isFunction()) ? funobj->toFunction() : NULL);
+    types::StackTypeSet *funTypes = current->peek(funcDepth)->resultTypeSet();
+    RootedFunction target(cx, getSingleCallTarget(funTypes));
 
     // When this script isn't inlined, use MApplyArgs,
     // to copy the arguments from the stack and call the function
     if (inliningDepth_ == 0) {
 
         // Vp
         MPassArg *passVp = current->pop()->toPassArg();
         passVp->replaceAllUsesWith(passVp->getArgument());
@@ -4226,19 +4336,18 @@ IonBuilder::jsop_funapplyarguments(uint3
         current->add(numArgs);
 
         MApplyArgs *apply = MApplyArgs::New(target, argFunc, numArgs, argThis);
         current->add(apply);
         current->push(apply);
         if (!resumeAfter(apply))
             return false;
 
-        types::StackTypeSet *barrier;
-        types::StackTypeSet *types = oracle->returnTypeSet(script(), pc, &barrier);
-        return pushTypeBarrier(apply, types, barrier);
+        types::StackTypeSet *types = script()->analysis()->bytecodeTypes(pc);
+        return pushTypeBarrier(apply, types, true);
     }
 
     // When inlining we have the arguments the function gets called with
     // and can optimize even more, by just calling the functions with the args.
     JS_ASSERT(inliningDepth_ > 0);
 
     CallInfo callInfo(cx, false);
 
@@ -4247,23 +4356,16 @@ IonBuilder::jsop_funapplyarguments(uint3
     passVp->replaceAllUsesWith(passVp->getArgument());
     passVp->block()->discard(passVp);
 
     // Arguments
     Vector<MDefinition *> args(cx);
     if (!args.append(inlinedArguments_.begin(), inlinedArguments_.end()))
         return false;
     callInfo.setArgs(&args);
-    RootedScript scriptRoot(cx, script());
-    RootedScript parentScriptRoot(cx, callerBuilder_->script());
-    Vector<types::StackTypeSet *> argTypes(cx);
-    if (!argTypes.append(inlinedArgumentTypes_.begin(), inlinedArgumentTypes_.end()))
-        return false;
-    if (!callInfo.initFunApplyArguments(oracle, scriptRoot, pc, &argTypes))
-        return false;
 
     // This
     MPassArg *passThis = current->pop()->toPassArg();
     MDefinition *argThis = passThis->getArgument();
     passThis->replaceAllUsesWith(argThis);
     passThis->block()->discard(passThis);
     callInfo.setThis(argThis);
 
@@ -4273,35 +4375,32 @@ IonBuilder::jsop_funapplyarguments(uint3
     passFunc->replaceAllUsesWith(argFunc);
     passFunc->block()->discard(passFunc);
 
     callInfo.setFun(argFunc);
 
     // Pop apply function.
     current->pop();
 
-    // Set type information
-    types::StackTypeSet *barrier;
-    types::StackTypeSet *types = oracle->returnTypeSet(script(), pc, &barrier);
-    callInfo.setTypeInfo(types, barrier);
-
     // Try inlining call
     if (makeInliningDecision(target, callInfo) && target->isInterpreted())
         return inlineScriptedCall(callInfo, target);
 
     callInfo.wrapArgs(current);
-    return makeCallBarrier(target, callInfo, funTypes, false);
+    return makeCall(target, callInfo, false);
 }
 
 bool
 IonBuilder::jsop_call(uint32_t argc, bool constructing)
 {
+    int calleeDepth = -((int)argc + 2);
+
     // Acquire known call target if existent.
     AutoObjectVector originals(cx);
-    types::StackTypeSet *calleeTypes = oracle->getCallTarget(script(), argc, pc);
+    types::StackTypeSet *calleeTypes = current->peek(calleeDepth)->resultTypeSet();
     if (calleeTypes) {
         if (!getPolyCallTargets(calleeTypes, originals, 4))
             return false;
     }
 
     // If any call targets need to be cloned, clone them. Keep track of the
     // originals as we need to case on them for poly inline.
     bool hasClones = false;
@@ -4319,35 +4418,29 @@ IonBuilder::jsop_call(uint32_t argc, boo
         if (!targets.append(fun))
             return false;
     }
 
     CallInfo callInfo(cx, constructing);
     if (!callInfo.init(current, argc))
         return false;
 
-    types::StackTypeSet *barrier;
-    types::StackTypeSet *types = oracle->returnTypeSet(script(), pc, &barrier);
-    callInfo.setTypeInfo(types, barrier);
-    if (!callInfo.initCallType(oracle, scriptRoot, pc))
-        return false;
-
     // Try inlining
     InliningStatus status = inlineCallsite(targets, originals, callInfo);
     if (status == InliningStatus_Inlined)
         return true;
     if (status == InliningStatus_Error)
         return false;
 
     // No inline, just make the call.
     RootedFunction target(cx, NULL);
     if (targets.length() == 1)
         target = targets[0]->toFunction();
 
-    return makeCallBarrier(target, callInfo, calleeTypes, hasClones);
+    return makeCall(target, callInfo, hasClones);
 }
 
 MDefinition *
 IonBuilder::makeCallsiteClone(HandleFunction target, MDefinition *fun)
 {
     // Bake in the clone eagerly if we have a known target. We have arrived here
     // because TI told us that the known target is a should-clone-at-callsite
     // function, which means that target already is the clone.
@@ -4437,33 +4530,32 @@ TestAreKnownDOMTypes(JSContext *cx, type
     // If we didn't check anything, no reason to say yes.
     if (inTypes->getObjectCount() > 0)
         return true;
 
     return false;
 }
 
 MCall *
-IonBuilder::makeCallHelper(HandleFunction target, CallInfo &callInfo,
-                           types::StackTypeSet *calleeTypes, bool cloneAtCallsite)
+IonBuilder::makeCallHelper(HandleFunction target, CallInfo &callInfo, bool cloneAtCallsite)
 {
     JS_ASSERT(callInfo.isWrapped());
 
     // This function may be called with mutated stack.
     // Querying TI for popped types is invalid.
 
     uint32_t targetArgs = callInfo.argc();
 
     // Collect number of missing arguments provided that the target is
     // scripted. Native functions are passed an explicit 'argc' parameter.
     if (target && !target->isNative())
         targetArgs = Max<uint32_t>(target->nargs, callInfo.argc());
 
     MCall *call =
-        MCall::New(target, targetArgs + 1, callInfo.argc(), callInfo.constructing(), calleeTypes);
+        MCall::New(target, targetArgs + 1, callInfo.argc(), callInfo.constructing());
     if (!call)
         return NULL;
 
     // Save the script for inspection by visitCallKnown().
     if (target && target->isInterpreted()) {
         if (!target->getOrCreateScript(cx))
             return NULL;
         call->rootTargetScript(target);
@@ -4522,93 +4614,76 @@ IonBuilder::makeCallHelper(HandleFunctio
         MDefinition *fun = makeCallsiteClone(target, callInfo.fun());
         callInfo.setFun(fun);
     }
 
     if (target && JSOp(*pc) == JSOP_CALL) {
         // We know we have a single call target.  Check whether the "this" types
         // are DOM types and our function a DOM function, and if so flag the
         // MCall accordingly.
-        types::StackTypeSet *thisTypes = oracle->getCallArg(script(), callInfo.argc(), 0, pc);
+        types::StackTypeSet *thisTypes = thisArg->resultTypeSet();
         if (thisTypes &&
             TestAreKnownDOMTypes(cx, thisTypes) &&
             TestShouldDOMCall(cx, thisTypes, target, JSJitInfo::Method))
         {
             call->setDOMFunction();
         }
     }
 
     call->initFunction(callInfo.fun());
 
     current->add(call);
     return call;
 }
 
-static types::StackTypeSet*
-AdjustTypeBarrierForDOMCall(const JSJitInfo* jitinfo, types::StackTypeSet *types,
-                            types::StackTypeSet *barrier)
+static bool
+DOMCallNeedsBarrier(const JSJitInfo* jitinfo, types::StackTypeSet *types)
 {
     // If the return type of our DOM native is in "types" already, we don't
     // actually need a barrier.
     if (jitinfo->returnType == JSVAL_TYPE_UNKNOWN)
-        return barrier;
+        return true;
 
     // JSVAL_TYPE_OBJECT doesn't tell us much; we still have to barrier on the
     // actual type of the object.
     if (jitinfo->returnType == JSVAL_TYPE_OBJECT)
-        return barrier;
-
-    if (jitinfo->returnType != types->getKnownTypeTag())
-        return barrier;
+        return true;
 
     // No need for a barrier if we're already expecting the type we'll produce.
-    return NULL;
+    return jitinfo->returnType == types->getKnownTypeTag();
 }
 
 bool
-IonBuilder::makeCallBarrier(HandleFunction target, CallInfo &callInfo,
-                            types::StackTypeSet *calleeTypes, bool cloneAtCallsite)
-{
-    JS_ASSERT(callInfo.hasTypeInfo());
-
-    MCall *call = makeCallHelper(target, callInfo, calleeTypes, cloneAtCallsite);
+IonBuilder::makeCall(HandleFunction target, CallInfo &callInfo, bool cloneAtCallsite)
+{
+    MCall *call = makeCallHelper(target, callInfo, cloneAtCallsite);
     if (!call)
         return false;
 
     current->push(call);
     if (!resumeAfter(call))
         return false;
 
-    types::StackTypeSet *barrier = callInfo.barrier();
+    types::StackTypeSet *types = script()->analysis()->bytecodeTypes(pc);
+
+    bool barrier = true;
     if (call->isDOMFunction()) {
         JSFunction* target = call->getSingleTarget();
         JS_ASSERT(target && target->isNative() && target->jitInfo());
-        barrier = AdjustTypeBarrierForDOMCall(target->jitInfo(), callInfo.types(), barrier);
-    }
-
-    return pushTypeBarrier(call, callInfo.types(), barrier);
-}
-
-bool
-IonBuilder::makeCall(HandleFunction target, CallInfo &callInfo,
-                     types::StackTypeSet *calleeTypes, bool cloneAtCallsite)
-{
-    JS_ASSERT(!callInfo.hasTypeInfo());
-
-    types::StackTypeSet *barrier;
-    types::StackTypeSet *types = oracle->returnTypeSet(script(), pc, &barrier);
-    callInfo.setTypeInfo(types, barrier);
-
-    return makeCallBarrier(target, callInfo, calleeTypes, cloneAtCallsite);
+        barrier = DOMCallNeedsBarrier(target->jitInfo(), types);
+    }
+
+    return pushTypeBarrier(call, types, barrier);
 }
 
 bool
 IonBuilder::jsop_eval(uint32_t argc)
 {
-    types::StackTypeSet *calleeTypes = oracle->getCallTarget(script(), argc, pc);
+    int calleeDepth = -((int)argc + 2);
+    types::StackTypeSet *calleeTypes = current->peek(calleeDepth)->resultTypeSet();
 
     // Emit a normal call if the eval has never executed. This keeps us from
     // disabling compilation for the script when testing with --ion-eager.
     if (calleeTypes && calleeTypes->empty())
         return jsop_call(argc, /* constructing = */ false);
 
     RootedFunction singleton(cx, getSingleCallTarget(calleeTypes));
     if (!singleton)
@@ -4616,17 +4691,17 @@ IonBuilder::jsop_eval(uint32_t argc)
 
     if (IsBuiltinEvalForScope(&script()->global(), ObjectValue(*singleton))) {
         if (argc != 1)
             return abort("Direct eval with more than one argument");
 
         if (!info().fun())
             return abort("Direct eval in global code");
 
-        types::StackTypeSet *thisTypes = oracle->thisTypeSet(script());
+        types::StackTypeSet *thisTypes = types::TypeScript::ThisTypes(script());
 
         // The 'this' value for the outer and eval scripts must be the
         // same. This is not guaranteed if a primitive string/number/etc.
         // is passed through to the eval invoke as the primitive may be
         // boxed into different objects if accessed via 'this'.
         JSValueType type = thisTypes->getKnownTypeTag();
         if (type != JSVAL_TYPE_OBJECT && type != JSVAL_TYPE_NULL && type != JSVAL_TYPE_UNDEFINED)
             return abort("Direct eval from script with maybe-primitive 'this'");
@@ -4664,47 +4739,45 @@ IonBuilder::jsop_eval(uint32_t argc)
 
                 current->push(dynamicName);
                 current->push(thisv);
 
                 CallInfo evalCallInfo(cx, /* constructing = */ false);
                 if (!evalCallInfo.init(current, /* argc = */ 0))
                     return false;
 
-                return makeCall(NullPtr(), evalCallInfo, NULL, false);
+                return makeCall(NullPtr(), evalCallInfo, false);
             }
         }
 
         MInstruction *filterArguments = MFilterArguments::New(string);
         current->add(filterArguments);
 
         MInstruction *ins = MCallDirectEval::New(scopeChain, string, thisValue, pc);
         current->add(ins);
         current->push(ins);
 
-        types::StackTypeSet *barrier;
-        types::StackTypeSet *types = oracle->returnTypeSet(script(), pc, &barrier);
-        return resumeAfter(ins) && pushTypeBarrier(ins, types, barrier);
+        types::StackTypeSet *types = script()->analysis()->bytecodeTypes(pc);
+        return resumeAfter(ins) && pushTypeBarrier(ins, types, true);
     }
 
     return jsop_call(argc, /* constructing = */ false);
 }
 
 bool
 IonBuilder::jsop_compare(JSOp op)
 {
     MDefinition *right = current->pop();
     MDefinition *left = current->pop();
 
     MCompare *ins = MCompare::New(left, right, op);
     current->add(ins);
     current->push(ins);
 
-    TypeOracle::BinaryTypes b = oracle->binaryTypes(script(), pc);
-    ins->infer(b, cx);
+    ins->infer(cx);
 
     if (ins->isEffectful() && !resumeAfter(ins))
         return false;
     return true;
 }
 
 JSObject *
 IonBuilder::getNewArrayTemplateObject(uint32_t count)
@@ -4729,17 +4802,19 @@ bool
 IonBuilder::jsop_newarray(uint32_t count)
 {
     JS_ASSERT(script()->compileAndGo);
 
     JSObject *templateObject = getNewArrayTemplateObject(count);
     if (!templateObject)
         return false;
 
-    if (oracle->arrayResultShouldHaveDoubleConversion(script(), pc))
+    types::StackTypeSet::DoubleConversion conversion =
+        script()->analysis()->bytecodeTypes(pc)->convertDoubleElements(cx);
+    if (conversion == types::StackTypeSet::AlwaysConvertToDoubles)
         templateObject->setShouldConvertDoubleElements();
 
     MNewArray *ins = new MNewArray(count, templateObject, MNewArray::NewArray_Allocating);
 
     current->add(ins);
     current->push(ins);
 
     return true;
@@ -4781,16 +4856,40 @@ IonBuilder::jsop_newobject(HandleObject 
 }
 
 bool
 IonBuilder::jsop_initelem_array()
 {
     MDefinition *value = current->pop();
     MDefinition *obj = current->peek(-1);
 
+    // Make sure that arrays have the type being written to them by the
+    // intializer, and that arrays are marked as non-packed when writing holes
+    // to them during initialization.
+    bool needStub = false;
+    types::TypeObject *initializer = obj->resultTypeSet()->getTypeObject(0);
+    if (value->isConstant() && value->toConstant()->value().isMagic(JS_ELEMENTS_HOLE)) {
+        if (!(initializer->flags & types::OBJECT_FLAG_NON_PACKED))
+            needStub = true;
+    } else if (!initializer->unknownProperties()) {
+        types::HeapTypeSet *elemTypes = initializer->getProperty(cx, JSID_VOID, false);
+        if (!elemTypes)
+            return false;
+        if (!TypeSetIncludes(elemTypes, value->type(), value->resultTypeSet())) {
+            elemTypes->addFreeze(cx);
+            needStub = true;
+        }
+    }
+
+    if (needStub) {
+        MCallInitElementArray *store = MCallInitElementArray::New(obj, GET_UINT24(pc), value);
+        current->add(store);
+        return resumeAfter(store);
+    }
+
     MConstant *id = MConstant::New(Int32Value(GET_UINT24(pc)));
     current->add(id);
 
     // Get the elements vector.
     MElements *elements = MElements::New(obj);
     current->add(elements);
 
     if (obj->toNewArray()->templateObject()->shouldConvertDoubleElements()) {
@@ -4829,44 +4928,40 @@ CanEffectlesslyCallLookupGenericOnObject
 bool
 IonBuilder::jsop_initprop(HandlePropertyName name)
 {
     MDefinition *value = current->pop();
     MDefinition *obj = current->peek(-1);
 
     RootedObject templateObject(cx, obj->toNewObject()->templateObject());
 
-    if (!oracle->propertyWriteCanSpecialize(script(), pc)) {
-        // This should only happen for a few names like __proto__.
-        return abort("INITPROP Monitored initprop");
-    }
-
     if (!CanEffectlesslyCallLookupGenericOnObject(templateObject))
         return abort("INITPROP template object is special");
 
     RootedObject holder(cx);
     RootedShape shape(cx);
     RootedId id(cx, NameToId(name));
     bool res = LookupPropertyWithFlags(cx, templateObject, id,
                                        0, &holder, &shape);
     if (!res)
         return false;
 
-    if (!shape || holder != templateObject) {
+    if (!shape || holder != templateObject ||
+        propertyWriteNeedsTypeBarrier(obj, name, &value))
+    {
         // JSOP_NEWINIT becomes an MNewObject without preconfigured properties.
         MInitProp *init = MInitProp::New(obj, name, value);
         current->add(init);
         return resumeAfter(init);
     }
 
     bool needsBarrier = true;
-    TypeOracle::BinaryTypes b = oracle->binaryTypes(script(), pc);
-    if (b.lhsTypes &&
-        (id == types::IdToTypeId(id)) &&
-        !b.lhsTypes->propertyNeedsBarrier(cx, id))
+    if ((id == types::IdToTypeId(id)) &&
+        obj->resultTypeSet() &&
+        !obj->resultTypeSet()->propertyNeedsBarrier(cx, id))
     {
         needsBarrier = false;
     }
 
     // In parallel execution, we never require write barriers.  See
     // forkjoin.cpp for more information.
     switch (info().executionMode()) {
       case SequentialExecution:
@@ -4962,17 +5057,26 @@ IonBuilder::newOsrPreheader(MBasicBlock 
 
     MOsrEntry *entry = MOsrEntry::New();
     osrBlock->add(entry);
 
     // Initialize |scopeChain|.
     {
         uint32_t slot = info().scopeChainSlot();
 
-        MOsrScopeChain *scopev = MOsrScopeChain::New(entry);
+        MInstruction *scopev;
+        if (script()->analysis()->usesScopeChain()) {
+            scopev = MOsrScopeChain::New(entry);
+        } else {
+            // Use an undefined value if the script does not need its scope
+            // chain, to match the type that is already being tracked for the
+            // slot.
+            scopev = MConstant::New(UndefinedValue());
+        }
+
         osrBlock->add(scopev);
         osrBlock->initSlot(slot, scopev);
     }
 
     if (info().fun()) {
         // Initialize |this| parameter.
         uint32_t slot = info().thisSlot();
         ptrdiff_t offset = StackFrame::offsetOfThis(info().fun());
@@ -5028,43 +5132,92 @@ IonBuilder::newOsrPreheader(MBasicBlock 
     // Unboxes in the MResumePiont, so that the MStart always sees Values.
     osrBlock->linkOsrValues(start);
 
     // Clone types of the other predecessor of the pre-header to the osr block,
     // such as pre-header phi's won't discard specialized type of the
     // predecessor.
     JS_ASSERT(predecessor->stackDepth() == osrBlock->stackDepth());
     JS_ASSERT(info().scopeChainSlot() == 0);
-    JS_ASSERT(osrBlock->scopeChain()->type() == MIRType_Object);
-
-    Vector<MIRType> slotTypes(cx);
-    if (!slotTypes.growByUninitialized(osrBlock->stackDepth()))
-        return NULL;
-
-    // Fill slotTypes with the types of the predecessor block.
-    for (uint32_t i = 0; i < osrBlock->stackDepth(); i++)
-        slotTypes[i] = MIRType_Value;
-
-    // Update slotTypes for slots that may have a different type at this join point.
-    if (!oracle->getOsrTypes(loopEntry, slotTypes))
-        return NULL;
-
+
+    // Unbox the MOsrValue if it is known to be unboxable.
     for (uint32_t i = 1; i < osrBlock->stackDepth(); i++) {
-        // Unbox the MOsrValue if it is known to be unboxable.
-        switch (slotTypes[i]) {
+        MDefinition *existing = current->getSlot(i);
+        MDefinition *def = osrBlock->getSlot(i);
+        JS_ASSERT(def->type() == MIRType_Value);
+
+        MIRType existingType = existing->type();
+        types::StackTypeSet *existingTypeSet = existing->resultTypeSet();
+
+        // The existing slot only reflects types for variables on entry to the
+        // loop, not those resulting from writes within the loop. Moreover,
+        // there may be types for variables not known at all to the compiler,
+        // either from values assigned to variables before the baseline
+        // compiler originally ran or from other value transformations such as
+        // using undefined values in bailouts for dead variables.
+        //
+        // Address all of these issues by incorporating the variable's type
+        // from the OSR frame into the types used for unboxing the OSR value.
+
+        bool haveValue = false;
+        Value existingValue;
+        {
+            uint32_t arg = i - info().firstArgSlot();
+            uint32_t var = i - info().firstLocalSlot();
+            if (arg < info().nargs()) {
+                if (!script()->formalIsAliased(arg)) {
+                    haveValue = true;
+                    existingValue = fp.unaliasedFormal(arg);
+                }
+            } else if (var < info().nlocals()) {
+                if (!script()->varIsAliased(var)) {
+                    haveValue = true;
+                    existingValue = fp.unaliasedVar(var);
+                }
+            }
+        }
+        if (haveValue) {
+            MIRType type = existingValue.isDouble()
+                           ? MIRType_Double
+                           : MIRTypeFromValueType(existingValue.extractNonDoubleType());
+            types::Type ntype = types::GetValueType(cx, existingValue);
+            types::StackTypeSet *typeSet =
+                GetIonContext()->temp->lifoAlloc()->new_<types::StackTypeSet>(ntype);
+            MergeTypes(&existingType, &existingTypeSet, type, typeSet);
+        }
+
+        if (existingTypeSet) {
+            MInstruction *barrier = MTypeBarrier::New(def, existingTypeSet);
+            osrBlock->add(barrier);
+            osrBlock->rewriteSlot(i, barrier);
+            def = barrier;
+        } else if (existingType == MIRType_Null ||
+                   existingType == MIRType_Undefined ||
+                   existingType == MIRType_Magic)
+        {
+            // No unbox instruction will be added below, so check the type by
+            // adding a type barrier for a singleton type set.
+            types::Type ntype = types::Type::PrimitiveType(ValueTypeFromMIRType(existingType));
+            existingTypeSet = GetIonContext()->temp->lifoAlloc()->new_<types::StackTypeSet>(ntype);
+            if (!existingTypeSet)
+                return NULL;
+            MInstruction *barrier = MTypeBarrier::New(def, existingTypeSet);
+            osrBlock->add(barrier);
+            osrBlock->rewriteSlot(i, barrier);
+            def = barrier;
+        }
+
+        switch (existingType) {
           case MIRType_Boolean:
           case MIRType_Int32:
           case MIRType_Double:
           case MIRType_String:
           case MIRType_Object:
           {
-            MDefinition *def = osrBlock->getSlot(i);
-            JS_ASSERT(def->type() == MIRType_Value);
-
-            MInstruction *actual = MUnbox::New(def, slotTypes[i], MUnbox::Infallible);
+            MInstruction *actual = MUnbox::New(def, existingType, MUnbox::Fallible);
             osrBlock->add(actual);
             osrBlock->rewriteSlot(i, actual);
             break;
           }
 
           case MIRType_Null:
           {
             MConstant *c = MConstant::New(NullValue());
@@ -5178,17 +5331,18 @@ IonBuilder::maybeInsertResume()
 
     MNop *ins = MNop::New();
     current->add(ins);
 
     return resumeAfter(ins);
 }
 
 static inline bool
-TestSingletonProperty(JSContext *cx, HandleObject obj, HandleId id, bool *isKnownConstant)
+TestSingletonProperty(JSContext *cx, HandleObject obj, JSObject *singleton,
+                      HandleId id, bool *isKnownConstant)
 {
     // We would like to completely no-op property/global accesses which can
     // produce only a particular JSObject. When indicating the access result is
     // definitely an object, type inference does not account for the
     // possibility that the property is entirely missing from the input object
     // and its prototypes (if this happens, a semantic trigger would be hit and
     // the pushed types updated, even if there is no type barrier).
     //
@@ -5212,149 +5366,165 @@ TestSingletonProperty(JSContext *cx, Han
 
     if (!shape->hasDefaultGetter())
         return true;
     if (!shape->hasSlot())
         return true;
     if (holder->getSlot(shape->slot()).isUndefined())
         return true;
 
+    types::TypeObject *objType = obj->getType(cx);
+    if (!objType)
+        return false;
+    if (objType->unknownProperties())
+        return true;
+
+    types::HeapTypeSet *property = objType->getProperty(cx, id, false);
+    if (!property)
+        return false;
+    objType->getFromPrototypes(cx, id, property);
+    if (property->getSingleton(cx) != singleton)
+        return true;
+
     *isKnownConstant = true;
     return true;
 }
 
 static inline bool
-TestSingletonPropertyTypes(JSContext *cx, types::StackTypeSet *types,
+TestSingletonPropertyTypes(JSContext *cx, MDefinition *obj, JSObject *singleton,
                            HandleObject globalObj, HandleId id,
                            bool *isKnownConstant, bool *testObject,
                            bool *testString)
 {
     // As for TestSingletonProperty, but the input is any value in a type set
     // rather than a specific object. If testObject is set then the constant
     // result can only be used after ensuring the input is an object.
 
     *isKnownConstant = false;
     *testObject = false;
     *testString = false;
 
-    if (!types || types->unknownObject())
+    types::StackTypeSet *types = obj->resultTypeSet();
+
+    if (!types && obj->type() != MIRType_String)
         return true;
 
-    RootedObject singleton(cx, types->getSingleton());
-    if (singleton)
-        return TestSingletonProperty(cx, singleton, id, isKnownConstant);
+    if (types && types->unknownObject())
+        return true;
+
+    RootedObject objectSingleton(cx, types ? types->getSingleton() : NULL);
+    if (objectSingleton)
+        return TestSingletonProperty(cx, objectSingleton, singleton, id, isKnownConstant);
 
     if (!globalObj)
         return true;
 
     JSProtoKey key;
-    JSValueType type = types->getKnownTypeTag();
-    switch (type) {
-      case JSVAL_TYPE_STRING:
+    switch (obj->type()) {
+      case MIRType_String:
         key = JSProto_String;
         break;
 
-      case JSVAL_TYPE_INT32:
-      case JSVAL_TYPE_DOUBLE:
+      case MIRType_Int32:
+      case MIRType_Double:
         key = JSProto_Number;
         break;
 
-      case JSVAL_TYPE_BOOLEAN:
+      case MIRType_Boolean:
         key = JSProto_Boolean;
         break;
 
-      case JSVAL_TYPE_OBJECT:
-      case JSVAL_TYPE_UNKNOWN: {
+      case MIRType_Object:
+      case MIRType_Value: {
         if (types->hasType(types::Type::StringType())) {
-            // Do not optimize if the object is either a String or an Object.
-            if (types->maybeObject())
-                return true;
             key = JSProto_String;
             *testString = true;
             break;
         }
 
+        if (!types->maybeObject())
+            return true;
+
         // For property accesses which may be on many objects, we just need to
         // find a prototype common to all the objects; if that prototype
         // has the singleton property, the access will not be on a missing property.
-        bool thoughtConstant = true;
         for (unsigned i = 0; i < types->getObjectCount(); i++) {
             types::TypeObject *object = types->getTypeObject(i);
             if (!object) {
                 // Try to get it through the singleton.
                 JSObject *curObj = types->getSingleObject(i);
                 // As per the comment in jsinfer.h, there can be holes in
                 // TypeSets, so just skip over them.
                 if (!curObj)
                     continue;
                 object = curObj->getType(cx);
                 if (!object)
                     return false;
             }
 
+            if (object->unknownProperties())
+                return true;
+            types::HeapTypeSet *property = object->getProperty(cx, id, false);
+            if (!property)
+                return false;
+            object->getFromPrototypes(cx, id, property);
+            if (property->getSingleton(cx) != singleton)
+                return true;
+
             if (object->proto) {
                 // Test this type.
                 RootedObject proto(cx, object->proto);
-                if (!TestSingletonProperty(cx, proto, id, &thoughtConstant))
+                bool thoughtConstant = false;
+                if (!TestSingletonProperty(cx, proto, singleton, id, &thoughtConstant))
                     return false;
-                // Short circuit
                 if (!thoughtConstant)
-                    break;
+                    return true;
             } else {
                 // Can't be on the prototype chain with no prototypes...
-                thoughtConstant = false;
-                break;
+                return true;
             }
         }
-        if (thoughtConstant) {
-            // If this is not a known object, a test will be needed.
-            *testObject = (type != JSVAL_TYPE_OBJECT);
-        }
-        *isKnownConstant = thoughtConstant;
+        // If this is not a known object, a test will be needed.
+        *testObject = (obj->type() != MIRType_Object);
+        *isKnownConstant = true;
         return true;
       }
       default:
         return true;
     }
 
     RootedObject proto(cx);
     if (!js_GetClassPrototype(cx, key, &proto, NULL))
         return false;
 
-    return TestSingletonProperty(cx, proto, id, isKnownConstant);
-}
-
-// Given an actual and observed type set, annotates the IR as much as possible:
+    return TestSingletonProperty(cx, proto, singleton, id, isKnownConstant);
+}
+
+// Given an observed type set, annotates the IR as much as possible:
 // (1) If no type information is provided, the value on the top of the stack is
 //     left in place.
-// (2) If a single type definitely exists, and no type barrier is in place,
+// (2) If a single type definitely exists, and no type barrier is needed,
 //     then an infallible unbox instruction replaces the value on the top of
 //     the stack.
-// (3) If a type barrier is in place, but has an unknown type set, leave the
+// (3) If a type barrier is needed, but has an unknown type set, leave the
 //     value at the top of the stack.
-// (4) If a type barrier is in place, and has a single type, an unbox
+// (4) If a type barrier is needed, and has a single type, an unbox
 //     instruction replaces the top of the stack.
 // (5) Lastly, a type barrier instruction replaces the top of the stack.
 bool
-IonBuilder::pushTypeBarrier(MInstruction *ins, types::StackTypeSet *actual,
-                            types::StackTypeSet *observed)
+IonBuilder::pushTypeBarrier(MInstruction *ins, types::StackTypeSet *observed, bool needsBarrier)
 {
     // If the instruction has no side effects, we'll resume the entire operation.
     // The actual type barrier will occur in the interpreter. If the
     // instruction is effectful, even if it has a singleton type, there
     // must be a resume point capturing the original def, and resuming
     // to that point will explicitly monitor the new type.
 
-    if (!actual) {
-        JS_ASSERT(!observed);
-        return true;
-    }
-
-    if (!observed) {
-        JSValueType type = actual->getKnownTypeTag();
+    if (!needsBarrier) {
+        JSValueType type = observed->getKnownTypeTag();
         MInstruction *replace = NULL;
         switch (type) {
           case JSVAL_TYPE_UNDEFINED:
             ins->setFoldedUnchecked();
             replace = MConstant::New(UndefinedValue());
             break;
           case JSVAL_TYPE_NULL:
             ins->setFoldedUnchecked();
@@ -5370,21 +5540,19 @@ IonBuilder::pushTypeBarrier(MInstruction
                 JS_ASSERT(ins->type() == replaceType);
             break;
           }
         }
         if (replace) {
             current->pop();
             current->add(replace);
             current->push(replace);
-            if (replace->acceptsTypeSet())
-                replace->setTypeSet(cloneTypeSet(actual));
+            replace->setResultTypeSet(cloneTypeSet(observed));
         } else {
-            if (ins->acceptsTypeSet())
-                ins->setTypeSet(cloneTypeSet(actual));
+            ins->setResultTypeSet(cloneTypeSet(observed));
         }
         return true;
     }
 
     if (observed->unknown())
         return true;
 
     current->pop();
@@ -5420,32 +5588,16 @@ IonBuilder::pushTypeBarrier(MInstruction
         MUnbox::Mode mode = ins->isEffectful() ? MUnbox::TypeBarrier : MUnbox::TypeGuard;
         barrier = MUnbox::New(ins, MIRTypeFromValueType(type), mode);
         current->add(barrier);
     }
     current->push(barrier);
     return true;
 }
 
-// Test the type of values returned by a VM call. This is an optimized version
-// of calling TypeScript::Monitor inside such stubs.
-void
-IonBuilder::monitorResult(MInstruction *ins, types::TypeSet *barrier, types::StackTypeSet *types)
-{
-    // MonitorTypes is redundant if we will also add a type barrier.
-    if (barrier)
-        return;
-
-    if (!types || types->unknown())
-        return;
-
-    MInstruction *monitor = MMonitorTypes::New(ins, cloneTypeSet(types));
-    current->add(monitor);
-}
-
 bool
 IonBuilder::jsop_getgname(HandlePropertyName name)
 {
     // Optimize undefined, NaN, and Infinity.
     if (name == cx->names().undefined)
         return pushConstant(UndefinedValue());
     if (name == cx->names().NaN)
         return pushConstant(cx->runtime->NaNValue);
@@ -5458,50 +5610,54 @@ IonBuilder::jsop_getgname(HandleProperty
     RootedId id(cx, NameToId(name));
 
     // For the fastest path, the property must be found, and it must be found
     // as a normal data property on exactly the global object.
     RootedShape shape(cx, globalObj->nativeLookup(cx, id));
     if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
         return jsop_getname(name);
 
-    types::HeapTypeSet *propertyTypes = oracle->globalPropertyTypeSet(script(), pc, id);
     types::TypeObject *globalType = globalObj->getType(cx);
     if (!globalType)
         return false;
+    types::HeapTypeSet *propertyTypes = NULL;
+    if (!globalType->unknownProperties()) {
+        propertyTypes = globalType->getProperty(cx, id, false);
+        if (!propertyTypes)
+            return false;
+    }
     if (propertyTypes && propertyTypes->isOwnProperty(cx, globalType, true)) {
         // The property has been reconfigured as non-configurable, non-enumerable
         // or non-writable.
         return jsop_getname(name);
     }
 
     // If the property is permanent, a shape guard isn't necessary.
-    JSValueType knownType = JSVAL_TYPE_UNKNOWN;
-
-    RootedScript scriptRoot(cx, script());
-    types::StackTypeSet *barrier = oracle->propertyReadBarrier(scriptRoot, pc);
-    types::StackTypeSet *types = oracle->propertyRead(script(), pc);
-    if (types) {
-        JSObject *singleton = types->getSingleton();
-
-        knownType = types->getKnownTypeTag();
-        if (!barrier) {
-            if (singleton) {
-                // Try to inline a known constant value.
-                bool isKnownConstant;
-                if (!TestSingletonProperty(cx, globalObj, id, &isKnownConstant))
-                    return false;
-                if (isKnownConstant)
-                    return pushConstant(ObjectValue(*singleton));
-            }
-            if (knownType == JSVAL_TYPE_UNDEFINED)
-                return pushConstant(UndefinedValue());
-            if (knownType == JSVAL_TYPE_NULL)
-                return pushConstant(NullValue());
+
+    types::StackTypeSet *types = script()->analysis()->bytecodeTypes(pc);
+    bool barrier = !propertyTypes || !propertyTypes->isSubset(types);
+    if (!barrier)
+        propertyTypes->addFreeze(cx);
+
+    JSObject *singleton = types->getSingleton();
+
+    JSValueType knownType = types->getKnownTypeTag();
+    if (!barrier) {
+        if (singleton) {
+            // Try to inline a known constant value.
+            bool isKnownConstant;
+            if (!TestSingletonProperty(cx, globalObj, singleton, id, &isKnownConstant))
+                return false;
+            if (isKnownConstant)
+                return pushConstant(ObjectValue(*singleton));
         }
+        if (knownType == JSVAL_TYPE_UNDEFINED)
+            return pushConstant(UndefinedValue());
+        if (knownType == JSVAL_TYPE_NULL)
+            return pushConstant(NullValue());
     }
 
     MInstruction *global = MConstant::New(ObjectValue(*globalObj));
     current->add(global);
 
     // If we have a property typeset, the isOwnProperty call will trigger recompilation if
     // the property is deleted or reconfigured.
     if (!propertyTypes && shape->configurable())
@@ -5517,61 +5673,96 @@ IonBuilder::jsop_getgname(HandleProperty
     // Slot loads can be typed, if they have a single, known, definitive type.
     if (knownType != JSVAL_TYPE_UNKNOWN && !barrier)
         load->setResultType(MIRTypeFromValueType(knownType));
 
     current->push(load);
     return pushTypeBarrier(load, types, barrier);
 }
 
+// Whether 'types' includes all possible values represented by input/inputTypes.
+bool
+ion::TypeSetIncludes(types::TypeSet *types, MIRType input, types::TypeSet *inputTypes)
+{
+    if (inputTypes)
+        return inputTypes->isSubset(types);
+
+    switch (input) {
+      case MIRType_Undefined:
+      case MIRType_Null:
+      case MIRType_Boolean:
+      case MIRType_Int32:
+      case MIRType_Double:
+      case MIRType_String:
+      case MIRType_Magic:
+        return types->hasType(types::Type::PrimitiveType(ValueTypeFromMIRType(input)));
+
+      case MIRType_Object:
+        return types->unknownObject();
+
+      case MIRType_Value:
+        return types->unknown();
+
+      default:
+        JS_NOT_REACHED("Bad input type");
+        return false;
+    }
+}
+
 bool
 IonBuilder::jsop_setgname(HandlePropertyName name)
 {
     RootedObject globalObj(cx, &script()->global());
     RootedId id(cx, NameToId(name));
 
     JS_ASSERT(globalObj->isNative());
 
-    bool canSpecialize;
-    types::HeapTypeSet *propertyTypes = oracle->globalPropertyWrite(script(), pc, id, &canSpecialize);
-
-    // This should only happen for a few names like __proto__.
-    if (!canSpecialize || globalObj->watched())
+    MDefinition *value = current->peek(-1);
+
+    if (globalObj->watched())
         return jsop_setprop(name);
 
     // For the fastest path, the property must be found, and it must be found
     // as a normal data property on exactly the global object.
     RootedShape shape(cx, globalObj->nativeLookup(cx, id));
     if (!shape || !shape->hasDefaultSetter() || !shape->writable() || !shape->hasSlot())
         return jsop_setprop(name);
 
     types::TypeObject *globalType = globalObj->getType(cx);
     if (!globalType)
         return false;
-    if (propertyTypes && propertyTypes->isOwnProperty(cx, globalType, true)) {
+    types::HeapTypeSet *propertyTypes = NULL;
+    if (!globalType->unknownProperties()) {
+        propertyTypes = globalType->getProperty(cx, id, false);
+        if (!propertyTypes)
+            return false;
+    }
+    if (!propertyTypes || propertyTypes->isOwnProperty(cx, globalType, true)) {
         // The property has been reconfigured as non-configurable, non-enumerable
         // or non-writable.
         return jsop_setprop(name);
     }
+    if (!TypeSetIncludes(propertyTypes, value->type(), value->resultTypeSet()))
+        return jsop_setprop(name);
 
     MInstruction *global = MConstant::New(ObjectValue(*globalObj));
     current->add(global);
 
     // If we have a property type set, the isOwnProperty call will trigger recompilation
     // if the property is deleted or reconfigured. Without TI, we always need a shape guard
     // to guard against the property being reconfigured as non-writable.
     if (!propertyTypes)
         global = addShapeGuard(global, globalObj->lastProperty(), Bailout_ShapeGuard);
 
     JS_ASSERT(shape->slot() >= globalObj->numFixedSlots());
 
     MSlots *slots = MSlots::New(global);
     current->add(slots);
 
-    MDefinition *value = current->pop();
+    current->pop();
     MStoreSlot *store = MStoreSlot::New(slots, shape->slot() - globalObj->numFixedSlots(), value);
     current->add(store);
 
     // Determine whether write barrier is required.
     if (!propertyTypes || propertyTypes->needsBarrier(cx))
         store->setNeedsBarrier();
 
     // Pop the global object pushed by bindgname.
@@ -5614,45 +5805,38 @@ IonBuilder::jsop_getname(HandlePropertyN
         ins = MGetNameCache::New(object, name, MGetNameCache::NAME);
 
     current->add(ins);
     current->push(ins);
 
     if (!resumeAfter(ins))
         return false;
 
-    RootedScript scriptRoot(cx, script());
-    types::StackTypeSet *barrier = oracle->propertyReadBarrier(scriptRoot, pc);
-    types::StackTypeSet *types = oracle->propertyRead(script(), pc);
-
-    monitorResult(ins, barrier, types);
-    return pushTypeBarrier(ins, types, barrier);
+    types::StackTypeSet *types = script()->analysis()->bytecodeTypes(pc);
+    return pushTypeBarrier(ins, types, true);
 }
 
 bool
 IonBuilder::jsop_intrinsic(HandlePropertyName name)
 {
-    types::StackTypeSet *types = oracle->propertyRead(script(), pc);
+    types::StackTypeSet *types = script()->analysis()->bytecodeTypes(pc);
     JSValueType type = types->getKnownTypeTag();
 
     // If we haven't executed this opcode yet, we need to get the intrinsic
     // value and monitor the result.
     if (type == JSVAL_TYPE_UNKNOWN) {
         MCallGetIntrinsicValue *ins = MCallGetIntrinsicValue::New(name);
 
         current->add(ins);
         current->push(ins);
 
         if (!resumeAfter(ins))
             return false;
 
-        RootedScript scriptRoot(cx, script());
-        types::StackTypeSet *barrier = oracle->propertyReadBarrier(scriptRoot, pc);
-        monitorResult(ins, barrier, types);
-        return pushTypeBarrier(ins, types, barrier);
+        return pushTypeBarrier(ins, types, true);
     }
 
     // Bake in the intrinsic. Make sure that TI agrees with us on the type.
     RootedValue vp(cx, UndefinedValue());
     if (!cx->global()->getIntrinsicValue(cx, name, &vp))
         return false;
 
     JS_ASSERT(types->hasType(types::GetValueType(cx, vp)));
@@ -5695,117 +5879,379 @@ GetElemKnownType(bool needsHoleCheck, ty
     // hole checks to be done as either value or typed reads.
     if (needsHoleCheck && !LIRGenerator::allowTypedElementHoleCheck())
         knownType = JSVAL_TYPE_UNKNOWN;
 
     return knownType;
 }
 
 bool
+IonBuilder::elementAccessIsDenseNative(MDefinition *obj, MDefinition *id)
+{
+    if (obj->mightBeType(MIRType_String))
+        return false;
+
+    if (id->type() != MIRType_Int32 && id->type() != MIRType_Double)
+        return false;
+
+    types::StackTypeSet *types = obj->resultTypeSet();
+    if (!types)
+        return false;
+
+    Class *clasp = types->getKnownClass();
+    return clasp && clasp->isNative();
+}
+
+bool
+IonBuilder::elementAccessIsTypedArray(MDefinition *obj, MDefinition *id, int *arrayType)
+{
+    if (obj->mightBeType(MIRType_String))
+        return false;
+
+    if (id->type() != MIRType_Int32 && id->type() != MIRType_Double)
+        return false;
+
+    types::StackTypeSet *types = obj->resultTypeSet();
+    if (!types)
+        return false;
+
+    *arrayType = types->getTypedArrayType();
+    return *arrayType != TypedArray::TYPE_MAX;
+}
+
+bool
+IonBuilder::elementAccessIsPacked(MDefinition *obj)
+{
+    types::StackTypeSet *types = obj->resultTypeSet();
+    return types && !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED);
+}
+
+bool
+IonBuilder::elementAccessHasExtraIndexedProperty(MDefinition *obj)
+{
+    types::StackTypeSet *types = obj->resultTypeSet();
+
+    if (!types || types->hasObjectFlags(cx, types::OBJECT_FLAG_LENGTH_OVERFLOW))
+        return true;
+
+    return types::TypeCanHaveExtraIndexedProperties(cx, types);
+}
+
+bool
+IonBuilder::propertyReadNeedsTypeBarrier(MDefinition *obj, PropertyName *name,
+                                         types::StackTypeSet *observed)
+{
+    // If any value being read from has types for the property which haven't
+    // been observed at this access site, the read could produce a new type and
+    // a barrier is needed. Note that this only covers reads from properties
+    // which are accounted for by type information, i.e. native data properties
+    // and elements.
+
+    if (observed->unknown())
+        return false;
+
+    types::TypeSet *types = obj->resultTypeSet();
+    if (!types || types->unknownObject())
+        return true;
+
+    jsid id = name ? types::IdToTypeId(NameToId(name)) : JSID_VOID;
+
+    for (size_t i = 0; i < types->getObjectCount(); i++) {
+        types::TypeObject *object = types->getTypeObject(i);
+        if (!object) {
+            JSObject *singleton = types->getSingleObject(i);
+            if (!singleton)
+                continue;
+            object = singleton->getType(cx);
+            if (!object)
+                return true;
+        }
+
+        if (object->unknownProperties())
+            return true;
+
+        types::HeapTypeSet *property = object->getProperty(cx, id, false);
+        if (!property)
+            return true;
+
+        if (!property->hasPropagatedProperty())
+            object->getFromPrototypes(cx, id, property);
+
+        if (!TypeSetIncludes(observed, MIRType_Value, property))
+            return true;
+
+        property->addFreeze(cx);
+    }
+
+    return false;
+}
+
+bool
+IonBuilder::propertyReadIsIdempotent(MDefinition *obj, PropertyName *name)
+{
+    // Determine if reading a property from obj is likely to be idempotent.
+
+    jsid id = types::IdToTypeId(NameToId(name));
+
+    types::TypeSet *types = obj->resultTypeSet();
+    if (!types || types->unknownObject())
+        return false;
+
+    for (size_t i = 0; i < types->getObjectCount(); i++) {
+        if (types->getSingleObject(i))
+            return false;
+
+        if (types::TypeObject *object = types->getTypeObject(i)) {
+            if (object->unknownProperties())
+                return false;
+
+            // Check if the property has been reconfigured or is a getter.
+            types::HeapTypeSet *property = object->getProperty(cx, id, false);
+            if (!property || property->isOwnProperty(cx, object, true))
+                return false;
+        }
+    }
+
+    return true;
+}
+
+bool
+IonBuilder::tryAddWriteBarrier(types::StackTypeSet *objTypes, jsid id, MDefinition **pvalue)
+{
+    // Return whether value was modified to include a write barrier on
+    // objTypes/id --- if executing value does not bail out, then writing it
+    // to the object will not require changing type information. If value is
+    // not updated, a VM call must always be performed for the write.
+
+    // All objects in the set must have the same types for id. Otherwise, we
+    // could bail out without subsequently triggering a type change that
+    // invalidates the compiled code.
+    types::HeapTypeSet *aggregateProperty = NULL;
+
+    for (size_t i = 0; i < objTypes->getObjectCount(); i++) {
+        types::TypeObject *object = objTypes->getTypeObject(i);
+        if (!object) {
+            JSObject *singleton = objTypes->getSingleObject(i);
+            if (!singleton)
+                continue;
+            object = singleton->getType(cx);
+            if (!object)
+                return false;
+        }
+
+        if (object->unknownProperties())
+            return false;
+
+        types::HeapTypeSet *property = object->getProperty(cx, id, false);
+        if (!property)
+            return false;
+
+        if (TypeSetIncludes(property, (*pvalue)->type(), (*pvalue)->resultTypeSet()))
+            return false;
+
+        // This freeze is not required for correctness, but ensures that we
+        // will recompile if the property types change and the barrier can
+        // be removed.
+        property->addFreeze(cx);
+
+        if (aggregateProperty) {
+            if (!aggregateProperty->isSubset(property) || !property->isSubset(aggregateProperty))
+                return false;
+        } else {
+            aggregateProperty = property;
+        }
+    }
+
+    JS_ASSERT(aggregateProperty);
+
+    MIRType propertyType = MIRTypeFromValueType(aggregateProperty->getKnownTypeTag(cx));
+    switch (propertyType) {
+      case MIRType_Boolean:
+      case MIRType_Int32:
+      case MIRType_Double:
+      case MIRType_String: {
+        // The property is a particular primitive type, guard by unboxing the
+        // value before the write.
+        if ((*pvalue)->type() != MIRType_Value) {
+            // The value is a different primitive, just do a VM call as it will
+            // always trigger invalidation of the compiled code.
+            JS_ASSERT((*pvalue)->type() != propertyType);
+            return false;
+        }
+        MInstruction *ins = MUnbox::New(*pvalue, propertyType, MUnbox::Fallible);
+        current->add(ins);
+        *pvalue = ins;
+        return true;
+      }
+      default:;
+    }
+
+    if ((*pvalue)->type() != MIRType_Value)
+        return false;
+
+    types::StackTypeSet *types = aggregateProperty->clone(GetIonContext()->temp->lifoAlloc());
+    if (!types)
+        return false;
+
+    MInstruction *ins = MTypeBarrier::New(*pvalue, types, Bailout_Normal);
+    current->add(ins);
+    *pvalue = ins;
+    return true;
+}
+
+bool
+IonBuilder::propertyWriteNeedsTypeBarrier(MDefinition *obj, PropertyName *name, MDefinition **pvalue)
+{
+    // If any value being written is not reflected in the type information for
+    // objects which obj could represent, a type barrier is needed when writing
+    // the value. As for propertyReadNeedsTypeBarrier, this only applies for
+    // properties that are accounted for by type information, i.e. normal data
+    // properties and elements.
+
+    types::StackTypeSet *types = obj->resultTypeSet();
+    if (!types || types->unknownObject())
+        return true;
+
+    jsid id = name ? types::IdToTypeId(NameToId(name)) : JSID_VOID;
+
+    for (size_t i = 0; i < types->getObjectCount(); i++) {
+        types::TypeObject *object = types->getTypeObject(i);
+        if (!object) {
+            JSObject *singleton = types->getSingleObject(i);
+            if (!singleton)
+                continue;
+            object = singleton->getType(cx);
+            if (!object)
+                return true;
+        }
+
+        if (object->unknownProperties())
+            continue;
+
+        types::HeapTypeSet *property = object->getProperty(cx, id, false);
+        if (!property)
+            return true;
+        if (!TypeSetIncludes(property, (*pvalue)->type(), (*pvalue)->resultTypeSet()))
+            return !tryAddWriteBarrier(types, id, pvalue);
+    }
+
+    return false;
+}
+
+bool
 IonBuilder::jsop_getelem()
 {
-    RootedScript script(cx, this->script());
-
-    if (oracle->elementReadIsDenseNative(script, pc))
-        return jsop_getelem_dense();
+    MDefinition *obj = current->peek(-2);
+    MDefinition *index = current->peek(-1);
+
+    if (elementAccessIsDenseNative(obj, index)) {
+        // Don't generate a fast path if there have been bounds check failures
+        // and this access might be on a sparse property.
+        if (!elementAccessHasExtraIndexedProperty(obj) || !failedBoundsCheck_)
+            return jsop_getelem_dense();
+    }
 
     int arrayType = TypedArray::TYPE_MAX;
-    if (oracle->elementReadIsTypedArray(script, pc, &arrayType))
+    if (elementAccessIsTypedArray(obj, index, &arrayType))
         return jsop_getelem_typed(arrayType);
 
-    if (oracle->elementReadIsString(script, pc))
+    if (obj->type() == MIRType_String)
         return jsop_getelem_string();
 
-    LazyArgumentsType isArguments = oracle->elementReadMagicArguments(script, pc);
-    if (isArguments == MaybeArguments)
+    if (obj->type() == MIRType_Magic)
+        return jsop_arguments_getelem();
+
+    if (script()->argumentsHasVarBinding() && obj->mightBeType(MIRType_Magic))
         return abort("Type is not definitely lazy arguments.");
-    if (isArguments == DefinitelyArguments)
-        return jsop_arguments_getelem();
-
-    MDefinition *rhs = current->pop();
-    MDefinition *lhs = current->pop();
+
+    current->popn(2);
 
     MInstruction *ins;
 
-    // TI does not account for GETELEM with string indexes, so we have to monitor
-    // the result of MGetElementCache if it's expected to access string properties.
-    // If the result of MGetElementCache is not monitored, we won't generate any
-    // getprop stubs.
-    bool mustMonitorResult = false;
-    bool cacheable = false;
-    bool intIndex = false;
-
-    oracle->elementReadGeneric(script, pc, &cacheable, &mustMonitorResult, &intIndex);
-
-    if (cacheable)
-        ins = MGetElementCache::New(lhs, rhs, mustMonitorResult);
-    else
-        ins = MCallGetElement::New(lhs, rhs);
+    bool cacheable = obj->type() == MIRType_Object &&
+        (index->mightBeType(MIRType_Int32) || index->mightBeType(MIRType_String));
+
+    // Turn off cacheing if the element is int32 and we've seen non-native objects as the target
+    // of this getelem.
+    if (index->mightBeType(MIRType_Int32) && script()->analysis()->getCode(pc).nonNativeGetElement)
+        cacheable = false;
+
+    types::StackTypeSet *types = script()->analysis()->bytecodeTypes(pc);
+    bool barrier = propertyReadNeedsTypeBarrier(obj, NULL, types);
+
+    // Always add a barrier if the index might be a string, so that the cache
+    // can attach stubs for particular properties.
+    if (index->mightBeType(MIRType_String))
+        barrier = true;
+
+    if (cacheable) {
+        ins = MGetElementCache::New(obj, index, barrier);
+    } else {
+        ins = MCallGetElement::New(obj, index);
+        barrier = true;
+    }
 
     current->add(ins);
     current->push(ins);
 
     if (!resumeAfter(ins))
         return false;
 
-    types::StackTypeSet *barrier = oracle->propertyReadBarrier(script, pc);
-    types::StackTypeSet *types = oracle->propertyRead(script, pc);
-
-    if (cacheable && intIndex && !barrier && !mustMonitorResult) {
-        bool needHoleCheck = !oracle->elementReadIsPacked(script, pc);
+    if (cacheable && index->type() == MIRType_Int32 && !barrier) {
+        bool needHoleCheck = !elementAccessIsPacked(obj);
         JSValueType knownType = GetElemKnownType(needHoleCheck, types);
 
         if (knownType != JSVAL_TYPE_UNKNOWN && knownType != JSVAL_TYPE_DOUBLE)
             ins->setResultType(MIRTypeFromValueType(knownType));
     }
 
-    if (mustMonitorResult)
-        monitorResult(ins, barrier, types);
     return pushTypeBarrier(ins, types, barrier);
 }
 
 bool
 IonBuilder::jsop_getelem_dense()
 {
-    RootedScript scriptRoot(cx, script());
-    types::StackTypeSet *barrier = oracle->propertyReadBarrier(scriptRoot, pc);
-    types::StackTypeSet *types = oracle->propertyRead(script(), pc);
-    bool needsHoleCheck = !oracle->elementReadIsPacked(script(), pc);
+    MDefinition *id = current->pop();
+    MDefinition *obj = current->pop();
+
+    types::StackTypeSet *types = script()->analysis()->bytecodeTypes(pc);
+    bool barrier = propertyReadNeedsTypeBarrier(obj, NULL, types);
+    bool needsHoleCheck = !elementAccessIsPacked(obj);
 
     // Reads which are on holes in the object do not have to bail out if
     // undefined values have been observed at this access site and the access
     // cannot hit another indexed property on the object or its prototypes.
     bool readOutOfBounds =
         types->hasType(types::Type::UndefinedType()) &&
-        !oracle->elementReadHasExtraIndexedProperty(script(), pc);
-
-    MDefinition *id = current->pop();
-    MDefinition *obj = current->pop();
+        !elementAccessHasExtraIndexedProperty(obj);
 
     JSValueType knownType = JSVAL_TYPE_UNKNOWN;
     if (!barrier)
         knownType = GetElemKnownType(needsHoleCheck, types);
 
     // Ensure id is an integer.
     MInstruction *idInt32 = MToInt32::New(id);
     current->add(idInt32);
     id = idInt32;
 
     // Get the elements vector.
     MInstruction *elements = MElements::New(obj);
     current->add(elements);
 
     // If we can load the element as a definite double, make sure to check that
     // the array has been converted to homogenous doubles first.
-    bool loadDouble = !barrier &&
-                      loopDepth_ &&
-                      !readOutOfBounds &&
-                      !needsHoleCheck &&
-                      knownType == JSVAL_TYPE_DOUBLE &&
-                      oracle->elementReadShouldAlwaysLoadDoubles(script(), pc);
+    types::StackTypeSet *objTypes = obj->resultTypeSet();
+    bool loadDouble =
+        !barrier &&
+        loopDepth_ &&
+        !readOutOfBounds &&
+        !needsHoleCheck &&
+        knownType == JSVAL_TYPE_DOUBLE &&
+        objTypes &&
+        objTypes->convertDoubleElements(cx) == types::StackTypeSet::AlwaysConvertToDoubles;
     if (loadDouble)
         elements = addConvertElementsToDoubles(elements);
 
     MInitializedLength *initLength = MInitializedLength::New(elements);
     current->add(initLength);
 
     MInstruction *load;
 
@@ -5865,19 +6311,17 @@ IonBuilder::getTypedArrayElements(MDefin
         return MConstantElements::New(data);
     }
     return MTypedArrayElements::New(obj);
 }
 
 bool
 IonBuilder::jsop_getelem_typed(int arrayType)
 {
-    RootedScript scriptRoot(cx, script());
-    types::StackTypeSet *barrier = oracle->propertyReadBarrier(scriptRoot, pc);
-    types::StackTypeSet *types = oracle->propertyRead(script(), pc);
+    types::StackTypeSet *types = script()->analysis()->bytecodeTypes(pc);
 
     MDefinition *id = current->pop();
     MDefinition *obj = current->pop();
 
     bool maybeUndefined = types->hasType(types::Type::UndefinedType());
 
     // Reading from an Uint32Array will result in a double for values
     // that don't fit in an int32. We have to bailout if this happens
@@ -5889,18 +6333,19 @@ IonBuilder::jsop_getelem_typed(int array
     current->add(idInt32);
     id = idInt32;
 
     if (!maybeUndefined) {
         // Assume the index is in range, so that we can hoist the length,
         // elements vector and bounds check.
 
         // If we are reading in-bounds elements, we can use knowledge about
-        // the array type to determine the result type. This may be more
-        // precise than the known pushed type.
+        // the array type to determine the result type, even if the opcode has
+        // never executed. The known pushed type is only used to distinguish
+        // uint32 reads that may produce either doubles or integers.
         MIRType knownType;
         switch (arrayType) {
           case TypedArray::TYPE_INT8:
           case TypedArray::TYPE_UINT8:
           case TypedArray::TYPE_UINT8_CLAMPED:
           case TypedArray::TYPE_INT16:
           case TypedArray::TYPE_UINT16:
           case TypedArray::TYPE_INT32:
@@ -5929,130 +6374,165 @@ IonBuilder::jsop_getelem_typed(int array
         MInstruction *elements = getTypedArrayElements(obj);
         current->add(elements);
 
         // Load the element.
         MLoadTypedArrayElement *load = MLoadTypedArrayElement::New(elements, id, arrayType);
         current->add(load);
         current->push(load);
 
-        load->setResultType(knownType);
-
         // Note: we can ignore the type barrier here, we know the type must
         // be valid and unbarriered.
-        JS_ASSERT_IF(knownType == MIRType_Int32, types->hasType(types::Type::Int32Type()));
-        JS_ASSERT_IF(knownType == MIRType_Double, types->hasType(types::Type::DoubleType()));
+        load->setResultType(knownType);
         return true;
     } else {
         // Assume we will read out-of-bound values. In this case the
         // bounds check will be part of the instruction, and the instruction
         // will always return a Value.
         MLoadTypedArrayElementHole *load =
             MLoadTypedArrayElementHole::New(obj, id, arrayType, allowDouble);
         current->add(load);
         current->push(load);
 
-        return resumeAfter(load) && pushTypeBarrier(load, types, barrier);
+        return resumeAfter(load) && pushTypeBarrier(load, types, false);
     }
 }
 
 bool
 IonBuilder::jsop_getelem_string()
 {
     MDefinition *id = current->pop();
     MDefinition *str = current->pop();
 
     MInstruction *idInt32 = MToInt32::New(id);
     current->add(idInt32);
     id = idInt32;
 
     MStringLength *length = MStringLength::New(str);
     current->add(length);
 
-    // This will cause an invalidation of this script once the 'undefined' type
-    // is monitored by the interpreter.
-    JS_ASSERT(oracle->propertyRead(script(), pc)->getKnownTypeTag() == JSVAL_TYPE_STRING);
     id = addBoundsCheck(id, length);
 
     MCharCodeAt *charCode = MCharCodeAt::New(str, id);
     current->add(charCode);
 
     MFromCharCode *result = MFromCharCode::New(charCode);
     current->add(result);
     current->push(result);
     return true;
 }
 
 bool
 IonBuilder::jsop_setelem()
 {
-    RootedScript script(cx, this->script());
-
-    if (oracle->propertyWriteCanSpecialize(script, pc)) {
-        if (oracle->elementWriteIsDenseNative(script, pc))
-            return jsop_setelem_dense();
-
-        int arrayType = TypedArray::TYPE_MAX;
-        if (oracle->elementWriteIsTypedArray(script, pc, &arrayType))
-            return jsop_setelem_typed(arrayType);
-    }
-
-    LazyArgumentsType isArguments = oracle->elementWriteMagicArguments(script, pc);
-    if (isArguments == MaybeArguments)
+    MDefinition *value = current->peek(-1);
+    MDefinition *index = current->peek(-2);
+    MDefinition *object = current->peek(-3);
+
+    if (!propertyWriteNeedsTypeBarrier(object, NULL, &value)) {
+        if (elementAccessIsDenseNative(object, index)) {
+            types::StackTypeSet::DoubleConversion conversion =
+                object->resultTypeSet()->convertDoubleElements(cx);
+            if (conversion != types::StackTypeSet::AmbiguousDoubleConversion)
+                return jsop_setelem_dense(conversion);
+        }
+    }
+
+    int arrayType = TypedArray::TYPE_MAX;
+    if (elementAccessIsTypedArray(object, index, &arrayType))
+        return jsop_setelem_typed(arrayType);
+
+    if (object->type() == MIRType_Magic)
+        return jsop_arguments_setelem();
+
+    if (script()->argumentsHasVarBinding() && object->mightBeType(MIRType_Magic))
         return abort("Type is not definitely lazy arguments.");
-    if (isArguments == DefinitelyArguments)
-        return jsop_arguments_setelem();
-
-    MDefinition *value = current->pop();
-    MDefinition *index = current->pop();
-    MDefinition *object = current->pop();
+
+    current->pop();
+    current->pop();
+    current->pop();
 
     MInstruction *ins = MCallSetElement::New(object, index, value);
     current->add(ins);
     current->push(value);
 
     return resumeAfter(ins);
 }
 
+MIRType
+IonBuilder::denseNativeElementType(MDefinition *obj)
+{
+    types::StackTypeSet *types = obj->resultTypeSet();
+    MIRType elementType = MIRType_None;
+    unsigned count = types->getObjectCount();
+
+    for (unsigned i = 0; i < count; i++) {
+        if (types->getSingleObject(i))
+            return MIRType_None;
+
+        if (types::TypeObject *object = types->getTypeObject(i)) {
+            if (object->unknownProperties())
+                return MIRType_None;
+
+            types::HeapTypeSet *elementTypes = object->getProperty(cx, JSID_VOID, false);
+            if (!elementTypes)
+                return MIRType_None;
+
+            MIRType type = MIRTypeFromValueType(elementTypes->getKnownTypeTag(cx));
+            if (type == MIRType_None)
+                return MIRType_None;
+
+            if (elementType == MIRType_None)
+                elementType = type;
+            else if (elementType != type)
+                return MIRType_None;
+        }
+    }
+
+    return elementType;
+}
+
 bool
-IonBuilder::jsop_setelem_dense()
-{
-    MIRType elementType = oracle->elementWrite(script(), pc);
-    bool packed = oracle->elementWriteIsPacked(script(), pc);
+IonBuilder::jsop_setelem_dense(types::StackTypeSet::DoubleConversion conversion)
+{
+    MDefinition *value = current->pop();
+    MDefinition *id = current->pop();
+    MDefinition *obj = current->pop();
+
+    MIRType elementType = denseNativeElementType(obj);
+    bool packed = elementAccessIsPacked(obj);
 
     // Writes which are on holes in the object do not have to bail out if they
     // cannot hit another indexed property on the object or its prototypes.
-    bool writeOutOfBounds = !oracle->elementWriteHasExtraIndexedProperty(script(), pc);
-
-    MDefinition *value = current->pop();
-    MDefinition *id = current->pop();
-    MDefinition *obj = current->pop();
+    bool writeOutOfBounds = !elementAccessHasExtraIndexedProperty(obj);
 
     // Ensure id is an integer.
     MInstruction *idInt32 = MToInt32::New(id);
     current->add(idInt32);
     id = idInt32;
 
     // Ensure the value is a double, if double conversion might be needed.
     MDefinition *newValue = value;
-    if (oracle->elementWriteNeedsDoubleConversion(script(), pc)) {
+    if (conversion == types::StackTypeSet::AlwaysConvertToDoubles ||
+        conversion == types::StackTypeSet::MaybeConvertToDoubles)
+    {
         MInstruction *valueDouble = MToDouble::New(value);
         current->add(valueDouble);
         newValue = valueDouble;
     }
 
     // Get the elements vector.
     MElements *elements = MElements::New(obj);
     current->add(elements);
 
     // Use MStoreElementHole if this SETELEM has written to out-of-bounds
     // indexes in the past. Otherwise, use MStoreElement so that we can hoist
     // the initialized length and bounds check.
     MStoreElementCommon *store;
-    if (oracle->setElementHasWrittenHoles(script(), pc) && writeOutOfBounds) {
+    if (script()->analysis()->getCode(pc).arrayWriteHole && writeOutOfBounds) {
         MStoreElementHole *ins = MStoreElementHole::New(obj, elements, id, newValue);
         store = ins;
 
         current->add(ins);
         current->push(value);
 
         if (!resumeAfter(ins))
             return false;
@@ -6069,17 +6549,18 @@ IonBuilder::jsop_setelem_dense()
 
         current->add(ins);
         current->push(value);
 
         if (!resumeAfter(ins))
             return false;
     }
 
-    if (oracle->elementWriteNeedsBarrier(script(), pc))
+    // Determine whether a write barrier is required.
+    if (obj->resultTypeSet()->propertyNeedsBarrier(cx, JSID_VOID))
         store->setNeedsBarrier();
 
     if (elementType != MIRType_None && packed)
         store->setElementType(elementType);
 
     return true;
 }
 
@@ -6139,60 +6620,56 @@ IonBuilder::jsop_length()
 
     RootedPropertyName name(cx, info().getAtom(pc)->asPropertyName());
     return jsop_getprop(name);
 }
 
 bool
 IonBuilder::jsop_length_fastPath()
 {
-    TypeOracle::UnaryTypes sig = oracle->unaryTypes(script(), pc);
-    if (!sig.inTypes || !sig.outTypes)
+    types::StackTypeSet *types = script()->analysis()->bytecodeTypes(pc);
+
+    if (types->getKnownTypeTag() != JSVAL_TYPE_INT32)
         return false;
 
-    if (sig.outTypes->getKnownTypeTag() != JSVAL_TYPE_INT32)
-        return false;
-
-    switch (sig.inTypes->getKnownTypeTag()) {
-      case JSVAL_TYPE_STRING: {
-        MDefinition *obj = current->pop();
+    MDefinition *obj = current->peek(-1);
+
+    if (obj->type() == MIRType_String) {
+        current->pop();
         MStringLength *ins = MStringLength::New(obj);
         current->add(ins);
         current->push(ins);
         return true;
-      }
-
-      case JSVAL_TYPE_OBJECT: {
-        if (sig.inTypes->getKnownClass() == &ArrayClass &&
-            !sig.inTypes->hasObjectFlags(cx, types::OBJECT_FLAG_LENGTH_OVERFLOW))
+    }
+
+    if (obj->type() == MIRType_Object) {
+        types::StackTypeSet *objTypes = obj->resultTypeSet();
+
+        if (objTypes &&
+            objTypes->getKnownClass() == &ArrayClass &&
+            !objTypes->hasObjectFlags(cx, types::OBJECT_FLAG_LENGTH_OVERFLOW))
         {
-            MDefinition *obj = current->pop();
+            current->pop();
             MElements *elements = MElements::New(obj);
             current->add(elements);
 
             // Read length.
             MArrayLength *length = new MArrayLength(elements);
             current->add(length);
             current->push(length);
             return true;
         }
 
-        if (sig.inTypes->getTypedArrayType() != TypedArray::TYPE_MAX) {
-            MDefinition *obj = current->pop();
+        if (objTypes && objTypes->getTypedArrayType() != TypedArray::TYPE_MAX) {
+            current->pop();
             MInstruction *length = getTypedArrayLength(obj);
             current->add(length);
             current->push(length);
             return true;
         }
-
-        return false;
-      }
-
-      default:
-        break;
     }
 
     return false;
 }
 
 bool
 IonBuilder::jsop_arguments()
 {
@@ -6221,20 +6698,16 @@ IonBuilder::jsop_arguments_length()
 }
 
 bool
 IonBuilder::jsop_arguments_getelem()
 {
     if (inliningDepth_ != 0)
         return abort("NYI inlined get argument element");
 
-    RootedScript scriptRoot(cx, script());
-    types::StackTypeSet *barrier = oracle->propertyReadBarrier(scriptRoot, pc);
-    types::StackTypeSet *types = oracle->propertyRead(script(), pc);
-
     MDefinition *idx = current->pop();
 
     // Type Inference has guaranteed this is an optimized arguments object.
     MDefinition *args = current->pop();
     args->setFoldedUnchecked();
 
     // To ensure that we are not looking above the number of actual arguments.
     MArgumentsLength *length = MArgumentsLength::New();
@@ -6247,17 +6720,18 @@ IonBuilder::jsop_arguments_getelem()
     // Bailouts if we read more than the number of actual arguments.
     index = addBoundsCheck(index, length);
 
     // Load the argument from the actual arguments.
     MGetArgument *load = MGetArgument::New(index);
     current->add(load);
     current->push(load);
 
-    return pushTypeBarrier(load, types, barrier);
+    types::StackTypeSet *types = script()->analysis()->bytecodeTypes(pc);
+    return pushTypeBarrier(load, types, true);
 }
 
 bool
 IonBuilder::jsop_arguments_setelem()
 {
     return abort("NYI arguments[]=");
 }
 
@@ -6289,18 +6763,17 @@ GetDefiniteSlot(JSContext *cx, types::St
 bool
 IonBuilder::jsop_not()
 {
     MDefinition *value = current->pop();
 
     MNot *ins = new MNot(value);
     current->add(ins);
     current->push(ins);
-    TypeOracle::UnaryTypes types = oracle->unaryTypes(script(), pc);
-    ins->infer(types, cx);
+    ins->infer(cx);
     return true;
 }
 
 
 inline bool
 IonBuilder::TestCommonPropFunc(JSContext *cx, types::StackTypeSet *types, HandleId id,
                                JSFunction **funcp, bool isGetter, bool *isDOM,
                                MDefinition **guardOut)
@@ -6505,17 +6978,17 @@ IonBuilder::annotateGetPropertyCache(JSC
         return true;
 
     for (unsigned i = 0; i < pushedTypes->getObjectCount(); i++) {
         if (pushedTypes->getTypeObject(i) != NULL)
             return true;
     }
 
     // Object's typeset should be a proper object
-    if (objTypes->baseFlags() || objTypes->unknownObject())
+    if (!objTypes || objTypes->baseFlags() || objTypes->unknownObject())
         return true;
 
     unsigned int objCount = objTypes->getObjectCount();
     if (objCount == 0)
         return true;
 
     InlinePropertyTable *inlinePropTable = getPropCache->initInlinePropertyTable(pc);
     if (!inlinePropTable)
@@ -6530,35 +7003,35 @@ IonBuilder::annotateGetPropertyCache(JSC
 
         types::HeapTypeSet *ownTypes = typeObj->getProperty(cx, id, false);
         if (!ownTypes)
             continue;
 
         if (ownTypes->isOwnProperty(cx, typeObj, false))
             continue;
 
-        bool knownConstant = false;
-        Rooted<JSObject*> proto(cx, typeObj->proto);
-        if (!TestSingletonProperty(cx, proto, id, &knownConstant))
-            return false;
-
+        RootedObject proto(cx, typeObj->proto);
         types::TypeObject *protoType = proto->getType(cx);
         if (!protoType)
             return false;
-        if (!knownConstant || protoType->unknownProperties())
+        if (protoType->unknownProperties())
             continue;
 
         types::HeapTypeSet *protoTypes = protoType->getProperty(cx, id, false);
         if (!protoTypes)
-            continue;
+            return false;
 
         JSObject *obj = protoTypes->getSingleton(cx);
         if (!obj || !obj->isFunction())
             continue;
 
+        bool knownConstant = false;
+        if (!TestSingletonProperty(cx, proto, obj, id, &knownConstant))
+            return false;
+
         // Don't add cases corresponding to non-observed pushes
         if (!pushedTypes->hasType(types::Type::ObjectType(obj)))
             continue;
 
         if (!inlinePropTable->addEntry(typeObj, obj->toFunction()))
             return false;
     }
 
@@ -6601,25 +7074,22 @@ IonBuilder::invalidatedIdempotentCache()
             return true;
         builder = builder->callerBuilder_;
     } while (builder);
 
     return false;
 }
 
 bool
-IonBuilder::loadSlot(MDefinition *obj, HandleShape shape, MIRType rvalType)
+IonBuilder::loadSlot(MDefinition *obj, HandleShape shape, MIRType rvalType,
+                     bool barrier, types::StackTypeSet *types)
 {
     JS_ASSERT(shape->hasDefaultGetter());
     JS_ASSERT(shape->hasSlot());
 
-    RootedScript scriptRoot(cx, script());
-    types::StackTypeSet *barrier = oracle->propertyReadBarrier(scriptRoot, pc);
-    types::StackTypeSet *types = oracle->propertyRead(script(), pc);
-
     if (shape->slot() < shape->numFixedSlots()) {
         MLoadFixedSlot *load = MLoadFixedSlot::New(obj, shape->slot());
         current->add(load);
         current->push(load);
 
         load->setResultType(rvalType);
         return pushTypeBarrier(load, types, barrier);
     }
@@ -6662,95 +7132,85 @@ IonBuilder::storeSlot(MDefinition *obj, 
     return resumeAfter(store);
 }
 
 bool
 IonBuilder::jsop_getprop(HandlePropertyName name)
 {
     RootedId id(cx, NameToId(name));
 
-    // GetDefiniteSlot may cause type information to shift, and it's done inside
-    // getPropTryDefiniteSlot.  Do it here first to ensure that all type info changes
-    // occur before handling the op.
-    GetDefiniteSlot(cx, oracle->unaryTypes(script(), pc).inTypes, name);
-
-    RootedScript scriptRoot(cx, script());
-    types::StackTypeSet *barrier = oracle->propertyReadBarrier(scriptRoot, pc);
-    types::StackTypeSet *types = oracle->propertyRead(script(), pc);
-    TypeOracle::Unary unary = oracle->unaryOp(script(), pc);
-    TypeOracle::UnaryTypes uTypes = oracle->unaryTypes(script(), pc);
-
     bool emitted = false;
 
+    types::StackTypeSet *types = script()->analysis()->bytecodeTypes(pc);
+
     // Try to optimize arguments.length.
     if (!getPropTryArgumentsLength(&emitted) || emitted)
         return emitted;
 
     // Try to hardcode known constants.
-    if (!getPropTryConstant(&emitted, id, barrier, types, uTypes) || emitted)
+    if (!getPropTryConstant(&emitted, id, types) || emitted)
         return emitted;
 
+    bool barrier = propertyReadNeedsTypeBarrier(current->peek(-1), name, types);
+
     // Try to emit loads from definite slots.
-    if (!getPropTryDefiniteSlot(&emitted, name, barrier, types, unary, uTypes) || emitted)
+    if (!getPropTryDefiniteSlot(&emitted, name, barrier, types) || emitted)
         return emitted;
 
     // Try to inline a common property getter, or make a call.
-    if (!getPropTryCommonGetter(&emitted, id, barrier, types, uTypes) || emitted)
+    if (!getPropTryCommonGetter(&emitted, id, barrier, types) || emitted)
         return emitted;
 
     // Try to emit a monomorphic cache based on data in JM caches.
-    if (!getPropTryMonomorphic(&emitted, id, barrier, unary, uTypes) || emitted)
+    if (!getPropTryMonomorphic(&emitted, id, barrier, types) || emitted)
         return emitted;
 
     // Try to emit a polymorphic cache.
-    if (!getPropTryPolymorphic(&emitted, name, id, barrier, types, unary, uTypes) || emitted)
+    if (!getPropTryPolymorphic(&emitted, name, id, barrier, types) || emitted)
         return emitted;
 
     // Emit a call.
     MDefinition *obj = current->pop();
     MCallGetProperty *call = MCallGetProperty::New(obj, name);
     current->add(call);
     current->push(call);
     if (!resumeAfter(call))
         return false;
 
-    monitorResult(call, barrier, types);
-    return pushTypeBarrier(call, types, barrier);
+    return pushTypeBarrier(call, types, true);
 }
 
 bool
 IonBuilder::getPropTryArgumentsLength(bool *emitted)
 {
     JS_ASSERT(*emitted == false);
-    LazyArgumentsType isArguments = oracle->propertyReadMagicArguments(script(), pc);
-
-    if (isArguments == MaybeArguments)
-        return abort("Type is not definitely lazy arguments.");
-    if (isArguments != DefinitelyArguments)
+    if (current->peek(-1)->type() != MIRType_Magic) {
+        if (script()->argumentsHasVarBinding() && current->peek(-1)->mightBeType(MIRType_Magic))
+            return abort("Type is not definitely lazy arguments.");
         return true;
+    }
     if (JSOp(*pc) != JSOP_LENGTH)
         return true;
 
     *emitted = true;
     return jsop_arguments_length();
 }
 
 bool
-IonBuilder::getPropTryConstant(bool *emitted, HandleId id, types::StackTypeSet *barrier,
-                               types::StackTypeSet *types, TypeOracle::UnaryTypes unaryTypes)
+IonBuilder::getPropTryConstant(bool *emitted, HandleId id, types::StackTypeSet *types)
 {
     JS_ASSERT(*emitted == false);
     JSObject *singleton = types ? types->getSingleton() : NULL;
-    if (!singleton || barrier)
+    if (!singleton)
         return true;
 
     RootedObject global(cx, &script()->global());
 
     bool isConstant, testObject, testString;
-    if (!TestSingletonPropertyTypes(cx, unaryTypes.inTypes, global, id,
+    if (!TestSingletonPropertyTypes(cx, current->peek(-1), singleton, global, id,
                                     &isConstant, &testObject, &testString))
         return false;
 
     if (!isConstant)
         return true;
 
     MDefinition *obj = current->pop();
 
@@ -6769,114 +7229,113 @@ IonBuilder::getPropTryConstant(bool *emi
     current->push(known);
 
     *emitted = true;
     return true;
 }
 
 bool
 IonBuilder::getPropTryDefiniteSlot(bool *emitted, HandlePropertyName name,
-                                   types::StackTypeSet *barrier, types::StackTypeSet *types,
-                                   TypeOracle::Unary unary, TypeOracle::UnaryTypes unaryTypes)
+                                   bool barrier, types::StackTypeSet *types)
 {
     JS_ASSERT(*emitted == false);
-    types::TypeSet *propTypes = GetDefiniteSlot(cx, unaryTypes.inTypes, name);
+    types::TypeSet *propTypes = GetDefiniteSlot(cx, current->peek(-1)->resultTypeSet(), name);
     if (!propTypes)
         return true;
 
     MDefinition *obj = current->pop();
     MDefinition *useObj = obj;
-    if (unaryTypes.inTypes && unaryTypes.inTypes->baseFlags()) {
+    if (obj->type() != MIRType_Object) {
         MGuardObject *guard = MGuardObject::New(obj);
         current->add(guard);
         useObj = guard;
     }
 
     MLoadFixedSlot *fixed = MLoadFixedSlot::New(useObj, propTypes->definiteSlot());
     if (!barrier)
-        fixed->setResultType(unary.rval);
+        fixed->setResultType(MIRTypeFromValueType(types->getKnownTypeTag()));
 
     current->add(fixed);
     current->push(fixed);
 
     if (!pushTypeBarrier(fixed, types, barrier))
         return false;
 
     *emitted = true;
     return true;
 }
 
 bool
-IonBuilder::getPropTryCommonGetter(bool *emitted, HandleId id, types::StackTypeSet *barrier,
-                                   types::StackTypeSet *types, TypeOracle::UnaryTypes unaryTypes)
+IonBuilder::getPropTryCommonGetter(bool *emitted, HandleId id,
+                                   bool barrier, types::StackTypeSet *types)
 {
     JS_ASSERT(*emitted == false);
     JSFunction *commonGetter;
     bool isDOM;
     MDefinition *guard;
 
-    if (!TestCommonPropFunc(cx, unaryTypes.inTypes, id, &commonGetter, true,
-                            &isDOM, &guard))
-    {
+    types::StackTypeSet *objTypes = current->peek(-1)->resultTypeSet();
+
+    if (!TestCommonPropFunc(cx, objTypes, id, &commonGetter, true, &isDOM, &guard))
         return false;
-    }
     if (!commonGetter)
         return true;
 
     MDefinition *obj = current->pop();
     RootedFunction getter(cx, commonGetter);
 
-    if (isDOM && TestShouldDOMCall(cx, unaryTypes.inTypes, getter, JSJitInfo::Getter)) {
+    if (isDOM && TestShouldDOMCall(cx, objTypes, getter, JSJitInfo::Getter)) {
         const JSJitInfo *jitinfo = getter->jitInfo();
         MGetDOMProperty *get = MGetDOMProperty::New(jitinfo, obj, guard);
         current->add(get);
         current->push(get);
 
         if (get->isEffectful() && !resumeAfter(get))
             return false;
-        barrier = AdjustTypeBarrierForDOMCall(jitinfo, types, barrier);
+        if (!DOMCallNeedsBarrier(jitinfo, types))
+            barrier = false;
         if (!pushTypeBarrier(get, types, barrier))
             return false;
 
         *emitted = true;
         return true;
     }
 
     // Don't call the getter with a primitive value.
-    if (unaryTypes.inTypes->getKnownTypeTag() != JSVAL_TYPE_OBJECT) {
+    if (objTypes->getKnownTypeTag() != JSVAL_TYPE_OBJECT) {
         MGuardObject *guardObj = MGuardObject::New(obj);
         current->add(guardObj);
         obj = guardObj;
     }
 
     // Spoof stack to expected state for call.
     pushConstant(ObjectValue(*commonGetter));
 
     MPassArg *wrapper = MPassArg::New(obj);
     current->add(wrapper);
     current->push(wrapper);
 
-    CallInfo callInfo(cx, false, types, barrier);
+    CallInfo callInfo(cx, false);
     if (!callInfo.init(current, 0))
         return false;
-    if (!makeCallBarrier(getter, callInfo, unaryTypes.inTypes, false))
+    if (!makeCall(getter, callInfo, false))
         return false;
 
     *emitted = true;
     return true;
 }
 
 bool
-IonBuilder::getPropTryMonomorphic(bool *emitted, HandleId id, types::StackTypeSet *barrier,
-                                  TypeOracle::Unary unary, TypeOracle::UnaryTypes unaryTypes)
+IonBuilder::getPropTryMonomorphic(bool *emitted, HandleId id,
+                                  bool barrier, types::StackTypeSet *types)
 {
     JS_ASSERT(*emitted == false);
-    bool accessGetter = oracle->propertyReadAccessGetter(script(), pc);
-
-    if (unary.ival != MIRType_Object)
+    bool accessGetter = script()->analysis()->getCode(pc).accessGetter;
+
+    if (current->peek(-1)->type() != MIRType_Object)
         return true;
 
     RootedShape objShape(cx, mjit::GetPICSingleShape(cx, script(), pc, info().constructing()));
     if (!objShape)
         objShape = inspector->maybeMonomorphicShapeForPropertyOp(pc);
 
     if (!objShape || objShape->inDictionary()) {
         spew("GETPROP not monomorphic");
@@ -6890,189 +7349,195 @@ IonBuilder::getPropTryMonomorphic(bool *
     // still a lastProperty, and calling Shape::search() on dictionary mode
     // shapes that aren't lastProperty is invalid.
     obj = addShapeGuard(obj, objShape, Bailout_CachedShapeGuard);
 
     spew("Inlining monomorphic GETPROP");
     RootedShape shape(cx, objShape->search(cx, id));
     JS_ASSERT(shape);
 
-    MIRType rvalType = unary.rval;
-    if (barrier || IsNullOrUndefined(unary.rval) || accessGetter)
+    MIRType rvalType = MIRTypeFromValueType(types->getKnownTypeTag());
+    if (barrier || IsNullOrUndefined(rvalType) || accessGetter)
         rvalType = MIRType_Value;
 
-    if (!loadSlot(obj, shape, rvalType))
+    if (!loadSlot(obj, shape, rvalType, barrier, types))
         return false;
 
     *emitted = true;
     return true;
 }
 
 bool
 IonBuilder::getPropTryPolymorphic(bool *emitted, HandlePropertyName name, HandleId id,
-                                  types::StackTypeSet *barrier, types::StackTypeSet *types,
-                                  TypeOracle::Unary unary, TypeOracle::UnaryTypes unaryTypes)
+                                  bool barrier, types::StackTypeSet *types)
 {
     JS_ASSERT(*emitted == false);
-    bool accessGetter = oracle->propertyReadAccessGetter(script(), pc);
+    bool accessGetter = script()->analysis()->getCode(pc).accessGetter;
+
+    MDefinition *obj = current->peek(-1);
 
     // The input value must either be an object, or we should have strong suspicions
     // that it can be safely unboxed to an object.
-    if (unary.ival != MIRType_Object && !unaryTypes.inTypes->objectOrSentinel())
-        return true;
-
-    MIRType rvalType = unary.rval;
-    if (barrier || IsNullOrUndefined(unary.rval) || accessGetter)
+    if (obj->type() != MIRType_Object) {
+        types::StackTypeSet *types = obj->resultTypeSet();
+        if (!types || !types->objectOrSentinel())
+            return true;
+    }
+
+    MIRType rvalType = MIRTypeFromValueType(types->getKnownTypeTag());
+    if (barrier || IsNullOrUndefined(rvalType) || accessGetter)
         rvalType = MIRType_Value;
 
-    MDefinition *obj = current->pop();
+    current->pop();
     MGetPropertyCache *load = MGetPropertyCache::New(obj, name);
     load->setResultType(rvalType);
 
     // Try to mark the cache as idempotent. We only do this if JM is enabled
     // (its ICs are used to mark property reads as likely non-idempotent) or
     // if we are compiling eagerly (to improve test coverage).
-    if (unary.ival == MIRType_Object && !invalidatedIdempotentCache()) {
-        RootedScript scriptRoot(cx, script());
-        if (oracle->propertyReadIdempotent(scriptRoot, pc, id))
+    if (obj->type() == MIRType_Object && !invalidatedIdempotentCache()) {
+        if (propertyReadIsIdempotent(obj, name))
             load->setIdempotent();
     }
 
     if (JSOp(*pc) == JSOP_CALLPROP) {
-        if (!annotateGetPropertyCache(cx, obj, load, unaryTypes.inTypes, types))
+        if (!annotateGetPropertyCache(cx, obj, load, obj->resultTypeSet(), types))
             return false;
     }
 
     // If the cache is known to access getters, then enable generation of getter stubs.
     if (accessGetter)
         load->setAllowGetters();
 
     current->add(load);
     current->push(load);
 
     if (load->isEffectful() && !resumeAfter(load))
         return false;
 
     if (accessGetter)
-        monitorResult(load, barrier, types);
+        barrier = true;
 
     if (!pushTypeBarrier(load, types, barrier))
         return false;
 
     *emitted = true;
     return true;
 }
 
 bool
 IonBuilder::jsop_setprop(HandlePropertyName name)
 {
     MDefinition *value = current->pop();
     MDefinition *obj = current->pop();
 
-    bool monitored = !oracle->propertyWriteCanSpecialize(script(), pc);
-
-    TypeOracle::BinaryTypes binaryTypes = oracle->binaryTypes(script(), pc);
-
-    if (!monitored) {
-        if (types::HeapTypeSet *propTypes = GetDefiniteSlot(cx, binaryTypes.lhsTypes, name)) {
-            MStoreFixedSlot *fixed = MStoreFixedSlot::New(obj, propTypes->definiteSlot(), value);
-            current->add(fixed);
-            current->push(value);
-            if (propTypes->needsBarrier(cx))
-                fixed->setNeedsBarrier();
-            return resumeAfter(fixed);
-        }
-    }
+    types::StackTypeSet *objTypes = obj->resultTypeSet();
 
     RootedId id(cx, NameToId(name));
-    types::StackTypeSet *types = binaryTypes.lhsTypes;
 
     JSFunction *commonSetter;
     bool isDOM;
-    if (!TestCommonPropFunc(cx, types, id, &commonSetter, false, &isDOM, NULL))
+    if (!TestCommonPropFunc(cx, objTypes, id, &commonSetter, false, &isDOM, NULL))
         return false;
-    if (!monitored && commonSetter) {
+    if (commonSetter) {
+        // Setters can be called even if the property write needs a type
+        // barrier, as calling the setter does not actually write any data
+        // properties.
         RootedFunction setter(cx, commonSetter);
-        if (isDOM && TestShouldDOMCall(cx, types, setter, JSJitInfo::Setter)) {
+        if (isDOM && TestShouldDOMCall(cx, objTypes, setter, JSJitInfo::Setter)) {
             MSetDOMProperty *set = MSetDOMProperty::New(setter->jitInfo()->op, obj, value);
             if (!set)
                 return false;
 
             current->add(set);
             current->push(value);
 
             return resumeAfter(set);
         }
 
         // Don't call the setter with a primitive value.
-        if (types->getKnownTypeTag() != JSVAL_TYPE_OBJECT) {
+        if (objTypes->getKnownTypeTag() != JSVAL_TYPE_OBJECT) {
             MGuardObject *guardObj = MGuardObject::New(obj);
             current->add(guardObj);
             obj = guardObj;
         }
 
-        // Dummy up the stack, as in getprop
+        // Dummy up the stack, as in getprop. We are pushing an extra value, so
+        // ensure there is enough space.
+        uint32_t depth = current->stackDepth() + 3;
+        if (depth > current->nslots()) {
+            if (!current->increaseSlots(depth - current->nslots()))
+                return false;
+        }
+
         pushConstant(ObjectValue(*setter));
 
         MPassArg *wrapper = MPassArg::New(obj);
         current->push(wrapper);
         current->add(wrapper);
 
         MPassArg *arg = MPassArg::New(value);
         current->push(arg);
         current->add(arg);
 
         // Call the setter. Note that we have to push the original value, not
         // the setter's return value.
         CallInfo callInfo(cx, false);
         if (!callInfo.init(current, 1))
             return false;
-        MCall *call = makeCallHelper(setter, callInfo, types, false);
+        MCall *call = makeCallHelper(setter, callInfo, false);
         if (!call)
             return false;
 
         current->push(value);
         return resumeAfter(call);
     }
 
-    oracle->binaryOp(script(), pc);
-
-    MSetPropertyInstruction *ins;
-    if (monitored) {
-        ins = MCallSetProperty::New(obj, value, name, script()->strict);
-    } else {
-        RawShape objShape = mjit::GetPICSingleShape(cx, script(), pc, info().constructing());
-        if (!objShape)
-            objShape = inspector->maybeMonomorphicShapeForPropertyOp(pc);
-
-        if (objShape && !objShape->inDictionary()) {
-            // The JM IC was monomorphic, so we inline the property access as
-            // long as the shape is not in dictionary mode. We cannot be sure
-            // that the shape is still a lastProperty, and calling Shape::search
-            // on dictionary mode shapes that aren't lastProperty is invalid.
-            obj = addShapeGuard(obj, objShape, Bailout_CachedShapeGuard);
-
-            RootedShape shape(cx, objShape->search(cx, NameToId(name)));
-            JS_ASSERT(shape);
-
-            spew("Inlining monomorphic SETPROP");
-
-            RawId typeId = types::IdToTypeId(id);
-            bool needsBarrier = oracle->propertyWriteNeedsBarrier(script(), pc, typeId);
-
-            return storeSlot(obj, shape, value, needsBarrier);
-        }
-
-        spew("SETPROP not monomorphic");
-
-        ins = MSetPropertyCache::New(obj, value, name, script()->strict);
-
-        if (!binaryTypes.lhsTypes || binaryTypes.lhsTypes->propertyNeedsBarrier(cx, id))
-            ins->setNeedsBarrier();
-    }
+    if (propertyWriteNeedsTypeBarrier(obj, name, &value)) {
+        MInstruction *ins = MCallSetProperty::New(obj, value, name, script()->strict);
+        current->add(ins);
+        current->push(value);
+        return resumeAfter(ins);
+    }
+
+    if (types::HeapTypeSet *propTypes = GetDefiniteSlot(cx, objTypes, name)) {
+        MStoreFixedSlot *fixed = MStoreFixedSlot::New(obj, propTypes->definiteSlot(), value);
+        current->add(fixed);
+        current->push(value);
+        if (propTypes->needsBarrier(cx))
+            fixed->setNeedsBarrier();
+        return resumeAfter(fixed);
+    }
+
+    RawShape objShape = mjit::GetPICSingleShape(cx, script(), pc, info().constructing());
+    if (!objShape)
+        objShape = inspector->maybeMonomorphicShapeForPropertyOp(pc);
+
+    if (objShape && !objShape->inDictionary()) {
+        // The JM IC was monomorphic, so we inline the property access as
+        // long as the shape is not in dictionary mode. We cannot be sure
+        // that the shape is still a lastProperty, and calling Shape::search
+        // on dictionary mode shapes that aren't lastProperty is invalid.
+        obj = addShapeGuard(obj, objShape, Bailout_CachedShapeGuard);
+
+        RootedShape shape(cx, objShape->search(cx, NameToId(name)));
+        JS_ASSERT(shape);
+
+        spew("Inlining monomorphic SETPROP");
+
+        bool needsBarrier = objTypes->propertyNeedsBarrier(cx, id);
+        return storeSlot(obj, shape, value, needsBarrier);
+    }
+
+    spew("SETPROP not monomorphic");
+
+    MSetPropertyCache *ins = MSetPropertyCache::New(obj, value, name, script()->strict);
+
+    if (!objTypes || objTypes->propertyNeedsBarrier(cx, id))
+        ins->setNeedsBarrier();
 
     current->add(ins);
     current->push(value);
 
     return resumeAfter(ins);
 }
 
 bool
@@ -7185,64 +7650,64 @@ IonBuilder::jsop_this()
     if (!info().fun())
         return abort("JSOP_THIS outside of a JSFunction.");
 
     if (script()->strict) {
         current->pushSlot(info().thisSlot());
         return true;
     }
 
-    types::StackTypeSet *types = oracle->thisTypeSet(script());
+    types::StackTypeSet *types = types::TypeScript::ThisTypes(script());
     if (types && types->getKnownTypeTag() == JSVAL_TYPE_OBJECT) {
         // This is safe, because if the entry type of |this| is an object, it
         // will necessarily be an object throughout the entire function. OSR
         // can introduce a phi, but this phi will be specialized.
         current->pushSlot(info().thisSlot());
         return true;
     }
 
     return abort("JSOP_THIS hard case not yet handled");
 }
 
 bool
 IonBuilder::jsop_typeof()
 {
-    TypeOracle::Unary unary = oracle->unaryOp(script(), pc);
-
     MDefinition *input = current->pop();
-    MTypeOf *ins = MTypeOf::New(input, unary.ival);
+    MTypeOf *ins = MTypeOf::New(input, input->type());
 
     current->add(ins);
     current->push(ins);
 
     if (ins->isEffectful() && !resumeAfter(ins))
         return false;
     return true;
 }
 
 bool
 IonBuilder::jsop_toid()
 {
     // No-op if the index is an integer.
-    TypeOracle::Unary unary = oracle->unaryOp(script(), pc);
-    if (unary.ival == MIRType_Int32)
+    if (current->peek(-1)->type() == MIRType_Int32)
         return true;
 
     MDefinition *index = current->pop();
     MToId *ins = MToId::New(current->peek(-1), index);
 
     current->add(ins);
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
 bool
 IonBuilder::jsop_iter(uint8_t flags)
 {
+    if (flags != JSITER_ENUMERATE)
+        nonStringIteration_ = true;
+
     MDefinition *obj = current->pop();
     MInstruction *ins = MIteratorStart::New(obj, flags);
 
     if (!iterators_.append(ins))
         return false;
 
     current->add(ins);
     current->push(ins);
@@ -7254,17 +7719,26 @@ bool
 IonBuilder::jsop_iternext()
 {
     MDefinition *iter = current->peek(-1);
     MInstruction *ins = MIteratorNext::New(iter);
 
     current->add(ins);
     current->push(ins);
 
-    return resumeAfter(ins);
+    if (!resumeAfter(ins))
+        return false;
+
+    if (!nonStringIteration_ && types::IterationValuesMustBeStrings(script())) {
+        ins = MUnbox::New(ins, MIRType_String, MUnbox::Infallible);
+        current->add(ins);
+        current->rewriteAtDepth(-1, ins);
+    }
+
+    return true;
 }
 
 bool
 IonBuilder::jsop_itermore()
 {
     MDefinition *iter = current->peek(-1);
     MInstruction *ins = MIteratorMore::New(iter);
 
@@ -7297,47 +7771,35 @@ IonBuilder::walkScopeChain(unsigned hops
     }
 
     return scope;
 }
 
 bool
 IonBuilder::jsop_getaliasedvar(ScopeCoordinate sc)
 {
-    types::StackTypeSet *barrier;
-    types::StackTypeSet *actual = oracle->aliasedVarBarrier(script(), pc, &barrier);
-
     MDefinition *obj = walkScopeChain(sc.hops);
 
     RootedShape shape(cx, ScopeCoordinateToStaticScopeShape(cx, script(), pc));
 
     MInstruction *load;
     if (shape->numFixedSlots() <= sc.slot) {
         MInstruction *slots = MSlots::New(obj);
         current->add(slots);
 
         load = MLoadSlot::New(slots, sc.slot - shape->numFixedSlots());
     } else {
         load = MLoadFixedSlot::New(obj, sc.slot);
     }
 
-    if (!barrier) {
-        JSValueType type = actual->getKnownTypeTag();
-        if (type != JSVAL_TYPE_UNKNOWN &&
-            type != JSVAL_TYPE_UNDEFINED &&
-            type != JSVAL_TYPE_NULL)
-        {
-            load->setResultType(MIRTypeFromValueType(type));
-        }
-    }
-
     current->add(load);
     current->push(load);
 
-    return pushTypeBarrier(load, actual, barrier);
+    types::StackTypeSet *types = script()->analysis()->bytecodeTypes(pc);
+    return pushTypeBarrier(load, types, true);
 }
 
 bool
 IonBuilder::jsop_setaliasedvar(ScopeCoordinate sc)
 {
     MDefinition *rval = current->peek(-1);
     MDefinition *obj = walkScopeChain(sc.hops);
 
@@ -7355,38 +7817,40 @@ IonBuilder::jsop_setaliasedvar(ScopeCoor
 
     current->add(store);
     return resumeAfter(store);
 }
 
 bool
 IonBuilder::jsop_in()
 {
-    RootedScript scriptRoot(cx, script());
-    if (oracle->inObjectIsDenseNativeWithoutExtraIndexedProperties(scriptRoot, pc))
+    MDefinition *obj = current->peek(-1);
+    MDefinition *id = current->peek(-2);
+
+    if (elementAccessIsDenseNative(obj, id) && !elementAccessHasExtraIndexedProperty(obj))
         return jsop_in_dense();
 
-    MDefinition *obj = current->pop();
-    MDefinition *id = current->pop();
+    current->pop();
+    current->pop();
     MIn *ins = new MIn(id, obj);
 
     current->add(ins);
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
 bool
 IonBuilder::jsop_in_dense()
 {
-    bool needsHoleCheck = !oracle->inArrayIsPacked(script(), pc);
-
     MDefinition *obj = current->pop();
     MDefinition *id = current->pop();
 
+    bool needsHoleCheck = !elementAccessIsPacked(obj);
+
     // Ensure id is an integer.
     MInstruction *idInt32 = MToInt32::New(id);
     current->add(idInt32);
     id = idInt32;
 
     // Get the elements vector.
     MElements *elements = MElements::New(obj);
     current->add(elements);
@@ -7404,22 +7868,21 @@ IonBuilder::jsop_in_dense()
 }
 
 bool
 IonBuilder::jsop_instanceof()
 {
     MDefinition *rhs = current->pop();
     MDefinition *obj = current->pop();
 
-    TypeOracle::BinaryTypes types = oracle->binaryTypes(script(), pc);
-
     // If this is an 'x instanceof function' operation and we can determine the
     // exact function and prototype object being tested for, use a typed path.
     do {
-        RawObject rhsObject = types.rhsTypes ? types.rhsTypes->getSingleton() : NULL;
+        types::StackTypeSet *rhsTypes = rhs->resultTypeSet();
+        RawObject rhsObject = rhsTypes ? rhsTypes->getSingleton() : NULL;
         if (!rhsObject || !rhsObject->isFunction() || rhsObject->isBoundFunction())
             break;
 
         types::TypeObject *rhsType = rhsObject->getType(cx);
         if (!rhsType || rhsType->unknownProperties())
             break;
 
         types::HeapTypeSet *protoTypes =
@@ -7473,18 +7936,18 @@ IonBuilder::addShapeGuard(MDefinition *o
 
     // If a shape guard failed in the past, don't optimize shape guard.
     if (failedShapeGuard_)
         guard->setNotMovable();
 
     return guard;
 }
 
-const types::StackTypeSet *
-IonBuilder::cloneTypeSet(const types::StackTypeSet *types)
+types::StackTypeSet *
+IonBuilder::cloneTypeSet(types::StackTypeSet *types)
 {
     if (!js_IonOptions.parallelCompilation)
         return types;
 
     // Clone a type set so that it can be stored into the MIR and accessed
     // during off thread compilation. This is necessary because main thread
     // updates to type sets can race with reads in the compiler backend, and
     // after bug 804676 this code can be removed.
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -94,23 +94,32 @@ class IonBuilder : public MIRGenerator
 
                 // Position of where the loop body starts and ends.
                 jsbytecode *bodyStart;
                 jsbytecode *bodyEnd;
 
                 // pc immediately after the loop exits.
                 jsbytecode *exitpc;
 
+                // pc for 'continue' jumps.
+                jsbytecode *continuepc;
+
                 // Common exit point. Created lazily, so it may be NULL.
                 MBasicBlock *successor;
 
                 // Deferred break and continue targets.
                 DeferredEdge *breaks;
                 DeferredEdge *continues;
 
+                // Initial state, in case loop processing is restarted.
+                State initialState;
+                jsbytecode *initialPc;
+                jsbytecode *initialStopAt;
+                jsbytecode *loopHead;
+
                 // For-loops only.
                 jsbytecode *condpc;
                 jsbytecode *updatepc;
                 jsbytecode *updateEnd;
             } loop;
             struct {
                 // pc immediately after the switch.
                 jsbytecode *exitpc;
@@ -169,17 +178,17 @@ class IonBuilder : public MIRGenerator
         static CFGState CondSwitch(jsbytecode *exitpc, jsbytecode *defaultTarget);
         static CFGState Label(jsbytecode *exitpc);
     };
 
     static int CmpSuccessors(const void *a, const void *b);
 
   public:
     IonBuilder(JSContext *cx, TempAllocator *temp, MIRGraph *graph,
-               TypeOracle *oracle, BaselineInspector *inspector, CompileInfo *info,
+               BaselineInspector *inspector, CompileInfo *info, AbstractFramePtr fp,
                size_t inliningDepth = 0, uint32_t loopDepth = 0);
 
     bool build();
     bool buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoint,
                      CallInfo &callInfo);
 
   private:
     bool traverseBytecode();
@@ -196,16 +205,17 @@ class IonBuilder : public MIRGenerator
     }
 
     JSFunction *getSingleCallTarget(types::StackTypeSet *calleeTypes);
     bool getPolyCallTargets(types::StackTypeSet *calleeTypes,
                             AutoObjectVector &targets, uint32_t maxTargets);
     bool canInlineTarget(JSFunction *target, CallInfo &callInfo);
 
     void popCfgStack();
+    DeferredEdge *filterDeadDeferredEdges(DeferredEdge *edge);
     bool processDeferredContinues(CFGState &state);
     ControlStatus processControlEnd();
     ControlStatus processCfgStack();
     ControlStatus processCfgEntry(CFGState &state);
     ControlStatus processIfEnd(CFGState &state);
     ControlStatus processIfElseTrueEnd(CFGState &state);
     ControlStatus processIfElseFalseEnd(CFGState &state);
     ControlStatus processDoWhileBodyEnd(CFGState &state);
@@ -223,18 +233,20 @@ class IonBuilder : public MIRGenerator
     ControlStatus processAndOrEnd(CFGState &state);
     ControlStatus processLabelEnd(CFGState &state);
     ControlStatus processReturn(JSOp op);
     ControlStatus processThrow();
     ControlStatus processContinue(JSOp op);
     ControlStatus processBreak(JSOp op, jssrcnote *sn);
     ControlStatus maybeLoop(JSOp op, jssrcnote *sn);
     bool pushLoop(CFGState::State state, jsbytecode *stopAt, MBasicBlock *entry,
+                  jsbytecode *loopHead, jsbytecode *initialPc,
                   jsbytecode *bodyStart, jsbytecode *bodyEnd, jsbytecode *exitpc,
                   jsbytecode *continuepc = NULL);
+    void analyzeNewLoopTypes(MBasicBlock *entry, jsbytecode *start, jsbytecode *end);
 
     MBasicBlock *addBlock(MBasicBlock *block, uint32_t loopDepth);
     MBasicBlock *newBlock(MBasicBlock *predecessor, jsbytecode *pc);
     MBasicBlock *newBlock(MBasicBlock *predecessor, jsbytecode *pc, uint32_t loopDepth);
     MBasicBlock *newBlock(MBasicBlock *predecessor, jsbytecode *pc, MResumePoint *priorResumePoint);
     MBasicBlock *newBlockPopN(MBasicBlock *predecessor, jsbytecode *pc, uint32_t popped);
     MBasicBlock *newBlockAfter(MBasicBlock *at, MBasicBlock *predecessor, jsbytecode *pc);
     MBasicBlock *newOsrPreheader(MBasicBlock *header, jsbytecode *loopEntry);
@@ -253,16 +265,20 @@ class IonBuilder : public MIRGenerator
     // Finishes loops that do not actually loop, containing only breaks or
     // returns.
     ControlStatus processBrokenLoop(CFGState &state);
 
     // Computes loop phis, places them in all successors of a loop, then
     // handles any pending breaks.
     ControlStatus finishLoop(CFGState &state, MBasicBlock *successor);
 
+    // Restarts processing of a loop if the type information at its header was
+    // incomplete.
+    ControlStatus restartLoop(CFGState state);
+
     void assertValidLoopHeadOp(jsbytecode *pc);
 
     ControlStatus forLoop(JSOp op, jssrcnote *sn);
     ControlStatus whileOrForInLoop(jssrcnote *sn);
     ControlStatus doWhileLoop(JSOp op, jssrcnote *sn);
     ControlStatus tableSwitch(JSOp op, jssrcnote *sn);
     ControlStatus condSwitch(JSOp op, jssrcnote *sn);
 
@@ -274,26 +290,18 @@ class IonBuilder : public MIRGenerator
     bool maybeInsertResume();
 
     bool initParameters();
     void rewriteParameters();
     bool initScopeChain();
     bool pushConstant(const Value &v);
 
     // Add a guard which ensure that the set of type which goes through this
-    // generated code correspond to the observed or infered (actual) type.
-    bool pushTypeBarrier(MInstruction *ins, types::StackTypeSet *actual, types::StackTypeSet *observed);
-
-    // Add a guard which ensure that the set of type does not go through. Some
-    // instructions, such as function calls, can have an excluded set of types
-    // which would be added after invalidation if they are observed in the
-    // callee.
-    void addTypeBarrier(uint32_t i, CallInfo &callinfo, types::StackTypeSet *calleeObs);
-
-    void monitorResult(MInstruction *ins, types::TypeSet *barrier, types::StackTypeSet *types);
+    // generated code correspond to the observed types for the bytecode.
+    bool pushTypeBarrier(MInstruction *ins, types::StackTypeSet *observed, bool needBarrier);
 
     JSObject *getSingletonPrototype(JSFunction *target);
 
     MDefinition *createThisScripted(MDefinition *callee);
     MDefinition *createThisScriptedSingleton(HandleFunction target, MDefinition *callee);
     MDefinition *createThis(HandleFunction target, MDefinition *callee);
     MInstruction *createDeclEnvObject(MDefinition *callee, MDefinition *scopeObj);
     MInstruction *createCallObject(MDefinition *callee, MDefinition *scopeObj);
@@ -303,33 +311,31 @@ class IonBuilder : public MIRGenerator
     MInstruction *addConvertElementsToDoubles(MDefinition *elements);
     MInstruction *addBoundsCheck(MDefinition *index, MDefinition *length);
     MInstruction *addShapeGuard(MDefinition *obj, const RawShape shape, BailoutKind bailoutKind);
 
     JSObject *getNewArrayTemplateObject(uint32_t count);
 
     bool invalidatedIdempotentCache();
 
-    bool loadSlot(MDefinition *obj, HandleShape shape, MIRType rvalType);
+    bool loadSlot(MDefinition *obj, HandleShape shape, MIRType rvalType,
+                  bool barrier, types::StackTypeSet *types);
     bool storeSlot(MDefinition *obj, RawShape shape, MDefinition *value, bool needsBarrier);
 
     // jsop_getprop() helpers.
     bool getPropTryArgumentsLength(bool *emitted);
-    bool getPropTryConstant(bool *emitted, HandleId id, types::StackTypeSet *barrier,
-                            types::StackTypeSet *types, TypeOracle::UnaryTypes unaryTypes);
+    bool getPropTryConstant(bool *emitted, HandleId id, types::StackTypeSet *types);
     bool getPropTryDefiniteSlot(bool *emitted, HandlePropertyName name,
-                            types::StackTypeSet *barrier, types::StackTypeSet *types,
-                            TypeOracle::Unary unary, TypeOracle::UnaryTypes unaryTypes);
-    bool getPropTryCommonGetter(bool *emitted, HandleId id, types::StackTypeSet *barrier,
-                                types::StackTypeSet *types, TypeOracle::UnaryTypes unaryTypes);
-    bool getPropTryMonomorphic(bool *emitted, HandleId id, types::StackTypeSet *barrier,
-                               TypeOracle::Unary unary, TypeOracle::UnaryTypes unaryTypes);
+                                bool barrier, types::StackTypeSet *types);
+    bool getPropTryCommonGetter(bool *emitted, HandleId id,
+                                bool barrier, types::StackTypeSet *types);
+    bool getPropTryMonomorphic(bool *emitted, HandleId id,
+                               bool barrier, types::StackTypeSet *types);
     bool getPropTryPolymorphic(bool *emitted, HandlePropertyName name, HandleId id,
-                               types::StackTypeSet *barrier, types::StackTypeSet *types,
-                               TypeOracle::Unary unary, TypeOracle::UnaryTypes unaryTypes);
+                               bool barrier, types::StackTypeSet *types);
 
     // Typed array helpers.
     MInstruction *getTypedArrayLength(MDefinition *obj);
     MInstruction *getTypedArrayElements(MDefinition *obj);
 
     bool jsop_add(MDefinition *left, MDefinition *right);
     bool jsop_bitnot();
     bool jsop_bitop(JSOp op);
@@ -357,17 +363,17 @@ class IonBuilder : public MIRGenerator
     bool jsop_getname(HandlePropertyName name);
     bool jsop_intrinsic(HandlePropertyName name);
     bool jsop_bindname(PropertyName *name);
     bool jsop_getelem();
     bool jsop_getelem_dense();
     bool jsop_getelem_typed(int arrayType);
     bool jsop_getelem_string();
     bool jsop_setelem();
-    bool jsop_setelem_dense();
+    bool jsop_setelem_dense(types::StackTypeSet::DoubleConversion conversion);
     bool jsop_setelem_typed(int arrayType);
     bool jsop_length();
     bool jsop_length_fastPath();
     bool jsop_arguments();
     bool jsop_arguments_length();
     bool jsop_arguments_getelem();
     bool jsop_arguments_setelem();
     bool jsop_not();
@@ -399,26 +405,33 @@ class IonBuilder : public MIRGenerator
     enum InliningStatus
     {
         InliningStatus_Error,
         InliningStatus_NotInlined,
         InliningStatus_Inlined
     };
 
     // Oracles.
+    bool canEnterInlinedFunction(JSFunction *target);
     bool makeInliningDecision(JSFunction *target, CallInfo &callInfo);
     uint32_t selectInliningTargets(AutoObjectVector &targets, CallInfo &callInfo, Vector<bool> &choiceSet);
+    bool elementAccessIsDenseNative(MDefinition *obj, MDefinition *id);
+    bool elementAccessIsTypedArray(MDefinition *obj, MDefinition *id, int *arrayType);
+    bool elementAccessIsPacked(MDefinition *obj);
+    bool elementAccessHasExtraIndexedProperty(MDefinition *obj);
+    MIRType denseNativeElementType(MDefinition *obj);
+    bool propertyReadNeedsTypeBarrier(MDefinition *obj, PropertyName *name,
+                                      types::StackTypeSet *observed);
+    bool propertyReadIsIdempotent(MDefinition *obj, PropertyName *name);
+    bool propertyWriteNeedsTypeBarrier(MDefinition *obj, PropertyName *name, MDefinition **pvalue);
+    bool tryAddWriteBarrier(types::StackTypeSet *objTypes, jsid id, MDefinition **pvalue);
 
     // Native inlining helpers.
     types::StackTypeSet *getInlineReturnTypeSet();
     MIRType getInlineReturnType();
-    types::StackTypeSet *getInlineThisTypeSet(CallInfo &callInfo);
-    MIRType getInlineThisType(CallInfo &callInfo);
-    types::StackTypeSet *getInlineArgTypeSet(CallInfo &callInfo, uint32_t arg);
-    MIRType getInlineArgType(CallInfo &callInfo, uint32_t arg);
 
     // Array natives.
     InliningStatus inlineArray(CallInfo &callInfo);
     InliningStatus inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode);
     InliningStatus inlineArrayPush(CallInfo &callInfo);
     InliningStatus inlineArrayConcat(CallInfo &callInfo);
 
     // Math natives.
@@ -475,22 +488,18 @@ class IonBuilder : public MIRGenerator
     bool inlineGenericFallback(JSFunction *target, CallInfo &callInfo, MBasicBlock *dispatchBlock,
                                bool clonedAtCallsite);
     bool inlineTypeObjectFallback(CallInfo &callInfo, MBasicBlock *dispatchBlock,
                                   MTypeObjectDispatch *dispatch, MGetPropertyCache *cache,
                                   MBasicBlock **fallbackTarget);
 
     bool anyFunctionIsCloneAtCallsite(types::StackTypeSet *funTypes);
     MDefinition *makeCallsiteClone(HandleFunction target, MDefinition *fun);
-    MCall *makeCallHelper(HandleFunction target, CallInfo &callInfo,
-                          types::StackTypeSet *calleeTypes, bool cloneAtCallsite);
-    bool makeCallBarrier(HandleFunction target,  CallInfo &callInfo,
-                         types::StackTypeSet *calleeTypes, bool cloneAtCallsite);
-    bool makeCall(HandleFunction target, CallInfo &callInfo,
-                  types::StackTypeSet *calleeTypes, bool cloneAtCallsite);
+    MCall *makeCallHelper(HandleFunction target, CallInfo &callInfo, bool cloneAtCallsite);
+    bool makeCall(HandleFunction target, CallInfo &callInfo, bool cloneAtCallsite);
 
     MDefinition *patchInlinedReturn(CallInfo &callInfo, MBasicBlock *exit, MBasicBlock *bottom);
     MDefinition *patchInlinedReturns(CallInfo &callInfo, MIRGraphExits &exits, MBasicBlock *bottom);
 
     inline bool TestCommonPropFunc(JSContext *cx, types::StackTypeSet *types,
                                    HandleId id, JSFunction **funcp,
                                    bool isGetter, bool *isDOM,
                                    MDefinition **guardOut);
@@ -500,17 +509,23 @@ class IonBuilder : public MIRGenerator
 
     MGetPropertyCache *getInlineableGetPropertyCache(CallInfo &callInfo);
 
     MPolyInlineDispatch *
     makePolyInlineDispatch(JSContext *cx, CallInfo &callInfo,
                            MGetPropertyCache *getPropCache, MBasicBlock *bottom,
                            Vector<MDefinition *, 8, IonAllocPolicy> &retvalDefns);
 
-    const types::StackTypeSet *cloneTypeSet(const types::StackTypeSet *types);
+    types::StackTypeSet *cloneTypeSet(types::StackTypeSet *types);
+
+    void setCurrent(MBasicBlock *block) {
+        current = block;
+        if (current)
+            current->specializePhis();
+    }
 
     // A builder is inextricably tied to a particular script.
     HeapPtrScript script_;
 
     // If off thread compilation is successful, the final code generator is
     // attached here. Code has been generated, but not linked (there is not yet
     // an IonScript). This is heap allocated, and must be explicitly destroyed,
     // performed by FinishOffThreadBuilder().
@@ -526,16 +541,17 @@ class IonBuilder : public MIRGenerator
 
     CodeGenerator *backgroundCodegen() const { return backgroundCodegen_; }
     void setBackgroundCodegen(CodeGenerator *codegen) { backgroundCodegen_ = codegen; }
 
     AbortReason abortReason() { return abortReason_; }
 
   private:
     JSContext *cx;
+    AbstractFramePtr fp;
     AbortReason abortReason_;
 
     jsbytecode *pc;
     MBasicBlock *current;
     uint32_t loopDepth_;
 
     /* Information used for inline-call builders. */
     MResumePoint *callerResumePoint_;
@@ -544,95 +560,62 @@ class IonBuilder : public MIRGenerator
     }
     IonBuilder *callerBuilder_;
 
     Vector<CFGState, 8, IonAllocPolicy> cfgStack_;
     Vector<ControlFlowInfo, 4, IonAllocPolicy> loops_;
     Vector<ControlFlowInfo, 0, IonAllocPolicy> switches_;
     Vector<ControlFlowInfo, 2, IonAllocPolicy> labels_;
     Vector<MInstruction *, 2, IonAllocPolicy> iterators_;
-    TypeOracle *oracle;
     BaselineInspector *inspector;
 
     size_t inliningDepth_;
     Vector<MDefinition *, 0, IonAllocPolicy> inlinedArguments_;
-    Vector<types::StackTypeSet *, 0, IonAllocPolicy> inlinedArgumentTypes_;
 
     // True if script->failedBoundsCheck is set for the current script or
     // an outer script.
     bool failedBoundsCheck_;
 
     // True if script->failedShapeGuard is set for the current script or
     // an outer script.
     bool failedShapeGuard_;
 
+    // Has an iterator other than 'for in'.
+    bool nonStringIteration_;
+
     // If this script can use a lazy arguments object, it will be pre-created
     // here.
     MInstruction *lazyArguments_;
 };
 
 class CallInfo
 {
-    types::StackTypeSet *barrier_;
-    types::StackTypeSet *types_;
-
-    types::TypeBarrier *argsBarriers_;
-    types::StackTypeSet *thisType_;
-    Vector<types::StackTypeSet *> argsType_;
-
     MDefinition *fun_;
     MDefinition *thisArg_;
     Vector<MDefinition *> args_;
 
     bool constructing_;
 
   public:
     CallInfo(JSContext *cx, bool constructing)
-      : barrier_(NULL),
-        types_(NULL),
-        argsBarriers_(NULL),
-        thisType_(NULL),
-        argsType_(cx),
-        fun_(NULL),
-        thisArg_(NULL),
-        args_(cx),
-        constructing_(constructing)
-    { }
-
-    CallInfo(JSContext *cx, bool constructing,
-             types::StackTypeSet *types, types::StackTypeSet *barrier)
-      : barrier_(barrier),
-        types_(types),
-        argsBarriers_(NULL),
-        thisType_(NULL),
-        argsType_(cx),
-        fun_(NULL),
+      : fun_(NULL),
         thisArg_(NULL),
         args_(cx),
         constructing_(constructing)
     { }
 
     bool init(CallInfo &callInfo) {
         JS_ASSERT(constructing_ == callInfo.constructing());
 
-        thisType_ = callInfo.thisType_;
-        if (thisType_ && !argsType_.append(callInfo.argsType_.begin(), callInfo.argsType_.end()))
-            return false;
-
         fun_ = callInfo.fun();
         thisArg_ = callInfo.thisArg();
 
         if (!args_.append(callInfo.argv().begin(), callInfo.argv().end()))
             return false;
 
-        if (callInfo.hasTypeInfo())
-            setTypeInfo(callInfo.types(), callInfo.barrier());
-
-        argsBarriers_ = callInfo.argsBarriers_;
-
         return true;
     }
 
     bool init(MBasicBlock *current, uint32_t argc) {
         JS_ASSERT(args_.length() == 0);
 
         // Get the arguments in the right order
         if (!args_.reserve(argc))
@@ -643,133 +626,67 @@ class CallInfo
 
         // Get |this| and |fun|
         setThis(current->pop());
         setFun(current->pop());
 
         return true;
     }
 
-    bool initCallType(TypeOracle *oracle, HandleScript script, jsbytecode *pc) {
-        argsBarriers_ = oracle->callArgsBarrier(script, pc);
-        thisType_ = oracle->getCallArg(script, argc(), 0, pc);
-        if (!argsType_.reserve(argc()))
-            return false;
-        for (uint32_t i = 1; i <= argc(); i++)
-            argsType_.infallibleAppend(oracle->getCallArg(script, argc(), i, pc));
-        return true;
-    }
-
-    bool initFunApplyArguments(TypeOracle *oracle, HandleScript script, jsbytecode *pc,
-                               Vector<types::StackTypeSet *> *types) {
-        argsBarriers_ = oracle->callArgsBarrier(script, pc);
-        thisType_ = oracle->getCallArg(script, 2, 1, pc);
-        if (!argsType_.append(types->begin(), types->end()))
-            return false;
-        return true;
-    }
-
-    bool hasCallType() {
-        // argsBarriers can be NULL is the caller does not need to guard its
-        // arguments again some value type expected by callees. On the other
-        // hand we would always have a thisType if the type info is initialized.
-        return hasTypeInfo() && thisType_;
-    }
-
     void popFormals(MBasicBlock *current) {
         current->popn(numFormals());
     }
 
     void pushFormals(MBasicBlock *current) {
         current->push(fun());
         current->push(thisArg());
 
         for (uint32_t i = 0; i < argc(); i++)
             current->push(getArg(i));
     }
 
-    void setTypeInfo(types::StackTypeSet *types, types::StackTypeSet *barrier) {
-        types_ = types;
-        barrier_ = barrier;
-    }
-
-    bool hasTypeInfo() const {
-        JS_ASSERT_IF(barrier_, types_);
-        return types_;
-    }
-
     uint32_t argc() const {
         return args_.length();
     }
     uint32_t numFormals() const {
         return argc() + 2;
     }
 
     void setArgs(Vector<MDefinition *> *args) {
         JS_ASSERT(args_.length() == 0);
         args_.append(args->begin(), args->end());
     }
 
     Vector<MDefinition *> &argv() {
         return args_;
     }
 
-    Vector<types::StackTypeSet *> &argvType() {
-        return argsType_;
-    }
-
     MDefinition *getArg(uint32_t i) {
         JS_ASSERT(i < argc());
         return args_[i];
     }
 
-    types::StackTypeSet *getArgType(uint32_t i) {
-        JS_ASSERT(i < argc() && argc() == argsType_.length());
-        return argsType_[i];
-    }
-
     void setArg(uint32_t i, MDefinition *def) {
         JS_ASSERT(i < argc());
         args_[i] = def;
     }
 
     MDefinition *thisArg() {
         JS_ASSERT(thisArg_);
         return thisArg_;
     }
 
-    types::StackTypeSet *thisType() {
-        JS_ASSERT(thisType_);
-        return thisType_;
-    }
-
     void setThis(MDefinition *thisArg) {
         thisArg_ = thisArg;
     }
 
-    void setThisType(types::StackTypeSet *thisType) {
-        thisType_ = thisType;
-    }
-
     bool constructing() {
         return constructing_;
     }
 
-    types::StackTypeSet *types() {
-        return types_;
-    }
-
-    types::StackTypeSet *barrier() {
-        return barrier_;
-    }
-
-    types::TypeBarrier *argsBarrier() {
-        return argsBarriers_;
-    }
-
     void wrapArgs(MBasicBlock *current) {
         thisArg_ = wrap(current, thisArg_);
         for (uint32_t i = 0; i < argc(); i++)
             args_[i] = wrap(current, args_[i]);
     }
 
     void unwrapArgs() {
         thisArg_ = unwrap(thisArg_);
@@ -812,12 +729,14 @@ class CallInfo
     static MDefinition *wrap(MBasicBlock *current, MDefinition *arg) {
         JS_ASSERT(!arg->isPassArg());
         MPassArg *passArg = MPassArg::New(arg);
         current->add(passArg);
         return passArg;
     }
 };
 
+bool TypeSetIncludes(types::TypeSet *types, MIRType input, types::TypeSet *inputTypes);
+
 } // namespace ion
 } // namespace js
 
 #endif // jsion_bytecode_analyzer_h__
--- a/js/src/ion/IonCaches.h
+++ b/js/src/ion/IonCaches.h
@@ -4,17 +4,16 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jsion_caches_h__
 #define jsion_caches_h__
 
 #include "IonCode.h"
-#include "TypeOracle.h"
 #include "Registers.h"
 
 #include "vm/ForkJoin.h"
 
 class JSFunction;
 class JSScript;
 
 namespace js {
--- a/js/src/ion/IonMacroAssembler.h
+++ b/js/src/ion/IonMacroAssembler.h
@@ -12,17 +12,16 @@
 # include "ion/x86/MacroAssembler-x86.h"
 #elif defined(JS_CPU_X64)
 # include "ion/x64/MacroAssembler-x64.h"
 #elif defined(JS_CPU_ARM)
 # include "ion/arm/MacroAssembler-arm.h"
 #endif
 #include "ion/IonCompartment.h"
 #include "ion/IonInstrumentation.h"
-#include "ion/TypeOracle.h"
 #include "ion/ParallelFunctions.h"
 
 #include "vm/ForkJoin.h"
 
 #include "jstypedarray.h"
 #include "jscompartment.h"
 
 #include "vm/Shape.h"
--- a/js/src/ion/IonTypes.h
+++ b/js/src/ion/IonTypes.h
@@ -3,16 +3,17 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jsion_types_h_
 #define jsion_types_h_
 
+#include "js/Value.h"
 #include <jstypes.h>
 
 namespace js {
 namespace ion {
 
 typedef uint32_t SnapshotOffset;
 typedef uint32_t BailoutId;
 
@@ -90,16 +91,124 @@ enum MIRType
     MIRType_None,         // Invalid, used as a placeholder.
     MIRType_Slots,        // A slots vector
     MIRType_Elements,     // An elements vector
     MIRType_Pointer,      // An opaque pointer that receives no special treatment
     MIRType_Shape,        // A Shape pointer.
     MIRType_ForkJoinSlice // js::ForkJoinSlice*
 };
 
+static inline MIRType
+MIRTypeFromValueType(JSValueType type)
+{
+    switch (type) {
+      case JSVAL_TYPE_DOUBLE:
+        return MIRType_Double;
+      case JSVAL_TYPE_INT32:
+        return MIRType_Int32;
+      case JSVAL_TYPE_UNDEFINED:
+        return MIRType_Undefined;
+      case JSVAL_TYPE_STRING:
+        return MIRType_String;
+      case JSVAL_TYPE_BOOLEAN:
+        return MIRType_Boolean;
+      case JSVAL_TYPE_NULL:
+        return MIRType_Null;
+      case JSVAL_TYPE_OBJECT:
+        return MIRType_Object;
+      case JSVAL_TYPE_MAGIC:
+        return MIRType_Magic;
+      case JSVAL_TYPE_UNKNOWN:
+        return MIRType_Value;
+      default:
+        JS_NOT_REACHED("unexpected jsval type");
+        return MIRType_None;
+    }
+}
+
+static inline JSValueType
+ValueTypeFromMIRType(MIRType type)
+{
+  switch (type) {
+    case MIRType_Undefined:
+      return JSVAL_TYPE_UNDEFINED;
+    case MIRType_Null:
+      return JSVAL_TYPE_NULL;
+    case MIRType_Boolean:
+      return JSVAL_TYPE_BOOLEAN;
+    case MIRType_Int32:
+      return JSVAL_TYPE_INT32;
+    case MIRType_Double:
+      return JSVAL_TYPE_DOUBLE;
+    case MIRType_String:
+      return JSVAL_TYPE_STRING;
+    case MIRType_Magic:
+      return JSVAL_TYPE_MAGIC;
+    default:
+      JS_ASSERT(type == MIRType_Object);
+      return JSVAL_TYPE_OBJECT;
+  }
+}
+
+static inline JSValueTag
+MIRTypeToTag(MIRType type)
+{
+    return JSVAL_TYPE_TO_TAG(ValueTypeFromMIRType(type));
+}
+
+static inline const char *
+StringFromMIRType(MIRType type)
+{
+  switch (type) {
+    case MIRType_Undefined:
+      return "Undefined";
+    case MIRType_Null:
+      return "Null";
+    case MIRType_Boolean:
+      return "Bool";
+    case MIRType_Int32:
+      return "Int32";
+    case MIRType_Double:
+      return "Double";
+    case MIRType_String:
+      return "String";
+    case MIRType_Object:
+      return "Object";
+    case MIRType_Magic:
+      return "Magic";
+    case MIRType_Value:
+      return "Value";
+    case MIRType_None:
+      return "None";
+    case MIRType_Slots:
+      return "Slots";
+    case MIRType_Elements:
+      return "Elements";
+    case MIRType_Pointer:
+      return "Pointer";
+    case MIRType_ForkJoinSlice:
+      return "ForkJoinSlice";
+    default:
+      JS_NOT_REACHED("Unknown MIRType.");
+      return "";
+  }
+}
+
+static inline bool
+IsNumberType(MIRType type)
+{
+    return type == MIRType_Int32 || type == MIRType_Double;
+}
+
+static inline bool
+IsNullOrUndefined(MIRType type)
+{
+    return type == MIRType_Null || type == MIRType_Undefined;
+}
+
 #ifdef DEBUG
 // Track the pipeline of opcodes which has produced a snapshot.
 #define TRACK_SNAPSHOTS 1
 #endif
 
 } // namespace ion
 } // namespace js
 
--- a/js/src/ion/JSONSpewer.cpp
+++ b/js/src/ion/JSONSpewer.cpp
@@ -4,17 +4,16 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <stdarg.h>
 
 #include "JSONSpewer.h"
 #include "LIR.h"
-#include "TypeOracle.h"
 #include "MIR.h"
 #include "MIRGraph.h"
 #include "LinearScan.h"
 #include "RangeAnalysis.h"
 using namespace js;
 using namespace js::ion;
 
 JSONSpewer::~JSONSpewer()
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -3591,16 +3591,29 @@ class LCallSetElement : public LCallInst
   public:
     LIR_HEADER(CallSetElement)
     BOX_OUTPUT_ACCESSORS()
 
     static const size_t Index = 1;
     static const size_t Value = 1 + BOX_PIECES;
 };
 
+// Call js::InitElementArray.
+class LCallInitElementArray : public LCallInstructionHelper<0, 1 + BOX_PIECES, 0>
+{
+public:
+    LIR_HEADER(CallInitElementArray)
+
+    static const size_t Value = 1;
+
+    const MCallInitElementArray *mir() const {
+        return mir_->toCallInitElementArray();
+    }
+};
+
 // Call a VM function to perform a property or name assignment of a generic value.
 class LCallSetProperty : public LCallInstructionHelper<0, 1 + BOX_PIECES, 0>
 {
   public:
     LIR_HEADER(CallSetProperty)
 
     LCallSetProperty(const LAllocation &obj) {
         setOperand(0, obj);
--- a/js/src/ion/LIR.h
+++ b/js/src/ion/LIR.h
@@ -11,17 +11,16 @@
 // This file declares the core data structures for LIR: storage allocations for
 // inputs and outputs, as well as the interface instructions must conform to.
 
 #include "jscntxt.h"
 #include "IonAllocPolicy.h"
 #include "InlineList.h"
 #include "FixedArityList.h"
 #include "LOpcodes.h"
-#include "TypeOracle.h"
 #include "Registers.h"
 #include "MIR.h"
 #include "MIRGraph.h"
 #include "shared/Assembler-shared.h"
 #include "Safepoints.h"
 #include "Bailouts.h"
 #include "VMFunctions.h"
 
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -173,16 +173,17 @@
     _(GetElementCacheT)             \
     _(BindNameCache)                \
     _(CallGetProperty)              \
     _(GetNameCache)                 \
     _(CallGetIntrinsicValue)        \
     _(CallsiteCloneCache)           \
     _(CallGetElement)               \
     _(CallSetElement)               \
+    _(CallInitElementArray)         \
     _(CallSetProperty)              \
     _(CallDeleteProperty)           \
     _(SetPropertyCacheV)            \
     _(SetPropertyCacheT)            \
     _(CallIteratorStart)            \
     _(IteratorStart)                \
     _(IteratorNext)                 \
     _(IteratorMore)                 \
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -2285,16 +2285,26 @@ LIRGenerator::visitCallSetElement(MCallS
     if (!useBoxAtStart(lir, LCallSetElement::Index, ins->index()))
         return false;
     if (!useBoxAtStart(lir, LCallSetElement::Value, ins->value()))
         return false;
     return add(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
+LIRGenerator::visitCallInitElementArray(MCallInitElementArray *ins)
+{
+    LCallInitElementArray *lir = new LCallInitElementArray();
+    lir->setOperand(0, useRegisterAtStart(ins->object()));
+    if (!useBoxAtStart(lir, LCallInitElementArray::Value, ins->value()))
+        return false;
+    return add(lir, ins) && assignSafepoint(lir, ins);
+}
+
+bool
 LIRGenerator::visitIteratorStart(MIteratorStart *ins)
 {
     // Call a stub if this is not a simple for-in loop.
     if (ins->flags() != JSITER_ENUMERATE) {
         LCallIteratorStart *lir = new LCallIteratorStart(useRegisterAtStart(ins->object()));
         return defineReturn(lir, ins) && assignSafepoint(lir, ins);
     }
 
--- a/js/src/ion/Lowering.h
+++ b/js/src/ion/Lowering.h
@@ -197,16 +197,17 @@ class LIRGenerator : public LIRGenerator
     bool visitGuardString(MGuardString *ins);
     bool visitCallGetProperty(MCallGetProperty *ins);
     bool visitDeleteProperty(MDeleteProperty *ins);
     bool visitGetNameCache(MGetNameCache *ins);
     bool visitCallGetIntrinsicValue(MCallGetIntrinsicValue *ins);
     bool visitCallsiteCloneCache(MCallsiteCloneCache *ins);
     bool visitCallGetElement(MCallGetElement *ins);
     bool visitCallSetElement(MCallSetElement *ins);
+    bool visitCallInitElementArray(MCallInitElementArray *ins);
     bool visitSetPropertyCache(MSetPropertyCache *ins);
     bool visitCallSetProperty(MCallSetProperty *ins);
     bool visitIteratorStart(MIteratorStart *ins);
     bool visitIteratorNext(MIteratorNext *ins);
     bool visitIteratorMore(MIteratorMore *ins);
     bool visitIteratorEnd(MIteratorEnd *ins);
     bool visitStringLength(MStringLength *ins);
     bool visitArgumentsLength(MArgumentsLength *ins);
--- a/js/src/ion/MCallOptimize.cpp
+++ b/js/src/ion/MCallOptimize.cpp
@@ -125,71 +125,38 @@ IonBuilder::inlineNativeCall(CallInfo &c
 #endif
 
     return InliningStatus_NotInlined;
 }
 
 types::StackTypeSet *
 IonBuilder::getInlineReturnTypeSet()
 {
-    types::StackTypeSet *barrier;
-    types::StackTypeSet *returnTypes = oracle->returnTypeSet(script(), pc, &barrier);
-    JS_ASSERT(returnTypes);
-    return returnTypes;
+    return script()->analysis()->bytecodeTypes(pc);
 }
 
 MIRType
 IonBuilder::getInlineReturnType()
 {
     types::StackTypeSet *returnTypes = getInlineReturnTypeSet();
     return MIRTypeFromValueType(returnTypes->getKnownTypeTag());
 }
 
-types::StackTypeSet *
-IonBuilder::getInlineThisTypeSet(CallInfo &callInfo)
-{
-    types::StackTypeSet *thisTypes = oracle->getCallArg(script(), callInfo.argc(), 0, pc);
-    JS_ASSERT(thisTypes);
-    return thisTypes;
-}
-
-MIRType
-IonBuilder::getInlineThisType(CallInfo &callInfo)
-{
-    types::StackTypeSet *argTypes = getInlineThisTypeSet(callInfo);
-    return MIRTypeFromValueType(argTypes->getKnownTypeTag());
-}
-
-types::StackTypeSet *
-IonBuilder::getInlineArgTypeSet(CallInfo &callInfo, uint32_t arg)
-{
-    types::StackTypeSet *argTypes = oracle->getCallArg(script(), callInfo.argc(), arg + 1, pc);
-    JS_ASSERT(argTypes);
-    return argTypes;
-}
-
-MIRType
-IonBuilder::getInlineArgType(CallInfo &callInfo, uint32_t arg)
-{
-    types::StackTypeSet *argTypes = getInlineArgTypeSet(callInfo, arg);
-    return MIRTypeFromValueType(argTypes->getKnownTypeTag());
-}
-
 IonBuilder::InliningStatus
 IonBuilder::inlineMathFunction(CallInfo &callInfo, MMathFunction::Function function)
 {
     if (callInfo.constructing())
         return InliningStatus_NotInlined;
 
     if (callInfo.argc() != 1)
         return InliningStatus_NotInlined;
 
     if (getInlineReturnType() != MIRType_Double)
         return InliningStatus_NotInlined;
-    if (!IsNumberType(getInlineArgType(callInfo, 0)))
+    if (!IsNumberType(callInfo.getArg(0)->type()))
         return InliningStatus_NotInlined;
 
     callInfo.unwrapArgs();
 
     MathCache *cache = cx->runtime->getMathCache(cx);
     if (!cache)
         return InliningStatus_Error;
 
@@ -204,21 +171,38 @@ IonBuilder::inlineArray(CallInfo &callIn
 {
     uint32_t initLength = 0;
     MNewArray::AllocatingBehaviour allocating = MNewArray::NewArray_Unallocating;
 
     // Multiple arguments imply array initialization, not just construction.
     if (callInfo.argc() >= 2) {
         initLength = callInfo.argc();
         allocating = MNewArray::NewArray_Allocating;
+
+        types::TypeObject *type = types::TypeScript::InitObject(cx, script(), pc, JSProto_Array);
+        if (!type)
+            return InliningStatus_Error;
+        if (!type->unknownProperties()) {
+            types::HeapTypeSet *elemTypes = type->getProperty(cx, JSID_VOID, false);
+            if (!elemTypes)
+                return InliningStatus_Error;
+
+            for (uint32_t i = 0; i < initLength; i++) {
+                MDefinition *value = callInfo.getArg(i);
+                if (!TypeSetIncludes(elemTypes, value->type(), value->resultTypeSet())) {
+                    elemTypes->addFreeze(cx);
+                    return InliningStatus_NotInlined;
+                }
+            }
+        }
     }
 
     // A single integer argument denotes initial length.
     if (callInfo.argc() == 1) {
-        if (getInlineArgType(callInfo, 0) != MIRType_Int32)
+        if (callInfo.getArg(0)->type() != MIRType_Int32)
             return InliningStatus_NotInlined;
         MDefinition *arg = callInfo.getArg(0)->toPassArg()->getArgument();
         if (!arg->isConstant())
             return InliningStatus_NotInlined;
 
         // Negative lengths generate a RangeError, unhandled by the inline path.
         initLength = arg->toConstant()->value().toInt32();
         if (initLength >= JSObject::NELEMENTS_LIMIT)
@@ -226,18 +210,19 @@ IonBuilder::inlineArray(CallInfo &callIn
     }
 
     callInfo.unwrapArgs();
 
     JSObject *templateObject = getNewArrayTemplateObject(initLength);
     if (!templateObject)
         return InliningStatus_Error;
 
-    bool convertDoubles = oracle->arrayResultShouldHaveDoubleConversion(script(), pc);
-    if (convertDoubles)
+    types::StackTypeSet::DoubleConversion conversion =
+        getInlineReturnTypeSet()->convertDoubleElements(cx);
+    if (conversion == types::StackTypeSet::AlwaysConvertToDoubles)
         templateObject->setShouldConvertDoubleElements();
 
     MNewArray *ins = new MNewArray(initLength, templateObject, allocating);
     current->add(ins);
     current->push(ins);
 
     if (callInfo.argc() >= 2) {
         // Get the elements vector.
@@ -248,17 +233,17 @@ IonBuilder::inlineArray(CallInfo &callIn
         // jsop_initelem_array is doing because we do not expect to bailout
         // because the memory is supposed to be allocated by now.
         MConstant *id = NULL;
         for (uint32_t i = 0; i < initLength; i++) {
             id = MConstant::New(Int32Value(i));
             current->add(id);
 
             MDefinition *value = callInfo.getArg(i);
-            if (convertDoubles) {
+            if (conversion == types::StackTypeSet::AlwaysConvertToDoubles) {
                 MInstruction *valueDouble = MToDouble::New(value);
                 current->add(valueDouble);
                 value = valueDouble;
             }
 
             MStoreElement *store = MStoreElement::New(elements, id, value,
                                                       /* needsHoleCheck = */ false);
             current->add(store);
@@ -279,88 +264,95 @@ IonBuilder::InliningStatus
 IonBuilder::inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode)
 {
     if (callInfo.constructing())
         return InliningStatus_NotInlined;
 
     MIRType returnType = getInlineReturnType();
     if (returnType == MIRType_Undefined || returnType == MIRType_Null)
         return InliningStatus_NotInlined;
-    if (getInlineThisType(callInfo) != MIRType_Object)
+    if (callInfo.thisArg()->type() != MIRType_Object)
         return InliningStatus_NotInlined;
 
     // Pop and shift are only handled for dense arrays that have never been
     // used in an iterator: popping elements does not account for suppressing
     // deleted properties in active iterators.
-    //
-    // Inference's TypeConstraintCall generates the constraints that propagate
-    // properties directly into the result type set.
     types::TypeObjectFlags unhandledFlags =
         types::OBJECT_FLAG_SPARSE_INDEXES |
         types::OBJECT_FLAG_LENGTH_OVERFLOW |
         types::OBJECT_FLAG_ITERATED;
 
-    types::StackTypeSet *thisTypes = getInlineThisTypeSet(callInfo);
-    if (thisTypes->getKnownClass() != &ArrayClass)
+    types::StackTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet();
+    if (!thisTypes || thisTypes->getKnownClass() != &ArrayClass)
         return InliningStatus_NotInlined;
     if (thisTypes->hasObjectFlags(cx, unhandledFlags))
         return InliningStatus_NotInlined;
     RootedScript script(cx, script_);
     if (types::ArrayPrototypeHasIndexedProperty(cx, script))
         return InliningStatus_NotInlined;
 
     callInfo.unwrapArgs();
 
     types::StackTypeSet *returnTypes = getInlineReturnTypeSet();
     bool needsHoleCheck = thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED);
     bool maybeUndefined = returnTypes->hasType(types::Type::UndefinedType());
 
+    bool barrier = propertyReadNeedsTypeBarrier(callInfo.thisArg(), NULL, returnTypes);
+    if (barrier)
+        returnType = MIRType_Value;
+
     MArrayPopShift *ins = MArrayPopShift::New(callInfo.thisArg(), mode,
                                               needsHoleCheck, maybeUndefined);
     current->add(ins);
     current->push(ins);
     ins->setResultType(returnType);
 
     if (!resumeAfter(ins))
         return InliningStatus_Error;
+
+    if (!pushTypeBarrier(ins, returnTypes, barrier))
+        return InliningStatus_Error;
+
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineArrayPush(CallInfo &callInfo)
 {
     if (callInfo.argc() != 1 || callInfo.constructing())
         return InliningStatus_NotInlined;
 
-    if (getInlineReturnType() != MIRType_Int32)
-        return InliningStatus_NotInlined;
-    if (getInlineThisType(callInfo) != MIRType_Object)
+    MDefinition *value = callInfo.getArg(0);
+    if (propertyWriteNeedsTypeBarrier(callInfo.thisArg(), NULL, &value) || value != callInfo.getArg(0))
         return InliningStatus_NotInlined;
 
-    // Inference's TypeConstraintCall generates the constraints that propagate
-    // properties directly into the result type set.
-    types::StackTypeSet *thisTypes = getInlineThisTypeSet(callInfo);
-    if (thisTypes->getKnownClass() != &ArrayClass)
+    if (getInlineReturnType() != MIRType_Int32)
+        return InliningStatus_NotInlined;
+    if (callInfo.thisArg()->type() != MIRType_Object)
+        return InliningStatus_NotInlined;
+
+    types::StackTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet();
+    if (!thisTypes || thisTypes->getKnownClass() != &ArrayClass)
         return InliningStatus_NotInlined;
     if (thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPARSE_INDEXES |
                                   types::OBJECT_FLAG_LENGTH_OVERFLOW))
     {
         return InliningStatus_NotInlined;
     }
     RootedScript script(cx, script_);
     if (types::ArrayPrototypeHasIndexedProperty(cx, script))
         return InliningStatus_NotInlined;
 
     types::StackTypeSet::DoubleConversion conversion = thisTypes->convertDoubleElements(cx);
     if (conversion == types::StackTypeSet::AmbiguousDoubleConversion)
         return InliningStatus_NotInlined;
 
     callInfo.unwrapArgs();
+    value = callInfo.getArg(0);
 
-    MDefinition *value = callInfo.getArg(0);
     if (conversion == types::StackTypeSet::AlwaysConvertToDoubles ||
         conversion == types::StackTypeSet::MaybeConvertToDoubles)
     {
         MInstruction *valueDouble = MToDouble::New(value);
         current->add(valueDouble);
         value = valueDouble;
     }
 
@@ -377,24 +369,26 @@ IonBuilder::InliningStatus
 IonBuilder::inlineArrayConcat(CallInfo &callInfo)
 {
     if (callInfo.argc() != 1 || callInfo.constructing())
         return InliningStatus_NotInlined;
 
     // Ensure |this|, argument and result are objects.
     if (getInlineReturnType() != MIRType_Object)
         return InliningStatus_NotInlined;
-    if (getInlineThisType(callInfo) != MIRType_Object)
+    if (callInfo.thisArg()->type() != MIRType_Object)
         return InliningStatus_NotInlined;
-    if (getInlineArgType(callInfo, 0) != MIRType_Object)
+    if (callInfo.getArg(0)->type() != MIRType_Object)
         return InliningStatus_NotInlined;
 
     // |this| and the argument must be dense arrays.
-    types::StackTypeSet *thisTypes = getInlineThisTypeSet(callInfo);
-    types::StackTypeSet *argTypes = getInlineArgTypeSet(callInfo, 0);
+    types::StackTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet();
+    types::StackTypeSet *argTypes = callInfo.getArg(0)->resultTypeSet();
+    if (!thisTypes || !argTypes)
+        return InliningStatus_NotInlined;
 
     if (thisTypes->getKnownClass() != &ArrayClass)
         return InliningStatus_NotInlined;
     if (thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPARSE_INDEXES |
                                   types::OBJECT_FLAG_LENGTH_OVERFLOW))
     {
         return InliningStatus_NotInlined;
     }
@@ -413,18 +407,22 @@ IonBuilder::inlineArrayConcat(CallInfo &
         return InliningStatus_NotInlined;
 
     // Require the 'this' types to have a specific type matching the current
     // global, so we can create the result object inline.
     if (thisTypes->getObjectCount() != 1)
         return InliningStatus_NotInlined;
 
     types::TypeObject *thisType = thisTypes->getTypeObject(0);
-    if (!thisType || &thisType->proto->global() != &script->global())
+    if (!thisType ||
+        thisType->unknownProperties() ||
+        &thisType->proto->global() != &script->global())
+    {
         return InliningStatus_NotInlined;
+    }
 
     // Constraints modeling this concat have not been generated by inference,
     // so check that type information already reflects possible side effects of
     // this call.
     types::HeapTypeSet *thisElemTypes = thisType->getProperty(cx, JSID_VOID, false);
     if (!thisElemTypes)
         return InliningStatus_Error;
 
@@ -435,16 +433,19 @@ IonBuilder::inlineArrayConcat(CallInfo &
     for (unsigned i = 0; i < argTypes->getObjectCount(); i++) {
         if (argTypes->getSingleObject(i))
             return InliningStatus_NotInlined;
 
         types::TypeObject *argType = argTypes->getTypeObject(i);
         if (!argType)
             continue;
 
+        if (argType->unknownProperties())
+            return InliningStatus_NotInlined;
+
         types::HeapTypeSet *elemTypes = argType->getProperty(cx, JSID_VOID, false);
         if (!elemTypes)
             return InliningStatus_Error;
 
         if (!elemTypes->knownSubset(cx, thisElemTypes))
             return InliningStatus_NotInlined;
     }
 
@@ -470,17 +471,17 @@ IonBuilder::inlineMathAbs(CallInfo &call
 {
     if (callInfo.constructing())
         return InliningStatus_NotInlined;
 
     if (callInfo.argc() != 1)
         return InliningStatus_NotInlined;
 
     MIRType returnType = getInlineReturnType();
-    MIRType argType = getInlineArgType(callInfo, 0);
+    MIRType argType = callInfo.getArg(0)->type();
     if (argType != MIRType_Int32 && argType != MIRType_Double)
         return InliningStatus_NotInlined;
 
     if (argType != returnType && returnType != MIRType_Int32)
         return InliningStatus_NotInlined;
 
     callInfo.unwrapArgs();
 
@@ -503,17 +504,17 @@ IonBuilder::inlineMathFloor(CallInfo &ca
 {
 
     if (callInfo.constructing())
         return InliningStatus_NotInlined;
 
     if (callInfo.argc() != 1)
         return InliningStatus_NotInlined;
 
-    MIRType argType = getInlineArgType(callInfo, 0);
+    MIRType argType = callInfo.getArg(0)->type();
     if (getInlineReturnType() != MIRType_Int32)
         return InliningStatus_NotInlined;
 
     // Math.floor(int(x)) == int(x)
     if (argType == MIRType_Int32) {
         callInfo.unwrapArgs();
         current->push(callInfo.getArg(0));
         return InliningStatus_Inlined;
@@ -535,17 +536,17 @@ IonBuilder::inlineMathRound(CallInfo &ca
 {
     if (callInfo.constructing())
         return InliningStatus_NotInlined;
 
     if (callInfo.argc() != 1)
         return InliningStatus_NotInlined;
 
     MIRType returnType = getInlineReturnType();
-    MIRType argType = getInlineArgType(callInfo, 0);
+    MIRType argType = callInfo.getArg(0)->type();
 
     // Math.round(int(x)) == int(x)
     if (argType == MIRType_Int32 && returnType == MIRType_Int32) {
         callInfo.unwrapArgs();
         current->push(callInfo.getArg(0));
         return InliningStatus_Inlined;
     }
 
@@ -564,17 +565,17 @@ IonBuilder::InliningStatus
 IonBuilder::inlineMathSqrt(CallInfo &callInfo)
 {
     if (callInfo.constructing())
         return InliningStatus_NotInlined;
 
     if (callInfo.argc() != 1)
         return InliningStatus_NotInlined;
 
-    MIRType argType = getInlineArgType(callInfo, 0);
+    MIRType argType = callInfo.getArg(0)->type();
     if (getInlineReturnType() != MIRType_Double)
         return InliningStatus_NotInlined;
     if (argType != MIRType_Double && argType != MIRType_Int32)
         return InliningStatus_NotInlined;
 
     callInfo.unwrapArgs();
 
     MSqrt *sqrt = MSqrt::New(callInfo.getArg(0));
@@ -588,18 +589,18 @@ IonBuilder::inlineMathPow(CallInfo &call
 {
     if (callInfo.constructing())
         return InliningStatus_NotInlined;
 
     if (callInfo.argc() != 2)
         return InliningStatus_NotInlined;
 
     // Typechecking.
-    MIRType baseType = getInlineArgType(callInfo, 0);
-    MIRType powerType = getInlineArgType(callInfo, 1);
+    MIRType baseType = callInfo.getArg(0)->type();
+    MIRType powerType = callInfo.getArg(1)->type();
     MIRType outputType = getInlineReturnType();
 
     if (outputType != MIRType_Int32 && outputType != MIRType_Double)
         return InliningStatus_NotInlined;
     if (baseType != MIRType_Int32 && baseType != MIRType_Double)
         return InliningStatus_NotInlined;
     if (powerType != MIRType_Int32 && powerType != MIRType_Double)
         return InliningStatus_NotInlined;
@@ -709,19 +710,19 @@ IonBuilder::inlineMathImul(CallInfo &cal
 {
     if (callInfo.argc() != 2 || callInfo.constructing())
         return InliningStatus_NotInlined;
 
     MIRType returnType = getInlineReturnType();
     if (returnType != MIRType_Int32)
         return InliningStatus_NotInlined;
 
-    if (!IsNumberType(getInlineArgType(callInfo, 0)))
+    if (!IsNumberType(callInfo.getArg(0)->type()))
         return InliningStatus_NotInlined;
-    if (!IsNumberType(getInlineArgType(callInfo, 1)))
+    if (!IsNumberType(callInfo.getArg(1)->type()))
         return InliningStatus_NotInlined;
 
     callInfo.unwrapArgs();
 
     MInstruction *first = MTruncateToInt32::New(callInfo.getArg(0));
     current->add(first);
 
     MInstruction *second = MTruncateToInt32::New(callInfo.getArg(1));
@@ -738,20 +739,20 @@ IonBuilder::inlineMathMinMax(CallInfo &c
 {
     if (callInfo.argc() != 2 || callInfo.constructing())
         return InliningStatus_NotInlined;
 
     MIRType returnType = getInlineReturnType();
     if (!IsNumberType(returnType))
         return InliningStatus_NotInlined;
 
-    MIRType arg0Type = getInlineArgType(callInfo, 0);
+    MIRType arg0Type = callInfo.getArg(0)->type();
     if (!IsNumberType(arg0Type))
         return InliningStatus_NotInlined;
-    MIRType arg1Type = getInlineArgType(callInfo, 1);
+    MIRType arg1Type = callInfo.getArg(1)->type();
     if (!IsNumberType(arg1Type))
         return InliningStatus_NotInlined;
 
     if (returnType == MIRType_Int32 &&
         (arg0Type == MIRType_Double || arg1Type == MIRType_Double))
     {
         // We would need to inform TI, if we happen to return a double.
         return InliningStatus_NotInlined;
@@ -767,17 +768,17 @@ IonBuilder::inlineMathMinMax(CallInfo &c
 
 IonBuilder::InliningStatus
 IonBuilder::inlineStringObject(CallInfo &callInfo)
 {
     if (callInfo.argc() != 1 || !callInfo.constructing())
         return InliningStatus_NotInlined;
 
     // MToString only supports int32 or string values.
-    MIRType type = getInlineArgType(callInfo, 0);
+    MIRType type = callInfo.getArg(0)->type();
     if (type != MIRType_Int32 && type != MIRType_String)
         return InliningStatus_NotInlined;
 
     callInfo.unwrapArgs();
 
     RootedString emptyString(cx, cx->runtime->emptyString);
     RootedObject templateObj(cx, StringObject::create(cx, emptyString));
     if (!templateObj)
@@ -796,19 +797,19 @@ IonBuilder::inlineStringObject(CallInfo 
 IonBuilder::InliningStatus
 IonBuilder::inlineStrCharCodeAt(CallInfo &callInfo)
 {
     if (callInfo.argc() != 1 || callInfo.constructing())
         return InliningStatus_NotInlined;
 
     if (getInlineReturnType() != MIRType_Int32)
         return InliningStatus_NotInlined;
-    if (getInlineThisType(callInfo) != MIRType_String)
+    if (callInfo.thisArg()->type() != MIRType_String && callInfo.thisArg()->type() != MIRType_Value)
         return InliningStatus_NotInlined;
-    MIRType argType = getInlineArgType(callInfo, 0);
+    MIRType argType = callInfo.getArg(0)->type();
     if (argType != MIRType_Int32 && argType != MIRType_Double)
         return InliningStatus_NotInlined;
 
     callInfo.unwrapArgs();
 
     MInstruction *index = MToInt32::New(callInfo.getArg(0));
     current->add(index);
 
@@ -826,17 +827,17 @@ IonBuilder::inlineStrCharCodeAt(CallInfo
 IonBuilder::InliningStatus
 IonBuilder::inlineStrFromCharCode(CallInfo &callInfo)
 {
     if (callInfo.argc() != 1 || callInfo.constructing())
         return InliningStatus_NotInlined;
 
     if (getInlineReturnType() != MIRType_String)
         return InliningStatus_NotInlined;
-    if (getInlineArgType(callInfo, 0) != MIRType_Int32)
+    if (callInfo.getArg(0)->type() != MIRType_Int32)
         return InliningStatus_NotInlined;
 
     callInfo.unwrapArgs();
 
     MToInt32 *charCode = MToInt32::New(callInfo.getArg(0));
     current->add(charCode);
 
     MFromCharCode *string = MFromCharCode::New(charCode);
@@ -848,19 +849,19 @@ IonBuilder::inlineStrFromCharCode(CallIn
 IonBuilder::InliningStatus
 IonBuilder::inlineStrCharAt(CallInfo &callInfo)
 {
     if (callInfo.argc() != 1 || callInfo.constructing())
         return InliningStatus_NotInlined;
 
     if (getInlineReturnType() != MIRType_String)
         return InliningStatus_NotInlined;
-    if (getInlineThisType(callInfo) != MIRType_String)
+    if (callInfo.thisArg()->type() != MIRType_String)
         return InliningStatus_NotInlined;
-    MIRType argType = getInlineArgType(callInfo, 0);
+    MIRType argType = callInfo.getArg(0)->type();
     if (argType != MIRType_Int32 && argType != MIRType_Double)
         return InliningStatus_NotInlined;
 
     callInfo.unwrapArgs();
 
     MInstruction *index = MToInt32::New(callInfo.getArg(0));
     current->add(index);
 
@@ -884,22 +885,23 @@ IonBuilder::inlineRegExpTest(CallInfo &c
 {
     if (callInfo.argc() != 1 || callInfo.constructing())
         return InliningStatus_NotInlined;
 
     // TI can infer a NULL return type of regexp_test with eager compilation.
     if (CallResultEscapes(pc) && getInlineReturnType() != MIRType_Boolean)
         return InliningStatus_NotInlined;
 
-    if (getInlineThisType(callInfo) != MIRType_Object)
+    if (callInfo.thisArg()->type() != MIRType_Object)
         return InliningStatus_NotInlined;
-    Class *clasp = getInlineThisTypeSet(callInfo)->getKnownClass();
+    types::StackTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet();
+    Class *clasp = thisTypes ? thisTypes->getKnownClass() : NULL;
     if (clasp != &RegExpClass)
         return InliningStatus_NotInlined;
-    if (getInlineArgType(callInfo, 0) != MIRType_String)
+    if (callInfo.getArg(0)->type() != MIRType_String)
         return InliningStatus_NotInlined;
 
     callInfo.unwrapArgs();
 
     MInstruction *match = MRegExpTest::New(callInfo.thisArg(), callInfo.getArg(0));
     current->add(match);
     current->push(match);
     if (!resumeAfter(match))
@@ -921,23 +923,34 @@ IonBuilder::inlineUnsafeSetElement(CallI
      * %UnsafeSetElement().  It is essential that these stores occur
      * atomically and cannot be interrupted by a stack or recursion
      * check.  If this is not true, race conditions can occur.
      */
 
     for (uint32_t base = 0; base < argc; base += 3) {
         uint32_t arri = base + 0;
         uint32_t idxi = base + 1;
+        uint32_t elemi = base + 2;
 
-        types::StackTypeSet *obj = getInlineArgTypeSet(callInfo, arri);
-        types::StackTypeSet *id = getInlineArgTypeSet(callInfo, idxi);
+        MDefinition *obj = callInfo.getArg(arri);
+        MDefinition *id = callInfo.getArg(idxi);
+        MDefinition *elem = callInfo.getArg(elemi);
+
+        if (propertyWriteNeedsTypeBarrier(obj, NULL, &elem))
+            return InliningStatus_NotInlined;
 
         int arrayType;
-        if (!oracle->elementWriteIsDenseNative(obj, id) &&
-            !oracle->elementWriteIsTypedArray(obj, id, &arrayType))
+        if (!elementAccessIsDenseNative(obj, id) &&
+            !elementAccessIsTypedArray(obj, id, &arrayType))
+        {
+            return InliningStatus_NotInlined;
+        }
+
+        if (obj->resultTypeSet()->convertDoubleElements(cx) !=
+            types::StackTypeSet::DontConvertToDoubles)
         {
             return InliningStatus_NotInlined;
         }
     }
 
     callInfo.unwrapArgs();
 
     // Push the result first so that the stack depth matches up for
@@ -945,27 +958,27 @@ IonBuilder::inlineUnsafeSetElement(CallI
     MConstant *udef = MConstant::New(UndefinedValue());
     current->add(udef);
     current->push(udef);
 
     for (uint32_t base = 0; base < argc; base += 3) {
         uint32_t arri = base + 0;
         uint32_t idxi = base + 1;
 
-        types::StackTypeSet *obj = getInlineArgTypeSet(callInfo, arri);
-        types::StackTypeSet *id = getInlineArgTypeSet(callInfo, idxi);
+        MDefinition *obj = callInfo.getArg(arri);
+        MDefinition *id = callInfo.getArg(idxi);
 
-        if (oracle->elementWriteIsDenseNative(obj, id)) {
+        if (elementAccessIsDenseNative(obj, id)) {
             if (!inlineUnsafeSetDenseArrayElement(callInfo, base))
                 return InliningStatus_Error;
             continue;
         }
 
         int arrayType;
-        if (oracle->elementWriteIsTypedArray(obj, id, &arrayType)) {
+        if (elementAccessIsTypedArray(obj, id, &arrayType)) {
             if (!inlineUnsafeSetTypedArrayElement(callInfo, base, arrayType))
                 return InliningStatus_Error;
             continue;
         }
 
         JS_NOT_REACHED("Element access not dense array nor typed array");
     }
 
@@ -974,18 +987,18 @@ IonBuilder::inlineUnsafeSetElement(CallI
 
 bool
 IonBuilder::inlineUnsafeSetDenseArrayElement(CallInfo &callInfo, uint32_t base)
 {
     // Note: we do not check the conditions that are asserted as true
     // in intrinsic_UnsafeSetElement():
     // - arr is a dense array
     // - idx < initialized length
-    // Furthermore, note that inference should be propagating
-    // the type of the value to the JSID_VOID property of the array.
+    // Furthermore, note that inlineUnsafeSetElement ensures the type of the
+    // value is reflected in the JSID_VOID property of the array.
 
     uint32_t arri = base + 0;
     uint32_t idxi = base + 1;
     uint32_t elemi = base + 2;
 
     MElements *elements = MElements::New(callInfo.getArg(arri));
     current->add(elements);
 
@@ -996,17 +1009,17 @@ IonBuilder::inlineUnsafeSetDenseArrayEle
     // there were setters on the prototype, they would not be invoked.
     // But this is actually the desired behavior.
 
     MStoreElement *store = MStoreElement::New(elements, id,
                                               callInfo.getArg(elemi),
                                               /* needsHoleCheck = */ false);
     store->setRacy();
 
-    if (oracle->elementWriteNeedsBarrier(getInlineArgTypeSet(callInfo, arri)))
+    if (callInfo.getArg(arri)->resultTypeSet()->propertyNeedsBarrier(cx, JSID_VOID))
         store->setNeedsBarrier();
 
     current->add(store);
 
     if (!resumeAfter(store))
         return false;
 
     return true;
@@ -1087,18 +1100,18 @@ IonBuilder::inlineNewParallelArray(CallI
     //
     //    x = MNewParallelArray()
     //    ParallelArrayView(x, arg0, ..., argN)
 
     uint32_t argc = callInfo.argc();
     if (argc < 1 || callInfo.constructing())
         return InliningStatus_NotInlined;
 
-    types::StackTypeSet *ctorTypes = getInlineArgTypeSet(callInfo, 0);
-    RawObject targetObj = ctorTypes->getSingleton();
+    types::StackTypeSet *ctorTypes = callInfo.getArg(0)->resultTypeSet();
+    RawObject targetObj = ctorTypes ? ctorTypes->getSingleton() : NULL;
     RootedFunction target(cx);
     if (targetObj && targetObj->isFunction())
         target = targetObj->toFunction();
     if (target && target->isInterpreted() && target->nonLazyScript()->shouldCloneAtCallsite) {
         RootedScript scriptRoot(cx, script());
         target = CloneFunctionAtCallsite(cx, target, scriptRoot, pc);
         if (!target)
             return InliningStatus_Error;
@@ -1158,17 +1171,17 @@ IonBuilder::inlineParallelArrayTail(Call
         return InliningStatus_NotInlined;
     types::TypeObject *typeObject = returnTypes->getTypeObject(0);
 
     // Create the call and add in the non-this arguments.
     uint32_t targetArgs = argc;
     if (target && !target->isNative())
         targetArgs = Max<uint32_t>(target->nargs, argc);
 
-    MCall *call = MCall::New(target, targetArgs + 1, argc, false, ctorTypes);
+    MCall *call = MCall::New(target, targetArgs + 1, argc, false);
     if (!call)
         return InliningStatus_Error;
 
     // Save the script for inspection by visitCallKnown().
     if (target && target->isInterpreted()) {
         if (!target->getOrCreateScript(cx))
             return InliningStatus_Error;
         call->rootTargetScript(target);
@@ -1302,17 +1315,17 @@ IonBuilder::inlineThrowError(CallInfo &c
 
     callInfo.unwrapArgs();
 
     MParBailout *bailout = new MParBailout();
     if (!bailout)
         return InliningStatus_Error;
     current->end(bailout);
 
-    current = newBlock(pc);
+    setCurrent(newBlock(pc));
     if (!current)
         return InliningStatus_Error;
 
     MConstant *udef = MConstant::New(UndefinedValue());
     current->add(udef);
     current->push(udef);
 
     return InliningStatus_Inlined;
--- a/js/src/ion/MIR.cpp
+++ b/js/src/ion/MIR.cpp
@@ -178,32 +178,36 @@ MDefinition::analyzeEdgeCasesForward()
 }
 
 void
 MDefinition::analyzeEdgeCasesBackward()
 {
 }
 
 static bool
-MaybeEmulatesUndefined(types::StackTypeSet *types, JSContext *cx)
+MaybeEmulatesUndefined(JSContext *cx, MDefinition *op)
 {
+    if (!op->mightBeType(MIRType_Object))
+        return false;
+
+    types::StackTypeSet *types = op->resultTypeSet();
+    if (!types)
+        return true;
+
     if (!types->maybeObject())
         return false;
     return types->hasObjectFlags(cx, types::OBJECT_FLAG_EMULATES_UNDEFINED);
 }
 
 void
-MTest::infer(const TypeOracle::UnaryTypes &u, JSContext *cx)
+MTest::infer(JSContext *cx)
 {
-    if (!u.inTypes)
-        return;
-
     JS_ASSERT(operandMightEmulateUndefined());
 
-    if (!MaybeEmulatesUndefined(u.inTypes, cx))
+    if (!MaybeEmulatesUndefined(cx, getOperand(0)))
         markOperandCantEmulateUndefined();
 }
 
 MDefinition *
 MTest::foldsTo(bool useValueNumbers)
 {
     MDefinition *op = getOperand(0);
 
@@ -315,20 +319,38 @@ IsPowerOfTwo(uint32_t n)
 }
 
 MConstant *
 MConstant::New(const Value &v)
 {
     return new MConstant(v);
 }
 
+types::StackTypeSet *
+ion::MakeSingletonTypeSet(JSObject *obj)
+{
+    LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc();
+    types::StackTypeSet *types = alloc->new_<types::StackTypeSet>();
+    if (!types)
+        return NULL;
+    types::Type objectType = types::Type::ObjectType(obj);
+    types->addObject(objectType.objectKey(), alloc);
+    return types;
+}
+
 MConstant::MConstant(const js::Value &vp)
   : value_(vp)
 {
     setResultType(MIRTypeFromValue(vp));
+    if (vp.isObject()) {
+        // Create a singleton type set for the object. This isn't necessary for
+        // other types as the result type encodes all needed information.
+        setResultTypeSet(MakeSingletonTypeSet(&vp.toObject()));
+    }
+
     setMovable();
 }
 
 HashNumber
 MConstant::valueHash() const
 {
     // This disregards some state, since values are 64 bits. But for a hash,
     // it's completely acceptable.
@@ -398,17 +420,17 @@ MConstant::printOpcode(FILE *fp)
 void
 MConstantElements::printOpcode(FILE *fp)
 {
     PrintOpcodeName(fp, op());
     fprintf(fp, " %p", value());
 }
 
 MParameter *
-MParameter::New(int32_t index, const types::StackTypeSet *types)
+MParameter::New(int32_t index, types::StackTypeSet *types)
 {
     return new MParameter(index, types);
 }
 
 void
 MParameter::printOpcode(FILE *fp)
 {
     PrintOpcodeName(fp, op());
@@ -426,21 +448,20 @@ MParameter::congruentTo(MDefinition * co
 {
     if (!ins->isParameter())
         return false;
 
     return ins->toParameter()->index() == index_;
 }
 
 MCall *
-MCall::New(JSFunction *target, size_t maxArgc, size_t numActualArgs, bool construct,
-           types::StackTypeSet *calleeTypes)
+MCall::New(JSFunction *target, size_t maxArgc, size_t numActualArgs, bool construct)
 {
     JS_ASSERT(maxArgc >= numActualArgs);
-    MCall *ins = new MCall(target, numActualArgs, construct, calleeTypes);
+    MCall *ins = new MCall(target, numActualArgs, construct);
     if (!ins->init(maxArgc + NumNonArgumentOperands))
         return NULL;
     return ins;
 }
 
 MApplyArgs *
 MApplyArgs::New(JSFunction *target, MDefinition *fun, MDefinition *argc, MDefinition *self)
 {
@@ -597,30 +618,144 @@ MPhi::reserveLength(size_t length)
     // latter of which may call realloc().
     JS_ASSERT(numOperands() == 0);
 #if DEBUG
     capacity_ = length;
 #endif
     return inputs_.reserve(length);
 }
 
+static inline types::StackTypeSet *
+MakeMIRTypeSet(MIRType type)
+{
+    JS_ASSERT(type != MIRType_Value);
+    types::Type ntype = type == MIRType_Object
+                        ? types::Type::AnyObjectType()
+                        : types::Type::PrimitiveType(ValueTypeFromMIRType(type));
+    return GetIonContext()->temp->lifoAlloc()->new_<types::StackTypeSet>(ntype);
+}
+
+void
+ion::MergeTypes(MIRType *ptype, types::StackTypeSet **ptypeSet,
+                MIRType newType, types::StackTypeSet *newTypeSet)
+{
+    if (newTypeSet && newTypeSet->empty())
+        return;
+    if (newType != *ptype) {
+        if (IsNumberType(newType) && IsNumberType(*ptype)) {
+            *ptype = MIRType_Double;
+        } else if (*ptype != MIRType_Value) {
+            if (!*ptypeSet)
+                *ptypeSet = MakeMIRTypeSet(*ptype);
+            *ptype = MIRType_Value;
+        }
+    }
+    if (*ptypeSet) {
+        LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc();
+        if (!newTypeSet && newType != MIRType_Value)
+            newTypeSet = MakeMIRTypeSet(newType);
+        if (newTypeSet) {
+            if (!newTypeSet->isSubset(*ptypeSet))
+                *ptypeSet = types::TypeSet::unionSets(*ptypeSet, newTypeSet, alloc);
+        } else {
+            *ptypeSet = NULL;
+        }
+    }
+}
+
+void
+MPhi::specializeType()
+{
+#ifdef DEBUG
+    JS_ASSERT(!specialized_);
+    specialized_ = true;
+#endif
+
+    JS_ASSERT(!inputs_.empty());
+
+    size_t start;
+    if (hasBackedgeType_) {
+        // The type of this phi has already been populated with potential types
+        // that could come in via loop backedges.
+        start = 0;
+    } else {
+        setResultType(inputs_[0].producer()->type());
+        setResultTypeSet(inputs_[0].producer()->resultTypeSet());
+        start = 1;
+    }
+
+    MIRType resultType = this->type();
+    types::StackTypeSet *resultTypeSet = this->resultTypeSet();
+
+    for (size_t i = start; i < inputs_.length(); i++) {
+        MDefinition *def = inputs_[i].producer();
+        MergeTypes(&resultType, &resultTypeSet, def->type(), def->resultTypeSet());
+    }
+
+    setResultType(resultType);
+    setResultTypeSet(resultTypeSet);
+}
+
+void
+MPhi::addBackedgeType(MIRType type, types::StackTypeSet *typeSet)
+{
+    JS_ASSERT(!specialized_);
+
+    if (hasBackedgeType_) {
+        MIRType resultType = this->type();
+        types::StackTypeSet *resultTypeSet = this->resultTypeSet();
+
+        MergeTypes(&resultType, &resultTypeSet, type, typeSet);
+
+        setResultType(resultType);
+        setResultTypeSet(resultTypeSet);
+    } else {
+        setResultType(type);
+        setResultTypeSet(typeSet);
+        hasBackedgeType_ = true;
+    }
+}
+
+bool
+MPhi::typeIncludes(MDefinition *def)
+{
+    if (def->type() == MIRType_Int32 && this->type() == MIRType_Double)
+        return true;
+
+    if (types::StackTypeSet *types = def->resultTypeSet()) {
+        if (this->resultTypeSet())
+            return types->isSubset(this->resultTypeSet());
+        if (this->type() == MIRType_Value || types->empty())
+            return true;
+        return this->type() == MIRTypeFromValueType(types->getKnownTypeTag());
+    }
+
+    if (def->type() == MIRType_Value) {
+        // This phi must be able to be any value.
+        return this->type() == MIRType_Value
+            && (!this->resultTypeSet() || this->resultTypeSet()->unknown());
+    }
+
+    return this->mightBeType(def->type());
+}
+
 void
 MPhi::addInput(MDefinition *ins)
 {
     // This can only been done if the length was reserved through reserveLength,
     // else the slower addInputSlow need to get called.
     JS_ASSERT(inputs_.length() < capacity_);
 
     uint32_t index = inputs_.length();
     inputs_.append(MUse());
     MPhi::setOperand(index, ins);
 }
 
 bool
-MPhi::addInputSlow(MDefinition *ins)
+MPhi::addInputSlow(MDefinition *ins, bool *ptypeChange)
 {
     // The list of inputs to an MPhi is given as a vector of MUse nodes,
     // each of which is in the list of the producer MDefinition.
     // Because appending to a vector may reallocate the vector, it is possible
     // that this operation may cause the producers' linked lists to reference
     // invalid memory. Therefore, in the event of moving reallocation, each
     // MUse must be removed and reinserted from/into its producer's use chain.
     uint32_t index = inputs_.length();
@@ -632,18 +767,32 @@ MPhi::addInputSlow(MDefinition *ins)
             MUse *use = &inputs_[i];
             use->producer()->removeUse(use);
         }
     }
 
     // Insert the new input.
     if (!inputs_.append(MUse()))
         return false;
+
     MPhi::setOperand(index, ins);
 
+    if (ptypeChange) {
+        MIRType resultType = this->type();
+        types::StackTypeSet *resultTypeSet = this->resultTypeSet();
+
+        MergeTypes(&resultType, &resultTypeSet, ins->type(), ins->resultTypeSet());
+
+        if (resultType != this->type() || resultTypeSet != this->resultTypeSet()) {
+            *ptypeChange = true;
+            setResultType(resultType);
+            setResultTypeSet(resultTypeSet);
+        }
+    }
+
     // Add all previously-removed MUses back.
     if (performingRealloc) {
         for (uint32_t i = 0; i < index; i++) {
             MUse *use = &inputs_[i];
             use->producer()->addUse(use);
         }
     }
 
@@ -675,19 +824,19 @@ MCall::addArg(size_t argnum, MPassArg *a
 {
     // The operand vector is initialized in reverse order by the IonBuilder.
     // It cannot be checked for consistency until all arguments are added.
     arg->setArgnum(argnum);
     setOperand(argnum + NumNonArgumentOperands, arg->toDefinition());
 }
 
 void
-MBitNot::infer(const TypeOracle::UnaryTypes &u)
+MBitNot::infer()
 {
-    if (u.inTypes->maybeObject())
+    if (getOperand(0)->mightBeType(MIRType_Object))
         specialization_ = MIRType_None;
     else
         specialization_ = MIRType_Int32;
 }
 
 static inline bool
 IsConstant(MDefinition *def, double v)
 {
@@ -720,60 +869,59 @@ MBinaryBitwiseInstruction::foldsTo(bool 
 
     if (EqualValues(useValueNumbers, lhs, rhs))
         return foldIfEqual();
 
     return this;
 }
 
 void
-MBinaryBitwiseInstruction::infer(const TypeOracle::BinaryTypes &b)
+MBinaryBitwiseInstruction::infer()
 {
-    if (b.lhsTypes->maybeObject() || b.rhsTypes->maybeObject()) {
+    if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object)) {
         specialization_ = MIRType_None;
     } else {
         specialization_ = MIRType_Int32;
         setCommutative();
     }
 }
 
 void
 MBinaryBitwiseInstruction::specializeForAsmJS()
 {
     specialization_ = MIRType_Int32;
     JS_ASSERT(type() == MIRType_Int32);
     setCommutative();
 }
 
 void
-MShiftInstruction::infer(const TypeOracle::BinaryTypes &b)
+MShiftInstruction::infer()
 {
-    if (b.lhsTypes->maybeObject() || b.rhsTypes->maybeObject())
+    if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object))
         specialization_ = MIRType_None;
     else
         specialization_ = MIRType_Int32;
 }
 
 void
-MUrsh::infer(const TypeOracle::BinaryTypes &b)
+MUrsh::infer()
 {
-    if (b.lhsTypes->maybeObject() || b.rhsTypes->maybeObject()) {
+    if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object)) {
         specialization_ = MIRType_None;
         setResultType(MIRType_Value);
         return;
     }
 
-    if (b.outTypes->getKnownTypeTag() == JSVAL_TYPE_DOUBLE) {
-        specialization_ = MIRType_Double;
-        setResultType(MIRType_Double);
+    if (type() == MIRType_Int32) {
+        specialization_ = MIRType_Int32;
         return;
     }
 
-    specialization_ = MIRType_Int32;
-    JS_ASSERT(type() == MIRType_Int32);
+    specialization_ = MIRType_Double;
+    setResultType(MIRType_Double);
 }
 
 static inline bool
 NeedNegativeZeroCheck(MDefinition *def)
 {
     // Test if all uses have the same semantics for -0 and 0
     for (MUseIterator use = def->usesBegin(); use != def->usesEnd(); use++) {
         if (use->consumer()->isResumePoint())
@@ -1074,52 +1222,57 @@ MMul::updateForReplacement(MDefinition *
 bool
 MMul::canOverflow()
 {
     if (isTruncated())
         return false;
     return !range() || !range()->isInt32();
 }
 
-void
-MBinaryArithInstruction::infer(const TypeOracle::BinaryTypes &b, JSContext *cx)
+static inline bool
+KnownNonStringPrimitive(MDefinition *op)
 {
-    // Retrieve type information of lhs and rhs
-    // Rhs is defaulted to int32 first,
-    // because in some cases there is no rhs type information
-    MIRType lhs = MIRTypeFromValueType(b.lhsTypes->getKnownTypeTag());
-    MIRType rhs = MIRType_Int32;
-
-    // Test if types coerces to doubles
-    bool lhsCoerces = b.lhsTypes->knownNonStringPrimitive();
-    bool rhsCoerces = true;
+    return !op->mightBeType(MIRType_Object)
+        && !op->mightBeType(MIRType_String)
+        && !op->mightBeType(MIRType_Magic);
+}
 
-    // Use type information provided by oracle if available.
-    if (b.rhsTypes) {
-        rhs = MIRTypeFromValueType(b.rhsTypes->getKnownTypeTag());
-        rhsCoerces = b.rhsTypes->knownNonStringPrimitive();
-    }
+void
+MBinaryArithInstruction::infer(bool overflowed)
+{
+    JS_ASSERT(this->type() == MIRType_Value);
 
-    MIRType rval = MIRTypeFromValueType(b.outTypes->getKnownTypeTag());
+    specialization_ = MIRType_None;
 
-    // Don't specialize for neither-integer-nor-double results.
-    if (rval != MIRType_Int32 && rval != MIRType_Double) {
-        specialization_ = MIRType_None;
-        return;
-    }
+    // Retrieve type information of lhs and rhs.
+    MIRType lhs = getOperand(0)->type();
+    MIRType rhs = getOperand(1)->type();
 
     // Anything complex - strings and objects - are not specialized.
-    if (!lhsCoerces || !rhsCoerces) {
-        specialization_ = MIRType_None;
+    if (!KnownNonStringPrimitive(getOperand(0)) || !KnownNonStringPrimitive(getOperand(1)))
         return;
-    }
+
+    // Guess a result type based on the inputs.
+    // Don't specialize for neither-integer-nor-double results.
+    if (lhs == MIRType_Int32 && rhs == MIRType_Int32)
+        setResultType(MIRType_Int32);
+    else if (lhs == MIRType_Double || rhs == MIRType_Double)
+        setResultType(MIRType_Double);
+    else
+        return;
+
+    // If the operation has ever overflowed, use a double specialization.
+    if (overflowed)
+        setResultType(MIRType_Double);
 
     JS_ASSERT(lhs < MIRType_String || lhs == MIRType_Value);
     JS_ASSERT(rhs < MIRType_String || rhs == MIRType_Value);
 
+    MIRType rval = this->type();
+
     // Don't specialize values when result isn't double
     if (lhs == MIRType_Value || rhs == MIRType_Value) {
         if (rval != MIRType_Double) {
             specialization_ = MIRType_None;
             return;
         }
     }
 
@@ -1133,88 +1286,70 @@ MBinaryArithInstruction::infer(const Typ
     specialization_ = rval;
 
     if (isAdd() || isMul())
         setCommutative();
     setResultType(rval);
 }
 
 static bool
-SafelyCoercesToDouble(JSContext *cx, types::StackTypeSet *types)
+SafelyCoercesToDouble(MDefinition *op)
 {
-    types::TypeFlags flags = types->baseFlags();
-
     // Strings are unhandled -- visitToDouble() doesn't support them yet.
     // Null is unhandled -- ToDouble(null) == 0, but (0 == null) is false.
-    types::TypeFlags converts = types::TYPE_FLAG_UNDEFINED | types::TYPE_FLAG_DOUBLE |
-                                types::TYPE_FLAG_INT32 | types::TYPE_FLAG_BOOLEAN;
-
-    if ((flags & converts) == flags)
-        return true;
-
-    return false;
+    return KnownNonStringPrimitive(op) && !op->mightBeType(MIRType_Null);
 }
 
 static bool
-CanDoValueBitwiseCmp(JSContext *cx, types::StackTypeSet *lhs, types::StackTypeSet *rhs, bool looseEq)
+ObjectOrSimplePrimitive(MDefinition *op)
+{
+    // Return true if op is either undefined/null/bolean/int32 or an object.
+    return !op->mightBeType(MIRType_String)
+        && !op->mightBeType(MIRType_Double)
+        && !op->mightBeType(MIRType_Magic);
+}
+
+static bool
+CanDoValueBitwiseCmp(JSContext *cx, MDefinition *lhs, MDefinition *rhs, bool looseEq)
 {
     // Only primitive (not double/string) or objects are supported.
     // I.e. Undefined/Null/Boolean/Int32 and Object
-    if (!lhs->knownPrimitiveOrObject() ||
-        lhs->hasAnyFlag(types::TYPE_FLAG_STRING) ||
-        lhs->hasAnyFlag(types::TYPE_FLAG_DOUBLE) ||
-        !rhs->knownPrimitiveOrObject() ||
-        rhs->hasAnyFlag(types::TYPE_FLAG_STRING) ||
-        rhs->hasAnyFlag(types::TYPE_FLAG_DOUBLE))
-    {
+    if (!ObjectOrSimplePrimitive(lhs) || !ObjectOrSimplePrimitive(rhs))
         return false;
-    }
 
     // Objects that emulate undefined are not supported.
-    if (lhs->maybeObject() &&
-        lhs->hasObjectFlags(cx, types::OBJECT_FLAG_EMULATES_UNDEFINED))
-    {
+    if (MaybeEmulatesUndefined(cx, lhs) || MaybeEmulatesUndefined(cx, rhs))
         return false;
-    }
-    if (rhs->maybeObject() &&
-        rhs->hasObjectFlags(cx, types::OBJECT_FLAG_EMULATES_UNDEFINED))
-    {
-        return false;
-    }
 
     // In the loose comparison more values could be the same,
     // but value comparison reporting otherwise.
     if (looseEq) {
 
         // Undefined compared loosy to Null is not supported,
         // because tag is different, but value can be the same (undefined == null).
-        if ((lhs->hasAnyFlag(types::TYPE_FLAG_UNDEFINED) &&
-             rhs->hasAnyFlag(types::TYPE_FLAG_NULL)) ||
-            (lhs->hasAnyFlag(types::TYPE_FLAG_NULL) &&
-             rhs->hasAnyFlag(types::TYPE_FLAG_UNDEFINED)))
+        if ((lhs->mightBeType(MIRType_Undefined) && rhs->mightBeType(MIRType_Null)) ||
+            (lhs->mightBeType(MIRType_Null) && rhs->mightBeType(MIRType_Undefined)))
         {
             return false;
         }
 
         // Int32 compared loosy to Boolean is not supported,
         // because tag is different, but value can be the same (1 == true).
-        if ((lhs->hasAnyFlag(types::TYPE_FLAG_INT32) &&
-             rhs->hasAnyFlag(types::TYPE_FLAG_BOOLEAN)) ||
-            (lhs->hasAnyFlag(types::TYPE_FLAG_BOOLEAN) &&
-             rhs->hasAnyFlag(types::TYPE_FLAG_INT32)))
+        if ((lhs->mightBeType(MIRType_Int32) && rhs->mightBeType(MIRType_Boolean)) ||
+            (lhs->mightBeType(MIRType_Boolean) && rhs->mightBeType(MIRType_Int32)))
         {
             return false;
         }
 
         // For loosy comparison of an object with a Boolean/Number/String
         // the valueOf the object is taken. Therefore not supported.
-        types::TypeFlags numbers = types::TYPE_FLAG_BOOLEAN |
-                                   types::TYPE_FLAG_INT32;
-        if ((lhs->maybeObject() && rhs->hasAnyFlag(numbers)) ||
-            (rhs->maybeObject() && lhs->hasAnyFlag(numbers)))
+        bool simpleLHS = lhs->mightBeType(MIRType_Boolean) || lhs->mightBeType(MIRType_Int32);
+        bool simpleRHS = rhs->mightBeType(MIRType_Boolean) || rhs->mightBeType(MIRType_Int32);
+        if ((lhs->mightBeType(MIRType_Object) && simpleRHS) ||
+            (rhs->mightBeType(MIRType_Object) && simpleLHS))
         {
             return false;
         }
     }
 
     return true;
 }
 
@@ -1243,28 +1378,25 @@ MCompare::inputType()
         return MIRType_Value;
       default:
         JS_NOT_REACHED("No known conversion");
         return MIRType_None;
     }
 }
 
 void
-MCompare::infer(const TypeOracle::BinaryTypes &b, JSContext *cx)
+MCompare::infer(JSContext *cx)
 {
-    if (!b.lhsTypes || !b.rhsTypes)
-        return;
-
     JS_ASSERT(operandMightEmulateUndefined());
 
-    if (!MaybeEmulatesUndefined(b.lhsTypes, cx) && !MaybeEmulatesUndefined(b.rhsTypes, cx))
+    if (!MaybeEmulatesUndefined(cx, getOperand(0)) && !MaybeEmulatesUndefined(cx, getOperand(1)))
         markNoOperandEmulatesUndefined();
 
-    MIRType lhs = MIRTypeFromValueType(b.lhsTypes->getKnownTypeTag());
-    MIRType rhs = MIRTypeFromValueType(b.rhsTypes->getKnownTypeTag());
+    MIRType lhs = getOperand(0)->type();
+    MIRType rhs = getOperand(1)->type();
 
     bool looseEq = jsop() == JSOP_EQ || jsop() == JSOP_NE;
     bool strictEq = jsop() == JSOP_STRICTEQ || jsop() == JSOP_STRICTNE;
     bool relationalEq = !(looseEq || strictEq);
 
     // Integer to integer or boolean to boolean comparisons may be treated as Int32.
     if ((lhs == MIRType_Int32 && rhs == MIRType_Int32) ||
         (lhs == MIRType_Boolean && rhs == MIRType_Boolean))
@@ -1285,18 +1417,18 @@ MCompare::infer(const TypeOracle::Binary
     // Numeric comparisons against a double coerce to double.
     if (IsNumberType(lhs) && IsNumberType(rhs)) {
         compareType_ = Compare_Double;
         return;
     }
 
     // Any comparison is allowed except strict eq.
     if (!strictEq &&
-        ((lhs == MIRType_Double && SafelyCoercesToDouble(cx, b.rhsTypes)) ||
-         (rhs == MIRType_Double && SafelyCoercesToDouble(cx, b.lhsTypes))))
+        ((lhs == MIRType_Double && SafelyCoercesToDouble(getOperand(1))) ||
+         (rhs == MIRType_Double && SafelyCoercesToDouble(getOperand(0)))))
     {
         compareType_ = Compare_Double;
         return;
     }
 
     // Handle object comparison.
     if (!relationalEq && lhs == MIRType_Object && rhs == MIRType_Object) {
         compareType_ = Compare_Object;
@@ -1348,17 +1480,17 @@ MCompare::infer(const TypeOracle::Binary
         if (lhs == MIRType_Boolean)
              swapOperands();
 
         compareType_ = Compare_Boolean;
         return;
     }
 
     // Determine if we can do the compare based on a quick value check.
-    if (!relationalEq && CanDoValueBitwiseCmp(cx, b.lhsTypes, b.rhsTypes, looseEq)) {
+    if (!relationalEq && CanDoValueBitwiseCmp(cx, getOperand(0), getOperand(1), looseEq)) {
         compareType_ = Compare_Value;
         return;
     }
 }
 
 MBitNot *
 MBitNot::New(MDefinition *input)
 {
@@ -1834,24 +1966,21 @@ MCompare::foldsTo(bool useValueNumbers)
         JS_ASSERT(type() == MIRType_Boolean);
         return MConstant::New(BooleanValue(result));
     }
 
     return this;
 }
 
 void
-MNot::infer(const TypeOracle::UnaryTypes &u, JSContext *cx)
+MNot::infer(JSContext *cx)
 {
-    if (!u.inTypes)
-        return;
-
     JS_ASSERT(operandMightEmulateUndefined());
 
-    if (!MaybeEmulatesUndefined(u.inTypes, cx))
+    if (!MaybeEmulatesUndefined(cx, getOperand(0)))
         markOperandCantEmulateUndefined();
 }
 
 MDefinition *
 MNot::foldsTo(bool useValueNumbers)
 {
     // Fold if the input is constant
     if (operand()->isConstant()) {
@@ -2003,16 +2132,32 @@ InlinePropertyTable::hasFunction(JSFunct
 {
     for (size_t i = 0; i < numEntries(); i++) {
         if (entries_[i]->func == func)
             return true;
     }
     return false;
 }
 
+types::StackTypeSet *
+InlinePropertyTable::buildTypeSetForFunction(JSFunction *func) const
+{
+    LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc();
+    types::StackTypeSet *types = alloc->new_<types::StackTypeSet>();
+    if (!types)
+        return NULL;
+    for (size_t i = 0; i < numEntries(); i++) {
+        if (entries_[i]->func == func) {
+            if (!types->addObject(types::Type::ObjectType(entries_[i]->typeObj).objectKey(), alloc))
+                return NULL;
+        }
+    }
+    return types;
+}
+
 MDefinition *
 MAsmJSUnsignedToDouble::foldsTo(bool useValueNumbers)
 {
     if (input()->isConstant()) {
         const Value &v = input()->toConstant()->value();
         if (v.isInt32())
             return MConstant::New(DoubleValue(uint32_t(v.toInt32())));
     }
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -10,17 +10,16 @@
 
 // This file declares everything needed to build actual MIR instructions: the
 // actual opcodes and instructions themselves, the instruction interface, and
 // use chains.
 #include "jscntxt.h"
 #include "jslibmath.h"
 #include "jsinfer.h"
 #include "jsinferinlines.h"
-#include "TypeOracle.h"
 #include "TypePolicy.h"
 #include "IonAllocPolicy.h"
 #include "InlineList.h"
 #include "MOpcodes.h"
 #include "FixedArityList.h"
 #include "IonMacroAssembler.h"
 #include "Bailouts.h"
 #include "FixedList.h"
@@ -259,16 +258,17 @@ class MDefinition : public MNode
 
   private:
     InlineList<MUse> uses_;        // Use chain.
     uint32_t id_;                  // Instruction ID, which after block re-ordering
                                    // is sorted within a basic block.
     ValueNumberData *valueNumber_; // The instruction's value number (see GVN for details in use)
     Range *range_;                 // Any computed range for this def.
     MIRType resultType_;           // Representation of result type.
+    types::StackTypeSet *resultTypeSet_; // Optional refinement of the result type.
     uint32_t flags_;                 // Bit flags.
     union {
         MDefinition *dependency_;  // Implicit dependency (store, call, etc.) of this instruction.
                                    // Used by alias analysis, GVN and LICM.
         uint32_t virtualRegister_;   // Used by lowering to map definitions to virtual registers.
     };
 
     // Track bailouts by storing the current pc in MIR instruction. Also used
@@ -299,16 +299,17 @@ class MDefinition : public MNode
     }
     virtual bool neverHoist() const { return false; }
   public:
     MDefinition()
       : id_(0),
         valueNumber_(NULL),
         range_(NULL),
         resultType_(MIRType_None),
+        resultTypeSet_(NULL),
         flags_(0),
         dependency_(NULL),
         trackedPc_(NULL)
     { }
 
     virtual Opcode op() const = 0;
     virtual const char *opName() const = 0;
     void printName(FILE *fp);
@@ -388,16 +389,32 @@ class MDefinition : public MNode
 
     MIR_FLAG_LIST(FLAG_ACCESSOR)
 #undef FLAG_ACCESSOR
 
     MIRType type() const {
         return resultType_;
     }
 
+    types::StackTypeSet *resultTypeSet() const {
+        return resultTypeSet_;
+    }
+
+    bool mightBeType(MIRType type) const {
+        JS_ASSERT(type != MIRType_Value);
+
+        if (type == this->type())
+            return true;
+
+        if (MIRType_Value != this->type())
+            return false;
+
+        return !resultTypeSet() || resultTypeSet()->mightBeType(ValueTypeFromMIRType(type));
+    }
+
     // Returns the beginning of this definition's use chain.
     MUseIterator usesBegin() const {
         return uses_.begin();
     }
 
     // Returns the end of this definition's use chain.
     MUseIterator usesEnd() const {
         return uses_.end();
@@ -465,24 +482,18 @@ class MDefinition : public MNode
     inline MInstruction *toInstruction();
     bool isInstruction() const {
         return !isPhi();
     }
 
     void setResultType(MIRType type) {
         resultType_ = type;
     }
-
-    virtual bool acceptsTypeSet() const {
-        return false;
-    }
-    virtual void setTypeSet(const types::StackTypeSet *types) {
-    }
-    virtual const types::StackTypeSet *typeSet() const {
-        return NULL;
+    void setResultTypeSet(types::StackTypeSet *types) {
+        resultTypeSet_ = types;
     }
 
     MDefinition *dependency() const {
         return dependency_;
     }
     void setDependency(MDefinition *dependency) {
         dependency_ = dependency;
     }
@@ -712,38 +723,34 @@ class MConstant : public MNullaryInstruc
 
     void computeRange();
     bool truncate();
 };
 
 class MParameter : public MNullaryInstruction
 {
     int32_t index_;
-    const types::StackTypeSet *typeSet_;
 
   public:
     static const int32_t THIS_SLOT = -1;
 
-    MParameter(int32_t index, const types::StackTypeSet *types)
-      : index_(index),
-        typeSet_(types)
+    MParameter(int32_t index, types::StackTypeSet *types)
+      : index_(index)
     {
         setResultType(MIRType_Value);
+        setResultTypeSet(types);
     }
 
   public:
     INSTRUCTION_HEADER(Parameter)
-    static MParameter *New(int32_t index, const types::StackTypeSet *types);
+    static MParameter *New(int32_t index, types::StackTypeSet *types);
 
     int32_t index() const {
         return index_;
     }
-    const types::StackTypeSet *typeSet() const {
-        return typeSet_;
-    }
     void printOpcode(FILE *fp);
 
     HashNumber valueHash() const;
     bool congruentTo(MDefinition * const &ins) const;
 };
 
 class MCallee : public MNullaryInstruction
 {
@@ -996,17 +1003,17 @@ class MTest
     }
     TypePolicy *typePolicy() {
         return this;
     }
 
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
-    void infer(const TypeOracle::UnaryTypes &u, JSContext *cx);
+    void infer(JSContext *cx);
     MDefinition *foldsTo(bool useValueNumbers);
 
     void markOperandCantEmulateUndefined() {
         operandMightEmulateUndefined_ = false;
     }
     bool operandMightEmulateUndefined() const {
         return operandMightEmulateUndefined_;
     }
@@ -1081,16 +1088,24 @@ class MNewParallelArray : public MNullar
         return AliasSet::None();
     }
 
     JSObject *templateObject() const {
         return templateObject_;
     }
 };
 
+// Fabricate a type set containing only the type of the specified object.
+types::StackTypeSet *
+MakeSingletonTypeSet(JSObject *obj);
+
+void
+MergeTypes(MIRType *ptype, types::StackTypeSet **ptypeSet,
+           MIRType newType, types::StackTypeSet *newTypeSet);
+
 class MNewArray : public MNullaryInstruction
 {
   public:
     enum AllocatingBehaviour {
         NewArray_Allocating,
         NewArray_Unallocating
     };
 
@@ -1106,16 +1121,17 @@ class MNewArray : public MNullaryInstruc
     INSTRUCTION_HEADER(NewArray)
 
     MNewArray(uint32_t count, JSObject *templateObject, AllocatingBehaviour allocating)
       : count_(count),
         templateObject_(templateObject),
         allocating_(allocating)
     {
         setResultType(MIRType_Object);
+        setResultTypeSet(MakeSingletonTypeSet(templateObject));
     }
 
     uint32_t count() const {
         return count_;
     }
 
     JSObject *templateObject() const {
         return templateObject_;
@@ -1143,16 +1159,17 @@ class MNewArray : public MNullaryInstruc
 class MNewObject : public MNullaryInstruction
 {
     CompilerRootObject templateObject_;
 
     MNewObject(JSObject *templateObject)
       : templateObject_(templateObject)
     {
         setResultType(MIRType_Object);
+        setResultTypeSet(MakeSingletonTypeSet(templateObject));
     }
 
   public:
     INSTRUCTION_HEADER(NewObject)
 
     static MNewObject *New(JSObject *templateObject) {
         return new MNewObject(templateObject);
     }
@@ -1305,34 +1322,29 @@ class MCall
     // True if the call is for JSOP_NEW.
     bool construct_;
     // Monomorphic cache of single target from TI, or NULL.
     CompilerRootFunction target_;
     // Holds a target's Script alive.
     CompilerRootScript targetScript_;
     // Original value of argc from the bytecode.
     uint32_t numActualArgs_;
-    // The typeset of the callee, could be NULL.
-    types::StackTypeSet *calleeTypes_;
-
-    MCall(JSFunction *target, uint32_t numActualArgs, bool construct,
-          types::StackTypeSet *calleeTypes)
+
+    MCall(JSFunction *target, uint32_t numActualArgs, bool construct)
       : construct_(construct),
         target_(target),
         targetScript_(NULL),
-        numActualArgs_(numActualArgs),
-        calleeTypes_(calleeTypes)
+        numActualArgs_(numActualArgs)
     {
         setResultType(MIRType_Value);
     }
 
   public:
     INSTRUCTION_HEADER(Call)
-    static MCall *New(JSFunction *target, size_t maxArgc, size_t numActualArgs, bool construct,
-                      types::StackTypeSet *calleeTypes);
+    static MCall *New(JSFunction *target, size_t maxArgc, size_t numActualArgs, bool construct);
 
     void initPrepareCall(MDefinition *start) {
         JS_ASSERT(start->isPrepareCall());
         return setOperand(PrepareCallOperandIndex, start);
     }
     void initFunction(MDefinition *func) {
         JS_ASSERT(!func->isPassArg());
         return setOperand(FunctionOperandIndex, func);
@@ -1364,19 +1376,16 @@ class MCall
     // For TI-informed monomorphic callsites.
     JSFunction *getSingleTarget() const {
         return target_;
     }
 
     bool isConstructing() const {
         return construct_;
     }
-    types::StackTypeSet *calleeTypes() const {
-        return calleeTypes_;
-    }
 
     // The number of stack arguments is the max between the number of formal
     // arguments and the number of actual arguments. The number of stack
     // argument includes the |undefined| padding added in case of underflow.
     // Includes |this|.
     uint32_t numStackArgs() const {
         return numOperands() - NumNonArgumentOperands;
     }
@@ -1727,17 +1736,17 @@ class MCompare
     INSTRUCTION_HEADER(Compare)
     static MCompare *New(MDefinition *left, MDefinition *right, JSOp op);
     static MCompare *NewAsmJS(MDefinition *left, MDefinition *right, JSOp op, CompareType compareType);
 
     bool tryFold(bool *result);
     bool evaluateConstantOperands(bool *result);
     MDefinition *foldsTo(bool useValueNumbers);
 
-    void infer(const TypeOracle::BinaryTypes &b, JSContext *cx);
+    void infer(JSContext *cx);
     CompareType compareType() const {
         return compareType_;
     }
     void setCompareType(CompareType type) {
         compareType_ = type;
     }
     MIRType inputType();
 
@@ -1774,16 +1783,24 @@ class MCompare
 
 // Takes a typed value and returns an untyped value.
 class MBox : public MUnaryInstruction
 {
     MBox(MDefinition *ins)
       : MUnaryInstruction(ins)
     {
         setResultType(MIRType_Value);
+        if (ins->resultTypeSet()) {
+            setResultTypeSet(ins->resultTypeSet());
+        } else if (ins->type() != MIRType_Value) {
+            types::Type ntype = ins->type() == MIRType_Object
+                                ? types::Type::AnyObjectType()
+                                : types::Type::PrimitiveType(ValueTypeFromMIRType(ins->type()));
+            setResultTypeSet(GetIonContext()->temp->lifoAlloc()->new_<types::StackTypeSet>(ntype));
+        }
         setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(Box)
     static MBox *New(MDefinition *ins)
     {
         // Cannot box a box.
@@ -1833,16 +1850,17 @@ class MUnbox : public MUnaryInstruction
         JS_ASSERT(ins->type() == MIRType_Value);
         JS_ASSERT(type == MIRType_Boolean ||
                   type == MIRType_Int32   ||
                   type == MIRType_Double  ||
                   type == MIRType_String  ||
                   type == MIRType_Object);
 
         setResultType(type);
+        setResultTypeSet(ins->resultTypeSet());
         setMovable();
 
         if (mode_ == TypeBarrier || mode_ == TypeGuard)
             setGuard();
         if (mode_ == TypeGuard)
             mode_ = Fallible;
     }
 
@@ -1945,16 +1963,17 @@ class MCreateThisWithTemplate
 {
     // Template for |this|, provided by TI
     CompilerRootObject templateObject_;
 
     MCreateThisWithTemplate(JSObject *templateObject)
       : templateObject_(templateObject)
     {
         setResultType(MIRType_Object);
+        setResultTypeSet(MakeSingletonTypeSet(templateObject));
     }
 
   public:
     INSTRUCTION_HEADER(CreateThisWithTemplate);
     static MCreateThisWithTemplate *New(JSObject *templateObject)
     {
         return new MCreateThisWithTemplate(templateObject);
     }
@@ -2080,16 +2099,17 @@ class MPassArg : public MUnaryInstructio
 {
     int32_t argnum_;
 
   private:
     MPassArg(MDefinition *def)
       : MUnaryInstruction(def), argnum_(-1)
     {
         setResultType(def->type());
+        setResultTypeSet(def->resultTypeSet());
     }
 
   public:
     INSTRUCTION_HEADER(PassArg)
     static MPassArg *New(MDefinition *def)
     {
         return new MPassArg(def);
     }
@@ -2317,17 +2337,17 @@ class MBitNot
     static MBitNot *New(MDefinition *input);
     static MBitNot *NewAsmJS(MDefinition *input);
 
     TypePolicy *typePolicy() {
         return this;
     }
 
     MDefinition *foldsTo(bool useValueNumbers);
-    void infer(const TypeOracle::UnaryTypes &u);
+    void infer();
 
     bool congruentTo(MDefinition *const &ins) const {
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const {
         if (specialization_ == MIRType_None)
             return AliasSet::Store(AliasSet::Any);
         return AliasSet::None();
@@ -2414,17 +2434,17 @@ class MBinaryBitwiseInstruction
     TypePolicy *typePolicy() {
         return this;
     }
 
     MDefinition *foldsTo(bool useValueNumbers);
     virtual MDefinition *foldIfZero(size_t operand) = 0;
     virtual MDefinition *foldIfNegOne(size_t operand) = 0;
     virtual MDefinition *foldIfEqual()  = 0;
-    virtual void infer(const TypeOracle::BinaryTypes &b);
+    virtual void infer();
 
     bool congruentTo(MDefinition *const &ins) const {
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const {
         if (specialization_ >= MIRType_Object)
             return AliasSet::Store(AliasSet::Any);
         return AliasSet::None();
@@ -2510,17 +2530,17 @@ class MShiftInstruction
 
   public:
     MDefinition *foldIfNegOne(size_t operand) {
         return this;
     }
     MDefinition *foldIfEqual() {
         return this;
     }
-    virtual void infer(const TypeOracle::BinaryTypes &b);
+    virtual void infer();
 };
 
 class MLsh : public MShiftInstruction
 {
     MLsh(MDefinition *left, MDefinition *right)
       : MShiftInstruction(left, right)
     { }
 
@@ -2574,17 +2594,17 @@ class MUrsh : public MShiftInstruction
     MDefinition *foldIfZero(size_t operand) {
         // 0 >>> x => 0
         if (operand == 0)
             return getOperand(0);
 
         return this;
     }
 
-    void infer(const TypeOracle::BinaryTypes &b);
+    void infer();
 
     bool canOverflow() {
         // solution is only negative when lhs < 0 and rhs & 0x1f == 0
         MDefinition *lhs = getOperand(0);
         MDefinition *rhs = getOperand(1);
 
         if (lhs->isConstant()) {
             Value lhsv = lhs->toConstant()->value();
@@ -2634,17 +2654,17 @@ class MBinaryArithInstruction
     MIRType specialization() const {
         return specialization_;
     }
 
     MDefinition *foldsTo(bool useValueNumbers);
 
     virtual double getIdentity() = 0;
 
-    void infer(const TypeOracle::BinaryTypes &b, JSContext *cx);
+    void infer(bool overflowed);
 
     void setInt32() {
         specialization_ = MIRType_Int32;
         setResultType(MIRType_Int32);
     }
 
     bool congruentTo(MDefinition *const &ins) const {
         return MBinaryInstruction::congruentTo(ins);
@@ -3241,78 +3261,97 @@ class MFromCharCode
     }
 };
 
 class MPhi : public MDefinition, public InlineForwardListNode<MPhi>
 {
     js::Vector<MUse, 2, IonAllocPolicy> inputs_;
 
     uint32_t slot_;
+    bool hasBackedgeType_;
     bool triedToSpecialize_;
     bool isIterator_;
 
 #if DEBUG
+    bool specialized_;
     uint32_t capacity_;
 #endif
 
     MPhi(uint32_t slot)
       : slot_(slot),
+        hasBackedgeType_(false),
         triedToSpecialize_(false),
         isIterator_(false)
 #if DEBUG
+        , specialized_(false)
         , capacity_(0)
 #endif
     {
         setResultType(MIRType_Value);
     }
 
   protected:
     MUse *getUseFor(size_t index) {
         return &inputs_[index];
     }
 
   public:
     INSTRUCTION_HEADER(Phi)
     static MPhi *New(uint32_t slot);
 
     void setOperand(size_t index, MDefinition *operand) {
+        // Note: after the initial IonBuilder pass, it is OK to rearrange phi
+        // operands such that they do not include the type sets of their
+        // operands. This can arise during e.g. value numbering, where
+        // definitions producing the same value may have different type sets.
         JS_ASSERT(index < numOperands());
         inputs_[index].set(operand, this, index);
         operand->addUse(&inputs_[index]);
     }
 
     void removeOperand(size_t index);
 
     MDefinition *getOperand(size_t index) const {
         return inputs_[index].producer();
     }
     size_t numOperands() const {
         return inputs_.length();
     }
     uint32_t slot() const {
         return slot_;
     }
+    bool hasBackedgeType() const {
+        return hasBackedgeType_;
+    }
     bool triedToSpecialize() const {
         return triedToSpecialize_;
     }
     void specialize(MIRType type) {
         triedToSpecialize_ = true;
         setResultType(type);
     }
+    void specializeType();
+
+    // Whether this phi's type already includes information for def.
+    bool typeIncludes(MDefinition *def);
+
+    // Add types for this phi which speculate about new inputs that may come in
+    // via a loop backedge.
+    void addBackedgeType(MIRType type, types::StackTypeSet *typeSet);
 
     // Initializes the operands vector to the given capacity,
     // permitting use of addInput() instead of addInputSlow().
     bool reserveLength(size_t length);
 
     // Use only if capacity has been reserved by reserveLength
     void addInput(MDefinition *ins);
 
     // Appends a new input to the input vector. May call realloc().
     // Prefer reserveLength() and addInput() instead, where possible.
-    bool addInputSlow(MDefinition *ins);
+    bool addInputSlow(MDefinition *ins, bool *ptypeChange = NULL);
 
     MDefinition *foldsTo(bool useValueNumbers);
 
     bool congruentTo(MDefinition * const &ins) const;
 
     bool isIterator() const {
         return isIterator_;
     }
@@ -3346,16 +3385,17 @@ class MBeta : public MUnaryInstruction
     const Range *comparison_;
     MDefinition *val_;
     MBeta(MDefinition *val, const Range *comp)
         : MUnaryInstruction(val),
           comparison_(comp),
           val_(val)
     {
         setResultType(val->type());
+        setResultTypeSet(val->resultTypeSet());
     }
 
   public:
     INSTRUCTION_HEADER(Beta)
     void printOpcode(FILE *fp);
     static MBeta *New(MDefinition *val, const Range *comp)
     {
         return new MBeta(val, comp);
@@ -3550,16 +3590,19 @@ class MRegExp : public MNullaryInstructi
     CompilerRoot<RegExpObject *> source_;
     CompilerRootObject prototype_;
 
     MRegExp(RegExpObject *source, JSObject *prototype)
       : source_(source),
         prototype_(prototype)
     {
         setResultType(MIRType_Object);
+
+        JS_ASSERT(source->getProto() == prototype);
+        setResultTypeSet(MakeSingletonTypeSet(source));
     }
 
   public:
     INSTRUCTION_HEADER(RegExp)
 
     static MRegExp *New(RegExpObject *source, JSObject *prototype) {
         return new MRegExp(source, prototype);
     }
@@ -4019,17 +4062,17 @@ class MNot
     static MNot *NewAsmJS(MDefinition *elements) {
         MNot *ins = new MNot(elements);
         ins->setResultType(MIRType_Int32);
         return ins;
     }
 
     INSTRUCTION_HEADER(Not);
 
-    void infer(const TypeOracle::UnaryTypes &u, JSContext *cx);
+    void infer(JSContext *cx);
     MDefinition *foldsTo(bool useValueNumbers);
 
     void markOperandCantEmulateUndefined() {
         operandMightEmulateUndefined_ = false;
     }
     bool operandMightEmulateUndefined() const {
         return operandMightEmulateUndefined_;
     }
@@ -4770,47 +4813,36 @@ class MClampToUint8
     void computeRange();
 };
 
 class MLoadFixedSlot
   : public MUnaryInstruction,
     public SingleObjectPolicy
 {
     size_t slot_;
-    const types::StackTypeSet *types_;
 
   protected:
     MLoadFixedSlot(MDefinition *obj, size_t slot)
-      : MUnaryInstruction(obj), slot_(slot), types_(NULL)
+      : MUnaryInstruction(obj), slot_(slot)
     {
         setResultType(MIRType_Value);
         setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(LoadFixedSlot)
 
     static MLoadFixedSlot *New(MDefinition *obj, size_t slot) {
         return new MLoadFixedSlot(obj, slot);
     }
 
     TypePolicy *typePolicy() {
         return this;
     }
 
-    virtual bool acceptsTypeSet() const {
-        return true;
-    }
-    virtual void setTypeSet(const types::StackTypeSet *types) {
-        types_ = types;
-    }
-    virtual const types::StackTypeSet *typeSet() const {
-        return types_;
-    }
-
     MDefinition *object() const {
         return getOperand(0);
     }
     size_t slot() const {
         return slot_;
     }
     bool congruentTo(MDefinition * const &ins) const {
         if (!ins->isLoadFixedSlot())
@@ -4922,16 +4954,17 @@ class InlinePropertyTable : public TempO
     }
 
     JSFunction *getFunction(size_t i) const {
         JS_ASSERT(i < numEntries());
         return entries_[i]->func;
     }
 
     bool hasFunction(JSFunction *func) const;
+    types::StackTypeSet *buildTypeSetForFunction(JSFunction *func) const;
 
     // Remove targets that vetoed inlining from the InlinePropertyTable.
     void trimTo(AutoObjectVector &targets, Vector<bool> &choiceSet);
 
     // Ensure that the InlinePropertyTable's domain is a subset of |targets|.
     void trimToAndMaybePatchTargets(AutoObjectVector &targets, AutoObjectVector &originals);
 };
 
@@ -5496,22 +5529,20 @@ class MGuardClass
 };
 
 // Load from vp[slot] (slots that are not inline in an object).
 class MLoadSlot
   : public MUnaryInstruction,
     public SingleObjectPolicy
 {
     uint32_t slot_;
-    const types::StackTypeSet *types_;
 
     MLoadSlot(MDefinition *slots, uint32_t slot)
       : MUnaryInstruction(slots),
-        slot_(slot),
-        types_(NULL)
+        slot_(slot)
     {
         setResultType(MIRType_Value);
         setMovable();
         JS_ASSERT(slots->type() == MIRType_Slots);
     }
 
   public:
     INSTRUCTION_HEADER(LoadSlot)
@@ -5525,26 +5556,16 @@ class MLoadSlot
     }
     MDefinition *slots() const {
         return getOperand(0);
     }
     uint32_t slot() const {
         return slot_;
     }
 
-    virtual bool acceptsTypeSet() const {
-        return true;
-    }
-    virtual void setTypeSet(const types::StackTypeSet *types) {
-        types_ = types;
-    }
-    virtual const types::StackTypeSet *typeSet() const {
-        return types_;
-    }
-
     bool congruentTo(MDefinition * const &ins) const {
         if (!ins->isLoadSlot())
             return false;
         if (slot() != ins->toLoadSlot()->slot())
             return false;
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const {
@@ -5955,16 +5976,54 @@ class MCallSetElement
     MDefinition *index() const {
         return getOperand(1);
     }
     MDefinition *value() const {
         return getOperand(2);
     }
 };
 
+class MCallInitElementArray
+  : public MAryInstruction<2>,
+    public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >
+{
+    uint32_t index_;
+
+    MCallInitElementArray(MDefinition *obj, uint32_t index, MDefinition *val)
+      : index_(index)
+    {
+        setOperand(0, obj);
+        setOperand(1, val);
+    }
+
+  public:
+    INSTRUCTION_HEADER(CallInitElementArray)
+
+    static MCallInitElementArray *New(MDefinition *obj, uint32_t index, MDefinition *val)
+    {
+        return new MCallInitElementArray(obj, index, val);
+    }
+
+    MDefinition *object() const {
+        return getOperand(0);
+    }
+
+    uint32_t index() const {
+        return index_;
+    }
+
+    MDefinition *value() const {
+        return getOperand(1);
+    }
+
+    TypePolicy *typePolicy() {
+        return this;
+    }
+};
+
 class MSetDOMProperty
   : public MAryInstruction<2>,
     public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >
 {
     const JSJitPropertyOp func_;
 
     MSetDOMProperty(const JSJitPropertyOp func, MDefinition *obj, MDefinition *val)
       : func_(func)
@@ -6497,38 +6556,37 @@ class MParDump
 
 // Given a value, guard that the value is in a particular TypeSet, then returns
 // that value.
 class MTypeBarrier
   : public MUnaryInstruction,
     public BoxInputsPolicy
 {
     BailoutKind bailoutKind_;
-    const types::StackTypeSet *typeSet_;
-
-    MTypeBarrier(MDefinition *def, const types::StackTypeSet *types, BailoutKind bailoutKind)
-      : MUnaryInstruction(def),
-        typeSet_(types)
+
+    MTypeBarrier(MDefinition *def, types::StackTypeSet *types, BailoutKind bailoutKind)
+      : MUnaryInstruction(def)
     {
         setResultType(MIRType_Value);
+        setResultTypeSet(types);
         setGuard();
         setMovable();
         bailoutKind_ = bailoutKind;
     }
 
   public:
     INSTRUCTION_HEADER(TypeBarrier)
 
-    static MTypeBarrier *New(MDefinition *def, const types::StackTypeSet *types) {
+    static MTypeBarrier *New(MDefinition *def, types::StackTypeSet *types) {
         BailoutKind bailoutKind = def->isEffectful()
                                   ? Bailout_TypeBarrier
                                   : Bailout_Normal;
         return new MTypeBarrier(def, types, bailoutKind);
     }
-    static MTypeBarrier *New(MDefinition *def, const types::StackTypeSet *types,
+    static MTypeBarrier *New(MDefinition *def, types::StackTypeSet *types,
                              BailoutKind bailoutKind) {
         return new MTypeBarrier(def, types, bailoutKind);
     }
 
     TypePolicy *typePolicy() {
         return this;
     }
 
@@ -6536,24 +6594,21 @@ class MTypeBarrier
         return false;
     }
     MDefinition *input() const {
         return getOperand(0);
     }
     BailoutKind bailoutKind() const {
         return bailoutKind_;
     }
-    const types::StackTypeSet *typeSet() const {
-        return typeSet_;
-    }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
     virtual bool neverHoist() const {
-        return typeSet()->empty();
+        return resultTypeSet()->empty();
     }
 };
 
 // Like MTypeBarrier, guard that the value is in the given type set. This is
 // used after some VM calls (like GetElement) to avoid the slower calls to
 // TypeScript::Monitor inside these stubs.
 class MMonitorTypes : public MUnaryInstruction
 {
--- a/js/src/ion/MIRGraph.cpp
+++ b/js/src/ion/MIRGraph.cpp
@@ -59,16 +59,37 @@ void
 MIRGraph::insertBlockAfter(MBasicBlock *at, MBasicBlock *block)
 {
     block->setId(blockIdGen_++);
     blocks_.insertAfter(at, block);
     numBlocks_++;
 }
 
 void
+MIRGraph::removeBlocksAfter(MBasicBlock *start)
+{
+    MBasicBlockIterator iter(begin());
+    iter++;
+    while (iter != end()) {
+        MBasicBlock *block = *iter;
+        iter++;
+
+        if (block->id() <= start->id())
+            continue;
+
+        if (block == osrBlock_)
+            osrBlock_ = NULL;
+        block->discardAllInstructions();
+        block->discardAllPhis();
+        block->markAsDead();
+        removeBlock(block);
+    }
+}
+
+void
 MIRGraph::unmarkBlocks() {
     for (MBasicBlockIterator i(blocks_.begin()); i != blocks_.end(); i++)
         i->unmark();
 }
 
 MDefinition *
 MIRGraph::parSlice() {
     // Search the entry block to find a par slice instruction.  If we do not
@@ -342,20 +363,22 @@ void
 MBasicBlock::linkOsrValues(MStart *start)
 {
     JS_ASSERT(start->startType() == MStart::StartType_Osr);
 
     MResumePoint *res = start->resumePoint();
 
     for (uint32_t i = 0; i < stackDepth(); i++) {
         MDefinition *def = slots_[i];
-        if (i == info().scopeChainSlot())
-            def->toOsrScopeChain()->setResumePoint(res);
-        else
+        if (i == info().scopeChainSlot()) {
+            if (def->isOsrScopeChain())
+                def->toOsrScopeChain()->setResumePoint(res);
+        } else {
             def->toOsrValue()->setResumePoint(res);
+        }
     }
 }
 
 void
 MBasicBlock::setSlot(uint32_t slot, MDefinition *ins)
 {
     slots_[slot] = ins;
 }
@@ -569,16 +592,41 @@ MBasicBlock::discardDefAt(MDefinitionIte
         iter.phiIter_ = iter.block_->discardPhiAt(iter.phiIter_);
     else
         iter.iter_ = iter.block_->discardAt(iter.iter_);
 
     return iter;
 }
 
 void
+MBasicBlock::discardAllInstructions()
+{
+    for (MInstructionIterator iter = begin(); iter != end(); ) {
+        for (size_t i = 0; i < iter->numOperands(); i++)
+            iter->discardOperand(i);
+        iter = instructions_.removeAt(iter);
+    }
+    lastIns_ = NULL;
+}
+
+void
+MBasicBlock::discardAllPhis()
+{
+    for (MPhiIterator iter = phisBegin(); iter != phisEnd(); ) {
+        MPhi *phi = *iter;
+        for (size_t i = 0; i < phi->numOperands(); i++)
+            phi->discardOperand(i);
+        iter = phis_.removeAt(iter);
+    }
+
+    for (MBasicBlock **pred = predecessors_.begin(); pred != predecessors_.end(); pred++)
+        (*pred)->setSuccessorWithPhis(NULL, 0);
+}
+
+void
 MBasicBlock::insertBefore(MInstruction *at, MInstruction *ins)
 {
     ins->setBlock(this);
     graph().allocDefinitionId(ins);
     instructions_.insertBefore(at, ins);
     ins->setTrackedPc(at->trackedPc());
 }
 
@@ -717,27 +765,29 @@ MBasicBlock::assertUsesAreNotWithin(MUse
 bool
 MBasicBlock::dominates(MBasicBlock *other)
 {
     uint32_t high = domIndex() + numDominated();
     uint32_t low  = domIndex();
     return other->domIndex() >= low && other->domIndex() <= high;
 }
 
-bool
+AbortReason
 MBasicBlock::setBackedge(MBasicBlock *pred)
 {
     // Predecessors must be finished, and at the correct stack depth.
     JS_ASSERT(lastIns_);
     JS_ASSERT(pred->lastIns_);
     JS_ASSERT_IF(entryResumePoint(), pred->stackDepth() == entryResumePoint()->stackDepth());
 
     // We must be a pending loop header
     JS_ASSERT(kind_ == PENDING_LOOP_HEADER);
 
+    bool hadTypeChange = false;
+
     // Add exit definitions to each corresponding phi at the entry.
     for (MPhiIterator phi = phisBegin(); phi != phisEnd(); phi++) {
         MPhi *entryDef = *phi;
         MDefinition *exitDef = pred->slots_[entryDef->slot()];
 
         // Assert that we already placed phis for each slot.
         JS_ASSERT(entryDef->block() == this);
 
@@ -747,27 +797,40 @@ MBasicBlock::setBackedge(MBasicBlock *pr
             // know that that's just the first input.
             //
             // Note that we eliminate later rather than now, to avoid any
             // weirdness around pending continue edges which might still hold
             // onto phis.
             exitDef = entryDef->getOperand(0);
         }
 
-        if (!entryDef->addInputSlow(exitDef))
-            return false;
+        bool typeChange = false;
+
+        if (!entryDef->addInputSlow(exitDef, &typeChange))
+            return AbortReason_Alloc;
+
+        hadTypeChange |= typeChange;
 
         JS_ASSERT(entryDef->slot() < pred->stackDepth());
         setSlot(entryDef->slot(), entryDef);
     }
 
+    if (hadTypeChange) {
+        for (MPhiIterator phi = phisBegin(); phi != phisEnd(); phi++)
+            phi->removeOperand(phi->numOperands() - 1);
+        return AbortReason_Disable;
+    }
+
     // We are now a loop header proper
     kind_ = LOOP_HEADER;
 
-    return predecessors_.append(pred);
+    if (!predecessors_.append(pred))
+        return AbortReason_Alloc;
+
+    return AbortReason_NoAbort;
 }
 
 void
 MBasicBlock::clearLoopHeader()
 {
     JS_ASSERT(isLoopHeader());
     kind_ = NORMAL;
 }
@@ -883,16 +946,25 @@ MBasicBlock::inheritPhis(MBasicBlock *he
         // If the entryDef is the same as exitDef, then we must propagate the
         // phi down to this successor. This chance was missed as part of
         // setBackedge() because exits are not captured in resume points.
         setSlot(phi->slot(), phi);
     }
 }
 
 void
+MBasicBlock::specializePhis()
+{
+    for (MPhiIterator iter = phisBegin(); iter != phisEnd(); iter++) {
+        MPhi *phi = *iter;
+        phi->specializeType();
+    }
+}
+
+void
 MBasicBlock::dumpStack(FILE *fp)
 {
 #ifdef DEBUG
     fprintf(fp, " %-3s %-16s %-6s %-10s\n", "#", "name", "copyOf", "first/next");
     fprintf(fp, "-------------------------------------------\n");
     for (uint32_t i = 0; i < stackPosition_; i++) {
         fprintf(fp, " %-3d", i);
         fprintf(fp, " %-16p\n", (void *)slots_[i]);
--- a/js/src/ion/MIRGraph.h
+++ b/js/src/ion/MIRGraph.h
@@ -32,17 +32,18 @@ class LBlock;
 
 class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
 {
   public:
     enum Kind {
         NORMAL,
         PENDING_LOOP_HEADER,
         LOOP_HEADER,
-        SPLIT_EDGE
+        SPLIT_EDGE,
+        DEAD
     };
 
   private:
     MBasicBlock(MIRGraph &graph, CompileInfo &info, jsbytecode *pc, Kind kind);
     bool init();
     void copySlots(MBasicBlock *from);
     bool inherit(MBasicBlock *pred, uint32_t popped);
     bool inheritResumePoint(MBasicBlock *pred);
@@ -178,45 +179,56 @@ class MBasicBlock : public TempObject, p
     // This may introduce redundant phis if the new block has fewer
     // than two predecessors.
     void removePredecessor(MBasicBlock *pred);
 
     // Resets all the dominator info so that it can be recomputed.
     void clearDominatorInfo();
 
     // Sets a back edge. This places phi nodes and rewrites instructions within
-    // the current loop as necessary.
-    bool setBackedge(MBasicBlock *block);
+    // the current loop as necessary. If the backedge introduces new types for
+    // phis at the loop header, returns a disabling abort.
+    AbortReason setBackedge(MBasicBlock *block);
 
     // Resets a LOOP_HEADER block to a NORMAL block.  This is needed when
     // optimizations remove the backedge.
     void clearLoopHeader();
 
     // Propagates phis placed in a loop header down to this successor block.
     void inheritPhis(MBasicBlock *header);
 
+    // Compute the types for phis in this block according to their inputs.
+    void specializePhis();
+
     void insertBefore(MInstruction *at, MInstruction *ins);
     void insertAfter(MInstruction *at, MInstruction *ins);
 
     // Add an instruction to this block, from elsewhere in the graph.
     void addFromElsewhere(MInstruction *ins);
 
     // Move an instruction. Movement may cross block boundaries.
     void moveBefore(MInstruction *at, MInstruction *ins);
 
     // Removes an instruction with the intention to discard it.
     void discard(MInstruction *ins);
     void discardLastIns();
     MInstructionIterator discardAt(MInstructionIterator &iter);
     MInstructionReverseIterator discardAt(MInstructionReverseIterator &iter);
     MDefinitionIterator discardDefAt(MDefinitionIterator &iter);
+    void discardAllInstructions();
+    void discardAllPhis();
 
     // Discards a phi instruction and updates predecessor successorWithPhis.
     MPhiIterator discardPhiAt(MPhiIterator &at);
 
+    // Mark this block as having been removed from the graph.
+    void markAsDead() {
+        kind_ = DEAD;
+    }
+
     ///////////////////////////////////////////////////////
     /////////// END GRAPH BUILDING INSTRUCTIONS ///////////
     ///////////////////////////////////////////////////////
 
     MIRGraph &graph() {
         return graph_;
     }
     CompileInfo &info() const {
@@ -297,16 +309,19 @@ class MBasicBlock : public TempObject, p
         if (!numSuccessors())
             return false;
         MBasicBlock *lastSuccessor = getSuccessor(numSuccessors() - 1);
         return lastSuccessor->isLoopHeader() && lastSuccessor->backedge() == this;
     }
     bool isSplitEdge() const {
         return kind_ == SPLIT_EDGE;
     }
+    bool isDead() const {
+        return kind_ == DEAD;
+    }
 
     uint32_t stackDepth() const {
         return stackPosition_;
     }
     void setStackDepth(uint32_t depth) {
         stackPosition_ = depth;
     }
     bool isMarked() const {
@@ -549,16 +564,17 @@ class MIRGraph
         return blocks_.rend();
     }
     ReversePostorderIterator rpoBegin() {
         return blocks_.begin();
     }
     ReversePostorderIterator rpoEnd() {
         return blocks_.end();
     }
+    void removeBlocksAfter(MBasicBlock *block);
     void removeBlock(MBasicBlock *block) {
         blocks_.remove(block);
         numBlocks_--;
     }
     void moveBlockToEnd(MBasicBlock *block) {
         JS_ASSERT(block->id());
         blocks_.remove(block);
         blocks_.pushBack(block);
--- a/js/src/ion/MOpcodes.h
+++ b/js/src/ion/MOpcodes.h
@@ -130,16 +130,17 @@ namespace ion {
     _(StoreFixedSlot)                                                       \
     _(CallGetProperty)                                                      \
     _(GetNameCache)                                                         \
     _(CallGetIntrinsicValue)                                                \
     _(CallsiteCloneCache)                                                   \
     _(CallGetElement)                                                       \
     _(CallSetElement)                                                       \
     _(CallSetProperty)                                                      \
+    _(CallInitElementArray)                                                 \
     _(DeleteProperty)                                                       \
     _(SetPropertyCache)                                                     \
     _(IteratorStart)                                                        \
     _(IteratorNext)                                                         \
     _(IteratorMore)                                                         \
     _(IteratorEnd)                                                          \
     _(StringLength)                                                         \
     _(ArgumentsLength)                                                      \
--- a/js/src/ion/ParallelArrayAnalysis.cpp
+++ b/js/src/ion/ParallelArrayAnalysis.cpp
@@ -215,16 +215,17 @@ class ParallelArrayVisitor : public MIns
     SAFE_OP(LoadFixedSlot)
     WRITE_GUARDED_OP(StoreFixedSlot, object)
     UNSAFE_OP(CallGetProperty)
     UNSAFE_OP(GetNameCache)
     SAFE_OP(CallGetIntrinsicValue) // Bails in parallel mode
     UNSAFE_OP(CallsiteCloneCache)
     UNSAFE_OP(CallGetElement)
     UNSAFE_OP(CallSetElement)
+    UNSAFE_OP(CallInitElementArray)
     UNSAFE_OP(CallSetProperty)
     UNSAFE_OP(DeleteProperty)
     UNSAFE_OP(SetPropertyCache)
     UNSAFE_OP(IteratorStart)
     UNSAFE_OP(IteratorNext)
     UNSAFE_OP(IteratorMore)
     UNSAFE_OP(IteratorEnd)
     SAFE_OP(StringLength)
@@ -766,18 +767,16 @@ GetPossibleCallees(JSContext *cx, Handle
     }
 
     return true;
 }
 
 bool
 ParallelArrayVisitor::visitCall(MCall *ins)
 {
-    JS_ASSERT(ins->getSingleTarget() || ins->calleeTypes());
-
     // DOM? Scary.
     if (ins->isDOMFunction()) {
         SpewMIR(ins, "call to dom function");
         return markUnsafe();
     }
 
     RootedFunction target(cx_, ins->getSingleTarget());
     if (target) {
@@ -789,19 +788,22 @@ ParallelArrayVisitor::visitCall(MCall *i
         return graph_.addCallTarget(target->nonLazyScript());
     }
 
     if (ins->isConstructing()) {
         SpewMIR(ins, "call to unknown constructor");
         return markUnsafe();
     }
 
+    types::StackTypeSet *calleeTypes = ins->getFunction()->resultTypeSet();
+    JS_ASSERT(calleeTypes);
+
     RootedScript script(cx_, ins->block()->info().script());
     return GetPossibleCallees(cx_, script, ins->resumePoint()->pc(),
-                              ins->calleeTypes(), graph_);
+                              calleeTypes, graph_);
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // Stack limit, interrupts
 //
 // In sequential Ion code, the stack limit is stored in the JSRuntime.
 // We store it in the thread context.  We therefore need a separate
 // instruction to access it, one parameterized by the thread context.
--- a/js/src/ion/RegisterSets.h
+++ b/js/src/ion/RegisterSets.h
@@ -4,17 +4,16 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jsion_cpu_registersets_h__
 #define jsion_cpu_registersets_h__
 
 #include "Registers.h"
-#include "TypeOracle.h"
 #include "ion/IonAllocPolicy.h"
 
 namespace js {
 namespace ion {
 
 struct AnyRegister {
     typedef uint32_t Code;
 
deleted file mode 100644
--- a/js/src/ion/TypeOracle.cpp
+++ /dev/null
@@ -1,895 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=4 sw=4 et tw=99:
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "TypeOracle.h"
-
-#include "Ion.h"
-#include "IonSpewer.h"
-#include "jsinferinlines.h"
-#include "jsobjinlines.h"
-#include "jsanalyze.h"
-
-using namespace js;
-using namespace js::ion;
-using namespace js::types;
-using namespace js::analyze;
-
-TypeInferenceOracle::TypeInferenceOracle()
-  : cx(NULL),
-    script_(NULL)
-{}
-
-bool
-TypeInferenceOracle::init(JSContext *cx, JSScript *script, bool inlinedCall)
-{
-    AutoEnterAnalysis enter(cx);
-
-    this->cx = cx;
-    this->script_.init(script);
-
-    if (inlinedCall) {
-        JS_ASSERT_IF(script->length != 1, script->analysis()->ranInference());
-        return script->ensureRanInference(cx);
-    }
-
-    Vector<JSScript*> seen(cx);
-    return analyzeTypesForInlinableCallees(cx, script, seen);
-}
-
-bool
-TypeInferenceOracle::analyzeTypesForInlinableCallees(JSContext *cx, JSScript *script,
-                                                     Vector<JSScript*> &seen)
-{
-    // Don't analyze scripts which will not be inlined (but always analyze the first script).
-    if (seen.length() > 0 && script->getUseCount() < js_IonOptions.usesBeforeInlining())
-        return true;
-
-    for (size_t i = 0; i < seen.length(); i++) {
-        if (seen[i] == script)
-            return true;
-    }
-    if (!seen.append(script))
-        return false;
-
-    if (!script->ensureRanInference(cx))
-        return false;
-
-    ScriptAnalysis *analysis = script->analysis();
-    JS_ASSERT(analysis->ranInference());
-
-    for (jsbytecode *pc = script->code;
-         pc < script->code + script->length;
-         pc += GetBytecodeLength(pc))
-    {
-        if (!(js_CodeSpec[*pc].format & JOF_INVOKE))
-            continue;
-
-        if (!analysis->maybeCode(pc))
-            continue;
-
-        uint32_t argc = GET_ARGC(pc);
-
-        StackTypeSet *calleeTypes = analysis->poppedTypes(pc, argc + 1);
-        if (!analyzeTypesForInlinableCallees(cx, calleeTypes, seen))
-            return false;
-
-        // For foo.call() and foo.apply(), also look for any callees in the
-        // 'this' types of the call, which might be inlined by Ion.
-        if (*pc == JSOP_FUNCALL || *pc == JSOP_FUNAPPLY) {
-            StackTypeSet *thisTypes = analysis->poppedTypes(pc, argc);
-            if (!analyzeTypesForInlinableCallees(cx, thisTypes, seen))
-                return false;
-        }
-    }
-
-    return true;
-}
-
-bool
-TypeInferenceOracle::analyzeTypesForInlinableCallees(JSContext *cx, StackTypeSet *calleeTypes,
-                                                     Vector<JSScript*> &seen)
-{
-    if (calleeTypes->unknownObject())
-        return true;
-
-    size_t count = calleeTypes->getObjectCount();
-    for (size_t i = 0; i < count; i++) {
-        JSScript *script;
-        if (JSObject *singleton = calleeTypes->getSingleObject(i)) {
-            if (!singleton->isFunction() || !singleton->toFunction()->hasScript())
-                continue;
-            script = singleton->toFunction()->nonLazyScript();
-        } else if (TypeObject *type = calleeTypes->getTypeObject(i)) {
-            JSFunction *fun = type->interpretedFunction;
-            if (!fun || !fun->hasScript())
-                continue;
-            script = fun->nonLazyScript();
-        } else {
-            continue;
-        }
-
-        if (!analyzeTypesForInlinableCallees(cx, script, seen))
-            return false;
-
-        // The contents of type sets can change after analyzing the types in a
-        // script. Make a sanity check to ensure the set is ok to keep using.
-        if (calleeTypes->unknownObject() || calleeTypes->getObjectCount() != count)
-            break;
-    }
-
-    return true;
-}
-
-MIRType
-GetMIRType(JSValueType type)
-{
-    /* Get the suggested representation to use for values in a given type set. */
-    switch (type) {
-      case JSVAL_TYPE_UNDEFINED:
-        return MIRType_Undefined;
-      case JSVAL_TYPE_NULL:
-        return MIRType_Null;
-      case JSVAL_TYPE_BOOLEAN:
-        return MIRType_Boolean;
-      case JSVAL_TYPE_INT32:
-        return MIRType_Int32;
-      case JSVAL_TYPE_DOUBLE:
-        return MIRType_Double;
-      case JSVAL_TYPE_STRING:
-        return MIRType_String;
-      case JSVAL_TYPE_OBJECT:
-        return MIRType_Object;
-      case JSVAL_TYPE_MAGIC:
-        return MIRType_Magic;
-      default:
-        return MIRType_Value;
-    }
-}
-
-MIRType
-TypeInferenceOracle::getMIRType(StackTypeSet *types)
-{
-    return GetMIRType(types->getKnownTypeTag());
-}
-
-MIRType
-TypeInferenceOracle::getMIRType(HeapTypeSet *types)
-{
-    return GetMIRType(types->getKnownTypeTag(cx));
-}
-
-TypeOracle::UnaryTypes
-TypeInferenceOracle::unaryTypes(RawScript script, jsbytecode *pc)
-{
-    JS_ASSERT(script == this->script());
-
-    UnaryTypes res;
-    res.inTypes = script->analysis()->poppedTypes(pc, 0);
-    res.outTypes = script->analysis()->pushedTypes(pc, 0);
-    return res;
-}
-
-TypeOracle::BinaryTypes
-TypeInferenceOracle::binaryTypes(RawScript script, jsbytecode *pc)
-{
-    JS_ASSERT(script == this->script());
-
-    JSOp op = (JSOp)*pc;
-
-    BinaryTypes res;
-    if (op == JSOP_NEG || op == JSOP_POS) {
-        res.lhsTypes = script->analysis()->poppedTypes(pc, 0);
-        res.rhsTypes = NULL;
-        res.outTypes = script->analysis()->pushedTypes(pc, 0);
-    } else {
-        res.lhsTypes = script->analysis()->poppedTypes(pc, 1);
-        res.rhsTypes = script->analysis()->poppedTypes(pc, 0);
-        res.outTypes = script->analysis()->pushedTypes(pc, 0);
-    }
-    return res;
-}
-
-TypeOracle::Unary
-TypeInferenceOracle::unaryOp(RawScript script, jsbytecode *pc)
-{
-    JS_ASSERT(script == this->script());
-
-    Unary res;
-    res.ival = getMIRType(script->analysis()->poppedTypes(pc, 0));
-    res.rval = getMIRType(script->analysis()->pushedTypes(pc, 0));
-    return res;
-}
-
-TypeOracle::Binary
-TypeInferenceOracle::binaryOp(RawScript script, jsbytecode *pc)
-{
-    JS_ASSERT(script == this->script());
-
-    JSOp op = (JSOp)*pc;
-
-    Binary res;
-    if (op == JSOP_NEG || op == JSOP_POS) {
-        res.lhs = getMIRType(script->analysis()->poppedTypes(pc, 0));
-        res.rhs = MIRType_Int32;
-        res.rval = getMIRType(script->analysis()->pushedTypes(pc, 0));
-    } else {
-        res.lhs = getMIRType(script->analysis()->poppedTypes(pc, 1));
-        res.rhs = getMIRType(script->analysis()->poppedTypes(pc, 0));
-        res.rval = getMIRType(script->analysis()->pushedTypes(pc, 0));
-    }
-    return res;
-}
-
-StackTypeSet *
-TypeInferenceOracle::thisTypeSet(RawScript script)
-{
-    JS_ASSERT(script == this->script());
-    return TypeScript::ThisTypes(script);
-}
-
-bool
-TypeInferenceOracle::getOsrTypes(jsbytecode *osrPc, Vector<MIRType> &slotTypes)
-{
-    JS_ASSERT(JSOp(*osrPc) == JSOP_LOOPENTRY);
-    JS_ASSERT(script()->code < osrPc);
-    JS_ASSERT(osrPc < script()->code + script()->length);
-
-    Vector<types::StackTypeSet *> slotTypeSets(cx);
-    if (!slotTypeSets.resize(TotalSlots(script())))
-        return false;
-
-    for (uint32_t slot = ThisSlot(); slot < TotalSlots(script()); slot++)
-        slotTypeSets[slot] = TypeScript::SlotTypes(script(), slot);
-
-    jsbytecode *pc = script()->code;
-    ScriptAnalysis *analysis = script()->analysis();
-
-    // To determine the slot types at the OSR pc, we have to do a forward walk
-    // over the bytecode to reconstruct the types.
-    for (;;) {
-        Bytecode *opinfo = analysis->maybeCode(pc);
-        if (opinfo) {
-            if (opinfo->jumpTarget) {
-                // Update variable types for all new values at this bytecode.
-                if (const SlotValue *newv = analysis->newValues(pc)) {
-                    while (newv->slot) {
-                        if (newv->slot < TotalSlots(script()))
-                            slotTypeSets[newv->slot] = analysis->getValueTypes(newv->value);
-                        newv++;
-                    }
-                }
-            }
-
-            if (BytecodeUpdatesSlot(JSOp(*pc))) {
-                uint32_t slot = GetBytecodeSlot(script(), pc);
-                if (analysis->trackSlot(slot))
-                    slotTypeSets[slot] = analysis->pushedTypes(pc, 0);
-            }
-        }
-
-        if (pc == osrPc)
-            break;
-
-        pc += GetBytecodeLength(pc);
-    }
-
-    JS_ASSERT(pc == osrPc);
-
-    // TI always includes the |this| slot, but Ion only does so for function
-    // scripts. This means we have to subtract 1 for global/eval scripts.
-    JS_ASSERT(ThisSlot() == 1);
-    JS_ASSERT(ArgSlot(0) == 2);
-
-#ifdef DEBUG
-    uint32_t stackDepth = analysis->getCode(osrPc).stackDepth;
-#endif
-
-    if (script()->function()) {
-        JS_ASSERT(slotTypes.length() == TotalSlots(script()) + stackDepth);
-
-        for (size_t i = ThisSlot(); i < TotalSlots(script()); i++)
-            slotTypes[i] = getMIRType(slotTypeSets[i]);
-    } else {
-        JS_ASSERT(slotTypes.length() == TotalSlots(script()) + stackDepth - 1);
-
-        for (size_t i = ArgSlot(0); i < TotalSlots(script()); i++)
-            slotTypes[i - 1] = getMIRType(slotTypeSets[i]);
-    }
-
-    return true;
-}
-
-StackTypeSet *
-TypeInferenceOracle::parameterTypeSet(RawScript script, size_t index)
-{
-    JS_ASSERT(script == this->script());
-    return TypeScript::ArgTypes(script, index);
-}
-
-StackTypeSet *
-TypeInferenceOracle::propertyRead(RawScript script, jsbytecode *pc)
-{
-    return script->analysis()->pushedTypes(pc, 0);
-}
-
-StackTypeSet *
-TypeInferenceOracle::propertyReadBarrier(HandleScript script, jsbytecode *pc)
-{
-    if (script->analysis()->typeBarriers(cx, pc))
-        return script->analysis()->bytecodeTypes(pc);
-    return NULL;
-}
-
-bool
-TypeInferenceOracle::propertyReadIdempotent(HandleScript script, jsbytecode *pc, HandleId id)
-{
-    if (id != IdToTypeId(id))
-        return false;
-
-    StackTypeSet *types = script->analysis()->poppedTypes(pc, 0);
-    if (!types || types->unknownObject())
-        return false;
-
-    for (unsigned i = 0; i < types->getObjectCount(); i++) {
-        if (types->getSingleObject(i))
-            return false;
-
-        if (TypeObject *obj = types->getTypeObject(i)) {
-            if (obj->unknownProperties())
-                return false;
-
-            // Check if the property has been reconfigured or is a getter.
-            HeapTypeSet *propertyTypes = obj->getProperty(cx, id, false);
-            if (!propertyTypes || propertyTypes->isOwnProperty(cx, obj, true))
-                return false;
-        }
-    }
-
-    return true;
-}
-
-bool
-TypeInferenceOracle::propertyReadAccessGetter(RawScript script, jsbytecode *pc)
-{
-    return script->analysis()->getCode(pc).accessGetter;
-}
-
-bool
-TypeInferenceOracle::inObjectIsDenseNativeWithoutExtraIndexedProperties(HandleScript script, jsbytecode *pc)
-{
-    // Check whether the object is a native and index is int32 or double.
-    StackTypeSet *id = script->analysis()->poppedTypes(pc, 1);
-    StackTypeSet *obj = script->analysis()->poppedTypes(pc, 0);
-
-    JSValueType idType = id->getKnownTypeTag();
-    if (idType != JSVAL_TYPE_INT32 && idType != JSVAL_TYPE_DOUBLE)
-        return false;
-
-    Class *clasp = obj->getKnownClass();
-    if (!clasp || !clasp->isNative())
-        return false;
-
-    return !types::TypeCanHaveExtraIndexedProperties(cx, obj);
-}
-
-bool
-TypeInferenceOracle::inArrayIsPacked(RawScript script, jsbytecode *pc)
-{
-    StackTypeSet *types = script->analysis()->poppedTypes(pc, 0);
-    return !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED);
-}
-
-bool
-TypeInferenceOracle::elementReadIsDenseNative(RawScript script, jsbytecode *pc)
-{
-    // Check whether the object is a dense array and index is int32 or double.
-    StackTypeSet *obj = script->analysis()->poppedTypes(pc, 1);
-    StackTypeSet *id = script->analysis()->poppedTypes(pc, 0);
-
-    JSValueType idType = id->getKnownTypeTag();
-    if (idType != JSVAL_TYPE_INT32 && idType != JSVAL_TYPE_DOUBLE)
-        return false;
-
-    if (obj->hasType(types::Type::StringType()))
-        return false;
-
-    Class *clasp = obj->getKnownClass();
-    return clasp && clasp->isNative();
-}
-
-bool
-TypeInferenceOracle::elementReadIsTypedArray(RawScript script, jsbytecode *pc, int *arrayType)
-{
-    // Check whether the object is a typed array and index is int32 or double.
-    StackTypeSet *obj = script->analysis()->poppedTypes(pc, 1);
-    StackTypeSet *id = script->analysis()->poppedTypes(pc, 0);
-
-    JSValueType idType = id->getKnownTypeTag();
-    if (idType != JSVAL_TYPE_INT32 && idType != JSVAL_TYPE_DOUBLE)
-        return false;
-
-    if (obj->hasType(types::Type::StringType()))
-        return false;
-
-    *arrayType = obj->getTypedArrayType();
-    if (*arrayType == TypedArray::TYPE_MAX)
-        return false;
-
-    JS_ASSERT(*arrayType >= 0 && *arrayType < TypedArray::TYPE_MAX);
-
-    // Unlike dense arrays, the types of elements in typed arrays are not
-    // guaranteed to be present in the object's type, and we need to use
-    // knowledge about the possible contents of the array vs. the types
-    // that have been read out of it to figure out how to do the load.
-    types::TypeSet *result = propertyRead(script, pc);
-    if (*arrayType == TypedArray::TYPE_FLOAT32 || *arrayType == TypedArray::TYPE_FLOAT64) {
-        if (!result->hasType(types::Type::DoubleType()))
-            return false;
-    } else {
-        if (!result->hasType(types::Type::Int32Type()))
-            return false;
-    }
-
-    return true;
-}
-
-bool
-TypeInferenceOracle::elementReadIsString(RawScript script, jsbytecode *pc)
-{
-    // Check for string[index].
-    StackTypeSet *value = script->analysis()->poppedTypes(pc, 1);
-    StackTypeSet *id = script->analysis()->poppedTypes(pc, 0);
-
-    if (value->getKnownTypeTag() != JSVAL_TYPE_STRING)
-        return false;
-
-    JSValueType idType = id->getKnownTypeTag();
-    if (idType != JSVAL_TYPE_INT32 && idType != JSVAL_TYPE_DOUBLE)
-        return false;
-
-    // This function is used for jsop_getelem_string which should return
-    // undefined if this is out-side the string bounds. Currently we just
-    // fallback to a CallGetElement.
-    StackTypeSet *pushed = script->analysis()->pushedTypes(pc, 0);
-    if (pushed->getKnownTypeTag() != JSVAL_TYPE_STRING)
-        return false;
-
-    return true;
-}
-
-bool
-TypeInferenceOracle::elementReadShouldAlwaysLoadDoubles(RawScript script, jsbytecode *pc)
-{
-    StackTypeSet *types = script->analysis()->poppedTypes(pc, 1);
-    types::StackTypeSet::DoubleConversion conversion = types->convertDoubleElements(cx);
-    return conversion == StackTypeSet::AlwaysConvertToDoubles;
-}
-
-bool
-TypeInferenceOracle::elementReadHasExtraIndexedProperty(RawScript script, jsbytecode *pc)
-{
-    StackTypeSet *obj = script->analysis()->poppedTypes(pc, 1);
-    return types::TypeCanHaveExtraIndexedProperties(cx, obj);
-}
-
-bool
-TypeInferenceOracle::elementReadIsPacked(RawScript script, jsbytecode *pc)
-{
-    StackTypeSet *types = script->analysis()->poppedTypes(pc, 1);
-    return !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED);
-}
-
-void
-TypeInferenceOracle::elementReadGeneric(RawScript script, jsbytecode *pc, bool *cacheable, bool *monitorResult, bool *intIndex)
-{
-    MIRType obj = getMIRType(script->analysis()->poppedTypes(pc, 1));
-    MIRType id = getMIRType(script->analysis()->poppedTypes(pc, 0));
-
-    *cacheable = (obj == MIRType_Object &&
-                  (id == MIRType_Value || id == MIRType_Int32 || id == MIRType_String));
-
-    *intIndex = id == MIRType_Int32;
-
-    // Turn off cacheing if the element is int32 and we've seen non-native objects as the target
-    // of this getelem.
-    if (*cacheable && id == MIRType_Int32 && script->analysis()->getCode(pc).nonNativeGetElement)
-        *cacheable = false;
-
-    if (*cacheable)
-        *monitorResult = (id == MIRType_String || script->analysis()->getCode(pc).getStringElement);
-    else
-        *monitorResult = true;
-}
-
-bool
-TypeInferenceOracle::elementWriteIsDenseNative(HandleScript script, jsbytecode *pc)
-{
-    return elementWriteIsDenseNative(script->analysis()->poppedTypes(pc, 2),
-                                     script->analysis()->poppedTypes(pc, 1));
-}
-
-bool
-TypeInferenceOracle::elementWriteIsDenseNative(StackTypeSet *obj, StackTypeSet *id)
-{
-    // Check whether the object is a dense array and index is int32 or double.
-    JSValueType idType = id->getKnownTypeTag();
-    if (idType != JSVAL_TYPE_INT32 && idType != JSVAL_TYPE_DOUBLE)
-        return false;
-
-    Class *clasp = obj->getKnownClass();
-    if (!clasp || !clasp->isNative())
-        return false;
-
-    return obj->convertDoubleElements(cx) != StackTypeSet::AmbiguousDoubleConversion;
-}
-
-bool
-TypeInferenceOracle::elementWriteIsTypedArray(RawScript script, jsbytecode *pc, int *arrayType)
-{
-    return elementWriteIsTypedArray(script->analysis()->poppedTypes(pc, 2),
-                                    script->analysis()->poppedTypes(pc, 1),
-                                    arrayType);
-}
-
-bool
-TypeInferenceOracle::elementWriteIsTypedArray(StackTypeSet *obj, StackTypeSet *id, int *arrayType)
-{
-    // Check whether the object is a typed array and index is int32 or double.
-    JSValueType idType = id->getKnownTypeTag();
-    if (idType != JSVAL_TYPE_INT32 && idType != JSVAL_TYPE_DOUBLE)
-        return false;
-
-    *arrayType = obj->getTypedArrayType();
-    if (*arrayType == TypedArray::TYPE_MAX)
-        return false;
-
-    return true;
-}
-
-bool
-TypeInferenceOracle::elementWriteNeedsDoubleConversion(RawScript script, jsbytecode *pc)
-{
-    StackTypeSet *types = script->analysis()->poppedTypes(pc, 2);
-    types::StackTypeSet::DoubleConversion conversion = types->convertDoubleElements(cx);
-    return conversion == StackTypeSet::AlwaysConvertToDoubles ||
-           conversion == StackTypeSet::MaybeConvertToDoubles;
-}
-
-bool
-TypeInferenceOracle::elementWriteHasExtraIndexedProperty(RawScript script, jsbytecode *pc)
-{
-    StackTypeSet *obj = script->analysis()->poppedTypes(pc, 2);
-
-    if (obj->hasObjectFlags(cx, types::OBJECT_FLAG_LENGTH_OVERFLOW))
-        return true;
-
-    return types::TypeCanHaveExtraIndexedProperties(cx, obj);
-}
-
-bool
-TypeInferenceOracle::elementWriteIsPacked(RawScript script, jsbytecode *pc)
-{
-    StackTypeSet *types = script->analysis()->poppedTypes(pc, 2);
-    return !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED);
-}
-
-bool
-TypeInferenceOracle::setElementHasWrittenHoles(RawScript script, jsbytecode *pc)
-{
-    return script->analysis()->getCode(pc).arrayWriteHole;
-}
-
-MIRType
-TypeInferenceOracle::elementWrite(RawScript script, jsbytecode *pc)
-{
-    StackTypeSet *objTypes = script->analysis()->poppedTypes(pc, 2);
-    MIRType elementType = MIRType_None;
-    unsigned count = objTypes->getObjectCount();
-
-    for (unsigned i = 0; i < count; i++) {
-        if (objTypes->getSingleObject(i))
-            return MIRType_None;
-
-        if (TypeObject *object = objTypes->getTypeObject(i)) {
-            if (object->unknownProperties())
-                return MIRType_None;
-
-            types::HeapTypeSet *elementTypes = object->getProperty(cx, JSID_VOID, false);
-            if (!elementTypes)
-                return MIRType_None;
-
-            MIRType type = getMIRType(elementTypes);
-            if (type == MIRType_None)
-                return MIRType_None;
-
-            if (elementType == MIRType_None)
-                elementType = type;
-            else if (elementType != type)
-                return MIRType_None;
-        }
-    }
-
-    return elementType;
-}
-
-bool
-TypeInferenceOracle::arrayResultShouldHaveDoubleConversion(RawScript script, jsbytecode *pc)
-{
-    types::StackTypeSet::DoubleConversion conversion =
-        script->analysis()->pushedTypes(pc, 0)->convertDoubleElements(cx);
-    return conversion == types::StackTypeSet::AlwaysConvertToDoubles;
-}
-
-bool
-TypeInferenceOracle::propertyWriteCanSpecialize(RawScript script, jsbytecode *pc)
-{
-    return !script->analysis()->getCode(pc).monitoredTypes;
-}
-
-bool
-TypeInferenceOracle::propertyWriteNeedsBarrier(RawScript script, jsbytecode *pc, RawId id)
-{
-    StackTypeSet *types = script->analysis()->poppedTypes(pc, 1);
-    return types->propertyNeedsBarrier(cx, id);
-}
-
-bool
-TypeInferenceOracle::elementWriteNeedsBarrier(RawScript script, jsbytecode *pc)
-{
-    // Return true if SETELEM-like instructions need a write barrier before modifying
-    // a property. The object is the third value popped by SETELEM.
-    return elementWriteNeedsBarrier(script->analysis()->poppedTypes(pc, 2));
-}
-
-bool
-TypeInferenceOracle::elementWriteNeedsBarrier(StackTypeSet *obj)
-{
-    return obj->propertyNeedsBarrier(cx, JSID_VOID);
-}
-
-StackTypeSet *
-TypeInferenceOracle::getCallTarget(RawScript caller, uint32_t argc, jsbytecode *pc)
-{
-    JS_ASSERT(caller == this->script());
-    JS_ASSERT(js_CodeSpec[*pc].format & JOF_INVOKE);
-
-    ScriptAnalysis *analysis = script()->analysis();
-    return analysis->poppedTypes(pc, argc + 1);
-}
-
-StackTypeSet *
-TypeInferenceOracle::getCallArg(RawScript script, uint32_t argc, uint32_t arg, jsbytecode *pc)
-{
-    JS_ASSERT(argc >= arg);
-    // Bytecode order: Function, This, Arg0, Arg1, ..., ArgN, Call.
-    // |argc| does not include |this|.
-    return script->analysis()->poppedTypes(pc, argc - arg);
-}
-
-StackTypeSet *
-TypeInferenceOracle::getCallReturn(RawScript script, jsbytecode *pc)
-{
-    return script->analysis()->pushedTypes(pc, 0);
-}
-
-bool
-TypeInferenceOracle::canInlineCall(HandleScript caller, jsbytecode *pc)
-{
-    JS_ASSERT(types::IsInlinableCall(pc));
-
-    JSOp op = JSOp(*pc);
-    Bytecode *code = caller->analysis()->maybeCode(pc);
-
-    // For foo.apply(this, arguments), the caller is foo and not the js_fun_apply function.
-    // Ignore code->monitoredTypes, as we know the caller is foo
-    if (op != JSOP_FUNAPPLY && code->monitoredTypes)
-        return false;
-    return true;
-}
-
-types::TypeBarrier*
-TypeInferenceOracle::callArgsBarrier(HandleScript caller, jsbytecode *pc)
-{
-    JS_ASSERT(types::IsInlinableCall(pc));
-    return caller->analysis()->typeBarriers(cx, pc);
-}
-
-bool
-TypeInferenceOracle::canEnterInlinedFunction(RawFunction target)
-{
-    RootedScript targetScript(cx, target->nonLazyScript());
-
-    // Make sure empty script has type information, to allow inlining in more cases.
-    if (targetScript->length == 1) {
-        if (!targetScript->ensureRanInference(cx))
-            return false;
-    }
-
-    if (!targetScript->hasAnalysis() ||
-        !targetScript->analysis()->ranInference() ||
-        !targetScript->analysis()->ranSSA())
-    {
-        return false;
-    }
-
-    if (!targetScript->analysis()->ionInlineable())
-        return false;
-
-    if (targetScript->needsArgsObj())
-        return false;
-
-    if (!targetScript->compileAndGo)
-        return false;
-
-    if (targetScript->analysis()->usesScopeChain())
-        return false;
-
-    types::TypeObject *targetType = target->getType(cx);
-    if (!targetType || targetType->unknownProperties())
-        return false;
-
-    // TI calls ObjectStateChange to trigger invalidation of the caller.
-    HeapTypeSet::WatchObjectStateChange(cx, targetType);
-    return true;
-}
-
-bool
-TypeInferenceOracle::callReturnTypeSetMatches(RawScript callerScript, jsbytecode *callerPc,
-                                              RawFunction callee)
-{
-    RootedScript targetScript(cx, callee->nonLazyScript());
-
-    JSOp op = JSOp(*callerPc);
-    TypeSet *returnTypes = TypeScript::ReturnTypes(targetScript);
-    TypeSet *callReturn = getCallReturn(callerScript, callerPc);
-    if (op == JSOP_NEW) {
-        if (!returnTypes->isSubsetIgnorePrimitives(callReturn))
-            return false;
-    } else {
-        if (!returnTypes->isSubset(callReturn))
-            return false;
-    }
-
-    return true;
-}
-
-bool
-TypeInferenceOracle::callArgsTypeSetMatches(types::StackTypeSet *thisType, Vector<types::StackTypeSet *> &argvType, RawFunction callee)
-{
-    RootedScript targetScript(cx, callee->nonLazyScript());
-    types::TypeSet *calleeType;
-
-    size_t nargs = Min<size_t>(callee->nargs, argvType.length());
-
-    // This
-    calleeType = types::TypeScript::ThisTypes(targetScript);
-    if (!thisType->isSubset(calleeType))
-        return false;
-
-    // Arguments
-    for (size_t i = 0; i < nargs; i++) {
-        calleeType = types::TypeScript::ArgTypes(targetScript, i);
-        if (!argvType[i]->isSubset(calleeType))
-            return false;
-    }
-
-    // Arguments that weren't provided will be Undefined
-    for (size_t i = nargs; i < callee->nargs; i++) {
-        calleeType = types::TypeScript::ArgTypes(targetScript, i);
-        if (calleeType->unknown() ||
-            !calleeType->hasType(types::Type::UndefinedType()))
-        {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-bool
-TypeInferenceOracle::callArgsTypeSetIntersects(types::StackTypeSet *thisType, Vector<types::StackTypeSet *> &argvType, RawFunction callee)
-{
-    RootedScript targetScript(cx, callee->nonLazyScript());
-    types::TypeSet *calleeType;
-
-    size_t nargs = Min<size_t>(callee->nargs, argvType.length());
-
-    // This
-    if (thisType) {
-        calleeType = types::TypeScript::ThisTypes(targetScript);
-        if (thisType->intersectionEmpty(calleeType))
-            return false;
-    }
-
-    // Arguments
-    for (size_t i = 0; i < nargs; i++) {
-        calleeType = types::TypeScript::ArgTypes(targetScript, i);
-        if (argvType[i]->intersectionEmpty(calleeType))
-            return false;
-    }
-
-    // Arguments that weren't provided will be Undefined
-    for (size_t i = nargs; i < callee->nargs; i++) {
-        calleeType = types::TypeScript::ArgTypes(targetScript, i);
-        if (calleeType->unknown() ||
-            !calleeType->hasType(types::Type::UndefinedType()))
-        {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-HeapTypeSet *
-TypeInferenceOracle::globalPropertyWrite(RawScript script, jsbytecode *pc, jsid id,
-                                         bool *canSpecialize)
-{
-    *canSpecialize = !script->analysis()->getCode(pc).monitoredTypes;
-    if (!*canSpecialize)
-        return NULL;
-
-    return globalPropertyTypeSet(script, pc, id);
-}
-
-StackTypeSet *
-TypeInferenceOracle::returnTypeSet(RawScript script, jsbytecode *pc, types::StackTypeSet **barrier)
-{
-    if (script->analysis()->getCode(pc).monitoredTypesReturn)
-        *barrier = script->analysis()->bytecodeTypes(pc);
-    else
-        *barrier = NULL;
-    return script->analysis()->pushedTypes(pc, 0);
-}
-
-StackTypeSet *
-TypeInferenceOracle::aliasedVarBarrier(RawScript script, jsbytecode *pc, types::StackTypeSet **barrier)
-{
-    *barrier = script->analysis()->bytecodeTypes(pc);
-    return script->analysis()->pushedTypes(pc, 0);
-}
-
-HeapTypeSet *
-TypeInferenceOracle::globalPropertyTypeSet(RawScript script, jsbytecode *pc, jsid id)
-{
-    TypeObject *type = script->global().getType(cx);
-    if (!type || type->unknownProperties())
-        return NULL;
-
-    return type->getProperty(cx, id, false);
-}
-
-LazyArgumentsType
-TypeInferenceOracle::isArgumentObject(types::StackTypeSet *obj)
-{
-    if (obj->isMagicArguments())
-        return DefinitelyArguments;
-    if (obj->hasAnyFlag(TYPE_FLAG_LAZYARGS))
-        return MaybeArguments;
-    return NotArguments;
-}
-
-LazyArgumentsType
-TypeInferenceOracle::propertyReadMagicArguments(RawScript script, jsbytecode *pc)
-{
-    StackTypeSet *obj = script->analysis()->poppedTypes(pc, 0);
-    return isArgumentObject(obj);
-}
-
-LazyArgumentsType
-TypeInferenceOracle::elementReadMagicArguments(RawScript script, jsbytecode *pc)
-{
-    StackTypeSet *obj = script->analysis()->poppedTypes(pc, 1);
-    return isArgumentObject(obj);
-}
-
-LazyArgumentsType
-TypeInferenceOracle::elementWriteMagicArguments(RawScript script, jsbytecode *pc)
-{
-    StackTypeSet *obj = script->analysis()->poppedTypes(pc, 2);
-    return isArgumentObject(obj);
-}
deleted file mode 100644
--- a/js/src/ion/TypeOracle.h
+++ /dev/null
@@ -1,422 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=4 sw=4 et tw=99:
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef js_ion_type_oracle_h__
-#define js_ion_type_oracle_h__
-
-#include "jsscript.h"
-#include "IonTypes.h"
-
-namespace js {
-namespace ion {
-
-enum LazyArgumentsType {
-    MaybeArguments = 0,
-    DefinitelyArguments,
-    NotArguments
-};
-
-class TypeOracle
-{
-  public:
-    struct UnaryTypes {
-        types::StackTypeSet *inTypes;
-        types::StackTypeSet *outTypes;
-    };
-
-    struct BinaryTypes {
-        types::StackTypeSet *lhsTypes;
-        types::StackTypeSet *rhsTypes;
-        types::StackTypeSet *outTypes;
-    };
-
-    struct Unary {
-        MIRType ival;
-        MIRType rval;
-    };
-    struct Binary {
-        MIRType lhs;
-        MIRType rhs;
-        MIRType rval;
-    };
-
-  public:
-    virtual UnaryTypes unaryTypes(RawScript script, jsbytecode *pc) = 0;
-    virtual BinaryTypes binaryTypes(RawScript script, jsbytecode *pc) = 0;
-    virtual Unary unaryOp(RawScript script, jsbytecode *pc) = 0;
-    virtual Binary binaryOp(RawScript script, jsbytecode *pc) = 0;
-    virtual types::StackTypeSet *thisTypeSet(RawScript script) { return NULL; }
-    virtual bool getOsrTypes(jsbytecode *osrPc, Vector<MIRType> &slotTypes) { return true; }
-    virtual types::StackTypeSet *parameterTypeSet(RawScript script, size_t index) { return NULL; }
-    virtual types::HeapTypeSet *globalPropertyTypeSet(RawScript script, jsbytecode *pc, jsid id) {
-        return NULL;
-    }
-    virtual types::StackTypeSet *propertyRead(RawScript script, jsbytecode *pc) {
-        return NULL;
-    }
-    virtual types::StackTypeSet *propertyReadBarrier(HandleScript script, jsbytecode *pc) {
-        return NULL;
-    }
-    virtual bool propertyReadIdempotent(HandleScript script, jsbytecode *pc, HandleId id) {
-        return false;
-    }
-    virtual bool propertyReadAccessGetter(RawScript script, jsbytecode *pc) {
-        return false;
-    }
-    virtual types::HeapTypeSet *globalPropertyWrite(RawScript script, jsbytecode *pc,
-                                                jsid id, bool *canSpecialize) {
-        *canSpecialize = true;
-        return NULL;
-    }
-    virtual types::StackTypeSet *returnTypeSet(RawScript script, jsbytecode *pc, types::StackTypeSet **barrier) {
-        *barrier = NULL;
-        return NULL;
-    }
-    virtual bool inObjectIsDenseNativeWithoutExtraIndexedProperties(HandleScript script, jsbytecode *pc) {
-        return false;
-    }
-    virtual bool inArrayIsPacked(RawScript script, jsbytecode *pc) {
-        return false;
-    }
-    virtual bool elementReadIsDenseNative(RawScript script, jsbytecode *pc) {
-        return false;
-    }
-    virtual bool elementReadIsTypedArray(RawScript script, jsbytecode *pc, int *arrayType) {
-        return false;
-    }
-    virtual bool elementReadIsString(RawScript script, jsbytecode *pc) {
-        return false;
-    }
-    virtual bool elementReadShouldAlwaysLoadDoubles(RawScript script, jsbytecode *pc) {
-        return false;
-    }
-    virtual bool elementReadHasExtraIndexedProperty(RawScript, jsbytecode *pc) {
-        return false;
-    }
-    virtual bool elementReadIsPacked(RawScript script, jsbytecode *pc) {
-        return false;
-    }
-    virtual void elementReadGeneric(RawScript script, jsbytecode *pc, bool *cacheable, bool *monitorResult, bool *intIndex) {
-        *cacheable = false;
-        *monitorResult = true;
-        *intIndex = false;
-    }
-    virtual bool setElementHasWrittenHoles(RawScript script, jsbytecode *pc) {
-        return true;
-    }
-    virtual bool elementWriteIsDenseNative(HandleScript script, jsbytecode *pc) {
-        return false;
-    }
-    virtual bool elementWriteIsDenseNative(types::StackTypeSet *obj, types::StackTypeSet *id) {
-        return false;
-    }
-    virtual bool elementWriteIsTypedArray(RawScript script, jsbytecode *pc, int *arrayType) {
-        return false;
-    }
-    virtual bool elementWriteIsTypedArray(types::StackTypeSet *obj, types::StackTypeSet *id, int *arrayType) {
-        return false;
-    }
-    virtual bool elementWriteNeedsDoubleConversion(RawScript script, jsbytecode *pc) {
-        return false;
-    }
-    virtual bool elementWriteHasExtraIndexedProperty(RawScript script, jsbytecode *pc) {
-        return false;
-    }
-    virtual bool elementWriteIsPacked(RawScript script, jsbytecode *pc) {
-        return false;
-    }
-    virtual bool arrayResultShouldHaveDoubleConversion(RawScript script, jsbytecode *pc) {
-        return false;
-    }
-    virtual bool propertyWriteCanSpecialize(RawScript script, jsbytecode *pc) {
-        return true;
-    }
-    virtual bool propertyWriteNeedsBarrier(RawScript script, jsbytecode *pc, RawId id) {
-        return true;
-    }
-    virtual bool elementWriteNeedsBarrier(RawScript script, jsbytecode *pc) {
-        return true;
-    }
-    virtual bool elementWriteNeedsBarrier(types::StackTypeSet *obj) {
-        return true;
-    }
-    virtual MIRType elementWrite(RawScript script, jsbytecode *pc) {
-        return MIRType_None;
-    }
-
-    /* |pc| must be a |JSOP_CALL|. */
-    virtual types::StackTypeSet *getCallTarget(RawScript caller, uint32_t argc, jsbytecode *pc) {
-        // Same assertion as TypeInferenceOracle::getCallTarget.
-        JS_ASSERT(js_CodeSpec[*pc].format & JOF_INVOKE && JSOp(*pc) != JSOP_EVAL);
-        return NULL;
-    }
-    virtual types::StackTypeSet *getCallArg(RawScript script, uint32_t argc, uint32_t arg, jsbytecode *pc) {
-        return NULL;
-    }
-    virtual types::StackTypeSet *getCallReturn(RawScript script, jsbytecode *pc) {
-        return NULL;
-    }
-    virtual bool canInlineCall(HandleScript caller, jsbytecode *pc) {
-        return false;
-    }
-    virtual types::TypeBarrier *callArgsBarrier(HandleScript caller, jsbytecode *pc) {
-        return NULL;
-    }
-    virtual bool canEnterInlinedFunction(RawFunction callee) {
-        return false;
-    }
-    virtual bool callReturnTypeSetMatches(RawScript callerScript, jsbytecode *callerPc,
-                                     RawFunction callee)
-    {
-        return false;
-    }
-    virtual bool callArgsTypeSetIntersects(types::StackTypeSet *thisType, Vector<types::StackTypeSet *> &argvType, RawFunction callee)
-    {
-        return false;
-    }
-    virtual bool callArgsTypeSetMatches(types::StackTypeSet *thisType, Vector<types::StackTypeSet *> &argvType, RawFunction callee)
-    {
-        return false;
-    }
-    virtual types::StackTypeSet *aliasedVarBarrier(RawScript script, jsbytecode *pc,
-                                                   types::StackTypeSet **barrier)
-    {
-        return NULL;
-    }
-
-    virtual LazyArgumentsType isArgumentObject(types::StackTypeSet *obj) {
-        return MaybeArguments;
-    }
-    virtual LazyArgumentsType propertyReadMagicArguments(RawScript script, jsbytecode *pc) {
-        return MaybeArguments;
-    }
-    virtual LazyArgumentsType elementReadMagicArguments(RawScript script, jsbytecode *pc) {
-        return MaybeArguments;
-    }
-    virtual LazyArgumentsType elementWriteMagicArguments(RawScript script, jsbytecode *pc) {
-        return MaybeArguments;
-    }
-};
-
-class DummyOracle : public TypeOracle
-{
-  public:
-    UnaryTypes unaryTypes(RawScript script, jsbytecode *pc) {
-        UnaryTypes u;
-        u.inTypes = NULL;
-        u.outTypes = NULL;
-        return u;
-    }
-    BinaryTypes binaryTypes(RawScript script, jsbytecode *pc) {
-        BinaryTypes b;
-        b.lhsTypes = NULL;
-        b.rhsTypes = NULL;
-        b.outTypes = NULL;
-        return b;
-    }
-    Unary unaryOp(RawScript script, jsbytecode *pc) {
-        Unary u;
-        u.ival = MIRType_Int32;
-        u.rval = MIRType_Int32;
-        return u;
-    }
-    Binary binaryOp(RawScript script, jsbytecode *pc) {
-        Binary b;
-        b.lhs = MIRType_Int32;
-        b.rhs = MIRType_Int32;
-        b.rval = MIRType_Int32;
-        return b;
-    }
-};
-
-class TypeInferenceOracle : public TypeOracle
-{
-    JSContext *cx;
-    HeapPtrScript script_;
-
-    MIRType getMIRType(types::StackTypeSet *types);
-    MIRType getMIRType(types::HeapTypeSet *types);
-
-    bool analyzeTypesForInlinableCallees(JSContext *cx, JSScript *script,
-                                         Vector<JSScript*> &seen);
-    bool analyzeTypesForInlinableCallees(JSContext *cx, types::StackTypeSet *calleeTypes,
-                                         Vector<JSScript*> &seen);
-
-  public:
-    TypeInferenceOracle();
-
-    bool init(JSContext *cx, JSScript *script, bool inlinedCall);
-
-    RawScript script() { return script_.get(); }
-
-    UnaryTypes unaryTypes(RawScript script, jsbytecode *pc);
-    BinaryTypes binaryTypes(RawScript script, jsbytecode *pc);
-    Unary unaryOp(RawScript script, jsbytecode *pc);
-    Binary binaryOp(RawScript script, jsbytecode *pc);
-    types::StackTypeSet *thisTypeSet(RawScript script);
-    bool getOsrTypes(jsbytecode *osrPc, Vector<MIRType> &slotTypes);
-    types::StackTypeSet *parameterTypeSet(RawScript script, size_t index);
-    types::HeapTypeSet *globalPropertyTypeSet(RawScript script, jsbytecode *pc, jsid id);
-    types::StackTypeSet *propertyRead(RawScript script, jsbytecode *pc);
-    types::StackTypeSet *propertyReadBarrier(HandleScript script, jsbytecode *pc);
-    bool propertyReadIdempotent(HandleScript script, jsbytecode *pc, HandleId id);
-    bool propertyReadAccessGetter(RawScript script, jsbytecode *pc);
-    types::HeapTypeSet *globalPropertyWrite(RawScript script, jsbytecode *pc, jsid id, bool *canSpecialize);
-    types::StackTypeSet *returnTypeSet(RawScript script, jsbytecode *pc, types::StackTypeSet **barrier);
-    types::StackTypeSet *getCallTarget(RawScript caller, uint32_t argc, jsbytecode *pc);
-    types::StackTypeSet *getCallArg(RawScript caller, uint32_t argc, uint32_t arg, jsbytecode *pc);
-    types::StackTypeSet *getCallReturn(RawScript caller, jsbytecode *pc);
-    bool inObjectIsDenseNativeWithoutExtraIndexedProperties(HandleScript script, jsbytecode *pc);
-    bool inArrayIsPacked(RawScript script, jsbytecode *pc);
-    bool elementReadIsDenseNative(RawScript script, jsbytecode *pc);
-    bool elementReadIsTypedArray(RawScript script, jsbytecode *pc, int *atype);
-    bool elementReadIsString(RawScript script, jsbytecode *pc);
-    bool elementReadShouldAlwaysLoadDoubles(RawScript script, jsbytecode *pc);
-    bool elementReadHasExtraIndexedProperty(RawScript, jsbytecode *pc);
-    bool elementReadIsPacked(RawScript script, jsbytecode *pc);
-    void elementReadGeneric(RawScript script, jsbytecode *pc, bool *cacheable, bool *monitorResult, bool *intIndex);
-    bool elementWriteIsDenseNative(HandleScript script, jsbytecode *pc);
-    bool elementWriteIsDenseNative(types::StackTypeSet *obj, types::StackTypeSet *id);
-    bool elementWriteIsTypedArray(RawScript script, jsbytecode *pc, int *arrayType);
-    bool elementWriteIsTypedArray(types::StackTypeSet *obj, types::StackTypeSet *id, int *arrayType);
-    bool elementWriteNeedsDoubleConversion(RawScript script, jsbytecode *pc);
-    bool elementWriteHasExtraIndexedProperty(RawScript script, jsbytecode *pc);
-    bool elementWriteIsPacked(RawScript script, jsbytecode *pc);
-    bool arrayResultShouldHaveDoubleConversion(RawScript script, jsbytecode *pc);
-    bool setElementHasWrittenHoles(RawScript script, jsbytecode *pc);
-    bool propertyWriteCanSpecialize(RawScript script, jsbytecode *pc);
-    bool propertyWriteNeedsBarrier(RawScript script, jsbytecode *pc, RawId id);
-    bool elementWriteNeedsBarrier(RawScript script, jsbytecode *pc);
-    bool elementWriteNeedsBarrier(types::StackTypeSet *obj);
-    MIRType elementWrite(RawScript script, jsbytecode *pc);
-    bool canInlineCall(HandleScript caller, jsbytecode *pc);
-    types::TypeBarrier *callArgsBarrier(HandleScript caller, jsbytecode *pc);
-    bool canEnterInlinedFunction(RawFunction callee);
-    bool callReturnTypeSetMatches(RawScript callerScript, jsbytecode *callerPc, RawFunction callee);
-    bool callArgsTypeSetIntersects(types::StackTypeSet *thisType, Vector<types::StackTypeSet *> &argvType, RawFunction callee);
-    bool callArgsTypeSetMatches(types::StackTypeSet *thisType, Vector<types::StackTypeSet *> &argvType, RawFunction callee);
-    types::StackTypeSet *aliasedVarBarrier(RawScript script, jsbytecode *pc, types::StackTypeSet **barrier);
-
-    LazyArgumentsType isArgumentObject(types::StackTypeSet *obj);
-    LazyArgumentsType propertyReadMagicArguments(RawScript script, jsbytecode *pc);
-    LazyArgumentsType elementReadMagicArguments(RawScript script, jsbytecode *pc);
-    LazyArgumentsType elementWriteMagicArguments(RawScript script, jsbytecode *pc);
-
-};
-
-static inline MIRType
-MIRTypeFromValueType(JSValueType type)
-{
-    switch (type) {
-      case JSVAL_TYPE_DOUBLE:
-        return MIRType_Double;
-      case JSVAL_TYPE_INT32:
-        return MIRType_Int32;
-      case JSVAL_TYPE_UNDEFINED:
-        return MIRType_Undefined;
-      case JSVAL_TYPE_STRING:
-        return MIRType_String;
-      case JSVAL_TYPE_BOOLEAN:
-        return MIRType_Boolean;
-      case JSVAL_TYPE_NULL:
-        return MIRType_Null;
-      case JSVAL_TYPE_OBJECT:
-        return MIRType_Object;
-      case JSVAL_TYPE_MAGIC:
-        return MIRType_Magic;
-      case JSVAL_TYPE_UNKNOWN:
-        return MIRType_Value;
-      default:
-        JS_NOT_REACHED("unexpected jsval type");
-        return MIRType_None;
-    }
-}
-
-static inline JSValueType
-ValueTypeFromMIRType(MIRType type)
-{
-  switch (type) {
-    case MIRType_Undefined:
-      return JSVAL_TYPE_UNDEFINED;
-    case MIRType_Null:
-      return JSVAL_TYPE_NULL;
-    case MIRType_Boolean:
-      return JSVAL_TYPE_BOOLEAN;
-    case MIRType_Int32:
-      return JSVAL_TYPE_INT32;
-    case MIRType_Double:
-      return JSVAL_TYPE_DOUBLE;
-    case MIRType_String:
-      return JSVAL_TYPE_STRING;
-    case MIRType_Magic:
-      return JSVAL_TYPE_MAGIC;
-    default:
-      JS_ASSERT(type == MIRType_Object);
-      return JSVAL_TYPE_OBJECT;
-  }
-}
-
-static inline JSValueTag
-MIRTypeToTag(MIRType type)
-{
-    return JSVAL_TYPE_TO_TAG(ValueTypeFromMIRType(type));
-}
-
-static inline const char *
-StringFromMIRType(MIRType type)
-{
-  switch (type) {
-    case MIRType_Undefined:
-      return "Undefined";
-    case MIRType_Null:
-      return "Null";
-    case MIRType_Boolean:
-      return "Bool";
-    case MIRType_Int32:
-      return "Int32";
-    case MIRType_Double:
-      return "Double";
-    case MIRType_String:
-      return "String";
-    case MIRType_Object:
-      return "Object";
-    case MIRType_Magic:
-      return "Magic";
-    case MIRType_Value:
-      return "Value";
-    case MIRType_None:
-      return "None";
-    case MIRType_Slots:
-      return "Slots";
-    case MIRType_Elements:
-      return "Elements";
-    case MIRType_Pointer:
-      return "Pointer";
-    case MIRType_ForkJoinSlice:
-      return "ForkJoinSlice";
-    default:
-      JS_NOT_REACHED("Unknown MIRType.");
-      return "";
-  }
-}
-
-static inline bool
-IsNumberType(MIRType type)
-{
-    return type == MIRType_Int32 || type == MIRType_Double;
-}
-
-static inline bool
-IsNullOrUndefined(MIRType type)
-{
-    return type == MIRType_Null || type == MIRType_Undefined;
-}
-
-} /* ion */
-} /* js */
-
-#endif // js_ion_type_oracle_h__
--- a/js/src/ion/TypePolicy.h
+++ b/js/src/ion/TypePolicy.h
@@ -3,17 +3,17 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jsion_type_policy_h__
 #define jsion_type_policy_h__
 
-#include "TypeOracle.h"
+#include "IonTypes.h"
 
 namespace js {
 namespace ion {
 
 class MInstruction;
 class MDefinition;
 
 // A type policy directs the type analysis phases, which insert conversion,
--- a/js/src/ion/arm/LIR-arm.h
+++ b/js/src/ion/arm/LIR-arm.h
@@ -3,17 +3,17 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jsion_lir_arm_h__
 #define jsion_lir_arm_h__
 
-#include "ion/TypeOracle.h"
+#include "ion/LIR.h"
 
 namespace js {
 namespace ion {
 
 class LBox : public LInstructionHelper<2, 1, 0>
 {
     MIRType type_;
 
--- a/js/src/ion/x64/LIR-x64.h
+++ b/js/src/ion/x64/LIR-x64.h
@@ -3,17 +3,17 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jsion_lir_x64_h__
 #define jsion_lir_x64_h__
 
-#include "ion/TypeOracle.h"
+#include "ion/LIR.h"
 
 namespace js {
 namespace ion {
 
 // Given a typed input, returns an untyped box.
 class LBox : public LInstructionHelper<1, 1, 0>
 {
     MIRType type_;
--- a/js/src/ion/x86/LIR-x86.h
+++ b/js/src/ion/x86/LIR-x86.h
@@ -3,17 +3,17 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jsion_lir_x86_h__
 #define jsion_lir_x86_h__
 
-#include "ion/TypeOracle.h"
+#include "ion/LIR.h"
 
 namespace js {
 namespace ion {
 
 class LBox : public LInstructionHelper<2, 1, 0>
 {
     MIRType type_;
 
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -321,16 +321,35 @@ types::TypeFailure(JSContext *cx, const 
     MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
     MOZ_CRASH();
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeSet
 /////////////////////////////////////////////////////////////////////
 
+TypeSet::TypeSet(Type type)
+  : flags(0), objectSet(NULL), constraintList(NULL)
+{
+    if (type.isUnknown()) {
+        flags |= TYPE_FLAG_BASE_MASK;
+    } else if (type.isPrimitive()) {
+        flags = PrimitiveTypeFlag(type.primitive());
+        if (flags == TYPE_FLAG_DOUBLE)
+            flags |= TYPE_FLAG_INT32;
+    } else if (type.isAnyObject()) {
+        flags |= TYPE_FLAG_ANYOBJECT;
+    } else  if (type.isTypeObject() && type.typeObject()->unknownProperties()) {
+        flags |= TYPE_FLAG_ANYOBJECT;
+    } else {
+        setBaseObjectCount(1);
+        objectSet = reinterpret_cast<TypeObjectKey**>(type.objectKey());
+    }
+}
+
 bool
 TypeSet::isSubset(TypeSet *other)
 {
     if ((baseFlags() & other->baseFlags()) != baseFlags())
         return false;
 
     if (unknownObject()) {
         JS_ASSERT(other->unknownObject());
@@ -534,17 +553,17 @@ StackTypeSet::make(JSContext *cx, const 
     InferSpew(ISpewOps, "typeSet: %sT%p%s intermediate %s",
               InferSpewColor(res), res, InferSpewColorReset(),
               name);
     res->setPurged();
 
     return res;
 }
 
-const StackTypeSet *
+StackTypeSet *
 TypeSet::clone(LifoAlloc *alloc) const
 {
     unsigned objectCount = baseObjectCount();
     unsigned capacity = (objectCount >= 2) ? HashSetCapacity(objectCount) : 0;
 
     StackTypeSet *res = alloc->new_<StackTypeSet>();
     if (!res)
         return NULL;
@@ -558,16 +577,65 @@ TypeSet::clone(LifoAlloc *alloc) const
     }
 
     res->flags = this->flags;
     res->objectSet = capacity ? newSet : this->objectSet;
 
     return res;
 }
 
+bool
+TypeSet::addObject(TypeObjectKey *key, LifoAlloc *alloc)
+{
+    JS_ASSERT(!constraintList);
+
+    uint32_t objectCount = baseObjectCount();
+    TypeObjectKey **pentry = HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
+                                 (*alloc, objectSet, objectCount, key);
+    if (!pentry)
+        return false;
+    if (*pentry)
+        return true;
+    *pentry = key;
+
+    setBaseObjectCount(objectCount);
+
+    if (objectCount == TYPE_FLAG_OBJECT_COUNT_LIMIT) {
+        flags |= TYPE_FLAG_ANYOBJECT;
+        clearObjects();
+    }
+
+    return true;
+}
+
+/* static */ StackTypeSet *
+TypeSet::unionSets(TypeSet *a, TypeSet *b, LifoAlloc *alloc)
+{
+    StackTypeSet *res = alloc->new_<StackTypeSet>();
+    if (!res)
+        return NULL;
+
+    res->flags = a->baseFlags() | b->baseFlags();
+
+    if (!res->unknownObject()) {
+        for (size_t i = 0; i < a->getObjectCount() && !res->unknownObject(); i++) {
+            TypeObjectKey *key = a->getObject(i);
+            if (key && !res->addObject(key, alloc))
+                return NULL;
+        }
+        for (size_t i = 0; i < b->getObjectCount() && !res->unknownObject(); i++) {
+            TypeObjectKey *key = b->getObject(i);
+            if (key && !res->addObject(key, alloc))
+                return NULL;
+        }
+    }
+
+    return res;
+}
+
 /////////////////////////////////////////////////////////////////////
 // TypeSet constraints
 /////////////////////////////////////////////////////////////////////
 
 /* Standard subset constraint, propagate all types from one set to another. */
 class TypeConstraintSubset : public TypeConstraint
 {
   public:
@@ -1782,16 +1850,28 @@ HeapTypeSet::getKnownTypeTag(JSContext *
      * added to the set.
      */
     DebugOnly<bool> empty = flags == 0 && baseObjectCount() == 0;
     JS_ASSERT_IF(empty, type == JSVAL_TYPE_UNKNOWN);
 
     return type;
 }
 
+bool
+StackTypeSet::mightBeType(JSValueType type)
+{
+    if (unknown())
+        return true;
+
+    if (type == JSVAL_TYPE_OBJECT)
+        return unknownObject() || baseObjectCount() != 0;
+
+    return baseFlags() & PrimitiveTypeFlag(type);
+}
+
 /* Constraint which triggers recompilation if an object acquires particular flags. */
 class TypeConstraintFreezeObjectFlags : public TypeConstraint
 {
   public:
     RecompileInfo info;
 
     /* Flags we are watching for on this object. */
     TypeObjectFlags flags;
@@ -5998,58 +6078,16 @@ JSFunction::setTypeForScriptedFunction(J
 
         fun->setType(type);
         type->interpretedFunction = fun;
     }
 
     return true;
 }
 
-#ifdef DEBUG
-
-/* static */ void
-TypeScript::CheckBytecode(JSContext *cx, HandleScript script, jsbytecode *pc, const js::Value *sp)
-{
-    AutoEnterAnalysis enter(cx);
-
-    if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
-        return;
-
-    if (!script->hasAnalysis() || !script->analysis()->ranInference())
-        return;
-    ScriptAnalysis *analysis = script->analysis();
-
-    int defCount = GetDefCount(script, pc - script->code);
-
-    for (int i = 0; i < defCount; i++) {
-        const js::Value &val = sp[-defCount + i];
-        TypeSet *types = analysis->pushedTypes(pc, i);
-        if (IgnorePushed(pc, i))
-            continue;
-
-        /*
-         * Ignore undefined values, these may have been inserted by Ion to
-         * substitute for dead values.
-         */
-        if (val.isUndefined())
-            continue;
-
-        Type type = GetValueType(cx, val);
-
-        if (!types->hasType(type)) {
-            /* Display fine-grained debug information first */
-            fprintf(stderr, "Missing type at #%u:%05u pushed %u: %s\n",
-                    script->id(), unsigned(pc - script->code), i, TypeString(type));
-            TypeFailure(cx, "Missing type pushed %u: %s", i, TypeString(type));
-        }
-    }
-}
-
-#endif
-
 /////////////////////////////////////////////////////////////////////
 // JSObject
 /////////////////////////////////////////////////////////////////////
 
 bool
 JSObject::shouldSplicePrototype(JSContext *cx)
 {
     /*
@@ -6755,17 +6793,17 @@ TypeScript::destroy()
         js_delete(dynamicList);
         dynamicList = next;
     }
 
     js_free(this);
 }
 
 /* static */ void
-TypeScript::AddFreezeConstraints(JSContext *cx, HandleScript script)
+TypeScript::AddFreezeConstraints(JSContext *cx, JSScript *script)
 {
     /*
      * Adding freeze constraints to a script ensures that code for the script
      * will be recompiled any time any type set for stack values in the script
      * change: these type sets are implicitly frozen during compilation.
      *
      * To ensure this occurs, we don't need to add freeze constraints to the
      * type sets for every stack value, but rather only the input type sets
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -432,16 +432,18 @@ class TypeSet
 
     /* Chain of constraints which propagate changes out from this type set. */
     TypeConstraint *constraintList;
 
     TypeSet()
         : flags(0), objectSet(NULL), constraintList(NULL)
     {}
 
+    TypeSet(Type type);
+
     void print();
 
     inline void sweep(JS::Zone *zone);
 
     /* Whether this set contains a specific type. */
     inline bool hasType(Type type) const;
 
     TypeFlags baseFlags() const { return flags & TYPE_FLAG_BASE_MASK; }
@@ -463,28 +465,37 @@ class TypeSet
         JS_ASSERT(definiteProperty());
         return flags >> TYPE_FLAG_DEFINITE_SHIFT;
     }
 
     /*
      * Clone a type set into an arbitrary allocator. The result should not be
      * modified further.
      */
-    const StackTypeSet *clone(LifoAlloc *alloc) const;
+    StackTypeSet *clone(LifoAlloc *alloc) const;
+
+    /* Join two type sets into a new set. The result should not be modified further. */
+    static StackTypeSet *unionSets(TypeSet *a, TypeSet *b, LifoAlloc *alloc);
 
     /*
      * Add a type to this set, calling any constraint handlers if this is a new
      * possible type.
      */
     inline void addType(JSContext *cx, Type type);
 
     /* Mark this type set as representing an own property or configured property. */
     inline void setOwnProperty(JSContext *cx, bool configured);
 
     /*
+     * Add an object to this set using the specified allocator, without
+     * triggering constraints.
+     */
+    bool addObject(TypeObjectKey *key, LifoAlloc *alloc);
+
+    /*
      * 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() const;
     inline TypeObjectKey *getObject(unsigned i) const;
     inline RawObject getSingleObject(unsigned i) const;
     inline TypeObject *getTypeObject(unsigned i) const;
@@ -537,16 +548,19 @@ class TypeSet
  * during analysis purges; the contents of the sets are implicitly frozen
  * during compilation to ensure that changes to the sets trigger recompilation
  * of the associated script.
  */
 class StackTypeSet : public TypeSet
 {
   public:
 
+    StackTypeSet() : TypeSet() {}
+    StackTypeSet(Type type) : TypeSet(type) {}
+
     /*
      * Make a type set with the specified debugging name, not embedded in
      * another structure.
      */
     static StackTypeSet *make(JSContext *cx, const char *name);
 
     /* Constraints for type inference. */
 
@@ -572,16 +586,19 @@ class StackTypeSet : public TypeSet
      * currently being compiled (see AutoEnterCompilation) and will add
      * constraints ensuring that if the return value change in the future due
      * to new type information, the script's jitcode will be discarded.
      */
 
     /* Get any type tag which all values in this set must have. */
     JSValueType getKnownTypeTag();
 
+    /* Whether any values in this set might have the specified type. */
+    bool mightBeType(JSValueType type);
+
     bool isMagicArguments() { return getKnownTypeTag() == JSVAL_TYPE_MAGIC; }
 
     /* Whether this value may be an object. */
     bool maybeObject() { return unknownObject() || baseObjectCount() > 0; }
 
     /*
      * Whether this typeset represents a potentially sentineled object value:
      * the value may be an object or null or undefined.
@@ -623,24 +640,16 @@ class StackTypeSet : public TypeSet
     bool filtersType(const StackTypeSet *other, Type type) const;
 
     /*
      * Get whether this type only contains non-string primitives:
      * null/undefined/int/double, or some combination of those.
      */
     bool knownNonStringPrimitive();
 
-    bool knownPrimitiveOrObject() {
-        TypeFlags flags = TYPE_FLAG_PRIMITIVE | TYPE_FLAG_ANYOBJECT;
-        if (baseFlags() & (~flags & TYPE_FLAG_BASE_MASK))
-            return false;
-
-        return true;
-    }
-
     enum DoubleConversion {
         /* All types in the set should use eager double conversion. */
         AlwaysConvertToDoubles,
 
         /* Some types in the set should use eager double conversion. */
         MaybeConvertToDoubles,
 
         /* No types should use eager double conversion. */
@@ -1187,22 +1196,16 @@ class TypeScript
     static inline HeapTypeSet  *ReturnTypes(RawScript script);
     static inline StackTypeSet *ThisTypes(RawScript script);
     static inline StackTypeSet *ArgTypes(RawScript script, unsigned i);
     static inline StackTypeSet *LocalTypes(RawScript script, unsigned i);
 
     /* Follows slot layout in jsanalyze.h, can get this/arg/local type sets. */
     static inline StackTypeSet *SlotTypes(RawScript script, unsigned slot);
 
-#ifdef DEBUG
-    /* Check that correct types were inferred for the values pushed by this bytecode. */
-    static void CheckBytecode(JSContext *cx, HandleScript script, jsbytecode *pc,
-                              const js::Value *sp);
-#endif
-
     /* Get the default 'new' object for a given standard class, per the script's global. */
     static inline TypeObject *StandardType(JSContext *cx, JSProtoKey kind);
 
     /* Get a type object for an allocation site in this script. */
     static inline TypeObject *InitObject(JSContext *cx, JSScript *script, jsbytecode *pc,
                                          JSProtoKey kind);
 
     /*
@@ -1237,17 +1240,17 @@ class TypeScript
     static inline void SetThis(JSContext *cx, JSScript *script, const js::Value &value);
     static inline void SetLocal(JSContext *cx, JSScript *script, unsigned local, Type type);
     static inline void SetLocal(JSContext *cx, JSScript *script, unsigned local,
                                 const js::Value &value);
     static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type);
     static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg,
                                    const js::Value &value);
 
-    static void AddFreezeConstraints(JSContext *cx, HandleScript script);
+    static void AddFreezeConstraints(JSContext *cx, JSScript *script);
     static void Purge(JSContext *cx, HandleScript script);
 
     static void Sweep(FreeOp *fop, RawScript script);
     void destroy();
 };
 
 struct ArrayTableKey;
 typedef HashMap<ArrayTableKey,ReadBarriered<TypeObject>,ArrayTableKey,SystemAllocPolicy> ArrayTypeTable;
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -1741,16 +1741,44 @@ Property::Property(jsid id)
 }
 
 inline
 Property::Property(const Property &o)
   : id(o.id.get()), types(o.types)
 {
 }
 
+inline bool
+HasOperationOverflowed(JSScript *script, jsbytecode *pc)
+{
+    types::TypeResult *result = script->types->dynamicList;
+    while (result) {
+        if (result->offset == uint32_t(pc - script->code)) {
+            if (result->type == types::Type::DoubleType())
+                return true;
+        }
+        result = result->next;
+    }
+    return false;
+}
+
+inline bool
+IterationValuesMustBeStrings(JSScript *script)
+{
+    // Return true if no custom non-string-producing iterators have been used
+    // in a 'for in' loop within the script.
+    types::TypeResult *result = script->types->dynamicList;
+    while (result) {
+        if (result->offset == UINT32_MAX)
+            return false;
+        result = result->next;
+    }
+    return true;
+}
+
 } } /* namespace js::types */
 
 inline bool
 JSScript::ensureHasTypes(JSContext *cx)
 {
     return types || makeTypes(cx);
 }
 
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -1008,29 +1008,16 @@ js::IteratorNext(JSContext *cx, HandleOb
             rval.setString(*ni->current());
             ni->incCursor();
             return true;
         }
     }
     return js_IteratorNext(cx, iterobj, rval);
 }
 
-/*
- * For bytecodes which push values and then fall through, make sure the
- * types of the pushed values are consistent with type inference information.
- */
-static inline void
-TypeCheckNextBytecode(JSContext *cx, HandleScript script, unsigned n, const FrameRegs &regs)
-{
-#ifdef DEBUG
-    if (cx->typeInferenceEnabled() && n == GetBytecodeLength(regs.pc))
-        TypeScript::CheckBytecode(cx, script, regs.pc, regs.sp);
-#endif
-}
-
 JS_NEVER_INLINE InterpretStatus
 js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode, bool useNewType)
 {
     JSAutoResolveFlags rf(cx, RESOLVE_INFER);
 
     if (interpMode == JSINTERP_NORMAL)
         gc::MaybeVerifyBarriers(cx, true);
 
@@ -1243,17 +1230,16 @@ js::Interpret(JSContext *cx, StackFrame 
 
     DO_NEXT_OP(len);
 
     for (;;) {
       advance_pc_by_one:
         JS_ASSERT(js_CodeSpec[op].length == 1);
         len = 1;
       advance_pc:
-        TypeCheckNextBytecode(cx, script, len, regs);
         js::gc::MaybeVerifyBarriers(cx);
         regs.pc += len;
         op = (JSOp) *regs.pc;
 
       do_op:
         CHECK_PCCOUNT_INTERRUPTS();
         switchOp = int(op) | switchMask;
       do_switch:
@@ -3694,16 +3680,22 @@ js::SetObjectElement(JSContext *cx, Hand
     JS_ASSERT(pc);
     RootedId id(cx);
     if (!ValueToId<CanGC>(cx, index, &id))
         return false;
     return SetObjectElementOperation(cx, obj, id, value, strict, script, pc);
 }
 
 bool
+js::InitElementArray(JSContext *cx, jsbytecode *pc, HandleObject obj, uint32_t index, HandleValue value)
+{
+    return InitArrayElemOperation(cx, pc, obj, index, value);
+}
+
+bool
 js::AddValues(JSContext *cx, HandleScript script, jsbytecode *pc,
               MutableHandleValue lhs, MutableHandleValue rhs,
               Value *res)
 {
     return AddOperation(cx, script, pc, lhs, rhs, res);
 }
 
 bool
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -369,16 +369,20 @@ CallElement(JSContext *cx, MutableHandle
 bool
 SetObjectElement(JSContext *cx, HandleObject obj, HandleValue index, HandleValue value,
                  JSBool strict);
 bool
 SetObjectElement(JSContext *cx, HandleObject obj, HandleValue index, HandleValue value,
                  JSBool strict, HandleScript script, jsbytecode *pc);
 
 bool
+InitElementArray(JSContext *cx, jsbytecode *pc,
+                 HandleObject obj, uint32_t index, HandleValue value);
+
+bool
 AddValues(JSContext *cx, HandleScript script, jsbytecode *pc,
           MutableHandleValue lhs, MutableHandleValue rhs,
           Value *res);
 
 bool
 SubValues(JSContext *cx, HandleScript script, jsbytecode *pc,
           MutableHandleValue lhs, MutableHandleValue rhs,
           Value *res);
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -888,17 +888,17 @@ GetElementOperation(JSContext *cx, JSOp 
 
 static JS_ALWAYS_INLINE bool
 SetObjectElementOperation(JSContext *cx, Handle<JSObject*> obj, HandleId id, const Value &value,
                           bool strict, RawScript maybeScript = NULL, jsbytecode *pc = NULL)
 {
     RootedScript script(cx, maybeScript);
     types::TypeScript::MonitorAssign(cx, obj, id);
 
-    if (obj->isArray() && JSID_IS_INT(id)) {
+    if (obj->isNative() && JSID_IS_INT(id)) {
         uint32_t length = obj->getDenseInitializedLength();
         int32_t i = JSID_TO_INT(id);
         if ((uint32_t)i >= length) {
             // In an Ion activation, GetPcScript won't work.  For non-baseline activations,
             // that's ok, because optimized ion doesn't generate analysis info.  However,
             // baseline must generate this information, so it passes the script and pc in
             // as arguments.
             if (script || !cx->fp()->beginsIonActivation()) {
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -304,16 +304,18 @@ js_DumpPCCounts(JSContext *cx, HandleScr
     }
 
     ion::IonScriptCounts *ionCounts = script->getIonCounts();
 
     while (ionCounts) {
         Sprint(sp, "IonScript [%lu blocks]:\n", ionCounts->numBlocks());
         for (size_t i = 0; i < ionCounts->numBlocks(); i++) {
             const ion::IonBlockCounts &block = ionCounts->block(i);
+            if (block.hitCount() < 10)
+                continue;
             Sprint(sp, "BB #%lu [%05u]", block.id(), block.offset());
             for (size_t j = 0; j < block.numSuccessors(); j++)
                 Sprint(sp, " -> #%lu", block.successor(j));
             Sprint(sp, " :: %llu hits %u instruction bytes %u spill bytes\n",
                    block.hitCount(), block.instructionBytes(), block.spillBytes());
             Sprint(sp, "%s\n", block.code());
         }
         ionCounts = ionCounts->previous();
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -989,17 +989,17 @@ mjit::CanMethodJIT(JSContext *cx, JSScri
                    bool construct, CompileRequest request, StackFrame *frame)
 {
     bool compiledOnce = false;
   checkOutput:
     if (!cx->methodJitEnabled)
         return Compile_Abort;
 
 #ifdef JS_ION
-    if (ion::IsBaselineEnabled(cx))
+    if (ion::IsBaselineEnabled(cx) || ion::IsEnabled(cx))
         return Compile_Abort;
 #endif
 
     /*
      * If SPS (profiling) is enabled, then the emitted instrumentation has to be
      * careful to not wildly write to random locations. This is relevant
      * whenever the status of profiling (on/off) is changed while JS is running.
      * All pushed frames still need to be popped, but newly emitted code may
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -223,22 +223,21 @@ namespace ion {
     class BaselineFrame;
 }
 
 /* Pointer to either a StackFrame or a baseline JIT frame. */
 class AbstractFramePtr
 {
     uintptr_t ptr_;
 
-  protected:
+  public:
     AbstractFramePtr()
       : ptr_(0)
     {}
 
-  public:
     AbstractFramePtr(StackFrame *fp)
         : ptr_(fp ? uintptr_t(fp) | 0x1 : 0)
     {
         JS_ASSERT((uintptr_t(fp) & 1) == 0);
     }
 
     AbstractFramePtr(ion::BaselineFrame *fp)
       : ptr_(uintptr_t(fp))