Back out 755ecb4d6e2c and 7ea09c8bf385 (bug 925962) for bustage
authorPhil Ringnalda <philringnalda@gmail.com>
Mon, 14 Oct 2013 14:03:03 -0700
changeset 164535 27921f21cddf7abbba678f62e4a348f280312454
parent 164534 0a16850fbd85f06db425675422a2700667b2d146
child 164536 ddd03c32fab13099b81e21cdd38f1a2a4565fb3b
child 164550 b0e636c410b8ff97d12b65fa28caa428bda6c985
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs925962
milestone27.0a1
backs out755ecb4d6e2c33cd458ff96d08deb17ff0c1bdcf
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
Back out 755ecb4d6e2c and 7ea09c8bf385 (bug 925962) for bustage CLOSED TREE
js/src/jit/Ion.cpp
js/src/jit/IonAnalysis.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/MCallOptimize.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1591,19 +1591,17 @@ IonCompile(JSContext *cx, JSScript *scri
     if (!info)
         return AbortReason_Alloc;
 
     BaselineInspector inspector(cx, script);
 
     AutoFlushCache afc("IonCompile", cx->runtime()->ionRuntime());
 
     AutoTempAllocatorRooter root(cx, temp);
-    types::CompilerConstraintList *constraints = types::NewCompilerConstraintList();
-    if (!constraints)
-        return AbortReason_Alloc;
+    types::CompilerConstraintList *constraints = alloc->new_<types::CompilerConstraintList>();
 
     IonBuilder *builder = alloc->new_<IonBuilder>(cx, temp, graph, constraints,
                                                   &inspector, info, baselineFrame);
     if (!builder)
         return AbortReason_Alloc;
 
     JS_ASSERT(!GetIonScript(builder->script(), executionMode));
     JS_ASSERT(CanIonCompile(builder->script(), executionMode));
@@ -2335,18 +2333,17 @@ jit::Invalidate(types::TypeCompartment &
     IonSpew(IonSpew_Invalidate, "Start invalidation.");
     AutoFlushCache afc ("Invalidate", fop->runtime()->ionRuntime());
 
     // Add an invalidation reference to all invalidated IonScripts to indicate
     // to the traversal which frames have been invalidated.
     size_t numInvalidations = 0;
     for (size_t i = 0; i < invalid.length(); i++) {
         const types::CompilerOutput &co = *invalid[i].compilerOutput(types);
-        if (!co.isValid())
-            continue;
+        JS_ASSERT(co.isValid());
 
         CancelOffThreadIonCompile(co.script()->compartment(), co.script());
 
         if (!co.ion())
             continue;
 
         IonSpew(IonSpew_Invalidate, " Invalidate %s:%u, IonScript %p",
                 co.script()->filename(), co.script()->lineno, co.ion());
@@ -2366,19 +2363,17 @@ jit::Invalidate(types::TypeCompartment &
     for (JitActivationIterator iter(fop->runtime()); !iter.done(); ++iter)
         InvalidateActivation(fop, iter.jitTop(), false);
 
     // Drop the references added above. If a script was never active, its
     // IonScript will be immediately destroyed. Otherwise, it will be held live
     // until its last invalidated frame is destroyed.
     for (size_t i = 0; i < invalid.length(); i++) {
         types::CompilerOutput &co = *invalid[i].compilerOutput(types);
-        if (!co.isValid())
-            continue;
-
+        JS_ASSERT(co.isValid());
         ExecutionMode executionMode = co.mode();
         JSScript *script = co.script();
         IonScript *ionScript = co.ion();
         if (!ionScript)
             continue;
 
         SetIonScript(script, executionMode, nullptr);
         ionScript->detachDependentAsmJSModules(fop);
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -2077,19 +2077,19 @@ jit::AnalyzeNewScriptProperties(JSContex
 
     MIRGraph graph(&temp);
     CompileInfo info(fun->nonLazyScript(), fun,
                      /* osrPc = */ nullptr, /* constructing = */ false,
                      DefinitePropertiesAnalysis);
 
     AutoTempAllocatorRooter root(cx, &temp);
 
-    types::CompilerConstraintList *constraints = types::NewCompilerConstraintList();
+    types::CompilerConstraintList constraints;
     BaselineInspector inspector(cx, fun->nonLazyScript());
-    IonBuilder builder(cx, &temp, &graph, constraints,
+    IonBuilder builder(cx, &temp, &graph, &constraints,
                        &inspector, &info, /* baselineFrame = */ nullptr);
 
     if (!builder.build()) {
         if (builder.abortReason() == AbortReason_Alloc)
             return false;
         return true;
     }
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -42,20 +42,16 @@ IonBuilder::IonBuilder(JSContext *cx, Te
                        size_t inliningDepth, uint32_t loopDepth)
   : MIRGenerator(cx->compartment(), temp, graph, info),
     backgroundCodegen_(nullptr),
     cx(cx),
     baselineFrame_(baselineFrame),
     abortReason_(AbortReason_Disable),
     constraints_(constraints),
     analysis_(info->script()),
-    thisTypes(NULL),
-    argTypes(NULL),
-    typeArray(NULL),
-    typeArrayHint(0),
     loopDepth_(loopDepth),
     callerResumePoint_(nullptr),
     callerBuilder_(nullptr),
     inspector(inspector),
     inliningDepth_(inliningDepth),
     numLoopRestarts_(0),
     failedBoundsCheck_(info->script()->failedBoundsCheck),
     failedShapeGuard_(info->script()->failedShapeGuard),
@@ -510,22 +506,16 @@ IonBuilder::pushLoop(CFGState::State ini
 }
 
 bool
 IonBuilder::init()
 {
     if (!script()->ensureHasBytecodeTypeMap(cx))
         return false;
 
-    if (!types::TypeScript::FreezeTypeSets(constraints(), script(),
-                                           &thisTypes, &argTypes, &typeArray))
-    {
-        return false;
-    }
-
     if (!analysis().init(cx))
         return false;
 
     return true;
 }
 
 bool
 IonBuilder::build()
@@ -620,16 +610,18 @@ IonBuilder::build()
     }
 
     if (!traverseBytecode())
         return false;
 
     if (!processIterators())
         return false;
 
+    types::TypeScript::AddFreezeConstraints(cx, script());
+
     JS_ASSERT(loopDepth_ == 0);
     abortReason_ = AbortReason_NoAbort;
     return true;
 }
 
 bool
 IonBuilder::processIterators()
 {
@@ -768,16 +760,17 @@ IonBuilder::buildInline(IonBuilder *call
     if (script_->argumentsHasVarBinding()) {
         lazyArguments_ = MConstant::New(MagicValue(JS_OPTIMIZED_ARGUMENTS));
         current->add(lazyArguments_);
     }
 
     if (!traverseBytecode())
         return false;
 
+    types::TypeScript::AddFreezeConstraints(cx, script());
     return true;
 }
 
 void
 IonBuilder::rewriteParameter(uint32_t slotIdx, MDefinition *param, int32_t argIndex)
 {
     JS_ASSERT(param->isParameter() || param->isGetArgumentsObjectArg());
 
@@ -838,35 +831,33 @@ IonBuilder::initParameters()
 {
     if (!info().fun())
         return true;
 
     // If we are doing OSR on a frame which initially executed in the
     // interpreter and didn't accumulate type information, try to use that OSR
     // frame to determine possible initial types for 'this' and parameters.
 
-    if (thisTypes->empty() && baselineFrame_) {
-        if (!thisTypes->addType(types::GetValueType(baselineFrame_->thisValue()), temp_->lifoAlloc()))
-            return false;
-    }
-
-    MParameter *param = MParameter::New(MParameter::THIS_SLOT, thisTypes);
+    types::StackTypeSet *thisTypes = types::TypeScript::ThisTypes(script());
+    if (thisTypes->empty() && baselineFrame_)
+        thisTypes->addType(cx, types::GetValueType(baselineFrame_->thisValue()));
+
+    MParameter *param = MParameter::New(MParameter::THIS_SLOT, cloneTypeSet(thisTypes));
     current->add(param);
     current->initSlot(info().thisSlot(), param);
 
     for (uint32_t i = 0; i < info().nargs(); i++) {
-        types::TemporaryTypeSet *types = &argTypes[i];
-        if (types->empty() && baselineFrame_ &&
+        types::StackTypeSet *argTypes = types::TypeScript::ArgTypes(script(), i);
+        if (argTypes->empty() && baselineFrame_ &&
             !script_->baselineScript()->modifiesArguments())
         {
-            if (!types->addType(types::GetValueType(baselineFrame_->argv()[i]), temp_->lifoAlloc()))
-                return false;
+            argTypes->addType(cx, types::GetValueType(baselineFrame_->argv()[i]));
         }
 
-        param = MParameter::New(i, types);
+        param = MParameter::New(i, cloneTypeSet(argTypes));
         current->add(param);
         current->initSlot(info().argSlotUnchecked(i), param);
     }
 
     return true;
 }
 
 bool
@@ -948,17 +939,17 @@ IonBuilder::addOsrValueTypeBarrier(uint3
         def = barrier;
     } else if (type == MIRType_Null ||
                type == MIRType_Undefined ||
                type == 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(type));
-        typeSet = temp_->lifoAlloc()->new_<types::TemporaryTypeSet>(ntype);
+        typeSet = GetIonContext()->temp->lifoAlloc()->new_<types::TemporaryTypeSet>(ntype);
         if (!typeSet)
             return false;
         MInstruction *barrier = MTypeBarrier::New(def, typeSet);
         osrBlock->insertBefore(osrBlock->lastIns(), barrier);
         osrBlock->rewriteSlot(slot, barrier);
         def = barrier;
     }
 
@@ -2981,35 +2972,35 @@ IonBuilder::jsop_condswitch()
 
     // The current case now be the default case which jump to the body of the
     // default case, which might be behind the last target.
     JS_ASSERT(JSOp(*curCase) == JSOP_DEFAULT);
     jsbytecode *defaultTarget = GetJumpOffset(curCase) + curCase;
     JS_ASSERT(curCase < defaultTarget && defaultTarget <= exitpc);
 
     // Allocate the current graph state.
-    CFGState state = CFGState::CondSwitch(this, exitpc, defaultTarget);
+    CFGState state = CFGState::CondSwitch(exitpc, defaultTarget);
     if (!state.condswitch.bodies || !state.condswitch.bodies->init(nbBodies))
         return ControlStatus_Error;
 
     // We loop on case conditions with processCondSwitchCase.
     JS_ASSERT(JSOp(*firstCase) == JSOP_CASE);
     state.stopAt = firstCase;
     state.state = CFGState::COND_SWITCH_CASE;
 
     return cfgStack_.append(state);
 }
 
 IonBuilder::CFGState
-IonBuilder::CFGState::CondSwitch(IonBuilder *builder, jsbytecode *exitpc, jsbytecode *defaultTarget)
+IonBuilder::CFGState::CondSwitch(jsbytecode *exitpc, jsbytecode *defaultTarget)
 {
     CFGState state;
     state.state = COND_SWITCH_CASE;
     state.stopAt = nullptr;
-    state.condswitch.bodies = (FixedList<MBasicBlock *> *)builder->temp_->allocate(
+    state.condswitch.bodies = (FixedList<MBasicBlock *> *)GetIonContext()->temp->allocate(
         sizeof(FixedList<MBasicBlock *>));
     state.condswitch.currentIdx = 0;
     state.condswitch.defaultTarget = defaultTarget;
     state.condswitch.defaultIdx = uint32_t(-1);
     state.condswitch.exitpc = exitpc;
     state.condswitch.breaks = nullptr;
     return state;
 }
@@ -3756,25 +3747,24 @@ IonBuilder::inlineScriptedCall(CallInfo 
 
     // Improve type information of |this| when not set.
     if (callInfo.constructing() &&
         !callInfo.thisArg()->resultTypeSet() &&
         calleeScript->types)
     {
         types::StackTypeSet *types = types::TypeScript::ThisTypes(calleeScript);
         if (!types->unknown()) {
-            MTypeBarrier *barrier =
-                MTypeBarrier::New(callInfo.thisArg(), types->clone(temp_->lifoAlloc()));
+            MTypeBarrier *barrier = MTypeBarrier::New(callInfo.thisArg(), cloneTypeSet(types));
             current->add(barrier);
             callInfo.setThis(barrier);
         }
     }
 
     // Start inlining.
-    LifoAlloc *alloc = temp_->lifoAlloc();
+    LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc();
     CompileInfo *info = alloc->new_<CompileInfo>(calleeScript, target,
                                                  (jsbytecode *)nullptr, callInfo.constructing(),
                                                  this->info().executionMode());
     if (!info)
         return false;
 
     MIRGraphExits saveExits;
     AutoAccumulateExits aae(graph(), saveExits);
@@ -4906,27 +4896,25 @@ IonBuilder::jsop_funapplyarguments(uint3
     return makeCall(target, callInfo, false);
 }
 
 bool
 IonBuilder::jsop_call(uint32_t argc, bool constructing)
 {
     // If this call has never executed, try to seed the observed type set
     // based on how the call result is used.
-    types::TemporaryTypeSet *observed = bytecodeTypes(pc);
-    if (observed->empty()) {
+    types::StackTypeSet *observed = types::TypeScript::BytecodeTypes(script(), pc);
+    if (observed->empty() && observed->noConstraints()) {
         if (BytecodeFlowsToBitop(pc)) {
-            if (!observed->addType(types::Type::Int32Type(), temp_->lifoAlloc()))
-                return false;
+            observed->addType(cx, types::Type::Int32Type());
         } else if (*GetNextPc(pc) == JSOP_POS) {
             // Note: this is lame, overspecialized on the code patterns used
             // by asm.js and should be replaced by a more general mechanism.
             // See bug 870847.
-            if (!observed->addType(types::Type::DoubleType(), temp_->lifoAlloc()))
-                return false;
+            observed->addType(cx, types::Type::DoubleType());
         }
     }
 
     int calleeDepth = -((int)argc + 2);
 
     // Acquire known call target if existent.
     ObjectVector originals;
     bool gotLambda = false;
@@ -5048,17 +5036,17 @@ TestAreKnownDOMTypes(JSContext *cx, type
     if (inTypes->getObjectCount() > 0)
         return true;
 
     return false;
 }
 
 
 static bool
-ArgumentTypesMatch(MDefinition *def, types::StackTypeSet *calleeTypes)
+ArgumentTypesMatch(MDefinition *def, types::TemporaryTypeSet *calleeTypes)
 {
     if (def->resultTypeSet()) {
         JS_ASSERT(def->type() == MIRType_Value || def->mightBeType(def->type()));
         return def->resultTypeSet()->isSubset(calleeTypes);
     }
 
     if (def->type() == MIRType_Value)
         return false;
@@ -5077,25 +5065,25 @@ IonBuilder::testNeedsArgumentCheck(JSCon
     // the arguments anymore.
     if (!target->hasScript())
         return true;
 
     JSScript *targetScript = target->nonLazyScript();
     if (!targetScript->types)
         return true;
 
-    if (!ArgumentTypesMatch(callInfo.thisArg(), types::TypeScript::ThisTypes(targetScript)))
+    if (!ArgumentTypesMatch(callInfo.thisArg(), cloneTypeSet(types::TypeScript::ThisTypes(targetScript))))
         return true;
     uint32_t expected_args = Min<uint32_t>(callInfo.argc(), target->nargs);
     for (size_t i = 0; i < expected_args; i++) {
-        if (!ArgumentTypesMatch(callInfo.getArg(i), types::TypeScript::ArgTypes(targetScript, i)))
+        if (!ArgumentTypesMatch(callInfo.getArg(i), cloneTypeSet(types::TypeScript::ArgTypes(targetScript, i))))
             return true;
     }
     for (size_t i = callInfo.argc(); i < target->nargs; i++) {
-        if (!types::TypeScript::ArgTypes(targetScript, i)->mightBeType(JSVAL_TYPE_UNDEFINED))
+        if (!cloneTypeSet(types::TypeScript::ArgTypes(targetScript, i))->mightBeType(JSVAL_TYPE_UNDEFINED))
             return true;
     }
 
     return false;
 }
 
 MCall *
 IonBuilder::makeCallHelper(JSFunction *target, CallInfo &callInfo, bool cloneAtCallsite)
@@ -5256,16 +5244,18 @@ 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::TemporaryTypeSet *thisTypes = cloneTypeSet(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'");
 
@@ -5880,17 +5870,17 @@ IonBuilder::newPendingLoopHeader(MBasicB
             }
 
             // Extract typeset from value.
             MIRType type = existingValue.isDouble()
                          ? MIRType_Double
                          : MIRTypeFromValueType(existingValue.extractNonDoubleType());
             types::Type ntype = types::GetValueType(existingValue);
             types::TemporaryTypeSet *typeSet =
-                temp_->lifoAlloc()->new_<types::TemporaryTypeSet>(ntype);
+                GetIonContext()->temp->lifoAlloc()->new_<types::TemporaryTypeSet>(ntype);
             if (!typeSet)
                 return nullptr;
             phi->addBackedgeType(type, typeSet);
         }
     }
 
     return block;
 }
@@ -6234,19 +6224,20 @@ IonBuilder::getStaticName(JSObject *stat
         if (propertyTypes.ref().configured(constraints(), staticType)) {
             // The property has been reconfigured as non-configurable, non-enumerable
             // or non-writable.
             *psucceeded = false;
             return true;
         }
     }
 
-    types::TemporaryTypeSet *types = bytecodeTypes(pc);
+    types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc);
     bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), staticType,
-                                                name, types, /* updateObserved = */ true);
+                                                name, baseTypes, /* updateObserved = */ true);
+    types::TemporaryTypeSet *types = cloneTypeSet(baseTypes);
 
     // If the property is permanent, a shape guard isn't necessary.
 
     JSObject *singleton = types->getSingleton();
 
     JSValueType knownType = types->getKnownTypeTag();
     if (!barrier) {
         if (singleton) {
@@ -6746,18 +6737,19 @@ IonBuilder::getElemTryCache(bool *emitte
     // Turn off cacheing if the element is int32 and we've seen non-native objects as the target
     // of this getelem.
     bool nonNativeGetElement = inspector->hasSeenNonNativeGetElement(pc);
     if (index->mightBeType(MIRType_Int32) && nonNativeGetElement)
         return true;
 
     // Emit GetElementCache.
 
-    types::TemporaryTypeSet *types = bytecodeTypes(pc);
-    bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), obj, nullptr, types);
+    types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc);
+    bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), obj, nullptr, baseTypes);
+    types::TemporaryTypeSet *types = cloneTypeSet(baseTypes);
 
     // 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;
 
     // See note about always needing a barrier in jsop_getprop.
     if (needsToMonitorMissingProperties(types))
@@ -6785,27 +6777,29 @@ IonBuilder::getElemTryCache(bool *emitte
 
     *emitted = true;
     return true;
 }
 
 bool
 IonBuilder::jsop_getelem_dense(MDefinition *obj, MDefinition *index)
 {
-    types::TemporaryTypeSet *types = bytecodeTypes(pc);
-
-    if (JSOp(*pc) == JSOP_CALLELEM && !index->mightBeType(MIRType_String)) {
+    types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc);
+
+    if (JSOp(*pc) == JSOP_CALLELEM && !index->mightBeType(MIRType_String) && baseTypes->noConstraints()) {
         // Indexed call on an element of an array. Populate the observed types
         // with any objects that could be in the array, to avoid extraneous
         // type barriers.
-        if (!AddObjectsForPropertyRead(obj, nullptr, types))
+        if (!AddObjectsForPropertyRead(cx, obj, nullptr, baseTypes))
             return false;
     }
 
-    bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), obj, nullptr, types);
+    bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), obj, nullptr, baseTypes);
+    types::TemporaryTypeSet *types = cloneTypeSet(baseTypes);
+
     bool needsHoleCheck = !ElementAccessIsPacked(constraints(), 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()) &&
         !ElementAccessHasExtraIndexedProperty(constraints(), obj);
