Bug 804676 - Remove dependence of Ion compilation on ScriptAnalysis::analyzeTypes.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 11 Apr 2013 18:39:32 -0600
changeset 140540 ee14945b452c669ded1d6cabe7b51c12ec722ec1
parent 139458 d989eab66df4312452c5c53f2445d9f807d9570e
child 140541 79f78c194329eefadfffdd122e33007317a3e5e0
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs804676
milestone23.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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();