@@ -8043,29 +8037,30 @@ bool
 IonBuilder::jsop_getprop(PropertyName *name)
 {
     bool emitted = false;
 
     // Try to optimize arguments.length.
     if (!getPropTryArgumentsLength(&emitted) || emitted)
         return emitted;
 
-    types::TemporaryTypeSet *types = bytecodeTypes(pc);
+    types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc);
     bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(),
-                                                current->peek(-1), name, types);
+                                                current->peek(-1), name, baseTypes);
+    types::TemporaryTypeSet *types = cloneTypeSet(baseTypes);
 
     // Try to hardcode known constants.
     if (!getPropTryConstant(&emitted, name, types) || emitted)
         return emitted;
 
     // Except when loading constants above, always use a call if we are doing
     // the definite properties analysis and not actually emitting code, to
     // simplify later analysis. Also skip deeper analysis if there are no known
     // types for this operation, as it will always invalidate when executing.
-    if (info().executionMode() == DefinitePropertiesAnalysis || types->empty()) {
+    if (info().executionMode() == DefinitePropertiesAnalysis || baseTypes->empty()) {
         MDefinition *obj = current->pop();
         MCallGetProperty *call = MCallGetProperty::New(obj, name);
         current->add(call);
         current->push(call);
         return resumeAfter(call) && pushTypeBarrier(call, types, true);
     }
 
     // Try to emit loads from known binary data blocks
@@ -8989,19 +8984,21 @@ IonBuilder::jsop_setarg(uint32_t arg)
      {
          for (size_t i = 0; i < val->numOperands(); i++) {
             MDefinition *op = val->getOperand(i);
             if (op->isParameter() &&
                 op->toParameter()->index() == (int32_t)arg &&
                 op->resultTypeSet() &&
                 op->resultTypeSet()->empty())
             {
-                JS_ASSERT(op->resultTypeSet() == &argTypes[arg]);
-                if (!argTypes[arg].addType(types::Type::UnknownType(), temp_->lifoAlloc()))
-                    return false;
+                types::TypeSet *argTypes = types::TypeScript::ArgTypes(script(), arg);
+
+                // Update both the original and cloned type set.
+                argTypes->addType(cx, types::Type::UnknownType());
+                op->resultTypeSet()->addType(cx, types::Type::UnknownType());
             }
         }
     }
 
     current->setArg(arg);
     return true;
 }
 
@@ -9049,18 +9046,19 @@ IonBuilder::jsop_this()
         return abort("JSOP_THIS outside of a JSFunction.");
 
     if (script()->strict || info().fun()->isSelfHostedBuiltin()) {
         // No need to wrap primitive |this| in strict mode or self-hosted code.
         current->pushSlot(info().thisSlot());
         return true;
     }
 
-    if (thisTypes->getKnownTypeTag() == JSVAL_TYPE_OBJECT ||
-        (thisTypes->empty() && baselineFrame_ && baselineFrame_->thisValue().isObject()))
+    types::TemporaryTypeSet *types = cloneTypeSet(types::TypeScript::ThisTypes(script()));
+    if (types && (types->getKnownTypeTag() == JSVAL_TYPE_OBJECT ||
+                  (types->empty() && baselineFrame_ && baselineFrame_->thisValue().isObject())))
     {
         // 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;
     }
 
@@ -9464,17 +9462,27 @@ IonBuilder::addShapeGuard(MDefinition *o
         guard->setNotMovable();
 
     return guard;
 }
 
 types::TemporaryTypeSet *
 IonBuilder::bytecodeTypes(jsbytecode *pc)
 {
-    return types::TypeScript::BytecodeTypes(script(), pc, &typeArrayHint, typeArray);
+    return cloneTypeSet(types::TypeScript::BytecodeTypes(script(), pc));
+}
+
+types::TemporaryTypeSet *
+IonBuilder::cloneTypeSet(types::StackTypeSet *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.
+    return types->clone(GetIonContext()->temp->lifoAlloc());
 }
 
 TypeRepresentationSetHash *
 IonBuilder::getOrCreateReprSetHash()
 {
     if (!reprSetHash_) {
         TypeRepresentationSetHash* hash =
             cx->new_<TypeRepresentationSetHash>();
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -192,17 +192,17 @@ class IonBuilder : public MIRGenerator
                 return false;
             }
         }
 
         static CFGState If(jsbytecode *join, MBasicBlock *ifFalse);
         static CFGState IfElse(jsbytecode *trueEnd, jsbytecode *falseEnd, MBasicBlock *ifFalse);
         static CFGState AndOr(jsbytecode *join, MBasicBlock *joinStart);
         static CFGState TableSwitch(jsbytecode *exitpc, MTableSwitch *ins);
-        static CFGState CondSwitch(IonBuilder *builder, jsbytecode *exitpc, jsbytecode *defaultTarget);
+        static CFGState CondSwitch(jsbytecode *exitpc, jsbytecode *defaultTarget);
         static CFGState Label(jsbytecode *exitpc);
         static CFGState Try(jsbytecode *exitpc, MBasicBlock *successor);
     };
 
     static int CmpSuccessors(const void *a, const void *b);
 
   public:
     IonBuilder(JSContext *cx, TempAllocator *temp, MIRGraph *graph,
@@ -526,16 +526,17 @@ class IonBuilder : public MIRGenerator
 
     // Oracles.
     bool canEnterInlinedFunction(JSFunction *target);
     bool makeInliningDecision(JSFunction *target, CallInfo &callInfo);
     uint32_t selectInliningTargets(ObjectVector &targets, CallInfo &callInfo,
                                    BoolVector &choiceSet);
 
     // Native inlining helpers.
+    types::StackTypeSet *getOriginalInlineReturnTypeSet();
     types::TemporaryTypeSet *getInlineReturnTypeSet();
     MIRType getInlineReturnType();
 
     // Array natives.
     InliningStatus inlineArray(CallInfo &callInfo);
     InliningStatus inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode);
     InliningStatus inlineArrayPush(CallInfo &callInfo);
     InliningStatus inlineArrayConcat(CallInfo &callInfo);
@@ -639,16 +640,17 @@ class IonBuilder : public MIRGenerator
                                     bool *isKnownConstant, bool *testObject,
                                     bool *testString);
     bool getDefiniteSlot(types::TemporaryTypeSet *types, PropertyName *name,
                          types::HeapTypeSetKey *property);
     bool freezePropTypeSets(types::TemporaryTypeSet *types,
                             JSObject *foundProto, PropertyName *name);
 
     types::TemporaryTypeSet *bytecodeTypes(jsbytecode *pc);
+    types::TemporaryTypeSet *cloneTypeSet(types::StackTypeSet *types);
 
     // Use one of the below methods for updating the current block, rather than
     // updating |current| directly. setCurrent() should only be used in cases
     // where the block cannot have phis whose type needs to be computed.
 
     void setCurrentAndSpecializePhis(MBasicBlock *block) {
         if (block)
             block->specializePhis();
@@ -710,19 +712,16 @@ class IonBuilder : public MIRGenerator
     types::CompilerConstraintList *constraints_;
 
     // Basic analysis information about the script.
     BytecodeAnalysis analysis_;
     BytecodeAnalysis &analysis() {
         return analysis_;
     }
 
-    types::TemporaryTypeSet *thisTypes, *argTypes, *typeArray;
-    uint32_t typeArrayHint;
-
     GSNCache gsn;
 
     jsbytecode *pc;
     MBasicBlock *current;
     uint32_t loopDepth_;
 
     /* Information used for inline-call builders. */
     MResumePoint *callerResumePoint_;
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -151,20 +151,26 @@ IonBuilder::inlineNativeCall(CallInfo &c
     if (native == testingFunc_bailout)
         return inlineBailout(callInfo);
     if (native == testingFunc_assertFloat32)
         return inlineAssertFloat32(callInfo);
 
     return InliningStatus_NotInlined;
 }
 
+types::StackTypeSet *
+IonBuilder::getOriginalInlineReturnTypeSet()
+{
+    return types::TypeScript::BytecodeTypes(script(), pc);
+}
+
 types::TemporaryTypeSet *
 IonBuilder::getInlineReturnTypeSet()
 {
-    return bytecodeTypes(pc);
+    return cloneTypeSet(getOriginalInlineReturnTypeSet());
 }
 
 MIRType
 IonBuilder::getInlineReturnType()
 {
     types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet();
     return MIRTypeFromValueType(returnTypes->getKnownTypeTag());
 }
@@ -317,17 +323,17 @@ IonBuilder::inlineArrayPopShift(CallInfo
         return InliningStatus_NotInlined;
 
     RootedScript scriptRoot(cx, script());
     if (types::ArrayPrototypeHasIndexedProperty(constraints(), scriptRoot))
         return InliningStatus_NotInlined;
 
     callInfo.unwrapArgs();
 
-    types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet();
+    types::StackTypeSet *returnTypes = getOriginalInlineReturnTypeSet();
     bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED);
     bool maybeUndefined = returnTypes->hasType(types::Type::UndefinedType());
 
     bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(),
                                                 callInfo.thisArg(), nullptr, returnTypes);
     if (barrier)
         returnType = MIRType_Value;
 
@@ -335,17 +341,17 @@ IonBuilder::inlineArrayPopShift(CallInfo
                                               needsHoleCheck, maybeUndefined);
     current->add(ins);
     current->push(ins);
     ins->setResultType(returnType);
 
     if (!resumeAfter(ins))
         return InliningStatus_Error;
 
-    if (!pushTypeBarrier(ins, returnTypes, barrier))
+    if (!pushTypeBarrier(ins, cloneTypeSet(returnTypes), barrier))
         return InliningStatus_Error;
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineArrayPush(CallInfo &callInfo)
 {
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -2559,17 +2559,17 @@ types::TemporaryTypeSet *
 InlinePropertyTable::buildTypeSetForFunction(JSFunction *func) const
 {
     LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc();
     types::TemporaryTypeSet *types = alloc->new_<types::TemporaryTypeSet>();
     if (!types)
         return nullptr;
     for (size_t i = 0; i < numEntries(); i++) {
         if (entries_[i]->func == func) {
-            if (!types->addType(types::Type::ObjectType(entries_[i]->typeObj), alloc))
+            if (!types->addObject(types::Type::ObjectType(entries_[i]->typeObj).objectKey(), alloc))
                 return nullptr;
         }
     }
     return types;
 }
 
 void *
 MLoadTypedArrayElementStatic::base() const
@@ -2805,54 +2805,53 @@ PropertyReadNeedsTypeBarrier(JSContext *
     property.freeze(constraints);
     return false;
 }
 
 bool
 jit::PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
                                   types::CompilerConstraintList *constraints,
                                   types::TypeObjectKey *object, PropertyName *name,
-                                  types::TemporaryTypeSet *observed, bool updateObserved)
+                                  types::StackTypeSet *observed, bool updateObserved)
 {
     // If this access has never executed, try to add types to the observed set
     // according to any property which exists on the object or its prototype.
-    if (updateObserved && observed->empty() && name) {
+    if (updateObserved && observed->empty() && observed->noConstraints() && name) {
         JSObject *obj = object->singleton() ? object->singleton() : object->proto().toObjectOrNull();
 
         while (obj) {
             if (!obj->isNative())
                 break;
 
             types::TypeObjectKey *typeObj = types::TypeObjectKey::get(obj);
             if (!typeObj->unknownProperties()) {
                 types::HeapTypeSetKey property = typeObj->property(NameToId(name), propertycx);
                 if (property.maybeTypes()) {
                     types::TypeSet::TypeList types;
                     if (!property.maybeTypes()->enumerateTypes(&types))
                         return false;
                     if (types.length()) {
-                        if (!observed->addType(types[0], GetIonContext()->temp->lifoAlloc()))
-                            return false;
+                        observed->addType(cx, types[0]);
                         break;
                     }
                 }
             }
 
             obj = obj->getProto();
         }
     }
 
     return PropertyReadNeedsTypeBarrier(cx, constraints, object, name, observed);
 }
 
 bool
 jit::PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
                                   types::CompilerConstraintList *constraints,
                                   MDefinition *obj, PropertyName *name,
-                                  types::TemporaryTypeSet *observed)
+                                  types::StackTypeSet *observed)
 {
     if (observed->unknown())
         return false;
 
     types::TypeSet *types = obj->resultTypeSet();
     if (!types || types->unknownObject())
         return true;
 
@@ -2919,49 +2918,55 @@ jit::PropertyReadIsIdempotent(types::Com
                 return false;
         }
     }
 
     return true;
 }
 
 bool
-jit::AddObjectsForPropertyRead(MDefinition *obj, PropertyName *name,
-                               types::TemporaryTypeSet *observed)
+jit::AddObjectsForPropertyRead(JSContext *cx, MDefinition *obj, PropertyName *name,
+                               types::StackTypeSet *observed)
 {
     // Add objects to observed which *could* be observed by reading name from obj,
     // to hopefully avoid unnecessary type barriers and code invalidations.
 
-    LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc();
+    JS_ASSERT(observed->noConstraints());
 
     types::TemporaryTypeSet *types = obj->resultTypeSet();
-    if (!types || types->unknownObject())
-        return observed->addType(types::Type::AnyObjectType(), alloc);
+    if (!types || types->unknownObject()) {
+        observed->addType(cx, types::Type::AnyObjectType());
+        return true;
+    }
 
     for (size_t i = 0; i < types->getObjectCount(); i++) {
-        types::TypeObjectKey *object = types->getObject(i);
+        types::TypeObject *object;
+        if (!types->getTypeOrSingleObject(cx, i, &object))
+            return false;
+
         if (!object)
             continue;
 
-        if (object->unknownProperties())
-            return observed->addType(types::Type::AnyObjectType(), alloc);
+        if (object->unknownProperties()) {
+            observed->addType(cx, types::Type::AnyObjectType());
+            return true;
+        }
 
         jsid id = name ? NameToId(name) : JSID_VOID;
-        types::HeapTypeSetKey property = object->property(id);
-        types::HeapTypeSet *types = property.maybeTypes();
-        if (!types)
-            continue;
-
-        if (types->unknownObject())
-            return observed->addType(types::Type::AnyObjectType(), alloc);
-
-        for (size_t i = 0; i < types->getObjectCount(); i++) {
-            types::TypeObjectKey *object = types->getObject(i);
-            if (object && !observed->addType(types::Type::ObjectType(object), alloc))
-                return false;
+        types::HeapTypeSet *property = object->getProperty(cx, id);
+        if (property->unknownObject()) {
+            observed->addType(cx, types::Type::AnyObjectType());
+            return true;
+        }
+
+        for (size_t i = 0; i < property->getObjectCount(); i++) {
+            if (types::TypeObject *object = property->getTypeObject(i))
+                observed->addType(cx, types::Type::ObjectType(object));
+            else if (JSObject *object = property->getSingleObject(i))
+                observed->addType(cx, types::Type::ObjectType(object));
         }
     }
 
     return true;
 }
 
 static bool
 TryAddTypeBarrierForWrite(types::CompilerConstraintList *constraints,
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -8991,28 +8991,28 @@ bool ElementAccessIsTypedArray(MDefiniti
                                ScalarTypeRepresentation::Type *arrayType);
 bool ElementAccessIsPacked(types::CompilerConstraintList *constraints, MDefinition *obj);
 bool ElementAccessHasExtraIndexedProperty(types::CompilerConstraintList *constraints,
                                           MDefinition *obj);
 MIRType DenseNativeElementType(types::CompilerConstraintList *constraints, MDefinition *obj);
 bool PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
                                   types::CompilerConstraintList *constraints,
                                   types::TypeObjectKey *object, PropertyName *name,
-                                  types::TemporaryTypeSet *observed, bool updateObserved);
+                                  types::StackTypeSet *observed, bool updateObserved);
 bool PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
                                   types::CompilerConstraintList *constraints,
                                   MDefinition *obj, PropertyName *name,
-                                  types::TemporaryTypeSet *observed);
+                                  types::StackTypeSet *observed);
 bool PropertyReadOnPrototypeNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints,
                                              MDefinition *obj, PropertyName *name,
                                              types::TemporaryTypeSet *observed);
 bool PropertyReadIsIdempotent(types::CompilerConstraintList *constraints,
                               MDefinition *obj, PropertyName *name);
-bool AddObjectsForPropertyRead(MDefinition *obj, PropertyName *name,
-                               types::TemporaryTypeSet *observed);
+bool AddObjectsForPropertyRead(JSContext *cx, MDefinition *obj, PropertyName *name,
+                               types::StackTypeSet *observed);
 bool PropertyWriteNeedsTypeBarrier(types::CompilerConstraintList *constraints,
                                    MBasicBlock *current, MDefinition **pobj,
                                    PropertyName *name, MDefinition **pvalue,
                                    bool canModify);
 
 } // namespace jit
 } // namespace js
 
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -311,28 +311,16 @@ TemporaryTypeSet::TemporaryTypeSet(Type 
         flags |= TYPE_FLAG_ANYOBJECT;
     } else {
         setBaseObjectCount(1);
         objectSet = reinterpret_cast<TypeObjectKey**>(type.objectKey());
     }
 }
 
 bool
-TypeSet::mightBeType(JSValueType type)
-{
-    if (unknown())
-        return true;
-
-    if (type == JSVAL_TYPE_OBJECT)
-        return unknownObject() || baseObjectCount() != 0;
-
-    return baseFlags() & PrimitiveTypeFlag(type);
-}
-
-bool
 TypeSet::isSubset(TypeSet *other)
 {
     if ((baseFlags() & other->baseFlags()) != baseFlags())
         return false;
 
     if (unknownObject()) {
         JS_ASSERT(other->unknownObject());
     } else {
@@ -464,64 +452,77 @@ TypeSet::print()
         for (unsigned i = 0; i < count; i++) {
             TypeObjectKey *object = getObject(i);
             if (object)
                 fprintf(stderr, " %s", TypeString(Type::ObjectType(object)));
         }
     }
 }
 
-bool
-TypeSet::clone(LifoAlloc *alloc, TemporaryTypeSet *result) const
+TemporaryTypeSet *
+TypeSet::clone(LifoAlloc *alloc) const
 {
-    JS_ASSERT(result->empty());
-
     unsigned objectCount = baseObjectCount();
     unsigned capacity = (objectCount >= 2) ? HashSetCapacity(objectCount) : 0;
 
     TypeObjectKey **newSet;
     if (capacity) {
         newSet = alloc->newArray<TypeObjectKey*>(capacity);
         if (!newSet)
-            return false;
+            return nullptr;
         PodCopy(newSet, objectSet, capacity);
     }
 
     uint32_t newFlags = flags & ~(TYPE_FLAG_STACK_SET | TYPE_FLAG_HEAP_SET);
-
-    new(result) TemporaryTypeSet(newFlags, capacity ? newSet : objectSet);
-    return true;
+    TemporaryTypeSet *res = alloc->new_<TemporaryTypeSet>(newFlags, capacity ? newSet : objectSet);
+    if (!res)
+        return nullptr;
+
+    return res;
 }
 
-TemporaryTypeSet *
-TypeSet::clone(LifoAlloc *alloc) const
+bool
+TemporaryTypeSet::addObject(TypeObjectKey *key, LifoAlloc *alloc)
 {
-    TemporaryTypeSet *res = alloc->new_<TemporaryTypeSet>();
-    if (!res || !clone(alloc, res))
-        return nullptr;
-    return res;
+    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 */ TemporaryTypeSet *
 TypeSet::unionSets(TypeSet *a, TypeSet *b, LifoAlloc *alloc)
 {
     TemporaryTypeSet *res = alloc->new_<TemporaryTypeSet>(a->baseFlags() | b->baseFlags(),
                                                           static_cast<TypeObjectKey**>(nullptr));
     if (!res)
         return nullptr;
 
     if (!res->unknownObject()) {
         for (size_t i = 0; i < a->getObjectCount() && !res->unknownObject(); i++) {
             TypeObjectKey *key = a->getObject(i);
-            if (key && !res->addType(Type::ObjectType(key), alloc))
+            if (key && !res->addObject(key, alloc))
                 return nullptr;
         }
         for (size_t i = 0; i < b->getObjectCount() && !res->unknownObject(); i++) {
             TypeObjectKey *key = b->getObject(i);
-            if (key && !res->addType(Type::ObjectType(key), alloc))
+            if (key && !res->addObject(key, alloc))
                 return nullptr;
         }
     }
 
     return res;
 }
 
 /////////////////////////////////////////////////////////////////////
@@ -549,21 +550,19 @@ TypeSet::unionSets(TypeSet *a, TypeSet *
 static LifoAlloc *IonAlloc() {
 #ifdef JS_ION
     return jit::GetIonContext()->temp->lifoAlloc();
 #else
     MOZ_CRASH();
 #endif
 }
 
-namespace {
-
 // Superclass of all constraints generated during Ion compilation. These may
 // be allocated off the main thread, using the current Ion context's allocator.
-class CompilerConstraint
+class types::CompilerConstraint
 {
   public:
     // Property being queried by the compiler.
     HeapTypeSetKey property;
 
     // Contents of the property at the point when the query was performed. This
     // may differ from the actual property types later in compilation as the
     // main thread performs side effects.
@@ -574,148 +573,25 @@ class CompilerConstraint
         expected(property.maybeTypes() ? property.maybeTypes()->clone(IonAlloc()) : NULL)
     {}
 
     // Generate the type constraint recording the assumption made by this
     // compilation. Returns true if the assumption originally made still holds.
     virtual bool generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo) = 0;
 };
 
-} // anonymous namespace
-
-class types::CompilerConstraintList
+void
+CompilerConstraintList::add(CompilerConstraint *constraint)
 {
-  public:
-    struct FrozenScript
-    {
-        JSScript *script;
-        TemporaryTypeSet *thisTypes;
-        TemporaryTypeSet *argTypes;
-        TemporaryTypeSet *bytecodeTypes;
-    };
-
-  private:
-
 #ifdef JS_ION
-    // Constraints generated on heap properties.
-    Vector<CompilerConstraint *, 0, jit::IonAllocPolicy> constraints;
-
-    // Scripts whose stack type sets were frozen for the compilation.
-    Vector<FrozenScript, 1, jit::IonAllocPolicy> frozenScripts;
-#endif
-
-    // OOM during generation of some constraint.
-    bool failed_;
-
-  public:
-    CompilerConstraintList()
-      : failed_(false)
-    {}
-
-    void add(CompilerConstraint *constraint) {
-#ifdef JS_ION
-        if (!constraint || !constraints.append(constraint))
-            setFailed();
+    if (!constraint || !constraints.append(constraint))
+        setFailed();
 #else
-        MOZ_CRASH();
-#endif
-    }
-
-    void freezeScript(JSScript *script,
-                      TemporaryTypeSet *thisTypes,
-                      TemporaryTypeSet *argTypes,
-                      TemporaryTypeSet *bytecodeTypes)
-    {
-#ifdef JS_ION
-        FrozenScript entry;
-        entry.script = script;
-        entry.thisTypes = thisTypes;
-        entry.argTypes = argTypes;
-        entry.bytecodeTypes = bytecodeTypes;
-        if (!frozenScripts.append(entry))
-            setFailed();
-#else
-        MOZ_CRASH();
-#endif
-    }
-
-    size_t length() {
-#ifdef JS_ION
-        return constraints.length();
-#else
-        MOZ_CRASH();
+    MOZ_CRASH();
 #endif
-    }
-
-    CompilerConstraint *get(size_t i) {
-#ifdef JS_ION
-        return constraints[i];
-#else
-        MOZ_CRASH();
-#endif
-    }
-
-    size_t numFrozenScripts() {
-#ifdef JS_ION
-        return frozenScripts.length();
-#else
-        MOZ_CRASH();
-#endif
-    }
-
-    const FrozenScript &frozenScript(size_t i) {
-#ifdef JS_ION
-        return frozenScripts[i];
-#else
-        MOZ_CRASH();
-#endif
-    }
-
-    bool failed() {
-        return failed_;
-    }
-    void setFailed() {
-        failed_ = true;
-    }
-};
-
-CompilerConstraintList *
-types::NewCompilerConstraintList()
-{
-    return IonAlloc()->new_<CompilerConstraintList>();
-}
-
-/* static */ bool
-TypeScript::FreezeTypeSets(CompilerConstraintList *constraints, JSScript *script,
-                           TemporaryTypeSet **pThisTypes,
-                           TemporaryTypeSet **pArgTypes,
-                           TemporaryTypeSet **pBytecodeTypes)
-{
-    LifoAlloc *alloc = IonAlloc();
-    StackTypeSet *existing = script->types->typeArray();
-
-    size_t count = NumTypeSets(script);
-    TemporaryTypeSet *types = alloc->newArrayUninitialized<TemporaryTypeSet>(count);
-    if (!types)
-        return false;
-    PodZero(types, count);
-
-    for (size_t i = 0; i < count; i++) {
-        if (!existing[i].clone(alloc, &types[i]))
-            return false;
-    }
-
-    *pThisTypes = types + (ThisTypes(script) - existing);
-    *pArgTypes = (script->function() && script->function()->nargs)
-                 ? (types + (ArgTypes(script, 0) - existing))
-                 : NULL;
-    *pBytecodeTypes = types;
-
-    constraints->freezeScript(script, *pThisTypes, *pArgTypes, *pBytecodeTypes);
-    return true;
 }
 
 namespace {
 
 template <typename T>
 class CompilerConstraintInstance : public CompilerConstraint
 {
     T data;
@@ -865,71 +741,16 @@ HeapTypeSetKey::instantiate(JSContext *c
     if (maybeTypes())
         return true;
     if (object()->isSingleObject() && !object()->asSingleObject()->getType(cx))
         return false;
     maybeTypes_ = object()->maybeType()->getProperty(cx, id());
     return maybeTypes_ != NULL;
 }
 
-static bool
-CheckFrozenTypeSet(JSContext *cx, TemporaryTypeSet *frozen, StackTypeSet *actual)
-{
-    // Return whether the types frozen for a script during compilation are
-    // still valid. Also check for any new types added to the frozen set during
-    // compilation, and add them to the actual stack type sets. These new types
-    // indicate places where the compiler relaxed its possible inputs to be
-    // more tolerant of potential new types.
-
-    if (!actual->isSubset(frozen))
-        return false;
-
-    if (!frozen->isSubset(actual)) {
-        TypeSet::TypeList list;
-        frozen->enumerateTypes(&list);
-
-        for (size_t i = 0; i < list.length(); i++) {
-            // Note: On OOM this will preserve the type set's contents.
-            actual->addType(cx, list[i]);
-        }
-    }
-
-    return true;
-}
-
-namespace {
-
-/*
- * As for TypeConstraintFreeze, but describes an implicit freeze constraint
- * added for stack types within a script. Applies to all compilations of the
- * script, not just a single one.
- */
-class TypeConstraintFreezeStack : public TypeConstraint
-{
-    JSScript *script_;
-
-  public:
-    TypeConstraintFreezeStack(JSScript *script)
-        : script_(script)
-    {}
-
-    const char *kind() { return "freezeStack"; }
-
-    void newType(JSContext *cx, TypeSet *source, Type type)
-    {
-        /*
-         * Unlike TypeConstraintFreeze, triggering this constraint once does
-         * not disable it on future changes to the type set.
-         */
-        cx->compartment()->types.addPendingRecompile(cx, script_);
-    }
-};
-
-} /* anonymous namespace */
-
 bool
 types::FinishCompilation(JSContext *cx, JSScript *script, ExecutionMode executionMode,
                          CompilerConstraintList *constraints, RecompileInfo *precompileInfo)
 {
     if (constraints->failed())
         return false;
 
     CompilerOutput co(script, executionMode);
@@ -950,46 +771,17 @@ types::FinishCompilation(JSContext *cx, 
     bool succeeded = true;
 
     for (size_t i = 0; i < constraints->length(); i++) {
         CompilerConstraint *constraint = constraints->get(i);
         if (!constraint->generateTypeConstraint(cx, *precompileInfo))
             succeeded = false;
     }
 
-    for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
-        const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i);
-        JS_ASSERT(entry.script->types);
-
-        if (!CheckFrozenTypeSet(cx, entry.thisTypes, types::TypeScript::ThisTypes(entry.script)))
-            succeeded = false;
-        unsigned nargs = entry.script->function() ? entry.script->function()->nargs : 0;
-        for (size_t i = 0; i < nargs; i++) {
-            if (!CheckFrozenTypeSet(cx, &entry.argTypes[i], types::TypeScript::ArgTypes(entry.script, i)))
-                succeeded = false;
-        }
-        for (size_t i = 0; i < entry.script->nTypeSets; i++) {
-            if (!CheckFrozenTypeSet(cx, &entry.bytecodeTypes[i], &entry.script->types->typeArray()[i]))
-                succeeded = false;
-        }
-
-        // If necessary, add constraints to trigger invalidation on the script
-        // after any future changes to the stack type sets.
-        if (entry.script->hasFreezeConstraints)
-            continue;
-        entry.script->hasFreezeConstraints = true;
-
-        size_t count = TypeScript::NumTypeSets(entry.script);
-
-        StackTypeSet *array = entry.script->types->typeArray();
-        for (size_t i = 0; i < count; i++)
-            array[i].add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeStack>(entry.script), false);
-    }
-
-    if (!succeeded || types.constrainedOutputs->back().pendingInvalidation()) {
+    if (!succeeded) {
         types.constrainedOutputs->back().invalidate();
         return false;
     }
 
     return true;
 }
 
 namespace {
@@ -1067,16 +859,28 @@ TemporaryTypeSet::getKnownTypeTag()
      * added to the set.
      */
     DebugOnly<bool> empty = flags == 0 && baseObjectCount() == 0;
     JS_ASSERT_IF(empty, type == JSVAL_TYPE_UNKNOWN);
 
     return type;
 }
 
+bool
+TemporaryTypeSet::mightBeType(JSValueType type)
+{
+    if (unknown())
+        return true;
+
+    if (type == JSVAL_TYPE_OBJECT)
+        return unknownObject() || baseObjectCount() != 0;
+
+    return baseFlags() & PrimitiveTypeFlag(type);
+}
+
 JSValueType
 HeapTypeSetKey::knownTypeTag(CompilerConstraintList *constraints)
 {
     TypeSet *types = maybeTypes();
 
     if (!types || types->unknown())
         return JSVAL_TYPE_UNKNOWN;
 
@@ -1662,16 +1466,46 @@ TemporaryTypeSet::propertyNeedsBarrier(C
         HeapTypeSetKey property = type->property(id);
         if (property.needsBarrier(constraints))
             return true;
     }
 
     return false;
 }
 
+namespace {
+
+/*
+ * As for TypeConstraintFreeze, but describes an implicit freeze constraint
+ * added for stack types within a script. Applies to all compilations of the
+ * script, not just a single one.
+ */
+class TypeConstraintFreezeStack : public TypeConstraint
+{
+    JSScript *script_;
+
+  public:
+    TypeConstraintFreezeStack(JSScript *script)
+        : script_(script)
+    {}
+
+    const char *kind() { return "freezeStack"; }
+
+    void newType(JSContext *cx, TypeSet *source, Type type)
+    {
+        /*
+         * Unlike TypeConstraintFreeze, triggering this constraint once does
+         * not disable it on future changes to the type set.
+         */
+        cx->compartment()->types.addPendingRecompile(cx, script_);
+    }
+};
+
+} /* anonymous namespace */
+
 /////////////////////////////////////////////////////////////////////
 // TypeCompartment
 /////////////////////////////////////////////////////////////////////
 
 TypeCompartment::TypeCompartment()
 {
     PodZero(this);
 }
@@ -4224,16 +4058,50 @@ TypeScript::Sweep(FreeOp *fop, JSScript 
 }
 
 void
 TypeScript::destroy()
 {
     js_free(this);
 }
 
+/* static */ void
+TypeScript::AddFreezeConstraints(JSContext *cx, JSScript *script)
+{
+    if (script->hasFreezeConstraints)
+        return;
+    script->hasFreezeConstraints = true;
+
+    /*
+     * Adding freeze constraints to a script ensures that code for the script
+     * will be recompiled any time any type set for stack values in the script
+     * change: these type sets are implicitly frozen during compilation.
+     *
+     * To ensure this occurs, we don't need to add freeze constraints to the
+     * type sets for every stack value, but rather only the input type sets
+     * to analysis of the stack in a script. The contents of the stack sets
+     * are completely determined by these input sets and by any dynamic types
+     * in the script (for which TypeDynamicResult will trigger recompilation).
+     *
+     * Add freeze constraints to each input type set, which includes sets for
+     * all arguments, locals, and monitored type sets in the script. This
+     * includes all type sets in the TypeScript except the script's return
+     * value types.
+     */
+
+    size_t count = TypeScript::NumTypeSets(script);
+
+    TypeSet *array = script->types->typeArray();
+    for (size_t i = 0; i < count; i++) {
+        TypeSet *types = &array[i];
+        JS_ASSERT(types->isStackSet());
+        types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeStack>(script), false);
+    }
+}
+
 void
 Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t *typePool)
 {
     *typePool += types.typeLifoAlloc.sizeOfExcludingThis(mallocSizeOf);
 }
 
 void
 TypeCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
@@ -4401,17 +4269,17 @@ TypeScript::printTypes(JSContext *cx, Ha
 
     for (jsbytecode *pc = script->code;
          pc < script->code + script->length;
          pc += GetBytecodeLength(pc))
     {
         PrintBytecode(cx, script, pc);
 
         if (js_CodeSpec[*pc].format & JOF_TYPESET) {
-            StackTypeSet *types = TypeScript::BytecodeTypes(script, pc);
+            TypeSet *types = TypeScript::BytecodeTypes(script, pc);
             fprintf(stderr, "  typeset %u:", unsigned(types - typeArray()));
             types->print();
             fprintf(stderr, "\n");
         }
     }
 
     fprintf(stderr, "\n");
 }
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -507,17 +507,19 @@ class TypeSet
     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; }
     bool unknown() const { return !!(flags & TYPE_FLAG_UNKNOWN); }
     bool unknownObject() const { return !!(flags & (TYPE_FLAG_UNKNOWN | TYPE_FLAG_ANYOBJECT)); }
+
     bool empty() const { return !baseFlags() && !baseObjectCount(); }
+    bool noConstraints() const { return constraintList == nullptr; }
 
     bool hasAnyFlag(TypeFlags flags) const {
         JS_ASSERT((flags & TYPE_FLAG_BASE_MASK) == flags);
         return !!(baseFlags() & flags);
     }
 
     bool configuredProperty() const {
         return flags & TYPE_FLAG_CONFIGURED_PROPERTY;
@@ -526,19 +528,16 @@ class TypeSet
     unsigned definiteSlot() const {
         JS_ASSERT(definiteProperty());
         return flags >> TYPE_FLAG_DEFINITE_SHIFT;
     }
 
     /* Join two type sets into a new set. The result should not be modified further. */
     static TemporaryTypeSet *unionSets(TypeSet *a, TypeSet *b, LifoAlloc *alloc);
 
-    /* Add a type to this set using the specified allocator. */
-    inline bool addType(Type type, LifoAlloc *alloc, bool *padded = NULL);
-
     /*
      * Add a type to this set, calling any constraint handlers if this is a new
      * possible type.
      */
     inline void addType(ExclusiveContext *cx, Type type);
 
     /* Mark this type set as representing a configured property. */
     inline void setConfiguredProperty(ExclusiveContext *cx);
@@ -571,37 +570,36 @@ class TypeSet
 
     bool isStackSet() {
         return flags & TYPE_FLAG_STACK_SET;
     }
     bool isHeapSet() {
         return flags & TYPE_FLAG_HEAP_SET;
     }
 
-    /* Whether any values in this set might have the specified type. */
-    bool mightBeType(JSValueType type);
-
     /*
      * Get whether this type set is known to be a subset of other.
      * This variant doesn't freeze constraints. That variant is called knownSubset
      */
     bool isSubset(TypeSet *other);
 
     /* Forward all types in this set to the specified constraint. */
     void addTypesToConstraint(JSContext *cx, TypeConstraint *constraint);
 
     /* Add a new constraint to this set. */
     void add(JSContext *cx, TypeConstraint *constraint, bool callExisting = true);
 
     inline StackTypeSet *toStackSet();
     inline HeapTypeSet *toHeapSet();
 
-    // Clone a type set into an arbitrary allocator.
+    /*
+     * Clone a type set into an arbitrary allocator. The result should not be
+     * modified further.
+     */
     TemporaryTypeSet *clone(LifoAlloc *alloc) const;
-    bool clone(LifoAlloc *alloc, TemporaryTypeSet *result) const;
 
   protected:
     uint32_t baseObjectCount() const {
         return (flags & TYPE_FLAG_OBJECT_COUNT_MASK) >> TYPE_FLAG_OBJECT_COUNT_SHIFT;
     }
     inline void setBaseObjectCount(uint32_t count);
 
     inline void clearObjects();
@@ -616,43 +614,46 @@ class StackTypeSet : public TypeSet
 class HeapTypeSet : public TypeSet
 {
   public:
     HeapTypeSet() { flags |= TYPE_FLAG_HEAP_SET; }
 };
 
 class CompilerConstraintList;
 
-CompilerConstraintList *
-NewCompilerConstraintList();
-
 class TemporaryTypeSet : public TypeSet
 {
   public:
     TemporaryTypeSet() {}
     TemporaryTypeSet(Type type);
 
     TemporaryTypeSet(uint32_t flags, TypeObjectKey **objectSet) {
         this->flags = flags;
         this->objectSet = objectSet;
         JS_ASSERT(!isStackSet() && !isHeapSet());
     }
 
+    /* Add an object to this set using the specified allocator. */
+    bool addObject(TypeObjectKey *key, LifoAlloc *alloc);
+
     /*
      * Constraints for JIT compilation.
      *
      * Methods for JIT compilation. These must be used when a script is
      * currently being compiled (see AutoEnterCompilation) and will add
      * constraints ensuring that if the return value change in the future due
      * to new type information, the script's jitcode will be discarded.
      */
 
     /* 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.
@@ -1151,29 +1152,28 @@ class TypeScript
     /*
      * List mapping indexes of bytecode type sets to the offset of the opcode
      * they correspond to. Cleared on each GC.
      */
     uint32_t *bytecodeMap;
 
   public:
     /* Array of type type sets for variables and JOF_TYPESET ops. */
-    StackTypeSet *typeArray() const { return (StackTypeSet *) (uintptr_t(this) + sizeof(TypeScript)); }
+    TypeSet *typeArray() const { return (TypeSet *) (uintptr_t(this) + sizeof(TypeScript)); }
 
     static inline unsigned NumTypeSets(JSScript *script);
 
     static inline StackTypeSet *ThisTypes(JSScript *script);
     static inline StackTypeSet *ArgTypes(JSScript *script, unsigned i);
 
     /* Get the type set for values observed at an opcode. */
     static inline StackTypeSet *BytecodeTypes(JSScript *script, jsbytecode *pc);
 
-    template <typename TYPESET>
-    static inline TYPESET *BytecodeTypes(JSScript *script, jsbytecode *pc,
-                                         uint32_t *hint, TYPESET *typeArray);
+    /* 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);
 
     /*
      * Monitor a bytecode pushing any value. This must be called for any opcode
      * which is JOF_TYPESET, and where either the script has not been analyzed
@@ -1190,49 +1190,31 @@ class TypeScript
 
     /* Add a type for a variable in a script. */
     static inline void SetThis(JSContext *cx, JSScript *script, Type type);
     static inline void SetThis(JSContext *cx, JSScript *script, const js::Value &value);
     static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type);
     static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg,
                                    const js::Value &value);
 
-    /*
-     * Freeze all the stack type sets in a script, for a compilation. Returns
-     * copies of the type sets which will be checked against the actual ones
-     * under FinishCompilation, to detect any type changes.
-     */
-    static bool FreezeTypeSets(CompilerConstraintList *constraints, JSScript *script,
-                               TemporaryTypeSet **pThisTypes,
-                               TemporaryTypeSet **pArgTypes,
-                               TemporaryTypeSet **pBytecodeTypes);
-
+    static void AddFreezeConstraints(JSContext *cx, JSScript *script);
     static void Purge(JSContext *cx, HandleScript script);
 
     static void Sweep(FreeOp *fop, JSScript *script);
     void destroy();
 
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return mallocSizeOf(this);
     }
 
 #ifdef DEBUG
     void printTypes(JSContext *cx, HandleScript script) const;
 #endif
 };
 
-class RecompileInfo;
-
-// Allocate a CompilerOutput for a finished compilation and generate the type
-// constraints for the compilation. Returns whether the type constraints
-// still hold.
-bool
-FinishCompilation(JSContext *cx, JSScript *script, ExecutionMode executionMode,
-                  CompilerConstraintList *constraints, RecompileInfo *precompileInfo);
-
 struct ArrayTableKey;
 typedef HashMap<ArrayTableKey,ReadBarriered<TypeObject>,ArrayTableKey,SystemAllocPolicy> ArrayTypeTable;
 
 struct ObjectTableKey;
 struct ObjectTableEntry;
 typedef HashMap<ObjectTableKey,ObjectTableEntry,ObjectTableKey,SystemAllocPolicy> ObjectTypeTable;
 
 struct AllocationSiteKey;
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -583,51 +583,53 @@ extern void TypeDynamicResult(JSContext 
 TypeScript::NumTypeSets(JSScript *script)
 {
     return script->nTypeSets + analyze::LocalSlot(script, 0);
 }
 
 /* static */ inline StackTypeSet *
 TypeScript::ThisTypes(JSScript *script)
 {
-    return script->types->typeArray() + script->nTypeSets + js::analyze::ThisSlot();
+    TypeSet *types = script->types->typeArray() + script->nTypeSets + js::analyze::ThisSlot();
+    return types->toStackSet();
 }
 
 /*
  * Note: for non-escaping arguments and locals, argTypes/localTypes reflect
  * only the initial type of the variable (e.g. passed values for argTypes,
  * or undefined for localTypes) and not types from subsequent assignments.
  */
 
 /* static */ inline StackTypeSet *
 TypeScript::ArgTypes(JSScript *script, unsigned i)
 {
     JS_ASSERT(i < script->function()->nargs);
-    return script->types->typeArray() + script->nTypeSets + js::analyze::ArgSlot(i);
+    TypeSet *types = script->types->typeArray() + script->nTypeSets + js::analyze::ArgSlot(i);
+    return types->toStackSet();
 }
 
-template <typename TYPESET>
-/* static */ inline TYPESET *
-TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *hint, TYPESET *typeArray)
+/* static */ inline StackTypeSet *
+TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc)
 {
     JS_ASSERT(js_CodeSpec[*pc].format & JOF_TYPESET);
     JS_ASSERT(script->types && script->types->bytecodeMap);
     uint32_t *bytecodeMap = script->types->bytecodeMap;
+    uint32_t *hint = bytecodeMap + script->nTypeSets;
     uint32_t offset = pc - script->code;
     JS_ASSERT(offset < script->length);
 
     // See if this pc is the next typeset opcode after the last one looked up.
     if (bytecodeMap[*hint + 1] == offset && (*hint + 1) < script->nTypeSets) {
         (*hint)++;
-        return typeArray + *hint;
+        return script->types->typeArray()->toStackSet() + *hint;
     }
 
     // See if this pc is the same as the last one looked up.
     if (bytecodeMap[*hint] == offset)
-        return typeArray + *hint;
+        return script->types->typeArray()->toStackSet() + *hint;
 
     // Fall back to a binary search.
     size_t bottom = 0;
     size_t top = script->nTypeSets - 1;
     size_t mid = bottom + (top - bottom) / 2;
     while (mid < top) {
         if (bytecodeMap[mid] < offset)
             bottom = mid + 1;
@@ -639,25 +641,26 @@ TypeScript::BytecodeTypes(JSScript *scri
     }
 
     // We should have have zeroed in on either the exact offset, unless there
     // are more JOF_TYPESET opcodes than nTypeSets in the script (as can happen
     // if the script is very long).
     JS_ASSERT(bytecodeMap[mid] == offset || mid == top);
 
     *hint = mid;
-    return typeArray + *hint;
+    return script->types->typeArray()->toStackSet() + *hint;
 }
 
-/* static */ inline StackTypeSet *
-TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc)
+/* static */ inline TypeObject *
+TypeScript::StandardType(JSContext *cx, JSProtoKey key)
 {
-    JS_ASSERT(CurrentThreadCanAccessRuntime(script->runtimeFromMainThread()));
-    uint32_t *hint = script->types->bytecodeMap + script->nTypeSets;
-    return BytecodeTypes(script, pc, hint, script->types->typeArray());
+    RootedObject proto(cx);
+    if (!js_GetClassPrototype(cx, key, &proto, nullptr))
+        return nullptr;
+    return cx->getNewType(GetClassForProtoKey(key), proto.get());
 }
 
 struct AllocationSiteKey : public DefaultHasher<AllocationSiteKey> {
     JSScript *script;
 
     uint32_t offset : 24;
     JSProtoKey kind : 8;
 
@@ -1101,105 +1104,80 @@ TypeSet::setBaseObjectCount(uint32_t cou
 
 inline void
 TypeSet::clearObjects()
 {
     setBaseObjectCount(0);
     objectSet = nullptr;
 }
 
-bool
-TypeSet::addType(Type type, LifoAlloc *alloc, bool *padded)
+inline void
+TypeSet::addType(ExclusiveContext *cxArg, Type type)
 {
-    JS_ASSERT_IF(padded, !*padded);
+    JS_ASSERT(cxArg->compartment()->activeAnalysis);
+
+    // Temporary type sets use a separate LifoAlloc for storage.
+    JS_ASSERT_IF(!type.isUnknown() && !type.isAnyObject() && type.isObject(),
+                 isStackSet() || isHeapSet());
 
     if (unknown())
-        return true;
+        return;
 
     if (type.isUnknown()) {
         flags |= TYPE_FLAG_BASE_MASK;
         clearObjects();
         JS_ASSERT(unknown());
-        if (padded)
-            *padded = true;
-        return true;
-    }
-
-    if (type.isPrimitive()) {
+    } else if (type.isPrimitive()) {
         TypeFlags flag = PrimitiveTypeFlag(type.primitive());
         if (flags & flag)
-            return true;
+            return;
 
         /* If we add float to a type set it is also considered to contain int. */
         if (flag == TYPE_FLAG_DOUBLE)
             flag |= TYPE_FLAG_INT32;
 
         flags |= flag;
-        if (padded)
-            *padded = true;
-        return true;
-    }
+    } else {
+        if (flags & TYPE_FLAG_ANYOBJECT)
+            return;
+        if (type.isAnyObject())
+            goto unknownObject;
 
-    if (flags & TYPE_FLAG_ANYOBJECT)
-        return true;
-    if (type.isAnyObject())
-        goto unknownObject;
-
-    {
         uint32_t objectCount = baseObjectCount();
         TypeObjectKey *object = type.objectKey();
         TypeObjectKey **pentry = HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
-                                     (*alloc, objectSet, objectCount, object);
-        if (!pentry)
-            return false;
+                                     (cxArg->typeLifoAlloc(), objectSet, objectCount, object);
+        if (!pentry) {
+            cxArg->compartment()->types.setPendingNukeTypes(cxArg);
+            return;
+        }
         if (*pentry)
-            return true;
+            return;
         *pentry = object;
 
         setBaseObjectCount(objectCount);
 
         if (objectCount == TYPE_FLAG_OBJECT_COUNT_LIMIT)
             goto unknownObject;
-    }
 
-    if (type.isTypeObject()) {
-        TypeObject *nobject = type.typeObject();
-        JS_ASSERT(!nobject->singleton);
-        if (nobject->unknownProperties())
-            goto unknownObject;
+        if (type.isTypeObject()) {
+            TypeObject *nobject = type.typeObject();
+            JS_ASSERT(!nobject->singleton);
+            if (nobject->unknownProperties())
+                goto unknownObject;
+        }
     }
 
     if (false) {
     unknownObject:
         type = Type::AnyObjectType();
         flags |= TYPE_FLAG_ANYOBJECT;
         clearObjects();
     }
 
-    if (padded)
-        *padded = true;
-    return true;
-}
-
-inline void
-TypeSet::addType(ExclusiveContext *cxArg, Type type)
-{
-    JS_ASSERT(cxArg->compartment()->activeAnalysis);
-
-    // Temporary type sets use a separate LifoAlloc for storage.
-    JS_ASSERT(isStackSet() || isHeapSet());
-
-    bool added = false;
-    if (!addType(type, &cxArg->typeLifoAlloc(), &added)) {
-        cxArg->compartment()->types.setPendingNukeTypes(cxArg);
-        return;
-    }
-    if (!added)
-        return;
-
     InferSpew(ISpewOps, "addType: %sT%p%s %s",
               InferSpewColor(this), this, InferSpewColorReset(),
               TypeString(type));
 
     /* Propagate the type to all constraints. */
     if (JSContext *cx = cxArg->maybeJSContext()) {
         TypeConstraint *constraint = constraintList;
         while (constraint) {
@@ -1439,16 +1417,64 @@ TypeNewScript::writeBarrierPre(TypeNewSc
     JS::Zone *zone = newScript->fun->zoneFromAnyThread();
     if (zone->needsBarrier()) {
         MarkObject(zone->barrierTracer(), &newScript->fun, "write barrier");
         MarkShape(zone->barrierTracer(), &newScript->shape, "write barrier");
     }
 #endif
 }
 
+// Allocate a CompilerOutput for a finished compilation and generate the type
+// constraints for the compilation. Returns whether the type constraints
+// still hold.
+bool
+FinishCompilation(JSContext *cx, JSScript *script, ExecutionMode executionMode,
+                  CompilerConstraintList *constraints, RecompileInfo *precompileInfo);
+
+class CompilerConstraint;
+class CompilerConstraintList
+{
+#ifdef JS_ION
+    // Generated constraints.
+    Vector<CompilerConstraint *, 0, jit::IonAllocPolicy> constraints;
+#endif
+
+    // OOM during generation of some constraint.
+    bool failed_;
+
+  public:
+    CompilerConstraintList()
+      : failed_(false)
+    {}
+
+    void add(CompilerConstraint *constraint);
+
+    size_t length() {
+#ifdef JS_ION
+        return constraints.length();
+#else
+        MOZ_CRASH();
+#endif
+    }
+    CompilerConstraint *get(size_t i) {
+#ifdef JS_ION
+        return constraints[i];
+#else
+        MOZ_CRASH();
+#endif
+    }
+
+    bool failed() {
+        return failed_;
+    }
+    void setFailed() {
+        failed_ = true;
+    }
+};
+
 } } /* namespace js::types */
 
 inline bool
 JSScript::ensureHasTypes(JSContext *cx)
 {
     return types || makeTypes(cx);
 }