Bug 921902 - Separate generation and attaching of heap property type constraints, r=jandem.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 03 Oct 2013 21:44:13 -0600
changeset 149837 48582b2df0afee18a34e6142e2253ddf27047d0f
parent 149836 a70ad9d7e0f4acedfe530aee99e1a7d29d8ce53d
child 149889 a587ed24ade0f7df16c3c915bd463c2373bd2c3e
push id34690
push userbhackett@mozilla.com
push dateFri, 04 Oct 2013 03:45:13 +0000
treeherdermozilla-inbound@48582b2df0af [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs921902
milestone27.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 921902 - Separate generation and attaching of heap property type constraints, r=jandem.
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/FoldConstants.cpp
js/src/gc/Zone.cpp
js/src/gc/Zone.h
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/CompileInfo.h
js/src/jit/ExecutionModeInlines.h
js/src/jit/Ion.cpp
js/src/jit/IonAnalysis.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/IonCaches.cpp
js/src/jit/IonCode.h
js/src/jit/MCallOptimize.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jsgc.cpp
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -5841,34 +5841,47 @@ EmitObject(ExclusiveContext *cx, Bytecod
         obj = NewBuiltinClassInstance(cx, &JSObject::class_, kind);
         if (!obj)
             return false;
     }
 
     for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
         /* Emit an index for t[2] for later consumption by JSOP_INITELEM. */
         ParseNode *pn3 = pn2->pn_left;
+        bool isIndex = false;
         if (pn3->isKind(PNK_NUMBER)) {
             if (!EmitNumberOp(cx, pn3->pn_dval, bce))
                 return false;
+            isIndex = true;
+        } else {
+            // The parser already checked for atoms representing indexes and
+            // used PNK_NUMBER instead, but also watch for ids which TI treats
+            // as indexes for simpliciation of downstream analysis.
+            JS_ASSERT(pn3->isKind(PNK_NAME) || pn3->isKind(PNK_STRING));
+            jsid id = NameToId(pn3->pn_atom->asPropertyName());
+            if (id != types::IdToTypeId(id)) {
+                if (!EmitTree(cx, bce, pn3))
+                    return false;
+                isIndex = true;
+            }
         }
 
         /* Emit code for the property initializer. */
         if (!EmitTree(cx, bce, pn2->pn_right))
             return false;
 
         JSOp op = pn2->getOp();
         JS_ASSERT(op == JSOP_INITPROP ||
                   op == JSOP_INITPROP_GETTER ||
                   op == JSOP_INITPROP_SETTER);
 
         if (op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER)
             obj = nullptr;
 
-        if (pn3->isKind(PNK_NUMBER)) {
+        if (isIndex) {
             obj = nullptr;
             switch (op) {
               case JSOP_INITPROP:        op = JSOP_INITELEM;        break;
               case JSOP_INITPROP_GETTER: op = JSOP_INITELEM_GETTER; break;
               case JSOP_INITPROP_SETTER: op = JSOP_INITELEM_SETTER; break;
               default: MOZ_ASSUME_UNREACHABLE("Invalid op");
             }
             if (Emit1(cx, bce, op) < 0)
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -11,16 +11,17 @@
 
 #include "jslibmath.h"
 
 #include "frontend/ParseNode.h"
 #include "frontend/Parser.h"
 #include "vm/NumericConversions.h"
 
 #include "jscntxtinlines.h"
+#include "jsinferinlines.h"
 
 using namespace js;
 using namespace js::frontend;
 
 using mozilla::IsNaN;
 using mozilla::IsNegative;
 using mozilla::NegativeInfinity;
 using mozilla::PositiveInfinity;
@@ -757,20 +758,21 @@ Fold(ExclusiveContext *cx, ParseNode **p
                 // which enables optimization 3 below.
                 JSAtom *atom = ToAtom<NoGC>(cx, DoubleValue(number));
                 if (!atom)
                     return false;
                 name = atom->asPropertyName();
             }
         }
 
-        if (name) {
+        if (name && NameToId(name) == types::IdToTypeId(NameToId(name))) {
             // Optimization 3: We have pn1["foo"] where foo is not an index.
             // Convert to a property access (like pn1.foo) which we optimize
-            // better downstream.
+            // better downstream. Don't bother with this for names which TI
+            // considers to be indexes, to simplify downstream analysis.
             ParseNode *expr = handler.newPropertyAccess(pn->pn_left, name, pn->pn_pos.end);
             if (!expr)
                 return false;
             ReplaceNode(pnp, expr);
 
             pn->pn_left = nullptr;
             pn->pn_right = nullptr;
             handler.freeTree(pn);
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -178,17 +178,17 @@ Zone::sweepBreakpoints(FreeOp *fop)
                 if (scriptGone || IsObjectAboutToBeFinalized(&bp->debugger->toJSObjectRef()))
                     bp->destroy(fop);
             }
         }
     }
 }
 
 void
-Zone::discardJitCode(FreeOp *fop, bool discardConstraints)
+Zone::discardJitCode(FreeOp *fop)
 {
 #ifdef JS_ION
     if (isPreservingCode()) {
         PurgeJITCaches(this);
     } else {
 
 # ifdef DEBUG
         /* Assert no baseline scripts are marked as active. */
@@ -222,17 +222,17 @@ Zone::discardJitCode(FreeOp *fop, bool d
             script->resetUseCount();
         }
 
         for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next()) {
             /* Free optimized baseline stubs. */
             if (comp->ionCompartment())
                 comp->ionCompartment()->optimizedStubSpace()->free();
 
-            comp->types.sweepCompilerOutputs(fop, discardConstraints);
+            comp->types.clearCompilerOutputs(fop);
         }
     }
 #endif
 }
 
 JS::Zone *
 js::ZoneOfObject(const JSObject &obj)
 {
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -253,17 +253,17 @@ struct Zone : public JS::shadow::Zone,
     js::Vector<js::GrayRoot, 0, js::SystemAllocPolicy> gcGrayRoots;
 
     Zone(JSRuntime *rt);
     ~Zone();
     bool init(JSContext *cx);
 
     void findOutgoingEdges(js::gc::ComponentFinder<JS::Zone> &finder);
 
-    void discardJitCode(js::FreeOp *fop, bool discardConstraints);
+    void discardJitCode(js::FreeOp *fop);
 
     void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t *typePool);
 
     void setGCLastBytes(size_t lastBytes, js::JSGCInvocationKind gckind);
     void reduceGCTriggerBytes(size_t amount);
 
     void resetGCMallocBytes();
     void setGCMaxMallocBytes(size_t value);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -5703,42 +5703,44 @@ CodeGenerator::generate()
 #endif
     if (!generateOutOfLineCode())
         return false;
 
     return !masm.oom();
 }
 
 bool
-CodeGenerator::link()
-{
-    JSContext *cx = GetIonContext()->cx;
+CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints)
+{
+    JSScript *script = gen->info().script();
+    ExecutionMode executionMode = gen->info().executionMode();
+    JS_ASSERT(!HasIonScript(script, executionMode));
 
     // Check to make sure we didn't have a mid-build invalidation. If so, we
     // will trickle to jit::Compile() and return Method_Skipped.
-    if (cx->compartment()->types.compiledInfo.compilerOutput(cx)->isInvalidated())
+    types::RecompileInfo recompileInfo;
+    if (!types::FinishCompilation(cx, script, executionMode, constraints, &recompileInfo))
         return true;
 
-    ExecutionMode executionMode = gen->info().executionMode();
-
     uint32_t scriptFrameSize = frameClass_ == FrameSizeClass::None()
                            ? frameDepth_
                            : FrameSizeClass::FromDepth(frameDepth_).frameSize();
 
     // We encode safepoints after the OSI-point offsets have been determined.
     encodeSafepoints();
 
     // List of possible scripts that this graph may call. Currently this is
     // only tracked when compiling for parallel execution.
     CallTargetVector callTargets;
     if (executionMode == ParallelExecution)
         AddPossibleCallees(cx, graph.mir(), callTargets);
 
     IonScript *ionScript =
-      IonScript::New(cx, graph.totalSlotCount(), scriptFrameSize, snapshots_.size(),
+      IonScript::New(cx, recompileInfo,
+                     graph.totalSlotCount(), scriptFrameSize, snapshots_.size(),
                      bailouts_.length(), graph.numConstants(),
                      safepointIndices_.length(), osiIndices_.length(),
                      cacheList_.length(), runtimeData_.length(),
                      safepoints_.size(), callTargets.length(),
                      patchableBackedges_.length());
     if (!ionScript)
         return false;
 
@@ -5761,19 +5763,16 @@ CodeGenerator::link()
                     : linker.newCode(cx, JSC::ION_CODE);
     if (!code) {
         // Use js_free instead of IonScript::Destroy: the cache list and
         // backedge list are still uninitialized.
         js_free(ionScript);
         return false;
     }
 
-    JSScript *script = gen->info().script();
-    JS_ASSERT(!HasIonScript(script, executionMode));
-
     ionScript->setMethod(code);
     ionScript->setSkipArgCheckEntryOffset(getSkipArgCheckEntryOffset());
 
     // If SPS is enabled, mark IonScript as having been instrumented with SPS
     if (sps_.enabled())
         ionScript->setHasSPSInstrumentation();
 
     SetIonScript(script, executionMode, ionScript);
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -48,17 +48,17 @@ class CodeGenerator : public CodeGenerat
 
   public:
     CodeGenerator(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm = nullptr);
     ~CodeGenerator();
 
   public:
     bool generate();
     bool generateAsmJS();
-    bool link();
+    bool link(JSContext *cx, types::CompilerConstraintList *constraints);
 
     bool visitLabel(LLabel *lir);
     bool visitNop(LNop *lir);
     bool visitOsiPoint(LOsiPoint *lir);
     bool visitGoto(LGoto *lir);
     bool visitTableSwitch(LTableSwitch *ins);
     bool visitTableSwitchV(LTableSwitchV *ins);
     bool visitParameter(LParameter *lir);
--- a/js/src/jit/CompileInfo.h
+++ b/js/src/jit/CompileInfo.h
@@ -23,29 +23,16 @@ StartArgSlot(JSScript *script, JSFunctio
 }
 
 inline unsigned
 CountArgSlots(JSScript *script, JSFunction *fun)
 {
     return StartArgSlot(script, fun) + (fun ? fun->nargs + 1 : 0);
 }
 
-enum ExecutionMode {
-    // Normal JavaScript execution
-    SequentialExecution,
-
-    // JavaScript code to be executed in parallel worker threads,
-    // e.g. by ParallelArray
-    ParallelExecution,
-
-    // MIR analysis performed when invoking 'new' on a script, to determine
-    // definite properties
-    DefinitePropertiesAnalysis
-};
-
 // Not as part of the enum so we don't get warnings about unhandled enum
 // values.
 static const unsigned NumExecutionModes = ParallelExecution + 1;
 
 // Contains information about the compilation source for IR being generated.
 class CompileInfo
 {
   public:
--- a/js/src/jit/ExecutionModeInlines.h
+++ b/js/src/jit/ExecutionModeInlines.h
@@ -96,25 +96,14 @@ CompilingOffThread(HandleScript script, 
     switch (cmode) {
       case SequentialExecution: return script->isIonCompilingOffThread();
       case ParallelExecution: return script->isParallelIonCompilingOffThread();
       default:;
     }
     MOZ_ASSUME_UNREACHABLE("No such execution mode");
 }
 
-static inline types::CompilerOutput::Kind
-CompilerOutputKind(ExecutionMode cmode)
-{
-    switch (cmode) {
-      case SequentialExecution: return types::CompilerOutput::Ion;
-      case ParallelExecution: return types::CompilerOutput::ParallelIon;
-      default:;
-    }
-    MOZ_ASSUME_UNREACHABLE("No such execution mode");
-}
-
 } // namespace jit
 } // namespace js
 
 #endif  // JS_ION
 
 #endif /* jit_ExecutionModeInlines_h */
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -513,21 +513,18 @@ IonCompartment::ensureIonStubsExist(JSCo
 }
 
 void
 jit::FinishOffThreadBuilder(IonBuilder *builder)
 {
     ExecutionMode executionMode = builder->info().executionMode();
 
     // Clean up if compilation did not succeed.
-    if (CompilingOffThread(builder->script(), executionMode)) {
-        types::TypeCompartment &types = builder->script()->compartment()->types;
-        builder->recompileInfo.compilerOutput(types)->invalidate();
+    if (CompilingOffThread(builder->script(), executionMode))
         SetIonScript(builder->script(), executionMode, nullptr);
-    }
 
     // The builder is allocated into its LifoAlloc, so destroying that will
     // destroy the builder and all other data accumulated during compilation,
     // except any final codegen (which includes an assembler and needs to be
     // explicitly destroyed).
     js_delete(builder->backgroundCodegen());
     js_delete(builder->temp().lifoAlloc());
 }
@@ -743,17 +740,18 @@ IonScript::IonScript()
     osrPcMismatchCounter_(0),
     dependentAsmJSModules(nullptr)
 {
 }
 
 static const int DataAlignment = sizeof(void *);
 
 IonScript *
-IonScript::New(JSContext *cx, uint32_t frameSlots, uint32_t frameSize, size_t snapshotsSize,
+IonScript::New(JSContext *cx, types::RecompileInfo recompileInfo,
+               uint32_t frameSlots, uint32_t frameSize, size_t snapshotsSize,
                size_t bailoutEntries, size_t constants, size_t safepointIndices,
                size_t osiIndices, size_t cacheEntries, size_t runtimeSize,
                size_t safepointsSize, size_t callTargetEntries, size_t backedgeEntries)
 {
     if (snapshotsSize >= MAX_BUFFER_SIZE ||
         (bailoutEntries >= MAX_BUFFER_SIZE / sizeof(uint32_t)))
     {
         js_ReportOutOfMemory(cx);
@@ -830,17 +828,17 @@ IonScript::New(JSContext *cx, uint32_t f
 
     script->backedgeList_ = offsetCursor;
     script->backedgeEntries_ = backedgeEntries;
     offsetCursor += paddedBackedgeSize;
 
     script->frameSlots_ = frameSlots;
     script->frameSize_ = frameSize;
 
-    script->recompileInfo_ = cx->compartment()->types.compiledInfo;
+    script->recompileInfo_ = recompileInfo;
 
     return script;
 }
 
 void
 IonScript::trace(JSTracer *trc)
 {
     if (method_)
@@ -1505,27 +1503,23 @@ AttachFinishedCompilations(JSContext *cx
 
             // Root the assembler until the builder is finished below. As it
             // was constructed off thread, the assembler has not been rooted
             // previously, though any GC activity would discard the builder.
             codegen->masm.constructRoot(cx);
 
             types::AutoEnterAnalysis enterTypes(cx);
 
-            ExecutionMode executionMode = builder->info().executionMode();
-            types::AutoEnterCompilation enterCompiler(cx, CompilerOutputKind(executionMode));
-            enterCompiler.initExisting(builder->recompileInfo);
-
             bool success;
             {
                 // Release the worker thread lock and root the compiler for GC.
                 AutoTempAllocatorRooter root(cx, &builder->temp());
                 AutoUnlockWorkerThreadState unlock(cx->runtime());
                 AutoFlushCache afc("AttachFinishedCompilations", cx->runtime()->ionRuntime());
-                success = codegen->link();
+                success = codegen->link(cx, builder->constraints());
             }
 
             if (!success) {
                 // Silently ignore OOM during code generation, we're at an
                 // operation callback and can't propagate failures.
                 cx->clearPendingException();
             }
         }
@@ -1596,23 +1590,21 @@ IonCompile(JSContext *cx, JSScript *scri
                                                  executionMode);
     if (!info)
         return AbortReason_Alloc;
 
     BaselineInspector inspector(cx, script);
 
     AutoFlushCache afc("IonCompile", cx->runtime()->ionRuntime());
 
-    types::AutoEnterCompilation enterCompiler(cx, CompilerOutputKind(executionMode));
-    if (!enterCompiler.init(script))
-        return AbortReason_Disable;
-
     AutoTempAllocatorRooter root(cx, temp);
-
-    IonBuilder *builder = alloc->new_<IonBuilder>(cx, temp, graph, &inspector, info, baselineFrame);
+    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));
 
     RootedScript builderScript(cx, builder->script());
     IonSpewNewFunction(graph, builderScript);
@@ -1647,17 +1639,17 @@ IonCompile(JSContext *cx, JSScript *scri
     }
 
     ScopedJSDeletePtr<CodeGenerator> codegen(CompileBackEnd(builder));
     if (!codegen) {
         IonSpew(IonSpew_Abort, "Failed during back-end compilation.");
         return AbortReason_Disable;
     }
 
-    bool success = codegen->link();
+    bool success = codegen->link(cx, builder->constraints());
 
     IonSpewEndFunction();
 
     return success ? AbortReason_NoAbort : AbortReason_Disable;
 }
 
 static bool
 TooManyArguments(unsigned nargs)
@@ -2338,78 +2330,80 @@ void
 jit::Invalidate(types::TypeCompartment &types, FreeOp *fop,
                 const Vector<types::RecompileInfo> &invalid, bool resetUses)
 {
     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.
-    bool anyInvalidation = false;
+    size_t numInvalidations = 0;
     for (size_t i = 0; i < invalid.length(); i++) {
         const types::CompilerOutput &co = *invalid[i].compilerOutput(types);
-        switch (co.kind()) {
-          case types::CompilerOutput::Ion:
-          case types::CompilerOutput::ParallelIon:
-            JS_ASSERT(co.isValid());
-            IonSpew(IonSpew_Invalidate, " Invalidate %s:%u, IonScript %p",
-                    co.script->filename(), co.script->lineno, co.ion());
-
-            // Keep the ion script alive during the invalidation and flag this
-            // ionScript as being invalidated.  This increment is removed by the
-            // loop after the calls to InvalidateActivation.
-            co.ion()->incref();
-            anyInvalidation = true;
-        }
+        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());
+
+        // Keep the ion script alive during the invalidation and flag this
+        // ionScript as being invalidated.  This increment is removed by the
+        // loop after the calls to InvalidateActivation.
+        co.ion()->incref();
+        numInvalidations++;
     }
 
-    if (!anyInvalidation) {
+    if (!numInvalidations) {
         IonSpew(IonSpew_Invalidate, " No IonScript invalidation.");
         return;
     }
 
     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);
-        ExecutionMode executionMode = SequentialExecution;
-        switch (co.kind()) {
-          case types::CompilerOutput::Ion:
-            break;
-          case types::CompilerOutput::ParallelIon:
-            executionMode = ParallelExecution;
-            break;
-        }
         JS_ASSERT(co.isValid());
-        JSScript *script = co.script;
-        IonScript *ionScript = GetIonScript(script, executionMode);
+        ExecutionMode executionMode = co.mode();
+        JSScript *script = co.script();
+        IonScript *ionScript = co.ion();
+        if (!ionScript)
+            continue;
 
         SetIonScript(script, executionMode, nullptr);
         ionScript->detachDependentAsmJSModules(fop);
         ionScript->decref(fop);
         co.invalidate();
+        numInvalidations--;
 
         // Wait for the scripts to get warm again before doing another
         // compile, unless either:
         // (1) we are recompiling *because* a script got hot;
         //     (resetUses is false); or,
         // (2) we are invalidating a parallel script.  This is because
         //     the useCount only applies to sequential uses.  Parallel
         //     execution *requires* ion, and so we don't limit it to
         //     methods with a high usage count (though we do check that
         //     the useCount is at least 1 when compiling the transitive
         //     closure of potential callees, to avoid compiling things
         //     that are never run at all).
         if (resetUses && executionMode != ParallelExecution)
             script->resetUseCount();
     }
+
+    // Make sure we didn't leak references by invalidating the same IonScript
+    // multiple times in the above loop.
+    JS_ASSERT(!numInvalidations);
 }
 
 void
 jit::Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses)
 {
     jit::Invalidate(cx->compartment()->types, cx->runtime()->defaultFreeOp(), invalid, resetUses);
 }
 
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -1902,69 +1902,68 @@ LinearSum::dump(FILE *fp) const
     fprintf(fp, "%s\n", sp.string());
 }
 
 static bool
 AnalyzePoppedThis(JSContext *cx, types::TypeObject *type,
                   MDefinition *thisValue, MInstruction *ins, bool definitelyExecuted,
                   HandleObject baseobj,
                   Vector<types::TypeNewScript::Initializer> *initializerList,
-                  Vector<jsid> *accessedProperties,
+                  Vector<PropertyName *> *accessedProperties,
                   bool *phandled)
 {
     // Determine the effect that a use of the |this| value when calling |new|
     // on a script has on the properties definitely held by the new object.
 
     if (ins->isCallSetProperty()) {
         MCallSetProperty *setprop = ins->toCallSetProperty();
 
         if (setprop->obj() != thisValue)
             return true;
 
         // Don't use GetAtomId here, we need to watch for SETPROP on
         // integer properties and bail out. We can't mark the aggregate
         // JSID_VOID type property as being in a definite slot.
-        RootedId id(cx, NameToId(setprop->name()));
-        if (types::IdToTypeId(id) != id ||
-            id == NameToId(cx->names().classPrototype) ||
-            id == NameToId(cx->names().proto) ||
-            id == NameToId(cx->names().constructor))
+        if (setprop->name() == cx->names().classPrototype ||
+            setprop->name() == cx->names().proto ||
+            setprop->name() == cx->names().constructor)
         {
             return true;
         }
 
         // Ignore assignments to properties that were already written to.
-        if (baseobj->nativeLookup(cx, id)) {
+        if (baseobj->nativeLookup(cx, NameToId(setprop->name()))) {
             *phandled = true;
             return true;
         }
 
         // Don't add definite properties for properties that were already
         // read in the constructor.
         for (size_t i = 0; i < accessedProperties->length(); i++) {
-            if ((*accessedProperties)[i] == id)
+            if ((*accessedProperties)[i] == setprop->name())
                 return true;
         }
 
         if (baseobj->slotSpan() >= (types::TYPE_FLAG_DEFINITE_MASK >> types::TYPE_FLAG_DEFINITE_SHIFT)) {
             // Maximum number of definite properties added.
             return true;
         }
 
         // Assignments to new properties must always execute.
         if (!definitelyExecuted)
             return true;
 
-        if (!types::AddClearDefiniteGetterSetterForPrototypeChain(cx, type, id)) {
+        if (!types::AddClearDefiniteGetterSetterForPrototypeChain(cx, type, NameToId(setprop->name()))) {
             // The prototype chain already contains a getter/setter for this
             // property, or type information is too imprecise.
             return true;
         }
 
         DebugOnly<unsigned> slotSpan = baseobj->slotSpan();
+        RootedId id(cx, NameToId(setprop->name()));
         RootedValue value(cx, UndefinedValue());
         if (!DefineNativeProperty(cx, baseobj, id, value, nullptr, nullptr,
                                   JSPROP_ENUMERATE, 0, 0, DNP_SKIP_TYPE))
         {
             return false;
         }
         JS_ASSERT(baseobj->slotSpan() != slotSpan);
         JS_ASSERT(!baseobj->inDictionaryMode());
@@ -2010,19 +2009,17 @@ AnalyzePoppedThis(JSContext *cx, types::
          *   could cause 'this' to escape.
          *
          * - The accessed property is either already a definite property or
          *   is not later added as one. Since the definite properties are
          *   added to the object at the point of its creation, reading a
          *   definite property before it is assigned could incorrectly hit.
          */
         RootedId id(cx, NameToId(get->name()));
-        if (types::IdToTypeId(id) != id)
-            return true;
-        if (!baseobj->nativeLookup(cx, id) && !accessedProperties->append(id.get()))
+        if (!baseobj->nativeLookup(cx, id) && !accessedProperties->append(get->name()))
             return false;
 
         if (!types::AddClearDefiniteGetterSetterForPrototypeChain(cx, type, id)) {
             // The |this| value can escape if any property reads it does go
             // through a getter.
             return true;
         }
 
@@ -2061,17 +2058,17 @@ jit::AnalyzeNewScriptProperties(JSContex
     if (!fun->nonLazyScript()->compileAndGo)
         return true;
 
     if (!fun->nonLazyScript()->ensureHasTypes(cx))
         return false;
 
     types::TypeScript::SetThis(cx, fun->nonLazyScript(), types::Type::ObjectType(type));
 
-    Vector<jsid> accessedProperties(cx);
+    Vector<PropertyName *> accessedProperties(cx);
 
     LifoAlloc alloc(types::TypeZone::TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
 
     TempAllocator temp(&alloc);
     IonContext ictx(cx, &temp);
 
     types::AutoEnterAnalysis enter(cx);
 
@@ -2080,18 +2077,20 @@ jit::AnalyzeNewScriptProperties(JSContex
 
     MIRGraph graph(&temp);
     CompileInfo info(fun->nonLazyScript(), fun,
                      /* osrPc = */ nullptr, /* constructing = */ false,
                      DefinitePropertiesAnalysis);
 
     AutoTempAllocatorRooter root(cx, &temp);
 
+    types::CompilerConstraintList constraints;
     BaselineInspector inspector(cx, fun->nonLazyScript());
-    IonBuilder builder(cx, &temp, &graph, &inspector, &info, /* baselineFrame = */ nullptr);
+    IonBuilder builder(cx, &temp, &graph, &constraints,
+                       &inspector, &info, /* baselineFrame = */ nullptr);
 
     if (!builder.build()) {
         if (builder.abortReason() == AbortReason_Alloc)
             return false;
         return true;
     }
 
     if (!SplitCriticalEdges(graph))
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -29,26 +29,28 @@
 #include "jsscriptinlines.h"
 
 #include "jit/CompileInfo-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::DebugOnly;
+using mozilla::Maybe;
 
 IonBuilder::IonBuilder(JSContext *cx, TempAllocator *temp, MIRGraph *graph,
+                       types::CompilerConstraintList *constraints,
                        BaselineInspector *inspector, CompileInfo *info, BaselineFrame *baselineFrame,
                        size_t inliningDepth, uint32_t loopDepth)
   : MIRGenerator(cx->compartment(), temp, graph, info),
     backgroundCodegen_(nullptr),
-    recompileInfo(cx->compartment()->types.compiledInfo),
     cx(cx),
     baselineFrame_(baselineFrame),
     abortReason_(AbortReason_Disable),
+    constraints_(constraints),
     analysis_(info->script()),
     loopDepth_(loopDepth),
     callerResumePoint_(nullptr),
     callerBuilder_(nullptr),
     inspector(inspector),
     inliningDepth_(inliningDepth),
     numLoopRestarts_(0),
     failedBoundsCheck_(info->script()->failedBoundsCheck),
@@ -244,18 +246,18 @@ IonBuilder::canEnterInlinedFunction(JSFu
         return false;
 
     if (targetScript->needsArgsObj())
         return false;
 
     if (!targetScript->compileAndGo)
         return false;
 
-    types::TypeObject *targetType = target->getType(cx);
-    if (!targetType || targetType->unknownProperties())
+    types::TypeObjectKey *targetType = types::TypeObjectKey::get(target);
+    if (targetType->unknownProperties())
         return false;
 
     return true;
 }
 
 bool
 IonBuilder::canInlineTarget(JSFunction *target, bool constructing)
 {
@@ -3757,17 +3759,17 @@ IonBuilder::inlineScriptedCall(CallInfo 
     if (!info)
         return false;
 
     MIRGraphExits saveExits;
     AutoAccumulateExits aae(graph(), saveExits);
 
     // Build the graph.
     JS_ASSERT(!cx->isExceptionPending());
-    IonBuilder inlineBuilder(cx, &temp(), &graph(), &inspector, info, nullptr,
+    IonBuilder inlineBuilder(cx, &temp(), &graph(), constraints(), &inspector, info, nullptr,
                              inliningDepth_ + 1, loopDepth_);
     if (!inlineBuilder.buildInline(this, outerResumePoint, callInfo)) {
         if (cx->isExceptionPending()) {
             IonSpew(IonSpew_Abort, "Inline builder raised exception.");
             abortReason_ = AbortReason_Error;
             return false;
         }
 
@@ -3951,22 +3953,19 @@ IonBuilder::makeInliningDecision(JSFunct
             info().executionMode() != DefinitePropertiesAnalysis)
         {
             IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: callee is not hot.",
                     targetScript->filename(), targetScript->lineno);
             return false;
         }
     }
 
-    JS_ASSERT(!target->hasLazyType());
-    types::TypeObject *targetType = target->getType(cx);
-    JS_ASSERT(targetType && !targetType->unknownProperties());
-
     // TI calls ObjectStateChange to trigger invalidation of the caller.
-    types::HeapTypeSet::WatchObjectStateChange(cx, targetType);
+    types::TypeObjectKey *targetType = types::TypeObjectKey::get(target);
+    targetType->watchStateChange(constraints());
 
     return true;
 }
 
 uint32_t
 IonBuilder::selectInliningTargets(ObjectVector &targets, CallInfo &callInfo, BoolVector &choiceSet)
 {
     uint32_t totalSize = 0;
@@ -4602,26 +4601,24 @@ IonBuilder::createThisScripted(MDefiniti
     return createThis;
 }
 
 JSObject *
 IonBuilder::getSingletonPrototype(JSFunction *target)
 {
     if (!target || !target->hasSingletonType())
         return nullptr;
-    types::TypeObject *targetType = target->getType(cx);
+    types::TypeObjectKey *targetType = types::TypeObjectKey::get(target);
     if (targetType->unknownProperties())
         return nullptr;
 
     jsid protoid = NameToId(cx->names().classPrototype);
-    types::HeapTypeSet *protoTypes = targetType->getProperty(cx, protoid);
-    if (!protoTypes)
-        return nullptr;
-
-    return protoTypes->getSingleton(cx);
+    types::HeapTypeSetKey protoProperty = targetType->property(protoid);
+
+    return protoProperty.singleton(constraints());
 }
 
 MDefinition *
 IonBuilder::createThisScriptedSingleton(JSFunction *target, MDefinition *callee)
 {
     // Get the singleton prototype (if exists)
     JSObject *proto = getSingletonPrototype(target);
     if (!proto)
@@ -4639,18 +4636,19 @@ IonBuilder::createThisScriptedSingleton(
         return nullptr;
 
     RootedObject targetRoot(cx, target);
     JSObject *templateObject = CreateThisForFunctionWithProto(cx, targetRoot, proto, TenuredObject);
     if (!templateObject)
         return nullptr;
 
     // Trigger recompilation if the templateObject changes.
-    if (templateObject->type()->hasNewScript())
-        types::HeapTypeSet::WatchObjectStateChange(cx, templateObject->type());
+    types::TypeObjectKey *templateType = types::TypeObjectKey::get(templateObject);
+    if (templateType->newScript())
+        templateType->watchStateChange(constraints());
 
     MCreateThisWithTemplate *createThis = MCreateThisWithTemplate::New(templateObject);
     current->add(createThis);
 
     return createThis;
 }
 
 MDefinition *
@@ -4991,64 +4989,44 @@ TestShouldDOMCall(JSContext *cx, types::
     DOMInstanceClassMatchesProto instanceChecker =
         GetDOMCallbacks(cx->runtime())->instanceClassMatchesProto;
 
     const JSJitInfo *jinfo = func->jitInfo();
     if (jinfo->type != opType)
         return false;
 
     for (unsigned i = 0; i < inTypes->getObjectCount(); i++) {
-        types::TypeObject *curType = inTypes->getTypeObject(i);
-
-        if (!curType) {
-            JSObject *curObj = inTypes->getSingleObject(i);
-
-            if (!curObj)
-                continue;
-
-            curType = curObj->getType(cx);
-            if (!curType)
-                return false;
-        }
-
-        RootedObject protoRoot(cx, curType->proto);
+        types::TypeObjectKey *curType = inTypes->getObject(i);
+        if (!curType)
+            continue;
+
+        RootedObject protoRoot(cx, curType->proto().toObjectOrNull());
         if (!instanceChecker(protoRoot, jinfo->protoID, jinfo->depth))
             return false;
     }
 
     return true;
 }
 
 static bool
 TestAreKnownDOMTypes(JSContext *cx, types::TypeSet *inTypes)
 {
     if (inTypes->unknownObject())
         return false;
 
     // First iterate to make sure they all are DOM objects, then freeze all of
     // them as such if they are.
     for (unsigned i = 0; i < inTypes->getObjectCount(); i++) {
-        types::TypeObject *curType = inTypes->getTypeObject(i);
-
-        if (!curType) {
-            JSObject *curObj = inTypes->getSingleObject(i);
-
-            // Skip holes in TypeSets.
-            if (!curObj)
-                continue;
-
-            curType = curObj->getType(cx);
-            if (!curType)
-                return false;
-        }
+        types::TypeObjectKey *curType = inTypes->getObject(i);
+        if (!curType)
+            continue;
 
         if (curType->unknownProperties())
             return false;
-
-        if (!(curType->clasp->flags & JSCLASS_IS_DOMJSCLASS))
+        if (!(curType->clasp()->flags & JSCLASS_IS_DOMJSCLASS))
             return false;
     }
 
     // If we didn't check anything, no reason to say yes.
     if (inTypes->getObjectCount() > 0)
         return true;
 
     return false;
@@ -5383,17 +5361,17 @@ IonBuilder::jsop_newarray(uint32_t count
 
     if (templateObject->type()->unknownProperties()) {
         // We will get confused in jsop_initelem_array if we can't find the
         // type object being initialized.
         return abort("New array has unknown properties");
     }
 
     types::TemporaryTypeSet::DoubleConversion conversion =
-        bytecodeTypes(pc)->convertDoubleElements(cx);
+        bytecodeTypes(pc)->convertDoubleElements(constraints());
     if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles)
         templateObject->setShouldConvertDoubleElements();
 
     MNewArray *ins = new MNewArray(count, templateObject, MNewArray::NewArray_Allocating);
 
     current->add(ins);
     current->push(ins);
 
@@ -5458,26 +5436,24 @@ 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);
+    types::TypeObjectKey *initializer = obj->resultTypeSet()->getObject(0);
     if (value->isConstant() && value->toConstant()->value().isMagic(JS_ELEMENTS_HOLE)) {
-        if (!(initializer->flags & types::OBJECT_FLAG_NON_PACKED))
+        if (!initializer->hasFlags(constraints(), types::OBJECT_FLAG_NON_PACKED))
             needStub = true;
     } else if (!initializer->unknownProperties()) {
-        types::HeapTypeSet *elemTypes = initializer->getProperty(cx, JSID_VOID);
-        if (!elemTypes)
-            return false;
-        if (!TypeSetIncludes(elemTypes, value->type(), value->resultTypeSet())) {
-            elemTypes->addFreeze(cx);
+        types::HeapTypeSetKey elemTypes = initializer->property(JSID_VOID);
+        if (!TypeSetIncludes(elemTypes.actualTypes, value->type(), value->resultTypeSet())) {
+            elemTypes.freeze(constraints());
             needStub = true;
         }
     }
 
     if (NeedsPostBarrier(info(), value))
         current->add(MPostWriteBarrier::New(obj, value));
 
     if (needStub) {
@@ -5509,24 +5485,24 @@ IonBuilder::jsop_initelem_array()
 
     if (!resumeAfter(initLength))
         return false;
 
    return true;
 }
 
 static bool
-CanEffectlesslyCallLookupGenericOnObject(JSContext *cx, JSObject *obj, jsid id)
+CanEffectlesslyCallLookupGenericOnObject(JSContext *cx, JSObject *obj, PropertyName *name)
 {
     while (obj) {
         if (!obj->isNative())
             return false;
         if (obj->getClass()->ops.lookupGeneric)
             return false;
-        if (obj->nativeLookup(cx, id))
+        if (obj->nativeLookup(cx, NameToId(name)))
             return true;
         if (obj->getClass()->resolve != JS_ResolveStub &&
             obj->getClass()->resolve != (JSResolveOp)fun_resolve)
             return false;
         obj = obj->getProto();
     }
     return true;
 }
@@ -5534,54 +5510,49 @@ CanEffectlesslyCallLookupGenericOnObject
 bool
 IonBuilder::jsop_initprop(PropertyName *name)
 {
     MDefinition *value = current->pop();
     MDefinition *obj = current->peek(-1);
 
     RootedObject templateObject(cx, obj->toNewObject()->templateObject());
 
-    RootedId id(cx, NameToId(name));
-    if (!CanEffectlesslyCallLookupGenericOnObject(cx, templateObject, id))
+    if (!CanEffectlesslyCallLookupGenericOnObject(cx, templateObject, name))
         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) {
         // JSOP_NEWINIT becomes an MNewObject without preconfigured properties.
         MInitProp *init = MInitProp::New(obj, name, value);
         current->add(init);
         return resumeAfter(init);
     }
 
-    bool writeNeedsBarrier = false;
-    if (!PropertyWriteNeedsTypeBarrier(cx, current, &obj, name, &value, /* canModify = */ true,
-                                       &writeNeedsBarrier))
+    if (PropertyWriteNeedsTypeBarrier(constraints(), current,
+                                      &obj, name, &value, /* canModify = */ true))
     {
-        return false;
-    }
-    if (writeNeedsBarrier) {
         // JSOP_NEWINIT becomes an MNewObject without preconfigured properties.
         MInitProp *init = MInitProp::New(obj, name, value);
         current->add(init);
         return resumeAfter(init);
     }
 
     if (NeedsPostBarrier(info(), value))
         current->add(MPostWriteBarrier::New(obj, value));
 
     bool needsBarrier = true;
-    if ((id == types::IdToTypeId(id)) &&
-        obj->resultTypeSet() &&
-        !obj->resultTypeSet()->propertyNeedsBarrier(cx, id))
+    if (obj->resultTypeSet() &&
+        !obj->resultTypeSet()->propertyNeedsBarrier(constraints(), id))
     {
         needsBarrier = false;
     }
 
     // In parallel execution, we never require write barriers.  See
     // forkjoin.cpp for more information.
     switch (info().executionMode()) {
       case SequentialExecution:
@@ -5972,44 +5943,40 @@ IonBuilder::maybeInsertResume()
         return true;
 
     MNop *ins = MNop::New();
     current->add(ins);
 
     return resumeAfter(ins);
 }
 
-static inline bool
-TestSingletonProperty(JSContext *cx, JSObject *obj, JSObject *singleton,
-                      jsid id, bool *isKnownConstant)
+bool
+IonBuilder::testSingletonProperty(JSObject *obj, JSObject *singleton,
+                                  PropertyName *name, 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).
     //
     // If the access definitely goes through obj, either directly or on the
     // prototype chain, then if obj has a defined property now, and the
     // property has a default or method shape, then the property is not missing
     // and the only way it can become missing in the future is if it is deleted.
     // Deletion causes type properties to be explicitly marked with undefined.
 
     *isKnownConstant = false;
 
-    if (id != types::IdToTypeId(id))
-        return true;
-
-    if (!CanEffectlesslyCallLookupGenericOnObject(cx, obj, id))
+    if (!CanEffectlesslyCallLookupGenericOnObject(cx, obj, name))
         return true;
 
     RootedObject objRoot(cx, obj);
-    RootedId idRoot(cx, id);
-
+    RootedId idRoot(cx, NameToId(name));
     RootedObject holder(cx);
     RootedShape shape(cx);
     if (!JSObject::lookupGeneric(cx, objRoot, idRoot, &holder, &shape))
         return false;
     if (!shape)
         return true;
 
     if (!shape->hasDefaultGetter())
@@ -6018,47 +5985,42 @@ TestSingletonProperty(JSContext *cx, JSO
         return true;
     if (holder->getSlot(shape->slot()).isUndefined())
         return true;
 
     // Ensure the property does not appear anywhere on the prototype chain
     // before |holder|, and that |holder| only has the result object for its
     // property.
     while (true) {
-        types::TypeObject *objType = obj->getType(cx);
-        if (!objType)
-            return false;
+        types::TypeObjectKey *objType = types::TypeObjectKey::get(obj);
         if (objType->unknownProperties())
             return true;
 
-        types::HeapTypeSet *property = objType->getProperty(cx, id);
-        if (!property)
-            return false;
+        types::HeapTypeSetKey property = objType->property(idRoot);
         if (obj != holder) {
-            if (!property->empty())
+            if (property.notEmpty(constraints()))
                 return true;
-            property->addFreeze(cx);
         } else {
-            if (property->getSingleton(cx) != singleton)
+            if (property.singleton(constraints()) != singleton)
                 return true;
             break;
         }
 
         obj = obj->getProto();
     }
 
     *isKnownConstant = true;
     return true;
 }
 
-static inline bool
-TestSingletonPropertyTypes(JSContext *cx, MDefinition *obj, JSObject *singleton,
-                           JSObject *globalObj, jsid id,
-                           bool *isKnownConstant, bool *testObject,
-                           bool *testString)
+bool
+IonBuilder::testSingletonPropertyTypes(MDefinition *obj, JSObject *singleton,
+                                       JSObject *globalObj, PropertyName *name,
+                                       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;
@@ -6066,22 +6028,19 @@ TestSingletonPropertyTypes(JSContext *cx
     types::TemporaryTypeSet *types = obj->resultTypeSet();
 
     if (!types && obj->type() != MIRType_String)
         return true;
 
     if (types && types->unknownObject())
         return true;
 
-    if (id != types::IdToTypeId(id))
-        return true;
-
     JSObject *objectSingleton = types ? types->getSingleton() : nullptr;
     if (objectSingleton)
-        return TestSingletonProperty(cx, objectSingleton, singleton, id, isKnownConstant);
+        return testSingletonProperty(objectSingleton, singleton, name, isKnownConstant);
 
     if (!globalObj)
         return true;
 
     JSProtoKey key;
     switch (obj->type()) {
       case MIRType_String:
         key = JSProto_String;
@@ -6106,42 +6065,30 @@ TestSingletonPropertyTypes(JSContext *cx
 
         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.
         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;
-            }
+            types::TypeObjectKey *object = types->getObject(i);
+            if (!object)
+                continue;
 
             if (object->unknownProperties())
                 return true;
-            types::HeapTypeSet *property = object->getProperty(cx, id);
-            if (!property)
-                return false;
-            if (!property->empty())
+            types::HeapTypeSetKey property = object->property(NameToId(name));
+            if (property.notEmpty(constraints()))
                 return true;
-            property->addFreeze(cx);
-
-            if (object->proto) {
+
+            if (JSObject *proto = object->proto().toObjectOrNull()) {
                 // Test this type.
                 bool thoughtConstant = false;
-                if (!TestSingletonProperty(cx, object->proto, singleton, id, &thoughtConstant))
+                if (!testSingletonProperty(proto, singleton, name, &thoughtConstant))
                     return false;
                 if (!thoughtConstant)
                     return true;
             } else {
                 // Can't be on the prototype chain with no prototypes...
                 return true;
             }
         }
@@ -6153,17 +6100,17 @@ TestSingletonPropertyTypes(JSContext *cx
       default:
         return true;
     }
 
     RootedObject proto(cx);
     if (!js_GetClassPrototype(cx, key, &proto, nullptr))
         return false;
 
-    return TestSingletonProperty(cx, proto, singleton, id, isKnownConstant);
+    return testSingletonProperty(proto, singleton, name, 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 needed,
 //     then an infallible unbox instruction replaces the value on the top of
 //     the stack.
@@ -6258,67 +6205,59 @@ IonBuilder::getStaticName(JSObject *stat
     // For the fastest path, the property must be found, and it must be found
     // as a normal data property on exactly the global object.
     Shape *shape = staticObject->nativeLookup(cx, id);
     if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot()) {
         *psucceeded = false;
         return true;
     }
 
-    types::TypeObject *staticType = staticObject->getType(cx);
-    if (!staticType)
-        return false;
-    types::HeapTypeSet *propertyTypes = nullptr;
+    types::TypeObjectKey *staticType = types::TypeObjectKey::get(staticObject);
+    Maybe<types::HeapTypeSetKey> propertyTypes;
     if (!staticType->unknownProperties()) {
-        propertyTypes = staticType->getProperty(cx, id);
-        if (!propertyTypes)
-            return false;
-    }
-    if (propertyTypes && propertyTypes->isConfiguredProperty(cx, staticType)) {
-        // The property has been reconfigured as non-configurable, non-enumerable
-        // or non-writable.
-        *psucceeded = false;
-        return true;
+        propertyTypes.construct(staticType->property(id));
+        if (propertyTypes.ref().configured(constraints(), staticType)) {
+            // The property has been reconfigured as non-configurable, non-enumerable
+            // or non-writable.
+            *psucceeded = false;
+            return true;
+        }
     }
 
     types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc);
-    bool barrier;
-    if (!PropertyReadNeedsTypeBarrier(cx, staticType, name, baseTypes, /* updateObserved = */ true,
-                                      &barrier))
-    {
-        return false;
-    }
+    bool barrier = PropertyReadNeedsTypeBarrier(cx, constraints(), staticType,
+                                                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) {
             // Try to inline a known constant value.
             bool isKnownConstant;
-            if (!TestSingletonProperty(cx, staticObject, singleton, id, &isKnownConstant))
+            if (!testSingletonProperty(staticObject, singleton, name, &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 *obj = MConstant::New(ObjectValue(*staticObject));
     current->add(obj);
 
-    // If we have a property typeset, the isOwnProperty call will trigger recompilation if
-    // the property is deleted or reconfigured.
-    if (!propertyTypes && shape->configurable())
+    // If we have a property typeset, the HeapTypeSetIsConfigured call will
+    // trigger recompilation if the property is deleted or reconfigured.
+    if (propertyTypes.empty() && shape->configurable())
         obj = addShapeGuard(obj, staticObject->lastProperty(), Bailout_ShapeGuard);
 
     MIRType rvalType = MIRTypeFromValueType(types->getKnownTypeTag());
     if (barrier)
         rvalType = MIRType_Value;
 
     return loadSlot(obj, shape, rvalType, barrier, types);
 }
@@ -6369,60 +6308,51 @@ IonBuilder::setStaticName(JSObject *stat
         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.
     Shape *shape = staticObject->nativeLookup(cx, id);
     if (!shape || !shape->hasDefaultSetter() || !shape->writable() || !shape->hasSlot())
         return jsop_setprop(name);
 
-    types::TypeObject *staticType = staticObject->getType(cx);
-    if (!staticType)
-        return false;
-    types::HeapTypeSet *propertyTypes = nullptr;
-    if (!staticType->unknownProperties()) {
-        propertyTypes = staticType->getProperty(cx, id);
-        if (!propertyTypes)
-            return false;
-    }
-    if (!propertyTypes || propertyTypes->isConfiguredProperty(cx, staticType)) {
+    types::TypeObjectKey *staticType = types::TypeObjectKey::get(staticObject);
+    if (staticType->unknownProperties())
+        return jsop_setprop(name);
+
+    types::HeapTypeSetKey propertyTypes = staticType->property(id);
+    if (propertyTypes.configured(constraints(), staticType)) {
         // The property has been reconfigured as non-configurable, non-enumerable
         // or non-writable.
         return jsop_setprop(name);
     }
-    if (!TypeSetIncludes(propertyTypes, value->type(), value->resultTypeSet()))
+
+    if (!TypeSetIncludes(propertyTypes.actualTypes, value->type(), value->resultTypeSet()))
         return jsop_setprop(name);
 
     current->pop();
 
     // Pop the bound object on the stack.
     MDefinition *obj = current->pop();
     JS_ASSERT(&obj->toConstant()->value().toObject() == staticObject);
 
-    // 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)
-        obj = addShapeGuard(obj, staticObject->lastProperty(), Bailout_ShapeGuard);
-
     if (NeedsPostBarrier(info(), value))
         current->add(MPostWriteBarrier::New(obj, value));
 
     // If the property has a known type, we may be able to optimize typed stores by not
     // storing the type tag. This only works if the property does not have its initial
     // |undefined| value; if |undefined| is assigned at a later point, it will be added
     // to the type set.
     MIRType slotType = MIRType_None;
-    if (propertyTypes && !staticObject->getSlot(shape->slot()).isUndefined()) {
-        JSValueType knownType = propertyTypes->getKnownTypeTag(cx);
+    if (!staticObject->getSlot(shape->slot()).isUndefined()) {
+        JSValueType knownType = propertyTypes.knownTypeTag(constraints());
         if (knownType != JSVAL_TYPE_UNKNOWN)
             slotType = MIRTypeFromValueType(knownType);
     }
 
-    bool needsBarrier = !propertyTypes || propertyTypes->needsBarrier(cx);
+    bool needsBarrier = propertyTypes.needsBarrier(constraints());
     return storeSlot(obj, shape, value, needsBarrier, slotType);
 }
 
 bool
 IonBuilder::jsop_getname(PropertyName *name)
 {
     MDefinition *object;
     if (js_CodeSpec[*pc].format & JOF_GNAME) {
@@ -6570,17 +6500,17 @@ IonBuilder::getElemTryDense(bool *emitte
 {
     JS_ASSERT(*emitted == false);
 
     if (!ElementAccessIsDenseNative(obj, index))
         return true;
 
     // Don't generate a fast path if there have been bounds check failures
     // and this access might be on a sparse property.
-    if (ElementAccessHasExtraIndexedProperty(cx, obj) && failedBoundsCheck_)
+    if (ElementAccessHasExtraIndexedProperty(constraints(), obj) && failedBoundsCheck_)
         return true;
 
     // Don't generate a fast path if this pc has seen negative indexes accessed,
     // which will not appear to be extra indexed properties.
     if (inspector->hasSeenNegativeIndexGetElement(pc))
         return true;
 
     // Emit dense getelem variant.
@@ -6598,17 +6528,17 @@ IonBuilder::getElemTryTypedStatic(bool *
 
     ScalarTypeRepresentation::Type arrayType;
     if (!ElementAccessIsTypedArray(obj, index, &arrayType))
         return true;
 
     if (!LIRGenerator::allowStaticTypedArrayAccesses())
         return true;
 
-    if (ElementAccessHasExtraIndexedProperty(cx, obj))
+    if (ElementAccessHasExtraIndexedProperty(constraints(), obj))
         return true;
 
     if (!obj->resultTypeSet())
         return true;
 
     JSObject *tarrObj = obj->resultTypeSet()->getSingleton();
     if (!tarrObj)
         return true;
@@ -6798,19 +6728,17 @@ IonBuilder::getElemTryCache(bool *emitte
     // of this getelem.
     bool nonNativeGetElement = inspector->hasSeenNonNativeGetElement(pc);
     if (index->mightBeType(MIRType_Int32) && nonNativeGetElement)
         return true;
 
     // Emit GetElementCache.
 
     types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc);
-    bool barrier;
-    if (!PropertyReadNeedsTypeBarrier(cx, obj, nullptr, baseTypes, &barrier))
-        return false;
+    bool barrier = PropertyReadNeedsTypeBarrier(cx, 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.
@@ -6822,17 +6750,17 @@ IonBuilder::getElemTryCache(bool *emitte
     current->add(ins);
     current->push(ins);
 
     if (!resumeAfter(ins))
         return false;
 
     // Spice up type information.
     if (index->type() == MIRType_Int32 && !barrier) {
-        bool needHoleCheck = !ElementAccessIsPacked(cx, obj);
+        bool needHoleCheck = !ElementAccessIsPacked(constraints(), obj);
         JSValueType knownType = GetElemKnownType(needHoleCheck, types);
 
         if (knownType != JSVAL_TYPE_UNKNOWN && knownType != JSVAL_TYPE_DOUBLE)
             ins->setResultType(MIRTypeFromValueType(knownType));
     }
 
     if (!pushTypeBarrier(ins, types, barrier))
         return false;
@@ -6849,29 +6777,27 @@ IonBuilder::jsop_getelem_dense(MDefiniti
     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(cx, obj, nullptr, baseTypes))
             return false;
     }
 
-    bool barrier;
-    if (!PropertyReadNeedsTypeBarrier(cx, obj, nullptr, baseTypes, &barrier))
-        return false;
+    bool barrier = PropertyReadNeedsTypeBarrier(cx, constraints(), obj, nullptr, baseTypes);
     types::TemporaryTypeSet *types = cloneTypeSet(baseTypes);
 
-    bool needsHoleCheck = !ElementAccessIsPacked(cx, obj);
+    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(cx, obj);
+        !ElementAccessHasExtraIndexedProperty(constraints(), obj);
 
     JSValueType knownType = JSVAL_TYPE_UNKNOWN;
     if (!barrier)
         knownType = GetElemKnownType(needsHoleCheck, types);
 
     // Ensure index is an integer.
     MInstruction *idInt32 = MToInt32::New(index);
     current->add(idInt32);
@@ -6898,17 +6824,17 @@ IonBuilder::jsop_getelem_dense(MDefiniti
     bool loadDouble =
         executionMode == SequentialExecution &&
         !barrier &&
         loopDepth_ &&
         !readOutOfBounds &&
         !needsHoleCheck &&
         knownType == JSVAL_TYPE_DOUBLE &&
         objTypes &&
-        objTypes->convertDoubleElements(cx) == types::TemporaryTypeSet::AlwaysConvertToDoubles;
+        objTypes->convertDoubleElements(constraints()) == types::TemporaryTypeSet::AlwaysConvertToDoubles;
     if (loadDouble)
         elements = addConvertElementsToDoubles(elements);
 
     MInstruction *load;
 
     if (!readOutOfBounds) {
         // This load should not return undefined, so likely we're reading
         // in-bounds elements, and the array is packed or its holes are not
@@ -6954,17 +6880,18 @@ MInstruction *
 IonBuilder::getTypedArrayElements(MDefinition *obj)
 {
     if (obj->isConstant() && obj->toConstant()->value().isObject()) {
         TypedArrayObject *tarr = &obj->toConstant()->value().toObject().as<TypedArrayObject>();
         void *data = tarr->viewData();
 
         // The 'data' pointer can change in rare circumstances
         // (ArrayBufferObject::changeContents).
-        types::HeapTypeSet::WatchObjectStateChange(cx, tarr->getType(cx));
+        types::TypeObjectKey *tarrType = types::TypeObjectKey::get(tarr);
+        tarrType->watchStateChange(constraints());
 
         obj->setFoldedUnchecked();
         return MConstantElements::New(data);
     }
     return MTypedArrayElements::New(obj);
 }
 
 MDefinition *
@@ -7157,17 +7084,17 @@ IonBuilder::setElemTryTypedStatic(bool *
 
     ScalarTypeRepresentation::Type arrayType;
     if (!ElementAccessIsTypedArray(object, index, &arrayType))
         return true;
 
     if (!LIRGenerator::allowStaticTypedArrayAccesses())
         return true;
 
-    if (ElementAccessHasExtraIndexedProperty(cx, object))
+    if (ElementAccessHasExtraIndexedProperty(constraints(), object))
         return true;
 
     if (!object->resultTypeSet())
         return true;
     JSObject *tarrObj = object->resultTypeSet()->getSingleton();
     if (!tarrObj)
         return true;
 
@@ -7221,40 +7148,37 @@ IonBuilder::setElemTryTyped(bool *emitte
 bool
 IonBuilder::setElemTryDense(bool *emitted, MDefinition *object,
                             MDefinition *index, MDefinition *value)
 {
     JS_ASSERT(*emitted == false);
 
     if (!ElementAccessIsDenseNative(object, index))
         return true;
-    bool needsBarrier;
-    if (!PropertyWriteNeedsTypeBarrier(cx, current, &object, nullptr, &value,
-                                       /* canModify = */ true, &needsBarrier))
+    if (PropertyWriteNeedsTypeBarrier(constraints(), current,
+                                      &object, nullptr, &value, /* canModify = */ true))
     {
-        return false;
-    }
-    if (needsBarrier)
-        return true;
+        return true;
+    }
     if (!object->resultTypeSet())
         return true;
 
     types::TemporaryTypeSet::DoubleConversion conversion =
-        object->resultTypeSet()->convertDoubleElements(cx);
+        object->resultTypeSet()->convertDoubleElements(constraints());
 
     // If AmbiguousDoubleConversion, only handle int32 values for now.
     if (conversion == types::TemporaryTypeSet::AmbiguousDoubleConversion &&
         value->type() != MIRType_Int32)
     {
         return true;
     }
 
     // Don't generate a fast path if there have been bounds check failures
     // and this access might be on a sparse property.
-    if (ElementAccessHasExtraIndexedProperty(cx, object) && failedBoundsCheck_)
+    if (ElementAccessHasExtraIndexedProperty(constraints(), object) && failedBoundsCheck_)
         return true;
 
     // Emit dense setelem variant.
     if (!jsop_setelem_dense(conversion, SetElem_Normal, object, index, value))
         return false;
 
     *emitted = true;
     return true;
@@ -7287,25 +7211,21 @@ IonBuilder::setElemTryCache(bool *emitte
 
     // TODO: Bug 876650: remove this check:
     // Temporary disable the cache if non dense native,
     // until the cache supports more ics
     SetElemICInspector icInspect(inspector->setElemICInspector(pc));
     if (!icInspect.sawDenseWrite() && !icInspect.sawTypedArrayWrite())
         return true;
 
-    bool needsBarrier;
-    if (!PropertyWriteNeedsTypeBarrier(cx, current, &object, nullptr, &value,
-                                       /* canModify = */ true, &needsBarrier))
+    if (PropertyWriteNeedsTypeBarrier(constraints(), current,
+                                      &object, nullptr, &value, /* canModify = */ true))
     {
-        return false;
-    }
-
-    if (needsBarrier)
-        return true;
+        return true;
+    }
 
     // Emit SetElementCache.
     MInstruction *ins = MSetElementCache::New(object, index, value, script()->strict);
     current->add(ins);
     current->push(value);
 
     if (!resumeAfter(ins))
         return false;
@@ -7314,25 +7234,22 @@ IonBuilder::setElemTryCache(bool *emitte
     return true;
 }
 
 bool
 IonBuilder::jsop_setelem_dense(types::TemporaryTypeSet::DoubleConversion conversion,
                                SetElemSafety safety,
                                MDefinition *obj, MDefinition *id, MDefinition *value)
 {
-    MIRType elementType;
-    if (!DenseNativeElementType(cx, obj, &elementType))
-        return false;
-
-    bool packed = ElementAccessIsPacked(cx, obj);
+    MIRType elementType = DenseNativeElementType(constraints(), obj);
+    bool packed = ElementAccessIsPacked(constraints(), 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 = !ElementAccessHasExtraIndexedProperty(cx, obj);
+    bool writeOutOfBounds = !ElementAccessHasExtraIndexedProperty(constraints(), obj);
 
     if (NeedsPostBarrier(info(), value))
         current->add(MPostWriteBarrier::New(obj, value));
 
     // Ensure id is an integer.
     MInstruction *idInt32 = MToInt32::New(id);
     current->add(idInt32);
     id = idInt32;
@@ -7411,17 +7328,17 @@ IonBuilder::jsop_setelem_dense(types::Te
         if (safety == SetElem_Normal)
             current->push(value);
 
         if (!resumeAfter(ins))
             return false;
     }
 
     // Determine whether a write barrier is required.
-    if (obj->resultTypeSet()->propertyNeedsBarrier(cx, JSID_VOID))
+    if (obj->resultTypeSet()->propertyNeedsBarrier(constraints(), JSID_VOID))
         store->setNeedsBarrier();
 
     if (elementType != MIRType_None && packed)
         store->setElementType(elementType);
 
     return true;
 }
 
@@ -7517,17 +7434,17 @@ IonBuilder::jsop_length_fastPath()
         return true;
     }
 
     if (obj->mightBeType(MIRType_Object)) {
         types::TemporaryTypeSet *objTypes = obj->resultTypeSet();
 
         if (objTypes &&
             objTypes->getKnownClass() == &ArrayObject::class_ &&
-            !objTypes->hasObjectFlags(cx, types::OBJECT_FLAG_LENGTH_OVERFLOW))
+            !objTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_LENGTH_OVERFLOW))
         {
             current->pop();
             MElements *elements = MElements::New(obj);
             current->add(elements);
 
             // Read length.
             MArrayLength *length = new MArrayLength(elements);
             current->add(length);
@@ -7640,39 +7557,32 @@ IonBuilder::jsop_rest()
 
     MSetInitializedLength *initLength = MSetInitializedLength::New(elements, index);
     current->add(initLength);
     current->push(array);
 
     return true;
 }
 
-inline types::HeapTypeSet *
-GetDefiniteSlot(JSContext *cx, types::TemporaryTypeSet *types, JSAtom *atom)
+bool
+IonBuilder::getDefiniteSlot(types::TemporaryTypeSet *types, PropertyName *name,
+                            types::HeapTypeSetKey *property)
 {
     if (!types || types->unknownObject() || types->getObjectCount() != 1)
-        return nullptr;
-
-    types::TypeObject *type = types->getTypeObject(0);
-    if (!type || type->unknownProperties())
-        return nullptr;
-
-    jsid id = AtomToId(atom);
-    if (id != types::IdToTypeId(id))
-        return nullptr;
-
-    types::HeapTypeSet *propertyTypes = type->getProperty(cx, id);
-    if (!propertyTypes ||
-        !propertyTypes->definiteProperty() ||
-        propertyTypes->isConfiguredProperty(cx, type))
-    {
-        return nullptr;
-    }
-
-    return propertyTypes;
+        return false;
+
+    types::TypeObjectKey *type = types->getObject(0);
+    if (type->unknownProperties())
+        return false;
+
+    jsid id = NameToId(name);
+
+    *property = type->property(id);
+    return property->actualTypes->definiteProperty() &&
+           !property->configured(constraints(), type);
 }
 
 bool
 IonBuilder::jsop_runonce()
 {
     MRunOncePrologue *ins = MRunOncePrologue::New();
     current->add(ins);
     return resumeAfter(ins);
@@ -7696,30 +7606,29 @@ TestClassHasAccessorHook(const Class *cl
     if (isGetter && clasp->ops.getGeneric)
         return true;
     if (!isGetter && clasp->ops.setGeneric)
         return true;
     return false;
 }
 
 inline bool
-TestTypeHasOwnProperty(JSContext *cx, types::TypeObject *typeObj, jsid id, bool &cont)
+TestTypeHasOwnProperty(types::TypeObjectKey *typeObj, PropertyName *name, bool &cont)
 {
     cont = true;
-    types::HeapTypeSet *propSet = typeObj->getProperty(cx, types::IdToTypeId(id));
-    if (!propSet)
-        return false;
-    if (!propSet->empty())
+    types::HeapTypeSetKey propSet = typeObj->property(NameToId(name));
+    if (!propSet.actualTypes->empty())
         cont = false;
     // Note: Callers must explicitly freeze the property type set later on if optimizing.
     return true;
 }
 
 inline bool
-TestCommonAccessorProtoChain(JSContext *cx, jsid id, bool isGetter, JSObject *foundProto,
+TestCommonAccessorProtoChain(JSContext *cx, PropertyName *name,
+                             bool isGetter, JSObject *foundProto,
                              JSObject *obj, bool &cont)
 {
     cont = false;
     JSObject *curObj = obj;
     JSObject *stopAt = foundProto->getProto();
     while (curObj != stopAt) {
         // Don't optimize if we have a hook that would have to be called.
         if (TestClassHasAccessorHook(curObj->getClass(), isGetter))
@@ -7727,92 +7636,89 @@ TestCommonAccessorProtoChain(JSContext *
 
         // Check here to make sure that everyone has Type Objects with known
         // properties between them and the proto we found the accessor on. We
         // need those to add freezes safely. NOTE: We do not do this above, as
         // we may be able to freeze all the types up to where we found the
         // property, even if there are unknown types higher in the prototype
         // chain.
         if (curObj != foundProto) {
-            types::TypeObject *typeObj = curObj->getType(cx);
-            if (!typeObj)
-                return false;
-
+            types::TypeObjectKey *typeObj = types::TypeObjectKey::get(curObj);
             if (typeObj->unknownProperties())
                 return true;
 
             // Check here to make sure that nobody on the prototype chain is
             // marked as having the property as an "own property". This can
             // happen in cases of |delete| having been used, or cases with
             // watched objects. If TI ever decides to be more accurate about
             // |delete| handling, this should go back to curObj->watched().
 
             // Even though we are not directly accessing the properties on the whole
             // prototype chain, we need to fault in the sets anyway, as we need
             // to freeze on them.
             bool lcont;
-            if (!TestTypeHasOwnProperty(cx, typeObj, id, lcont))
+            if (!TestTypeHasOwnProperty(typeObj, name, lcont))
                 return false;
             if (!lcont)
                 return true;
         }
 
         curObj = curObj->getProto();
     }
     cont = true;
     return true;
 }
 
 inline bool
-SearchCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, jsid id, bool isGetter,
+SearchCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types,
+                     PropertyName *name, bool isGetter,
                      JSObject *&found, JSObject *&foundProto, bool &cont)
 {
     cont = false;
     for (unsigned i = 0; i < types->getObjectCount(); i++) {
         RootedObject curObj(cx, types->getSingleObject(i));
 
         // Non-Singleton type
         if (!curObj) {
-            types::TypeObject *typeObj = types->getTypeObject(i);
-
+            types::TypeObjectKey *typeObj = types->getObject(i);
             if (!typeObj)
                 continue;
 
             if (typeObj->unknownProperties())
                 return true;
 
             // If the class of the object has a hook, we can't
             // inline, as we would need to call the hook.
-            if (TestClassHasAccessorHook(typeObj->clasp, isGetter))
+            if (TestClassHasAccessorHook(typeObj->clasp(), isGetter))
                 return true;
 
             // If the type has an own property, we can't be sure we don't shadow
             // the chain.
             bool lcont;
-            if (!TestTypeHasOwnProperty(cx, typeObj, id, lcont))
+            if (!TestTypeHasOwnProperty(typeObj, name, lcont))
                 return false;
             if (!lcont)
                 return true;
 
             // Otherwise try using the prototype.
-            curObj = typeObj->proto;
+            curObj = typeObj->proto().toObjectOrNull();
         } else {
             // We can't optimize setters on watched singleton objects. A getter
             // on an own property can be protected with the prototype
             // shapeguard, though.
             if (!isGetter && curObj->watched())
                 return true;
         }
 
         // Turns out that we need to check for a property lookup op, else we
         // will end up calling it mid-compilation.
-        if (!CanEffectlesslyCallLookupGenericOnObject(cx, curObj, id))
+        if (!CanEffectlesslyCallLookupGenericOnObject(cx, curObj, name))
             return true;
 
-        RootedId idRoot(cx, id);
+        RootedId idRoot(cx, NameToId(name));
         RootedObject proto(cx);
         RootedShape shape(cx);
         if (!JSObject::lookupGeneric(cx, curObj, idRoot, &proto, &shape))
             return false;
 
         if (!shape)
             return true;
 
@@ -7842,87 +7748,75 @@ SearchCommonPropFunc(JSContext *cx, type
         // overwhelmingly more likely than having multiple different prototype
         // chains with the same custom property function.
         if (!foundProto)
             foundProto = proto;
         else if (foundProto != proto)
             return true;
 
         bool lcont;
-        if (!TestCommonAccessorProtoChain(cx, id, isGetter, foundProto, curObj, lcont))
+        if (!TestCommonAccessorProtoChain(cx, name, isGetter, foundProto, curObj, lcont))
             return false;
         if (!lcont)
             return true;
     }
     cont = true;
     return true;
 }
 
-inline bool
-FreezePropTypeSets(JSContext *cx, types::TemporaryTypeSet *types, JSObject *foundProto, jsid id)
-{
-    types::TypeObject *curType;
+bool
+IonBuilder::freezePropTypeSets(types::TemporaryTypeSet *types,
+                               JSObject *foundProto, PropertyName *name)
+{
     for (unsigned i = 0; i < types->getObjectCount(); i++) {
-        curType = types->getTypeObject(i);
-        JSObject *obj = nullptr;
-        if (!curType) {
-            obj = types->getSingleObject(i);
-            if (!obj)
-                continue;
-
-            curType = obj->getType(cx);
-            if (!curType)
-                return false;
-        }
-
         // If we found a Singleton object's own-property, there's nothing to
         // freeze.
-        if (obj != foundProto) {
-            // Walk the prototype chain. Everyone has to have the property, since we
-            // just checked, so propSet cannot be nullptr.
-            jsid typeId = types::IdToTypeId(id);
-            while (true) {
-                types::HeapTypeSet *propSet = curType->getProperty(cx, typeId);
-                // This assert is now assured, since we have faulted them in
-                // above.
-                JS_ASSERT(propSet && propSet->empty());
-                propSet->addFreeze(cx);
-                // Don't mark the proto. It will be held down by the shape
-                // guard. This allows us tp use properties found on prototypes
-                // with properties unknown to TI.
-                if (curType->proto == foundProto)
-                    break;
-                curType = curType->proto->getType(cx);
-                if (!curType)
-                    return false;
-            }
+        if (types->getSingleObject(i) == foundProto)
+            continue;
+
+        types::TypeObjectKey *type = types->getObject(i);
+        if (!type)
+            continue;
+
+        // Walk the prototype chain. Everyone has to have the property, since we
+        // just checked, so propSet cannot be NULL.
+        while (true) {
+            types::HeapTypeSetKey property = type->property(NameToId(name));
+            JS_ALWAYS_TRUE(!property.notEmpty(constraints()));
+
+            // Don't mark the proto. It will be held down by the shape
+            // guard. This allows us to use properties found on prototypes
+            // with properties unknown to TI.
+            if (type->proto() == foundProto)
+                break;
+            type = types::TypeObjectKey::get(type->proto().toObjectOrNull());
         }
     }
     return true;
 }
 
 inline bool
-IonBuilder::testCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, jsid id,
+IonBuilder::testCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, PropertyName *name,
                                JSFunction **funcp, bool isGetter, bool *isDOM,
                                MDefinition **guardOut)
 {
     JSObject *found = nullptr;
     JSObject *foundProto = nullptr;
 
     *funcp = nullptr;
     *isDOM = false;
 
     // No sense looking if we don't know what's going on.
     if (!types || types->unknownObject())
         return true;
 
     // Iterate down all the types to see if they all have the same getter or
     // setter.
     bool cont;
-    if (!SearchCommonPropFunc(cx, types, id, isGetter, found, foundProto, cont))
+    if (!SearchCommonPropFunc(cx, types, name, isGetter, found, foundProto, cont))
         return false;
     if (!cont)
         return true;
 
     // No need to add a freeze if we didn't find anything
     if (!found)
         return true;
 
@@ -7939,32 +7833,30 @@ IonBuilder::testCommonPropFunc(JSContext
     // Pass the guard back so it can be an operand.
     if (guardOut) {
         JS_ASSERT(wrapper->isGuardShape());
         *guardOut = wrapper;
     }
 
     // Now we have to freeze all the property typesets to ensure there isn't a
     // lower shadowing getter or setter installed in the future.
-    if (!FreezePropTypeSets(cx, types, foundProto, id))
+    if (!freezePropTypeSets(types, foundProto, name))
         return false;
 
     *funcp = &found->as<JSFunction>();
     *isDOM = types->isDOMClass();
 
     return true;
 }
 
 bool
 IonBuilder::annotateGetPropertyCache(JSContext *cx, MDefinition *obj, MGetPropertyCache *getPropCache,
                                     types::TemporaryTypeSet *objTypes, types::TemporaryTypeSet *pushedTypes)
 {
-    jsid id = NameToId(getPropCache->name());
-    if (id != types::IdToTypeId(id))
-        return true;
+    PropertyName *name = getPropCache->name();
 
     // Ensure every pushed value is a singleton.
     if (pushedTypes->unknownObject() || pushedTypes->baseFlags() != 0)
         return true;
 
     for (unsigned i = 0; i < pushedTypes->getObjectCount(); i++) {
         if (pushedTypes->getTypeObject(i) != nullptr)
             return true;
@@ -7980,63 +7872,58 @@ IonBuilder::annotateGetPropertyCache(JSC
 
     InlinePropertyTable *inlinePropTable = getPropCache->initInlinePropertyTable(pc);
     if (!inlinePropTable)
         return false;
 
     // Ensure that the relevant property typeset for each type object is
     // is a single-object typeset containing a JSFunction
     for (unsigned int i = 0; i < objCount; i++) {
-        types::TypeObject *typeObj = objTypes->getTypeObject(i);
-        if (!typeObj || typeObj->unknownProperties() || !typeObj->proto)
+        types::TypeObject *baseTypeObj = objTypes->getTypeObject(i);
+        if (!baseTypeObj)
             continue;
-
-        types::HeapTypeSet *ownTypes = typeObj->getProperty(cx, id);
-        if (!ownTypes)
+        types::TypeObjectKey *typeObj = types::TypeObjectKey::get(baseTypeObj);
+        if (typeObj->unknownProperties() || !typeObj->proto().isObject())
             continue;
 
-        if (!ownTypes->empty())
+        types::HeapTypeSetKey ownTypes = typeObj->property(NameToId(name));
+        if (ownTypes.notEmpty(constraints()))
             continue;
-        ownTypes->addFreeze(cx);
 
         JSObject *singleton = nullptr;
-        JSObject *proto = typeObj->proto;
+        JSObject *proto = typeObj->proto().toObject();
         while (true) {
-            types::TypeObject *protoType = proto->getType(cx);
-            if (!protoType)
-                return false;
+            types::TypeObjectKey *protoType = types::TypeObjectKey::get(proto);
             if (!protoType->unknownProperties()) {
-                types::HeapTypeSet *protoTypes = protoType->getProperty(cx, id);
-                if (!protoTypes)
-                    return false;
-
-                singleton = protoTypes->getSingleton(cx);
+                types::HeapTypeSetKey property = protoType->property(NameToId(name));
+
+                singleton = property.singleton(constraints());
                 if (singleton) {
                     if (singleton->is<JSFunction>())
                         break;
                     singleton = nullptr;
                 }
             }
             TaggedProto taggedProto = proto->getTaggedProto();
             if (!taggedProto.isObject())
                 break;
             proto = taggedProto.toObject();
         }
         if (!singleton)
             continue;
 
         bool knownConstant = false;
-        if (!TestSingletonProperty(cx, proto, singleton, id, &knownConstant))
+        if (!testSingletonProperty(proto, singleton, name, &knownConstant))
             return false;
 
         // Don't add cases corresponding to non-observed pushes
         if (!pushedTypes->hasType(types::Type::ObjectType(singleton)))
             continue;
 
-        if (!inlinePropTable->addEntry(typeObj, &singleton->as<JSFunction>()))
+        if (!inlinePropTable->addEntry(baseTypeObj, &singleton->as<JSFunction>()))
             return false;
     }
 
     if (inlinePropTable->numEntries() == 0) {
         getPropCache->clearInlinePropertyTable();
         return true;
     }
 
@@ -8133,63 +8020,60 @@ IonBuilder::storeSlot(MDefinition *obj, 
     if (slotType != MIRType_None)
         store->setSlotType(slotType);
     return resumeAfter(store);
 }
 
 bool
 IonBuilder::jsop_getprop(PropertyName *name)
 {
-    jsid id = NameToId(name);
-
     bool emitted = false;
 
     // Try to optimize arguments.length.
     if (!getPropTryArgumentsLength(&emitted) || emitted)
         return emitted;
 
     types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc);
-    bool barrier;
-    if (!PropertyReadNeedsTypeBarrier(cx, current->peek(-1), name, baseTypes, &barrier))
-        return false;
+    bool barrier = PropertyReadNeedsTypeBarrier(cx, constraints(),
+                                                current->peek(-1), name, baseTypes);
     types::TemporaryTypeSet *types = cloneTypeSet(baseTypes);
 
     // Try to hardcode known constants.
-    if (!getPropTryConstant(&emitted, id, types) || emitted)
+    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.
     if (info().executionMode() == DefinitePropertiesAnalysis) {
         MDefinition *obj = current->pop();
         MCallGetProperty *call = MCallGetProperty::New(obj, name);
         current->add(call);
         current->push(call);
         return resumeAfter(call);
     }
 
     // Try to emit loads from known binary data blocks
-    if (!getPropTryTypedObject(&emitted, id, types) || emitted)
+    if (!getPropTryTypedObject(&emitted, name, types) || emitted)
         return emitted;
 
     // Try to emit loads from definite slots.
     if (!getPropTryDefiniteSlot(&emitted, name, barrier, types) || emitted)
         return emitted;
 
     // Try to inline a common property getter, or make a call.
-    if (!getPropTryCommonGetter(&emitted, id, types) || emitted)
+    if (!getPropTryCommonGetter(&emitted, name, types) || emitted)
         return emitted;
 
     // Try to emit a monomorphic/polymorphic access based on baseline caches.
-    if (!getPropTryInlineAccess(&emitted, name, id, barrier, types) || emitted)
+    if (!getPropTryInlineAccess(&emitted, name, barrier, types) || emitted)
         return emitted;
 
     // Try to emit a polymorphic cache.
-    if (!getPropTryCache(&emitted, name, id, barrier, types) || emitted)
+    if (!getPropTryCache(&emitted, name, 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))
@@ -8210,27 +8094,28 @@ IonBuilder::getPropTryArgumentsLength(bo
     if (JSOp(*pc) != JSOP_LENGTH)
         return true;
 
     *emitted = true;
     return jsop_arguments_length();
 }
 
 bool
-IonBuilder::getPropTryConstant(bool *emitted, jsid id, types::TemporaryTypeSet *types)
+IonBuilder::getPropTryConstant(bool *emitted, PropertyName *name,
+                               types::TemporaryTypeSet *types)
 {
     JS_ASSERT(*emitted == false);
     JSObject *singleton = types ? types->getSingleton() : nullptr;
     if (!singleton)
         return true;
 
     JSObject *global = &script()->global();
 
     bool isConstant, testObject, testString;
-    if (!TestSingletonPropertyTypes(cx, current->peek(-1), singleton, global, id,
+    if (!testSingletonPropertyTypes(current->peek(-1), singleton, global, name,
                                     &isConstant, &testObject, &testString))
         return false;
 
     if (!isConstant)
         return true;
 
     MDefinition *obj = current->pop();
 
@@ -8248,24 +8133,23 @@ IonBuilder::getPropTryConstant(bool *emi
     current->add(known);
     current->push(known);
 
     *emitted = true;
     return true;
 }
 
 bool
-IonBuilder::getPropTryTypedObject(bool *emitted,
-                                  jsid id,
+IonBuilder::getPropTryTypedObject(bool *emitted, PropertyName *name,
                                   types::TemporaryTypeSet *resultTypes)
 {
     TypeRepresentationSet fieldTypeReprs;
     int32_t fieldOffset;
     size_t fieldIndex;
-    if (!lookupTypedObjectField(current->peek(-1), id, &fieldOffset,
+    if (!lookupTypedObjectField(current->peek(-1), name, &fieldOffset,
                                 &fieldTypeReprs, &fieldIndex))
         return false;
     if (fieldTypeReprs.empty())
         return true;
 
     switch (fieldTypeReprs.kind()) {
       case TypeRepresentation::Struct:
       case TypeRepresentation::Array:
@@ -8367,53 +8251,54 @@ IonBuilder::getPropTryComplexPropOfTyped
     return true;
 }
 
 bool
 IonBuilder::getPropTryDefiniteSlot(bool *emitted, PropertyName *name,
                                    bool barrier, types::TemporaryTypeSet *types)
 {
     JS_ASSERT(*emitted == false);
-    types::TypeSet *propTypes = GetDefiniteSlot(cx, current->peek(-1)->resultTypeSet(), name);
-    if (!propTypes)
+    types::HeapTypeSetKey property;
+    if (!getDefiniteSlot(current->peek(-1)->resultTypeSet(), name, &property))
         return true;
 
     MDefinition *obj = current->pop();
     MDefinition *useObj = obj;
     if (obj->type() != MIRType_Object) {
         MGuardObject *guard = MGuardObject::New(obj);
         current->add(guard);
         useObj = guard;
     }
 
-    MLoadFixedSlot *fixed = MLoadFixedSlot::New(useObj, propTypes->definiteSlot());
+    MLoadFixedSlot *fixed = MLoadFixedSlot::New(useObj, property.actualTypes->definiteSlot());
     if (!barrier)
         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, jsid id, types::TemporaryTypeSet *types)
+IonBuilder::getPropTryCommonGetter(bool *emitted, PropertyName *name,
+                                   types::TemporaryTypeSet *types)
 {
     JS_ASSERT(*emitted == false);
     JSFunction *commonGetter;
     bool isDOM;
     MDefinition *guard;
 
     types::TemporaryTypeSet *objTypes = current->peek(-1)->resultTypeSet();
 
-    if (!testCommonPropFunc(cx, objTypes, id, &commonGetter, true, &isDOM, &guard))
+    if (!testCommonPropFunc(cx, objTypes, name, &commonGetter, true, &isDOM, &guard))
         return false;
     if (!commonGetter)
         return true;
 
 #ifdef JSGC_GENERATIONAL
     if (GetIonContext()->runtime->gcNursery.isInside(commonGetter))
         return true;
 #endif
@@ -8478,17 +8363,17 @@ CanInlinePropertyOpShapes(const Baseline
         if (shapes[i]->inDictionary())
             return false;
     }
 
     return true;
 }
 
 bool
-IonBuilder::getPropTryInlineAccess(bool *emitted, PropertyName *name, jsid id,
+IonBuilder::getPropTryInlineAccess(bool *emitted, PropertyName *name,
                                    bool barrier, types::TemporaryTypeSet *types)
 {
     JS_ASSERT(*emitted == false);
     if (current->peek(-1)->type() != MIRType_Object)
         return true;
 
     BaselineInspector::ShapeVector shapes;
     if (!inspector->maybeShapesForPropertyOp(pc, shapes))
@@ -8505,32 +8390,32 @@ IonBuilder::getPropTryInlineAccess(bool 
     if (shapes.length() == 1) {
         // In the monomorphic case, use separate ShapeGuard and LoadSlot
         // instructions.
         spew("Inlining monomorphic GETPROP");
 
         Shape *objShape = shapes[0];
         obj = addShapeGuard(obj, objShape, Bailout_ShapeGuard);
 
-        Shape *shape = objShape->search(cx, id);
+        Shape *shape = objShape->search(cx, NameToId(name));
         JS_ASSERT(shape);
 
         if (!loadSlot(obj, shape, rvalType, barrier, types))
             return false;
     } else {
         JS_ASSERT(shapes.length() > 1);
         spew("Inlining polymorphic GETPROP");
 
         MGetPropertyPolymorphic *load = MGetPropertyPolymorphic::New(obj, name);
         current->add(load);
         current->push(load);
 
         for (size_t i = 0; i < shapes.length(); i++) {
             Shape *objShape = shapes[i];
-            Shape *shape =  objShape->search(cx, id);
+            Shape *shape =  objShape->search(cx, NameToId(name));
             JS_ASSERT(shape);
             if (!load->addShape(objShape, shape))
                 return false;
         }
 
         if (failedShapeGuard_)
             load->setNotMovable();
 
@@ -8539,17 +8424,17 @@ IonBuilder::getPropTryInlineAccess(bool 
             return false;
     }
 
     *emitted = true;
     return true;
 }
 
 bool
-IonBuilder::getPropTryCache(bool *emitted, PropertyName *name, jsid id,
+IonBuilder::getPropTryCache(bool *emitted, PropertyName *name,
                             bool barrier, types::TemporaryTypeSet *types)
 {
     JS_ASSERT(*emitted == false);
     bool accessGetter = inspector->hasSeenAccessedGetter(pc);
 
     MDefinition *obj = current->peek(-1);
 
     // The input value must either be an object, or we should have strong suspicions
@@ -8567,21 +8452,17 @@ IonBuilder::getPropTryCache(bool *emitte
     //
     // In parallel execution, idempotency of caches is ignored, since we
     // repeat the entire ForkJoin workload if we bail out. Note that it's
     // overly restrictive to mark everything as idempotent, because we can
     // treat non-idempotent caches in parallel as repeatable.
     if (obj->type() == MIRType_Object && !invalidatedIdempotentCache() &&
         info().executionMode() != ParallelExecution)
     {
-        bool idempotent;
-        if (!PropertyReadIsIdempotent(cx, obj, name, &idempotent))
-            return false;
-
-        if (idempotent)
+        if (PropertyReadIsIdempotent(constraints(), obj, name))
             load->setIdempotent();
     }
 
     if (JSOp(*pc) == JSOP_CALLPROP) {
         if (!annotateGetPropertyCache(cx, obj, load, obj->resultTypeSet(), types))
             return false;
     }
 
@@ -8598,18 +8479,18 @@ IonBuilder::getPropTryCache(bool *emitte
     if (accessGetter)
         barrier = true;
 
     if (needsToMonitorMissingProperties(types))
         barrier = true;
 
     // Caches can read values from prototypes, so update the barrier to
     // reflect such possible values.
-    if (!barrier && !PropertyReadOnPrototypeNeedsTypeBarrier(cx, obj, name, types, &barrier))
-        return false;
+    if (!barrier)
+        barrier = PropertyReadOnPrototypeNeedsTypeBarrier(cx, constraints(), obj, name, types);
 
     MIRType rvalType = MIRTypeFromValueType(types->getKnownTypeTag());
     if (barrier || IsNullOrUndefined(rvalType))
         rvalType = MIRType_Value;
     load->setResultType(rvalType);
 
     if (!pushTypeBarrier(load, types, barrier))
         return false;
@@ -8630,79 +8511,73 @@ IonBuilder::needsToMonitorMissingPropert
 }
 
 bool
 IonBuilder::jsop_setprop(PropertyName *name)
 {
     MDefinition *value = current->pop();
     MDefinition *obj = current->pop();
 
-    jsid id = NameToId(name);
     bool emitted = false;
 
     // Always use a call if we are doing the definite properties analysis and
     // not actually emitting code, to simplify later analysis.
     if (info().executionMode() == DefinitePropertiesAnalysis) {
         MInstruction *ins = MCallSetProperty::New(obj, value, name, script()->strict);
         current->add(ins);
         current->push(value);
         return resumeAfter(ins);
     }
 
     // Add post barrier if needed.
     if (NeedsPostBarrier(info(), value))
         current->add(MPostWriteBarrier::New(obj, value));
 
     // Try to inline a common property setter, or make a call.
-    if (!setPropTryCommonSetter(&emitted, obj, name, id, value) || emitted)
+    if (!setPropTryCommonSetter(&emitted, obj, name, value) || emitted)
         return emitted;
 
     types::TemporaryTypeSet *objTypes = obj->resultTypeSet();
-    bool barrier;
-    if (!PropertyWriteNeedsTypeBarrier(cx, current, &obj, name, &value,
-                                       /* canModify = */ true, &barrier))
-    {
-        return false;
-    }
+    bool barrier = PropertyWriteNeedsTypeBarrier(constraints(), current, &obj, name, &value,
+                                                 /* canModify = */ true);
 
     // Try to emit stores to known binary data blocks
-    if (!setPropTryTypedObject(&emitted, obj, id, value) || emitted)
+    if (!setPropTryTypedObject(&emitted, obj, name, value) || emitted)
         return emitted;
 
     // Try to emit store from definite slots.
     if (!setPropTryDefiniteSlot(&emitted, obj, name, value, barrier, objTypes) || emitted)
         return emitted;
 
     // Try to emit a monomorphic/polymorphic store based on baseline caches.
-    if (!setPropTryInlineAccess(&emitted, obj, name, id, value, barrier, objTypes) || emitted)
+    if (!setPropTryInlineAccess(&emitted, obj, name, value, barrier, objTypes) || emitted)
         return emitted;
 
     // Try to emit a polymorphic cache.
     if (!setPropTryCache(&emitted, obj, name, value, barrier, objTypes) || emitted)
         return emitted;
 
     // Emit call.
     MInstruction *ins = MCallSetProperty::New(obj, value, name, script()->strict);
     current->add(ins);
     current->push(value);
     return resumeAfter(ins);
 }
 
 bool
 IonBuilder::setPropTryCommonSetter(bool *emitted, MDefinition *obj,
-                                   PropertyName *name, jsid id,
-                                   MDefinition *value)
+                                   PropertyName *name, MDefinition *value)
 {
     JS_ASSERT(*emitted == false);
 
     JSFunction *commonSetter;
     bool isDOM;
 
     types::TemporaryTypeSet *objTypes = obj->resultTypeSet();
-    if (!testCommonPropFunc(cx, objTypes, id, &commonSetter, false, &isDOM, nullptr))
+    if (!testCommonPropFunc(cx, objTypes, name, &commonSetter, false, &isDOM, nullptr))
         return false;
 
     if (!commonSetter)
         return true;
 
     // Emit common setter.
 
     // Setters can be called even if the property write needs a type
@@ -8796,22 +8671,22 @@ IonBuilder::setPropTryCommonDOMSetter(bo
         return false;
 
     *emitted = true;
     return true;
 }
 
 bool
 IonBuilder::setPropTryTypedObject(bool *emitted, MDefinition *obj,
-                                  jsid id, MDefinition *value)
+                                  PropertyName *name, MDefinition *value)
 {
     TypeRepresentationSet fieldTypeReprs;
     int32_t fieldOffset;
     size_t fieldIndex;
-    if (!lookupTypedObjectField(obj, id, &fieldOffset, &fieldTypeReprs,
+    if (!lookupTypedObjectField(obj, name, &fieldOffset, &fieldTypeReprs,
                                 &fieldIndex))
         return false;
     if (fieldTypeReprs.empty())
         return true;
 
     switch (fieldTypeReprs.kind()) {
       case TypeRepresentation::Struct:
       case TypeRepresentation::Array:
@@ -8864,37 +8739,37 @@ IonBuilder::setPropTryDefiniteSlot(bool 
                                    PropertyName *name, MDefinition *value,
                                    bool barrier, types::TemporaryTypeSet *objTypes)
 {
     JS_ASSERT(*emitted == false);
 
     if (barrier)
         return true;
 
-    types::HeapTypeSet *propTypes = GetDefiniteSlot(cx, objTypes, name);
-    if (!propTypes)
-        return true;
-
-    MStoreFixedSlot *fixed = MStoreFixedSlot::New(obj, propTypes->definiteSlot(), value);
+    types::HeapTypeSetKey property;
+    if (!getDefiniteSlot(obj->resultTypeSet(), name, &property))
+        return true;
+
+    MStoreFixedSlot *fixed = MStoreFixedSlot::New(obj, property.actualTypes->definiteSlot(), value);
     current->add(fixed);
     current->push(value);
 
-    if (propTypes->needsBarrier(cx))
+    if (property.needsBarrier(constraints()))
         fixed->setNeedsBarrier();
 
     if (!resumeAfter(fixed))
         return false;
 
     *emitted = true;
     return true;
 }
 
 bool
 IonBuilder::setPropTryInlineAccess(bool *emitted, MDefinition *obj,
-                                   PropertyName *name, jsid id,
+                                   PropertyName *name,
                                    MDefinition *value, bool barrier,
                                    types::TemporaryTypeSet *objTypes)
 {
     JS_ASSERT(*emitted == false);
 
     if (barrier)
         return true;
 
@@ -8916,36 +8791,36 @@ IonBuilder::setPropTryInlineAccess(bool 
         // that the shape is still a lastProperty, and calling Shape::search
         // on dictionary mode shapes that aren't lastProperty is invalid.
         Shape *objShape = shapes[0];
         obj = addShapeGuard(obj, objShape, Bailout_ShapeGuard);
 
         Shape *shape = objShape->search(cx, NameToId(name));
         JS_ASSERT(shape);
 
-        bool needsBarrier = objTypes->propertyNeedsBarrier(cx, id);
+        bool needsBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
         if (!storeSlot(obj, shape, value, needsBarrier))
             return false;
     } else {
         JS_ASSERT(shapes.length() > 1);
         spew("Inlining polymorphic SETPROP");
 
         MSetPropertyPolymorphic *ins = MSetPropertyPolymorphic::New(obj, value);
         current->add(ins);
         current->push(value);
 
         for (size_t i = 0; i < shapes.length(); i++) {
             Shape *objShape = shapes[i];
-            Shape *shape =  objShape->search(cx, id);
+            Shape *shape =  objShape->search(cx, NameToId(name));
             JS_ASSERT(shape);
             if (!ins->addShape(objShape, shape))
                 return false;
         }
 
-        if (objTypes->propertyNeedsBarrier(cx, id))
+        if (objTypes->propertyNeedsBarrier(constraints(), NameToId(name)))
             ins->setNeedsBarrier();
 
         if (!resumeAfter(ins))
             return false;
     }
 
     *emitted = true;
     return true;
@@ -8956,18 +8831,17 @@ IonBuilder::setPropTryCache(bool *emitte
                             PropertyName *name, MDefinition *value,
                             bool barrier, types::TemporaryTypeSet *objTypes)
 {
     JS_ASSERT(*emitted == false);
 
     // Emit SetPropertyCache.
     MSetPropertyCache *ins = MSetPropertyCache::New(obj, value, name, script()->strict, barrier);
 
-    jsid id = NameToId(name);
-    if (!objTypes || objTypes->propertyNeedsBarrier(cx, id))
+    if (!objTypes || objTypes->propertyNeedsBarrier(constraints(), NameToId(name)))
         ins->setNeedsBarrier();
 
     current->add(ins);
     current->push(value);
 
     if (!resumeAfter(ins))
         return false;
 
@@ -9311,20 +9185,18 @@ IonBuilder::walkScopeChain(unsigned hops
 
 bool
 IonBuilder::hasStaticScopeObject(ScopeCoordinate sc, JSObject **pcall)
 {
     JSScript *outerScript = ScopeCoordinateFunctionScript(cx, script(), pc);
     if (!outerScript || !outerScript->treatAsRunOnce)
         return false;
 
-    types::TypeObject *funType = outerScript->function()->getType(cx);
-    if (!funType)
-        return false;
-    if (types::HeapTypeSet::HasObjectFlags(cx, funType, types::OBJECT_FLAG_RUNONCE_INVALIDATED))
+    types::TypeObjectKey *funType = types::TypeObjectKey::get(outerScript->function());
+    if (funType->hasFlags(constraints(), types::OBJECT_FLAG_RUNONCE_INVALIDATED))
         return false;
 
     // The script this aliased var operation is accessing will run only once,
     // so there will be only one call object and the aliased var access can be
     // compiled in the same manner as a global access. We still need to find
     // the call object though.
 
     // Look for the call object on the current script's function's scope chain.
@@ -9454,18 +9326,21 @@ IonBuilder::jsop_setaliasedvar(ScopeCoor
 }
 
 bool
 IonBuilder::jsop_in()
 {
     MDefinition *obj = current->peek(-1);
     MDefinition *id = current->peek(-2);
 
-    if (ElementAccessIsDenseNative(obj, id) && !ElementAccessHasExtraIndexedProperty(cx, obj))
+    if (ElementAccessIsDenseNative(obj, id) &&
+        !ElementAccessHasExtraIndexedProperty(constraints(), obj))
+    {
         return jsop_in_dense();
+    }
 
     current->pop();
     current->pop();
     MIn *ins = new MIn(id, obj);
 
     current->add(ins);
     current->push(ins);
 
@@ -9473,17 +9348,17 @@ IonBuilder::jsop_in()
 }
 
 bool
 IonBuilder::jsop_in_dense()
 {
     MDefinition *obj = current->pop();
     MDefinition *id = current->pop();
 
-    bool needsHoleCheck = !ElementAccessIsPacked(cx, obj);
+    bool needsHoleCheck = !ElementAccessIsPacked(constraints(), 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);
@@ -9510,23 +9385,23 @@ IonBuilder::jsop_instanceof()
     // 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 {
         types::TemporaryTypeSet *rhsTypes = rhs->resultTypeSet();
         JSObject *rhsObject = rhsTypes ? rhsTypes->getSingleton() : nullptr;
         if (!rhsObject || !rhsObject->is<JSFunction>() || rhsObject->isBoundFunction())
             break;
 
-        types::TypeObject *rhsType = rhsObject->getType(cx);
-        if (!rhsType || rhsType->unknownProperties())
+        types::TypeObjectKey *rhsType = types::TypeObjectKey::get(rhsObject);
+        if (rhsType->unknownProperties())
             break;
 
-        types::HeapTypeSet *protoTypes =
-            rhsType->getProperty(cx, NameToId(cx->names().classPrototype));
-        JSObject *protoObject = protoTypes ? protoTypes->getSingleton(cx) : nullptr;
+        types::HeapTypeSetKey protoProperty =
+            rhsType->property(NameToId(cx->names().classPrototype));
+        JSObject *protoObject = protoProperty.singleton(constraints());
         if (!protoObject)
             break;
 
         rhs->setFoldedUnchecked();
 
         MInstanceOf *ins = new MInstanceOf(obj, protoObject);
 
         current->add(ins);
@@ -9701,32 +9576,32 @@ IonBuilder::loadTypedObjectData(MDefinit
 }
 
 // Looks up the offset/type-repr-set of the field `id`, given the type
 // set `objTypes` of the field owner. Note that even when true is
 // returned, `*fieldTypeReprs` might be empty if no useful type/offset
 // pair could be determined.
 bool
 IonBuilder::lookupTypedObjectField(MDefinition *typedObj,
-                                   jsid id,
+                                   PropertyName *name,
                                    int32_t *fieldOffset,
                                    TypeRepresentationSet *fieldTypeReprs,
                                    size_t *fieldIndex)
 {
     TypeRepresentationSet objTypeReprs;
     if (!lookupTypeRepresentationSet(typedObj, &objTypeReprs))
         return false;
 
     // Must be accessing a struct.
     if (!objTypeReprs.allOfKind(TypeRepresentation::Struct))
         return true;
 
-    // Determine the type/offset of the field `id`, if any.
+    // Determine the type/offset of the field `name`, if any.
     size_t offset;
-    if (!objTypeReprs.fieldNamed(*this, id, &offset,
+    if (!objTypeReprs.fieldNamed(*this, NameToId(name), &offset,
                                  fieldTypeReprs, fieldIndex))
         return false;
     if (fieldTypeReprs->empty())
         return false;
 
     // Field offset must be representable as signed integer.
     if (offset >= size_t(INT_MAX)) {
         *fieldTypeReprs = TypeRepresentationSet();
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -201,16 +201,17 @@ class IonBuilder : public MIRGenerator
         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,
+               types::CompilerConstraintList *constraints,
                BaselineInspector *inspector, CompileInfo *info, BaselineFrame *baselineFrame,
                size_t inliningDepth = 0, uint32_t loopDepth = 0);
 
     bool build();
     bool buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoint,
                      CallInfo &callInfo);
 
   private:
@@ -353,62 +354,62 @@ class IonBuilder : public MIRGenerator
     bool hasStaticScopeObject(ScopeCoordinate sc, JSObject **pcall);
     bool loadSlot(MDefinition *obj, Shape *shape, MIRType rvalType,
                   bool barrier, types::TemporaryTypeSet *types);
     bool storeSlot(MDefinition *obj, Shape *shape, MDefinition *value, bool needsBarrier,
                    MIRType slotType = MIRType_None);
 
     // jsop_getprop() helpers.
     bool getPropTryArgumentsLength(bool *emitted);
-    bool getPropTryConstant(bool *emitted, jsid id, types::TemporaryTypeSet *types);
+    bool getPropTryConstant(bool *emitted, PropertyName *name,
+                            types::TemporaryTypeSet *types);
     bool getPropTryDefiniteSlot(bool *emitted, PropertyName *name,
                                 bool barrier, types::TemporaryTypeSet *types);
-    bool getPropTryCommonGetter(bool *emitted, jsid id, types::TemporaryTypeSet *types);
-    bool getPropTryInlineAccess(bool *emitted, PropertyName *name, jsid id,
+    bool getPropTryCommonGetter(bool *emitted, PropertyName *name,
+                                types::TemporaryTypeSet *types);
+    bool getPropTryInlineAccess(bool *emitted, PropertyName *name,
                                 bool barrier, types::TemporaryTypeSet *types);
-    bool getPropTryTypedObject(bool *emitted, jsid id,
+    bool getPropTryTypedObject(bool *emitted, PropertyName *name,
                                types::TemporaryTypeSet *resultTypes);
     bool getPropTryScalarPropOfTypedObject(bool *emitted,
                                            int32_t fieldOffset,
                                            TypeRepresentationSet fieldTypeReprs,
                                            types::TemporaryTypeSet *resultTypes);
     bool getPropTryComplexPropOfTypedObject(bool *emitted,
                                             int32_t fieldOffset,
                                             TypeRepresentationSet fieldTypeReprs,
                                             size_t fieldIndex,
                                             types::TemporaryTypeSet *resultTypes);
-    bool getPropTryCache(bool *emitted, PropertyName *name, jsid id,
+    bool getPropTryCache(bool *emitted, PropertyName *name,
                          bool barrier, types::TemporaryTypeSet *types);
     bool needsToMonitorMissingProperties(types::TemporaryTypeSet *types);
 
     // jsop_setprop() helpers.
     bool setPropTryCommonSetter(bool *emitted, MDefinition *obj,
-                                PropertyName *name, jsid id,
-                                MDefinition *value);
+                                PropertyName *name, MDefinition *value);
     bool setPropTryCommonDOMSetter(bool *emitted, MDefinition *obj,
                                    MDefinition *value, JSFunction *setter,
                                    bool isDOM);
     bool setPropTryDefiniteSlot(bool *emitted, MDefinition *obj,
                                 PropertyName *name, MDefinition *value,
                                 bool barrier, types::TemporaryTypeSet *objTypes);
     bool setPropTryInlineAccess(bool *emitted, MDefinition *obj,
-                                PropertyName *name, jsid id,
-                                MDefinition *value, bool barrier,
+                                PropertyName *name, MDefinition *value, bool barrier,
                                 types::TemporaryTypeSet *objTypes);
     bool setPropTryTypedObject(bool *emitted, MDefinition *obj,
-                               jsid id, MDefinition *value);
+                               PropertyName *name, MDefinition *value);
     bool setPropTryCache(bool *emitted, MDefinition *obj,
                          PropertyName *name, MDefinition *value,
                          bool barrier, types::TemporaryTypeSet *objTypes);
 
     // binary data lookup helpers.
     bool lookupTypeRepresentationSet(MDefinition *typedObj,
                                      TypeRepresentationSet *out);
     bool lookupTypedObjectField(MDefinition *typedObj,
-                                jsid id,
+                                PropertyName *name,
                                 int32_t *fieldOffset,
                                 TypeRepresentationSet *fieldTypeReprs,
                                 size_t *fieldIndex);
     MDefinition *loadTypedObjectType(MDefinition *value);
     void loadTypedObjectData(MDefinition *inOwner,
                              int32_t inOffset,
                              MDefinition **outOwner,
                              MDefinition **outOffset);
@@ -618,25 +619,36 @@ class IonBuilder : public MIRGenerator
     MDefinition *makeCallsiteClone(JSFunction *target, MDefinition *fun);
     MCall *makeCallHelper(JSFunction *target, CallInfo &callInfo, bool cloneAtCallsite);
     bool makeCall(JSFunction *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::TemporaryTypeSet *types,
-                                   jsid id, JSFunction **funcp,
+                                   PropertyName *name, JSFunction **funcp,
                                    bool isGetter, bool *isDOM,
                                    MDefinition **guardOut);
 
     bool annotateGetPropertyCache(JSContext *cx, MDefinition *obj, MGetPropertyCache *getPropCache,
                                   types::TemporaryTypeSet *objTypes, types::TemporaryTypeSet *pushedTypes);
 
     MGetPropertyCache *getInlineableGetPropertyCache(CallInfo &callInfo);
 
+    bool testSingletonProperty(JSObject *obj, JSObject *singleton, PropertyName *name,
+                               bool *isKnownConstant);
+    bool testSingletonPropertyTypes(MDefinition *obj, JSObject *singleton,
+                                    JSObject *globalObj, PropertyName *name,
+                                    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) {
@@ -654,42 +666,46 @@ class IonBuilder : public MIRGenerator
 
     // 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().
     CodeGenerator *backgroundCodegen_;
 
   public:
-    // Compilation index for this attempt.
-    types::RecompileInfo const recompileInfo;
-
     void clearForBackEnd();
 
     JSScript *script() const { return script_.get(); }
 
     CodeGenerator *backgroundCodegen() const { return backgroundCodegen_; }
     void setBackgroundCodegen(CodeGenerator *codegen) { backgroundCodegen_ = codegen; }
 
     AbortReason abortReason() { return abortReason_; }
 
     TypeRepresentationSetHash *getOrCreateReprSetHash(); // fallible
 
+    types::CompilerConstraintList *constraints() {
+        return constraints_;
+    }
+
     bool isInlineBuilder() const {
         return callerBuilder_ != NULL;
     }
 
   private:
     bool init();
 
     JSContext *cx;
     BaselineFrame *baselineFrame_;
     AbortReason abortReason_;
     ScopedJSDeletePtr<TypeRepresentationSetHash> reprSetHash_;
 
+    // Constraints for recording dependencies on type information.
+    types::CompilerConstraintList *constraints_;
+
     // Basic analysis information about the script.
     BytecodeAnalysis analysis_;
     BytecodeAnalysis &analysis() {
         return analysis_;
     }
 
     GSNCache gsn;
 
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -1959,18 +1959,17 @@ SetPropertyIC::attachNativeExisting(JSCo
         // Obtain and guard on the TypeObject of the object.
         types::TypeObject *type = obj->getType(cx);
         masm.branchPtr(Assembler::NotEqual,
                        Address(object(), JSObject::offsetOfType()),
                        ImmGCPtr(type), &failures);
 
         if (checkTypeset) {
             TypedOrValueRegister valReg = value().reg();
-            RootedId id(cx, types::IdToTypeId(AtomToId(name())));
-            types::HeapTypeSet *propTypes = type->maybeGetProperty(cx, id);
+            types::HeapTypeSet *propTypes = type->maybeGetProperty(cx, NameToId(name()));
             JS_ASSERT(propTypes);
             JS_ASSERT(!propTypes->unknown());
 
             Register scratchReg = object();
             masm.push(scratchReg);
 
             masm.guardTypeSet(valReg, propTypes, scratchReg, &barrierFailure);
             masm.pop(object());
@@ -2608,18 +2607,17 @@ IsPropertySetInlineable(JSContext *cx, c
         return false;
 
     if (!pshape->writable())
         return false;
 
     bool shouldCheck = false;
     types::TypeObject *type = obj->getType(cx);
     if (cache.needsTypeBarrier() && !type->unknownProperties()) {
-        RootedId typeId(cx, types::IdToTypeId(id));
-        types::HeapTypeSet *propTypes = type->maybeGetProperty(cx, typeId);
+        types::HeapTypeSet *propTypes = type->maybeGetProperty(cx, id);
         if (!propTypes)
             return false;
         if (!propTypes->unknown()) {
             shouldCheck = true;
             ConstantOrRegister val = cache.value();
             if (val.constant()) {
                 // If the input is a constant, then don't bother if the barrier will always fail.
                 if (!propTypes->hasType(types::GetValueType(cache.value().value())))
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -323,17 +323,18 @@ struct IonScript
 
   private:
     void trace(JSTracer *trc);
 
   public:
     // Do not call directly, use IonScript::New. This is public for cx->new_.
     IonScript();
 
-    static IonScript *New(JSContext *cx, uint32_t frameLocals, uint32_t frameSize,
+    static IonScript *New(JSContext *cx, types::RecompileInfo recompileInfo,
+                          uint32_t frameLocals, uint32_t frameSize,
                           size_t snapshotsSize, size_t snapshotEntries,
                           size_t constants, size_t safepointIndexEntries, size_t osiIndexEntries,
                           size_t cacheEntries, size_t runtimeSize, size_t safepointsSize,
                           size_t callTargetEntries, size_t backedgeEntries);
     static void Trace(JSTracer *trc, IonScript *script);
     static void Destroy(FreeOp *fop, IonScript *script);
 
     static inline size_t offsetOfMethod() {
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -207,28 +207,27 @@ 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)
+        types::TypeObject *baseType = types::TypeScript::InitObject(cx, script(), pc, JSProto_Array);
+        if (!baseType)
             return InliningStatus_Error;
+        types::TypeObjectKey *type = types::TypeObjectKey::get(baseType);
         if (!type->unknownProperties()) {
-            types::HeapTypeSet *elemTypes = type->getProperty(cx, JSID_VOID);
-            if (!elemTypes)
-                return InliningStatus_Error;
+            types::HeapTypeSetKey elemTypes = type->property(JSID_VOID);
 
             for (uint32_t i = 0; i < initLength; i++) {
                 MDefinition *value = callInfo.getArg(i);
-                if (!TypeSetIncludes(elemTypes, value->type(), value->resultTypeSet())) {
-                    elemTypes->addFreeze(cx);
+                if (!TypeSetIncludes(elemTypes.actualTypes, value->type(), value->resultTypeSet())) {
+                    elemTypes.freeze(constraints());
                     return InliningStatus_NotInlined;
                 }
             }
         }
     }
 
     // A single integer argument denotes initial length.
     if (callInfo.argc() == 1) {
@@ -246,17 +245,17 @@ IonBuilder::inlineArray(CallInfo &callIn
 
     callInfo.unwrapArgs();
 
     JSObject *templateObject = getNewArrayTemplateObject(initLength);
     if (!templateObject)
         return InliningStatus_Error;
 
     types::TemporaryTypeSet::DoubleConversion conversion =
-        getInlineReturnTypeSet()->convertDoubleElements(cx);
+        getInlineReturnTypeSet()->convertDoubleElements(constraints());
     if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles)
         templateObject->setShouldConvertDoubleElements();
 
     MNewArray *ins = new MNewArray(initLength, templateObject, allocating);
     current->add(ins);
     current->push(ins);
 
     if (callInfo.argc() >= 2) {
@@ -315,33 +314,31 @@ IonBuilder::inlineArrayPopShift(CallInfo
     types::TypeObjectFlags unhandledFlags =
         types::OBJECT_FLAG_SPARSE_INDEXES |
         types::OBJECT_FLAG_LENGTH_OVERFLOW |
         types::OBJECT_FLAG_ITERATED;
 
     types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet();
     if (!thisTypes || thisTypes->getKnownClass() != &ArrayObject::class_)
         return InliningStatus_NotInlined;
-    if (thisTypes->hasObjectFlags(cx, unhandledFlags))
+    if (thisTypes->hasObjectFlags(constraints(), unhandledFlags))
         return InliningStatus_NotInlined;
 
     RootedScript scriptRoot(cx, script());
-    if (types::ArrayPrototypeHasIndexedProperty(cx, scriptRoot))
+    if (types::ArrayPrototypeHasIndexedProperty(constraints(), scriptRoot))
         return InliningStatus_NotInlined;
 
     callInfo.unwrapArgs();
 
     types::StackTypeSet *returnTypes = getOriginalInlineReturnTypeSet();
-    bool needsHoleCheck = thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED);
+    bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED);
     bool maybeUndefined = returnTypes->hasType(types::Type::UndefinedType());
 
-    bool barrier;
-    if (!PropertyReadNeedsTypeBarrier(cx, callInfo.thisArg(), nullptr, returnTypes, &barrier))
-        return InliningStatus_Error;
-
+    bool barrier = PropertyReadNeedsTypeBarrier(cx, constraints(),
+                                                callInfo.thisArg(), nullptr, returnTypes);
     if (barrier)
         returnType = MIRType_Value;
 
     MArrayPopShift *ins = MArrayPopShift::New(callInfo.thisArg(), mode,
                                               needsHoleCheck, maybeUndefined);
     current->add(ins);
     current->push(ins);
     ins->setResultType(returnType);
@@ -358,45 +355,43 @@ IonBuilder::inlineArrayPopShift(CallInfo
 IonBuilder::InliningStatus
 IonBuilder::inlineArrayPush(CallInfo &callInfo)
 {
     if (callInfo.argc() != 1 || callInfo.constructing())
         return InliningStatus_NotInlined;
 
     MDefinition *obj = callInfo.thisArg();
     MDefinition *value = callInfo.getArg(0);
-    bool writeNeedsBarrier;
-    if (!PropertyWriteNeedsTypeBarrier(cx, current, &obj, nullptr, &value,
-                                       /* canModify = */ false, &writeNeedsBarrier))
+    if (PropertyWriteNeedsTypeBarrier(constraints(), current,
+                                      &obj, nullptr, &value, /* canModify = */ false))
     {
-        return InliningStatus_Error;
+        return InliningStatus_NotInlined;
     }
-    if (writeNeedsBarrier)
-        return InliningStatus_NotInlined;
     JS_ASSERT(obj == callInfo.thisArg() && value == callInfo.getArg(0));
 
     if (getInlineReturnType() != MIRType_Int32)
         return InliningStatus_NotInlined;
     if (callInfo.thisArg()->type() != MIRType_Object)
         return InliningStatus_NotInlined;
 
     types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet();
     if (!thisTypes || thisTypes->getKnownClass() != &ArrayObject::class_)
         return InliningStatus_NotInlined;
-    if (thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPARSE_INDEXES |
+    if (thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES |
                                   types::OBJECT_FLAG_LENGTH_OVERFLOW))
     {
         return InliningStatus_NotInlined;
     }
 
     RootedScript scriptRoot(cx, script());
-    if (types::ArrayPrototypeHasIndexedProperty(cx, scriptRoot))
+    if (types::ArrayPrototypeHasIndexedProperty(constraints(), scriptRoot))
         return InliningStatus_NotInlined;
 
-    types::TemporaryTypeSet::DoubleConversion conversion = thisTypes->convertDoubleElements(cx);
+    types::TemporaryTypeSet::DoubleConversion conversion =
+        thisTypes->convertDoubleElements(constraints());
     if (conversion == types::TemporaryTypeSet::AmbiguousDoubleConversion)
         return InliningStatus_NotInlined;
 
     callInfo.unwrapArgs();
     value = callInfo.getArg(0);
 
     if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles ||
         conversion == types::TemporaryTypeSet::MaybeConvertToDoubles)
@@ -435,91 +430,85 @@ IonBuilder::inlineArrayConcat(CallInfo &
     // |this| and the argument must be dense arrays.
     types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet();
     types::TemporaryTypeSet *argTypes = callInfo.getArg(0)->resultTypeSet();
     if (!thisTypes || !argTypes)
         return InliningStatus_NotInlined;
 
     if (thisTypes->getKnownClass() != &ArrayObject::class_)
         return InliningStatus_NotInlined;
-    if (thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPARSE_INDEXES |
+    if (thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES |
                                   types::OBJECT_FLAG_LENGTH_OVERFLOW))
     {
         return InliningStatus_NotInlined;
     }
 
     if (argTypes->getKnownClass() != &ArrayObject::class_)
         return InliningStatus_NotInlined;
-    if (argTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPARSE_INDEXES |
+    if (argTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES |
                                  types::OBJECT_FLAG_LENGTH_OVERFLOW))
     {
         return InliningStatus_NotInlined;
     }
 
     // Watch out for indexed properties on the prototype.
     RootedScript scriptRoot(cx, script());
-    if (types::ArrayPrototypeHasIndexedProperty(cx, scriptRoot))
+    if (types::ArrayPrototypeHasIndexedProperty(constraints(), scriptRoot))
         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->unknownProperties() ||
-        &thisType->proto->global() != &script()->global())
+    types::TypeObject *baseThisType = thisTypes->getTypeObject(0);
+    if (!baseThisType)
+        return InliningStatus_NotInlined;
+    types::TypeObjectKey *thisType = types::TypeObjectKey::get(baseThisType);
+    if (thisType->unknownProperties() ||
+        &thisType->proto().toObject()->global() != &script()->global())
     {
         return InliningStatus_NotInlined;
     }
 
     // Don't inline if 'this' is packed and the argument may not be packed
     // (the result array will reuse the 'this' type).
-    if (!thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED) &&
-        argTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED))
+    if (!thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED) &&
+        argTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED))
     {
         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);
-    if (!thisElemTypes)
-        return InliningStatus_Error;
+    types::HeapTypeSetKey thisElemTypes = thisType->property(JSID_VOID);
 
     types::TemporaryTypeSet *resTypes = getInlineReturnTypeSet();
     if (!resTypes->hasType(types::Type::ObjectType(thisType)))
         return InliningStatus_NotInlined;
 
     for (unsigned i = 0; i < argTypes->getObjectCount(); i++) {
-        if (argTypes->getSingleObject(i))
-            return InliningStatus_NotInlined;
-
-        types::TypeObject *argType = argTypes->getTypeObject(i);
+        types::TypeObjectKey *argType = argTypes->getObject(i);
         if (!argType)
             continue;
 
         if (argType->unknownProperties())
             return InliningStatus_NotInlined;
 
-        types::HeapTypeSet *elemTypes = argType->getProperty(cx, JSID_VOID);
-        if (!elemTypes)
-            return InliningStatus_Error;
-
-        if (!elemTypes->knownSubset(cx, thisElemTypes))
+        types::HeapTypeSetKey elemTypes = argType->property(JSID_VOID);
+        if (!elemTypes.knownSubset(constraints(), thisElemTypes))
             return InliningStatus_NotInlined;
     }
 
     // Inline the call.
-    JSObject *templateObj = NewDenseEmptyArray(cx, thisType->proto, TenuredObject);
+    JSObject *templateObj = NewDenseEmptyArray(cx, thisType->proto().toObject(), TenuredObject);
     if (!templateObj)
         return InliningStatus_Error;
-    templateObj->setType(thisType);
+    templateObj->setType(baseThisType);
 
     callInfo.unwrapArgs();
 
     MArrayConcat *ins = MArrayConcat::New(callInfo.thisArg(), callInfo.getArg(0), templateObj);
     current->add(ins);
     current->push(ins);
 
     if (!resumeAfter(ins))
@@ -1063,22 +1052,19 @@ IonBuilder::inlineUnsafePutElements(Call
         MDefinition *obj = callInfo.getArg(arri);
         MDefinition *id = callInfo.getArg(idxi);
         MDefinition *elem = callInfo.getArg(elemi);
 
         bool isDenseNative = ElementAccessIsDenseNative(obj, id);
 
         bool writeNeedsBarrier = false;
         if (isDenseNative) {
-            if (!PropertyWriteNeedsTypeBarrier(cx, current, &obj, nullptr, &elem,
-                                               /* canModify = */ false,
-                                               &writeNeedsBarrier))
-            {
-                return InliningStatus_Error;
-            }
+            writeNeedsBarrier = PropertyWriteNeedsTypeBarrier(constraints(), current,
+                                                              &obj, nullptr, &elem,
+                                                              /* canModify = */ false);
         }
 
         // We can only inline setelem on dense arrays that do not need type
         // barriers and on typed arrays.
         ScalarTypeRepresentation::Type arrayType;
         if ((!isDenseNative || writeNeedsBarrier) &&
             !ElementAccessIsTypedArray(obj, id, &arrayType))
         {
@@ -1130,17 +1116,17 @@ IonBuilder::inlineUnsafeSetDenseArrayEle
     // Furthermore, note that inlineUnsafePutElements ensures the type of the
     // value is reflected in the JSID_VOID property of the array.
 
     MDefinition *obj = callInfo.getArg(base + 0);
     MDefinition *id = callInfo.getArg(base + 1);
     MDefinition *elem = callInfo.getArg(base + 2);
 
     types::TemporaryTypeSet::DoubleConversion conversion =
-        obj->resultTypeSet()->convertDoubleElements(cx);
+        obj->resultTypeSet()->convertDoubleElements(constraints());
     if (!jsop_setelem_dense(conversion, SetElem_Unsafe, obj, id, elem))
         return false;
     return true;
 }
 
 bool
 IonBuilder::inlineUnsafeSetTypedArrayElement(CallInfo &callInfo,
                                              uint32_t base,
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -22,16 +22,17 @@
 #include "jsatominlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::DoublesAreIdentical;
+using mozilla::Maybe;
 
 void
 MDefinition::PrintOpcodeName(FILE *fp, MDefinition::Opcode op)
 {
     static const char * const names[] =
     {
 #define NAME(x) #x,
         MIR_OPCODE_LIST(NAME)
@@ -2676,253 +2677,205 @@ jit::ElementAccessIsTypedArray(MDefiniti
     if (!types)
         return false;
 
     *arrayType = (ScalarTypeRepresentation::Type) types->getTypedArrayType();
     return *arrayType != ScalarTypeRepresentation::TYPE_MAX;
 }
 
 bool
-jit::ElementAccessIsPacked(JSContext *cx, MDefinition *obj)
+jit::ElementAccessIsPacked(types::CompilerConstraintList *constraints, MDefinition *obj)
 {
     types::TemporaryTypeSet *types = obj->resultTypeSet();
-    return types && !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED);
+    return types && !types->hasObjectFlags(constraints, types::OBJECT_FLAG_NON_PACKED);
 }
 
 bool
-jit::ElementAccessHasExtraIndexedProperty(JSContext *cx, MDefinition *obj)
+jit::ElementAccessHasExtraIndexedProperty(types::CompilerConstraintList *constraints,
+                                          MDefinition *obj)
 {
     types::TemporaryTypeSet *types = obj->resultTypeSet();
 
-    if (!types || types->hasObjectFlags(cx, types::OBJECT_FLAG_LENGTH_OVERFLOW))
+    if (!types || types->hasObjectFlags(constraints, types::OBJECT_FLAG_LENGTH_OVERFLOW))
         return true;
 
-    return types::TypeCanHaveExtraIndexedProperties(cx, types);
+    return types::TypeCanHaveExtraIndexedProperties(constraints, types);
 }
 
-bool
-jit::DenseNativeElementType(JSContext *cx, MDefinition *obj, MIRType *result)
+MIRType
+jit::DenseNativeElementType(types::CompilerConstraintList *constraints, MDefinition *obj)
 {
-    JS_ASSERT(result);
-    *result = MIRType_None;
-
     types::TemporaryTypeSet *types = obj->resultTypeSet();
     MIRType elementType = MIRType_None;
     unsigned count = types->getObjectCount();
 
     for (unsigned i = 0; i < count; i++) {
-        types::TypeObject *object;
-        if (!types->getTypeOrSingleObject(cx, i, &object))
-            return false;
-
+        types::TypeObjectKey *object = types->getObject(i);
         if (!object)
             continue;
 
         if (object->unknownProperties())
-            return true;
-
-        types::HeapTypeSet *elementTypes = object->getProperty(cx, JSID_VOID);
-        if (!elementTypes)
-            return true;
-
-        MIRType type = MIRTypeFromValueType(elementTypes->getKnownTypeTag(cx));
+            return MIRType_None;
+
+        types::HeapTypeSetKey elementTypes = object->property(JSID_VOID);
+
+        MIRType type = MIRTypeFromValueType(elementTypes.knownTypeTag(constraints));
         if (type == MIRType_None)
-            return true;
+            return MIRType_None;
 
         if (elementType == MIRType_None)
             elementType = type;
         else if (elementType != type)
-            return true;
+            return MIRType_None;
     }
 
-    *result = elementType;
-    return true;
+    return elementType;
 }
 
 static bool
-PropertyReadNeedsTypeBarrier(JSContext *cx, types::TypeObject *object, PropertyName *name,
-                             types::TypeSet *observed, bool *result)
+PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints,
+                             types::TypeObjectKey *object, PropertyName *name,
+                             types::TypeSet *observed)
 {
-    jsid id = name ? types::IdToTypeId(NameToId(name)) : JSID_VOID;
-
     // If the object 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.
-    JS_ASSERT(result);
-    *result = false;
-
-    if (object->unknownProperties()) {
-        *result = true;
+
+    if (object->unknownProperties())
         return true;
-    }
-
-    types::HeapTypeSet *property = object->getProperty(cx, id);
-    if (!property) {
-        *result = true;
+
+    jsid id = name ? NameToId(name) : JSID_VOID;
+    types::HeapTypeSetKey property = object->property(id);
+    if (!TypeSetIncludes(observed, MIRType_Value, property.actualTypes))
         return true;
-    }
-
-    if (!TypeSetIncludes(observed, MIRType_Value, property)) {
-        *result = true;
-        return true;
-    }
 
     // Type information for singleton objects is not required to reflect the
     // initial 'undefined' value for native properties, in particular global
     // variables declared with 'var'. Until the property is assigned a value
     // other than undefined, a barrier is required.
-    if (name && object->singleton && object->singleton->isNative()) {
-        Shape *shape = object->singleton->nativeLookup(cx, name);
+    if (name && object->singleton() && object->singleton()->isNative()) {
+        Shape *shape = object->singleton()->nativeLookup(cx, name);
         if (shape &&
             shape->hasDefaultGetter() &&
-            object->singleton->nativeGetSlot(shape->slot()).isUndefined())
+            object->singleton()->nativeGetSlot(shape->slot()).isUndefined())
         {
-            *result = true;
             return true;
         }
     }
 
-    property->addFreeze(cx);
-    *result = false;
-    return true;
+    property.freeze(constraints);
+    return false;
 }
 
 bool
-jit::PropertyReadNeedsTypeBarrier(JSContext *cx, types::TypeObject *object, PropertyName *name,
-                                  types::StackTypeSet *observed, bool updateObserved, bool *result)
+jit::PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints,
+                                  types::TypeObjectKey *object, PropertyName *name,
+                                  types::StackTypeSet *observed, bool updateObserved)
 {
-    jsid id = name ? types::IdToTypeId(NameToId(name)) : JSID_VOID;
-
     // 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() && observed->noConstraints() && !JSID_IS_VOID(id)) {
-        JSObject *obj = object->singleton ? object->singleton : object->proto;
+    if (updateObserved && observed->empty() && observed->noConstraints() && name) {
+        JSObject *obj = object->singleton() ? object->singleton() : object->proto().toObjectOrNull();
 
         while (obj) {
             if (!obj->isNative())
                 break;
 
             Value v;
-            if (HasDataProperty(cx, obj, id, &v)) {
+            if (HasDataProperty(cx, obj, NameToId(name), &v)) {
                 if (v.isUndefined())
                     break;
                 observed->addType(cx, types::GetValueType(v));
             }
 
             obj = obj->getProto();
         }
     }
 
-    return PropertyReadNeedsTypeBarrier(cx, object, name, observed, result);
-}
-
-bool
-jit::PropertyReadNeedsTypeBarrier(JSContext *cx, MDefinition *obj, PropertyName *name,
-                                  types::StackTypeSet *observed, bool *result)
-{
-    JS_ASSERT(result);
-    *result = false;
-
-    if (observed->unknown())
-        return true;
-
-    types::TypeSet *types = obj->resultTypeSet();
-    if (!types || types->unknownObject()) {
-        *result = true;
-        return true;
-    }
-
-    bool updateObserved = types->getObjectCount() == 1;
-    for (size_t i = 0; i < types->getObjectCount(); i++) {
-        types::TypeObject *object;
-        if (!types->getTypeOrSingleObject(cx, i, &object))
-            return false;
-
-        if (object) {
-            if (!PropertyReadNeedsTypeBarrier(cx, object, name, observed, updateObserved, result))
-                return false;
-
-            if (*result)
-                return true;
-        }
-    }
-
-    return true;
+    return PropertyReadNeedsTypeBarrier(cx, constraints, object, name, observed);
 }
 
 bool
-jit::PropertyReadOnPrototypeNeedsTypeBarrier(JSContext *cx, MDefinition *obj, PropertyName *name,
-                                             types::TemporaryTypeSet *observed, bool *result)
+jit::PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints,
+                                  MDefinition *obj, PropertyName *name,
+                                  types::StackTypeSet *observed)
 {
-    JS_ASSERT(result);
-    *result = false;
-
     if (observed->unknown())
-        return true;
+        return false;
 
     types::TypeSet *types = obj->resultTypeSet();
-    if (!types || types->unknownObject()) {
-        *result = true;
+    if (!types || types->unknownObject())
         return true;
-    }
-
+
+    bool updateObserved = types->getObjectCount() == 1;
     for (size_t i = 0; i < types->getObjectCount(); i++) {
-        types::TypeObject *object;
-        if (!types->getTypeOrSingleObject(cx, i, &object))
-            return false;
-
-        if (!object)
-            continue;
-        while (object->proto) {
-            object = object->proto->getType(cx);
-            if (!object)
-                return false;
-
-            if (!PropertyReadNeedsTypeBarrier(cx, object, name, observed, result))
-                return false;
-            if (*result)
+        types::TypeObjectKey *object = types->getObject(i);
+        if (object) {
+            if (PropertyReadNeedsTypeBarrier(cx, constraints, object, name,
+                                             observed, updateObserved))
+            {
                 return true;
+            }
         }
     }
 
-    return true;
+    return false;
 }
 
 bool
-jit::PropertyReadIsIdempotent(JSContext *cx, MDefinition *obj, PropertyName *name, bool *result)
+jit::PropertyReadOnPrototypeNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints,
+                                             MDefinition *obj, PropertyName *name,
+                                             types::TemporaryTypeSet *observed)
 {
-    JS_ASSERT(result);
-    *result = false;
-    // Determine if reading a property from obj is likely to be idempotent.
-
-    jsid id = types::IdToTypeId(NameToId(name));
+    if (observed->unknown())
+        return false;
 
     types::TypeSet *types = obj->resultTypeSet();
     if (!types || types->unknownObject())
         return true;
 
     for (size_t i = 0; i < types->getObjectCount(); i++) {
-        types::TypeObject *object;
-        if (!types->getTypeOrSingleObject(cx, i, &object))
-            return false;
-
-        if (object) {
-            if (object->unknownProperties())
-                return true;
-
-            // Check if the property has been reconfigured or is a getter.
-            types::HeapTypeSet *property = object->getProperty(cx, id);
-            if (!property || property->isConfiguredProperty(cx, object))
+        types::TypeObjectKey *object = types->getObject(i);
+        if (!object)
+            continue;
+        while (object->proto().isObject()) {
+            object = types::TypeObjectKey::get(object->proto().toObject());
+            if (PropertyReadNeedsTypeBarrier(cx, constraints, object, name, observed))
                 return true;
         }
     }
 
-    *result = true;
+    return false;
+}
+
+bool
+jit::PropertyReadIsIdempotent(types::CompilerConstraintList *constraints,
+                              MDefinition *obj, PropertyName *name)
+{
+    // Determine if reading a property from obj is likely to be idempotent.
+
+    types::TypeSet *types = obj->resultTypeSet();
+    if (!types || types->unknownObject())
+        return false;
+
+    for (size_t i = 0; i < types->getObjectCount(); i++) {
+        types::TypeObjectKey *object = types->getObject(i);
+        if (object) {
+            if (object->unknownProperties())
+                return false;
+
+            // Check if the property has been reconfigured or is a getter.
+            types::HeapTypeSetKey property = object->property(NameToId(name));
+            if (property.configured(constraints, object))
+                return false;
+        }
+    }
+
     return true;
 }
 
 bool
 jit::AddObjectsForPropertyRead(JSContext *cx, MDefinition *obj, PropertyName *name,
                                types::StackTypeSet *observed)
 {
     // Add objects to observed which *could* be observed by reading name from obj,
@@ -2931,31 +2884,30 @@ jit::AddObjectsForPropertyRead(JSContext
     JS_ASSERT(observed->noConstraints());
 
     types::TemporaryTypeSet *types = obj->resultTypeSet();
     if (!types || types->unknownObject()) {
         observed->addType(cx, types::Type::AnyObjectType());
         return true;
     }
 
-    jsid id = name ? types::IdToTypeId(NameToId(name)) : JSID_VOID;
-
     for (size_t i = 0; i < types->getObjectCount(); i++) {
         types::TypeObject *object;
         if (!types->getTypeOrSingleObject(cx, i, &object))
             return false;
 
         if (!object)
             continue;
 
         if (object->unknownProperties()) {
             observed->addType(cx, types::Type::AnyObjectType());
             return true;
         }
 
+        jsid id = name ? NameToId(name) : JSID_VOID;
         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))
@@ -2964,62 +2916,61 @@ jit::AddObjectsForPropertyRead(JSContext
                 observed->addType(cx, types::Type::ObjectType(object));
         }
     }
 
     return true;
 }
 
 static bool
-TryAddTypeBarrierForWrite(JSContext *cx, MBasicBlock *current, types::TemporaryTypeSet *objTypes,
-                          jsid id, MDefinition **pvalue)
+TryAddTypeBarrierForWrite(types::CompilerConstraintList *constraints,
+                          MBasicBlock *current, types::TemporaryTypeSet *objTypes,
+                          PropertyName *name, MDefinition **pvalue)
 {
     // Return whether pvalue was modified to include a type barrier ensuring
     // that writing the value to objTypes/id will not require changing type
     // information.
 
-    // All objects in the set must have the same types for id. Otherwise, we
+    // All objects in the set must have the same types for name. Otherwise, we
     // could bail out without subsequently triggering a type change that
     // invalidates the compiled code.
-    types::HeapTypeSet *aggregateProperty = nullptr;
+    Maybe<types::HeapTypeSetKey> aggregateProperty;
 
     for (size_t i = 0; i < objTypes->getObjectCount(); i++) {
-        types::TypeObject *object;
-        if (!objTypes->getTypeOrSingleObject(cx, i, &object))
-            return false;
-
+        types::TypeObjectKey *object = objTypes->getObject(i);
         if (!object)
             continue;
 
         if (object->unknownProperties())
             return false;
 
-        types::HeapTypeSet *property = object->getProperty(cx, id);
-        if (!property)
-            return false;
-
-        if (TypeSetIncludes(property, (*pvalue)->type(), (*pvalue)->resultTypeSet()))
+        jsid id = name ? NameToId(name) : JSID_VOID;
+        types::HeapTypeSetKey property = object->property(id);
+        if (TypeSetIncludes(property.actualTypes, (*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
         // potentially be removed.
-        property->addFreeze(cx);
-
-        if (aggregateProperty) {
-            if (!aggregateProperty->isSubset(property) || !property->isSubset(aggregateProperty))
+        property.freeze(constraints);
+
+        if (aggregateProperty.empty()) {
+            aggregateProperty.construct(property);
+        } else {
+            if (!aggregateProperty.ref().actualTypes->isSubset(property.actualTypes) ||
+                !property.actualTypes->isSubset(aggregateProperty.ref().actualTypes))
+            {
                 return false;
-        } else {
-            aggregateProperty = property;
+            }
         }
     }
 
-    JS_ASSERT(aggregateProperty);
-
-    MIRType propertyType = MIRTypeFromValueType(aggregateProperty->getKnownTypeTag(cx));
+    JS_ASSERT(!aggregateProperty.empty());
+
+    MIRType propertyType = MIRTypeFromValueType(aggregateProperty.ref().knownTypeTag(constraints));
     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) {
@@ -3034,17 +2985,18 @@ TryAddTypeBarrierForWrite(JSContext *cx,
         return true;
       }
       default:;
     }
 
     if ((*pvalue)->type() != MIRType_Value)
         return false;
 
-    types::TemporaryTypeSet *types = aggregateProperty->clone(GetIonContext()->temp->lifoAlloc());
+    types::TemporaryTypeSet *types =
+        aggregateProperty.ref().actualTypes->clone(GetIonContext()->temp->lifoAlloc());
     if (!types)
         return false;
 
     MInstruction *ins = MMonitorTypes::New(*pvalue, types);
     current->add(ins);
     return true;
 }
 
@@ -3057,111 +3009,85 @@ AddTypeGuard(MBasicBlock *current, MDefi
 
     // For now, never move type object guards.
     guard->setNotMovable();
 
     return guard;
 }
 
 bool
-jit::PropertyWriteNeedsTypeBarrier(JSContext *cx, MBasicBlock *current, MDefinition **pobj,
-                                   PropertyName *name, MDefinition **pvalue, bool canModify,
-                                   bool *result)
+jit::PropertyWriteNeedsTypeBarrier(types::CompilerConstraintList *constraints,
+                                   MBasicBlock *current, MDefinition **pobj,
+                                   PropertyName *name, MDefinition **pvalue, bool canModify)
 {
-    JS_ASSERT(result);
-    *result = false;
-
     // 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::TemporaryTypeSet *types = (*pobj)->resultTypeSet();
-    if (!types || types->unknownObject()) {
-        *result = true;
+    if (!types || types->unknownObject())
         return true;
-    }
-
-    jsid id = name ? types::IdToTypeId(NameToId(name)) : JSID_VOID;
 
     // If all of the objects being written to have property types which already
     // reflect the value, no barrier at all is needed. Additionally, if all
     // objects being written to have the same types for the property, and those
     // types do *not* reflect the value, add a type barrier for the value.
 
     bool success = true;
     for (size_t i = 0; i < types->getObjectCount(); i++) {
-        types::TypeObject *object;
-        if (!types->getTypeOrSingleObject(cx, i, &object))
-            return false;
-
+        types::TypeObjectKey *object = types->getObject(i);
         if (!object || object->unknownProperties())
             continue;
 
         // TI doesn't track TypedArray objects and should never insert a type
         // barrier for them.
-        if (object->getTypedArrayType() < ScalarTypeRepresentation::TYPE_MAX)
+        if (IsTypedArrayClass(object->clasp()))
             continue;
 
-        types::HeapTypeSet *property = object->getProperty(cx, id);
-        if (!property) {
-            success = false;
-            break;
-        }
-        if (!TypeSetIncludes(property, (*pvalue)->type(), (*pvalue)->resultTypeSet())) {
+        jsid id = name ? NameToId(name) : JSID_VOID;
+        types::HeapTypeSetKey property = object->property(id);
+        if (!TypeSetIncludes(property.actualTypes, (*pvalue)->type(), (*pvalue)->resultTypeSet())) {
             // Either pobj or pvalue needs to be modified to filter out the
             // types which the value could have but are not in the property,
             // or a VM call is required. A VM call is always required if pobj
             // and pvalue cannot be modified.
-            if (!canModify) {
-                *result = true;
+            if (!canModify)
                 return true;
-            }
-            success = TryAddTypeBarrierForWrite(cx, current, types, id, pvalue);
+            success = TryAddTypeBarrierForWrite(constraints, current, types, name, pvalue);
             break;
         }
     }
 
     if (success)
-        return true;
+        return false;
 
     // If all of the objects except one have property types which reflect the
     // value, and the remaining object has no types at all for the property,
     // add a guard that the object does not have that remaining object's type.
 
-    if (types->getObjectCount() <= 1) {
-        *result = true;
+    if (types->getObjectCount() <= 1)
         return true;
-    }
 
     types::TypeObject *excluded = nullptr;
     for (size_t i = 0; i < types->getObjectCount(); i++) {
-        types::TypeObject *object;
-        if (!types->getTypeOrSingleObject(cx, i, &object))
-            return false;
-
+        types::TypeObjectKey *object = types->getObject(i);
         if (!object || object->unknownProperties())
             continue;
-        if (object->getTypedArrayType() < ScalarTypeRepresentation::TYPE_MAX)
+        if (IsTypedArrayClass(object->clasp()))
             continue;
 
-        types::HeapTypeSet *property = object->getProperty(cx, id);
-        if (!property) {
-            *result = true;
+        jsid id = name ? NameToId(name) : JSID_VOID;
+        types::HeapTypeSetKey property = object->property(id);
+        if (TypeSetIncludes(property.actualTypes, (*pvalue)->type(), (*pvalue)->resultTypeSet()))
+            continue;
+
+        if (!property.actualTypes->empty() || excluded)
             return true;
-        }
-
-        if (TypeSetIncludes(property, (*pvalue)->type(), (*pvalue)->resultTypeSet()))
-            continue;
-
-        if (!property->empty() || excluded) {
-            *result = true;
-            return true;
-        }
-        excluded = object;
+        excluded = object->isTypeObject() ? object->asTypeObject() : object->asSingleObject()->getType(GetIonContext()->cx);
     }
 
     JS_ASSERT(excluded);
 
     *pobj = AddTypeGuard(current, *pobj, excluded, /* bailOnEquality = */ true);
-    return true;
+    return false;
 }
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -8980,28 +8980,34 @@ static inline bool isOSRLikeValue (MDefi
 
 typedef Vector<MDefinition *, 8, IonAllocPolicy> MDefinitionVector;
 
 // Helper functions used to decide how to build MIR.
 
 bool ElementAccessIsDenseNative(MDefinition *obj, MDefinition *id);
 bool ElementAccessIsTypedArray(MDefinition *obj, MDefinition *id,
                                ScalarTypeRepresentation::Type *arrayType);
-bool ElementAccessIsPacked(JSContext *cx, MDefinition *obj);
-bool ElementAccessHasExtraIndexedProperty(JSContext *cx, MDefinition *obj);
-bool DenseNativeElementType(JSContext *cx, MDefinition *obj, MIRType *result);
-bool PropertyReadNeedsTypeBarrier(JSContext *cx, types::TypeObject *object, PropertyName *name,
-                                  types::StackTypeSet *observed, bool updateObserved, bool *result);
-bool PropertyReadNeedsTypeBarrier(JSContext *cx, MDefinition *obj, PropertyName *name,
-                                  types::StackTypeSet *observed, bool *result);
-bool PropertyReadOnPrototypeNeedsTypeBarrier(JSContext *cx, MDefinition *obj, PropertyName *name,
-                                             types::TemporaryTypeSet *observed, bool *result);
-bool PropertyReadIsIdempotent(JSContext *cx, MDefinition *obj, PropertyName *name, bool *result);
+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, types::CompilerConstraintList *constraints,
+                                  types::TypeObjectKey *object, PropertyName *name,
+                                  types::StackTypeSet *observed, bool updateObserved);
+bool PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints,
+                                  MDefinition *obj, PropertyName *name,
+                                  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(JSContext *cx, MDefinition *obj, PropertyName *name,
                                types::StackTypeSet *observed);
-bool PropertyWriteNeedsTypeBarrier(JSContext *cx, MBasicBlock *current, MDefinition **pobj,
+bool PropertyWriteNeedsTypeBarrier(types::CompilerConstraintList *constraints,
+                                   MBasicBlock *current, MDefinition **pobj,
                                    PropertyName *name, MDefinition **pvalue,
-                                   bool canModify, bool *result);
+                                   bool canModify);
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_MIR_h */
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2859,17 +2859,17 @@ BeginMarkPhase(JSRuntime *rt)
     rt->gcMarker.start();
     JS_ASSERT(!rt->gcMarker.callback);
     JS_ASSERT(IS_GC_MARKING_TRACER(&rt->gcMarker));
 
     /* For non-incremental GC the following sweep discards the jit code. */
     if (rt->gcIsIncremental) {
         for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
             gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_MARK_DISCARD_CODE);
-            zone->discardJitCode(rt->defaultFreeOp(), false);
+            zone->discardJitCode(rt->defaultFreeOp());
         }
     }
 
     GCMarker *gcmarker = &rt->gcMarker;
 
     rt->gcStartNumber = rt->gcNumber;
 
     /*
@@ -3734,17 +3734,17 @@ BeginSweepingZoneGroup(JSRuntime *rt)
     /* Detach unreachable debuggers and global objects from each other. */
     Debugger::sweepAll(&fop);
 
     {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_COMPARTMENTS);
 
         for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
             gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_DISCARD_CODE);
-            zone->discardJitCode(&fop, !zone->isPreservingCode());
+            zone->discardJitCode(&fop);
         }
 
         bool releaseTypes = ReleaseObservedTypes(rt);
         for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
             gcstats::AutoSCC scc(rt->gcStats, rt->gcZoneGroupIndex);
             c->sweep(&fop, releaseTypes);
         }
 
@@ -5004,17 +5004,17 @@ js::ReleaseAllJITCode(FreeOp *fop)
              * this also resets the active flag.
              */
             jit::FinishDiscardBaselineScript(fop, script);
         }
     }
 
     /* Sweep now invalidated compiler outputs from each compartment. */
     for (CompartmentsIter comp(fop->runtime()); !comp.done(); comp.next())
-        comp->types.sweepCompilerOutputs(fop, false);
+        comp->types.clearCompilerOutputs(fop);
 #endif
 }
 
 /*
  * There are three possible PCCount profiling states:
  *
  * 1. None: Neither scripts nor the runtime have count information.
  * 2. Profile: Active scripts have count information, the runtime does not.
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -517,53 +517,247 @@ TypeSet::unionSets(TypeSet *a, TypeSet *
                 return NULL;
         }
     }
 
     return res;
 }
 
 /////////////////////////////////////////////////////////////////////
-// Freeze constraints
+// Compiler constraints
 /////////////////////////////////////////////////////////////////////
 
-namespace {
-
-/* Constraint which triggers recompilation of a script if any type is added to a type set. */
-class TypeConstraintFreeze : public TypeConstraint
+// Compiler constraints overview
+//
+// Constraints generated during Ion compilation capture assumptions made about
+// heap properties that will trigger invalidation of the resulting Ion code if
+// the constraint is violated. Constraints can only be attached to type sets on
+// the main thread, so to allow compilation to occur almost entirely off thread
+// the generation is split into two phases.
+//
+// During compilation, CompilerConstraint values are constructed in a list,
+// recording the heap property type set which was read from and its expected
+// contents, along with the assumption made about those contents.
+//
+// At the end of compilation, when linking the result on the main thread, the
+// list of compiler constraints are read and converted to type constraints and
+// attached to the type sets. If the property type sets have changed so that the
+// assumptions no longer hold then the compilation is aborted and its result
+// discarded.
+
+static LifoAlloc *IonAlloc() {
+    return jit::GetIonContext()->temp->lifoAlloc();
+}
+
+// Superclass of all constraints generated during Ion compilation. These may
+// be allocated off the main thread, using the current Ion context's allocator.
+class types::CompilerConstraint
 {
   public:
-    RecompileInfo info;
-
-    /* Whether a new type has already been added, triggering recompilation. */
-    bool typeAdded;
-
-    TypeConstraintFreeze(RecompileInfo info)
-        : info(info), typeAdded(false)
+    // 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.
+    TemporaryTypeSet *expected;
+
+    CompilerConstraint(const HeapTypeSetKey &property)
+      : property(property),
+        expected(property.actualTypes->clone(IonAlloc()))
+    {
+        // Note: CompilerConstraintList::add watches for OOM under clone().
+    }
+
+    // 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;
+};
+
+void
+CompilerConstraintList::add(CompilerConstraint *constraint)
+{
+    if (!constraint || !constraint->expected || !constraints.append(constraint))
+        setFailed();
+}
+
+namespace {
+
+template <typename T>
+class CompilerConstraintInstance : public CompilerConstraint
+{
+    T data;
+
+  public:
+    CompilerConstraintInstance<T>(const HeapTypeSetKey &property, const T &data)
+      : CompilerConstraint(property), data(data)
+    {}
+
+    bool generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo);
+};
+
+// Constraint generated from a CompilerConstraint when linking the compilation.
+template <typename T>
+class TypeCompilerConstraint : public TypeConstraint
+{
+    // Compilation which this constraint may invalidate.
+    RecompileInfo compilation;
+
+    T data;
+
+  public:
+    TypeCompilerConstraint<T>(RecompileInfo compilation, const T &data)
+      : compilation(compilation), data(data)
     {}
 
+    const char *kind() { return data.kind(); }
+
+    void newType(JSContext *cx, TypeSet *source, Type type) {
+        if (data.invalidateOnNewType(type))
+            cx->compartment()->types.addPendingRecompile(cx, compilation);
+    }
+
+    void newPropertyState(JSContext *cx, TypeSet *source) {
+        if (data.invalidateOnNewPropertyState(source))
+            cx->compartment()->types.addPendingRecompile(cx, compilation);
+    }
+
+    void newObjectState(JSContext *cx, TypeObject *object, bool force) {
+        if (data.invalidateOnNewObjectState(object, force))
+            cx->compartment()->types.addPendingRecompile(cx, compilation);
+    }
+};
+
+template <typename T>
+bool
+CompilerConstraintInstance<T>::generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo)
+{
+    if (!data.constraintHolds(cx, property, expected))
+        return false;
+
+    property.actualTypes->add(cx, cx->typeLifoAlloc().new_<TypeCompilerConstraint<T> >(recompileInfo, data),
+                              /* callExisting = */ false);
+    return true;
+}
+
+} /* anonymous namespace */
+
+const Class *
+TypeObjectKey::clasp()
+{
+    return isTypeObject() ? asTypeObject()->clasp : asSingleObject()->getClass();
+}
+
+TaggedProto
+TypeObjectKey::proto()
+{
+    return isTypeObject() ? asTypeObject()->proto : asSingleObject()->getProto();
+}
+
+JSObject *
+TypeObjectKey::singleton()
+{
+    return isTypeObject() ? asTypeObject()->singleton : asSingleObject();
+}
+
+TypeNewScript *
+TypeObjectKey::newScript()
+{
+    if (isTypeObject()) {
+        TypeObjectAddendum *addendum = asTypeObject()->addendum;
+        if (addendum && addendum->isNewScript())
+            return addendum->asNewScript();
+    }
+    return NULL;
+}
+
+bool
+TypeObjectKey::unknownProperties()
+{
+    JSContext *cx = jit::GetIonContext()->cx;
+    TypeObject *type = isSingleObject() ? asSingleObject()->getType(cx) : asTypeObject();
+    if (!type)
+        MOZ_CRASH();
+    return type->unknownProperties();
+}
+
+HeapTypeSetKey
+TypeObjectKey::property(jsid id)
+{
+    JSContext *cx = jit::GetIonContext()->cx;
+    TypeObject *type = isSingleObject() ? asSingleObject()->getType(cx) : asTypeObject();
+    if (!type)
+        MOZ_CRASH();
+    HeapTypeSetKey property;
+    property.actualTypes = type->getProperty(cx, id);
+    if (!property.actualTypes)
+        MOZ_CRASH();
+    return property;
+}
+
+bool
+types::FinishCompilation(JSContext *cx, JSScript *script, jit::ExecutionMode executionMode,
+                         CompilerConstraintList *constraints, RecompileInfo *precompileInfo)
+{
+    if (constraints->failed())
+        return false;
+
+    CompilerOutput co(script, executionMode);
+
+    TypeCompartment &types = cx->compartment()->types;
+    if (!types.constrainedOutputs) {
+        types.constrainedOutputs = cx->new_< Vector<CompilerOutput> >(cx);
+        if (!types.constrainedOutputs)
+            return false;
+    }
+
+    uint32_t index = types.constrainedOutputs->length();
+    if (!types.constrainedOutputs->append(co))
+        return false;
+
+    *precompileInfo = RecompileInfo(index);
+
+    for (size_t i = 0; i < constraints->length(); i++) {
+        CompilerConstraint *constraint = constraints->get(i);
+        if (!constraint->generateTypeConstraint(cx, *precompileInfo)) {
+            types.constrainedOutputs->back().invalidate();
+            return false;
+        }
+    }
+
+    return true;
+}
+
+namespace {
+
+// Constraint which triggers recompilation of a script if any type is added to a type set. */
+class ConstraintDataFreeze
+{
+  public:
+    ConstraintDataFreeze() {}
+
     const char *kind() { return "freeze"; }
 
-    void newType(JSContext *cx, TypeSet *source, Type type)
+    bool invalidateOnNewType(Type type) { return true; }
+    bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
+    bool invalidateOnNewObjectState(TypeObject *object, bool force) { return false; }
+
+    bool constraintHolds(JSContext *cx,
+                         const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
-        if (typeAdded)
-            return;
-
-        typeAdded = true;
-        cx->compartment()->types.addPendingRecompile(cx, info);
+        return property.actualTypes->isSubset(expected);
     }
 };
 
 } /* anonymous namespace */
 
 void
-HeapTypeSet::addFreeze(JSContext *cx)
+HeapTypeSetKey::freeze(CompilerConstraintList *constraints)
 {
-    add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreeze>(
-                cx->compartment()->types.compiledInfo), false);
+    constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreeze> >(*this, ConstraintDataFreeze()));
 }
 
 static inline JSValueType
 GetValueTypeFromTypeFlags(TypeFlags flags)
 {
     switch (flags) {
       case TYPE_FLAG_UNDEFINED:
         return JSVAL_TYPE_UNDEFINED;
@@ -605,143 +799,194 @@ TemporaryTypeSet::getKnownTypeTag()
      * added to the set.
      */
     DebugOnly<bool> empty = flags == 0 && baseObjectCount() == 0;
     JS_ASSERT_IF(empty, type == JSVAL_TYPE_UNKNOWN);
 
     return type;
 }
 
-JSValueType
-HeapTypeSet::getKnownTypeTag(JSContext *cx)
-{
-    TypeFlags flags = baseFlags();
-    JSValueType type;
-
-    if (baseObjectCount())
-        type = flags ? JSVAL_TYPE_UNKNOWN : JSVAL_TYPE_OBJECT;
-    else
-        type = GetValueTypeFromTypeFlags(flags);
-
-    if (type != JSVAL_TYPE_UNKNOWN)
-        addFreeze(cx);
-
-    /*
-     * If the type set is totally empty then it will be treated as unknown,
-     * but we still need to record the dependency as adding a new type can give
-     * it a definite type tag. This is not needed if there are enough types
-     * that the exact tag is unknown, as it will stay unknown as more types are
-     * added to the set.
-     */
-    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)
+{
+    if (actualTypes->unknown())
+        return JSVAL_TYPE_UNKNOWN;
+
+    TypeFlags flags = actualTypes->baseFlags() & ~TYPE_FLAG_ANYOBJECT;
+    JSValueType type;
+
+    if (actualTypes->unknownObject() || actualTypes->getObjectCount())
+        type = flags ? JSVAL_TYPE_UNKNOWN : JSVAL_TYPE_OBJECT;
+    else
+        type = GetValueTypeFromTypeFlags(flags);
+
+    if (type != JSVAL_TYPE_UNKNOWN)
+        freeze(constraints);
+
+    /*
+     * If the type set is totally empty then it will be treated as unknown,
+     * but we still need to record the dependency as adding a new type can give
+     * it a definite type tag. This is not needed if there are enough types
+     * that the exact tag is unknown, as it will stay unknown as more types are
+     * added to the set.
+     */
+    JS_ASSERT_IF(actualTypes->empty(), type == JSVAL_TYPE_UNKNOWN);
+
+    return type;
+}
+
+bool
+HeapTypeSetKey::notEmpty(CompilerConstraintList *constraints)
+{
+    if (!actualTypes->empty())
+        return true;
+    freeze(constraints);
+    return false;
+}
+
+bool
+HeapTypeSetKey::knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other)
+{
+    if (!actualTypes->isSubset(other.actualTypes))
+        return false;
+    freeze(constraints);
+    return true;
+}
+
+JSObject *
+TemporaryTypeSet::getSingleton()
+{
+    if (baseFlags() != 0 || baseObjectCount() != 1)
+        return NULL;
+
+    return getSingleObject(0);
+}
+
+JSObject *
+HeapTypeSetKey::singleton(CompilerConstraintList *constraints)
+{
+    if (actualTypes->baseFlags() != 0 || actualTypes->getObjectCount() != 1)
+        return NULL;
+
+    JSObject *obj = actualTypes->getSingleObject(0);
+
+    if (obj)
+        freeze(constraints);
+
+    return obj;
+}
+
+bool
+HeapTypeSetKey::needsBarrier(CompilerConstraintList *constraints)
+{
+    bool result = actualTypes->unknownObject()
+               || actualTypes->getObjectCount() > 0
+               || actualTypes->hasAnyFlag(TYPE_FLAG_STRING);
+    if (!result)
+        freeze(constraints);
+    return result;
+}
+
 namespace {
 
-/* Constraint which triggers recompilation if an object acquires particular flags. */
-class TypeConstraintFreezeObjectFlags : public TypeConstraint
+// Constraint which triggers recompilation if an object acquires particular flags.
+class ConstraintDataFreezeObjectFlags
 {
   public:
-    RecompileInfo info;
-
-    /* Flags we are watching for on this object. */
+    // Object being queried.
+    TypeObjectKey *object;
+
+    // Flags we are watching for on this object.
     TypeObjectFlags flags;
 
-    /* Whether the object has already been marked as having one of the flags. */
-    bool marked;
-
-    TypeConstraintFreezeObjectFlags(RecompileInfo info, TypeObjectFlags flags)
-        : info(info), flags(flags),
-          marked(false)
+    ConstraintDataFreezeObjectFlags(TypeObjectKey *object, TypeObjectFlags flags)
+      : object(object), flags(flags)
     {}
 
     const char *kind() { return "freezeObjectFlags"; }
 
-    void newType(JSContext *cx, TypeSet *source, Type type) {}
-
-    void newObjectState(JSContext *cx, TypeObject *object, bool force)
+    bool invalidateOnNewType(Type type) { return false; }
+    bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
+    bool invalidateOnNewObjectState(TypeObject *object, bool force) {
+        return flags ? object->hasAnyFlags(flags) : force;
+    }
+
+    bool constraintHolds(JSContext *cx,
+                         const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
-        if (!marked && (object->hasAnyFlags(flags) || (!flags && force))) {
-            marked = true;
-            cx->compartment()->types.addPendingRecompile(cx, info);
-        }
+        // FIXME: There is not yet any way to test if constraints with no
+        // associated flags (i.e. those invalidated via |force|) still hold.
+        TypeObject *type = object->isSingleObject()
+                           ? object->asSingleObject()->type()
+                           : object->asTypeObject();
+        return !type->hasAnyFlags(flags);
     }
 };
 
 } /* anonymous namespace */
 
 bool
-TemporaryTypeSet::hasObjectFlags(JSContext *cx, TypeObjectFlags flags)
+TypeObjectKey::hasFlags(CompilerConstraintList *constraints, TypeObjectFlags flags)
+{
+    JS_ASSERT(flags);
+
+    JSContext *cx = jit::GetIonContext()->cx;
+    TypeObject *type = isSingleObject() ? asSingleObject()->getType(cx) : asTypeObject();
+    if (!type)
+        MOZ_CRASH();
+    if (type->hasAnyFlags(flags))
+        return true;
+
+    HeapTypeSetKey objectProperty = property(JSID_EMPTY);
+    constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> >(objectProperty, ConstraintDataFreezeObjectFlags(this, flags)));
+    return false;
+}
+
+void
+TypeObjectKey::watchStateChange(CompilerConstraintList *constraints)
+{
+    HeapTypeSetKey objectProperty = property(JSID_EMPTY);
+    constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> >(objectProperty, ConstraintDataFreezeObjectFlags(this, 0)));
+}
+
+bool
+TemporaryTypeSet::hasObjectFlags(CompilerConstraintList *constraints, TypeObjectFlags flags)
 {
     if (unknownObject())
         return true;
 
     /*
      * Treat type sets containing no objects as having all object flags,
      * to spare callers from having to check this.
      */
     if (baseObjectCount() == 0)
         return true;
 
-    RootedObject obj(cx);
     unsigned count = getObjectCount();
     for (unsigned i = 0; i < count; i++) {
-        TypeObject *object = getTypeObject(i);
-        if (!object) {
-            if (!(obj = getSingleObject(i)))
-                continue;
-            if (!(object = obj->getType(cx)))
-                return true;
-        }
-        if (object->hasAnyFlags(flags))
-            return true;
-
-        /*
-         * Add a constraint on the the object to pick up changes in the
-         * object's properties.
-         */
-        HeapTypeSet *types = object->getProperty(cx, JSID_EMPTY);
-        if (!types)
+        TypeObjectKey *object = getObject(i);
+        if (object && object->hasFlags(constraints, flags))
             return true;
-        types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>(
-                          cx->compartment()->types.compiledInfo, flags), false);
     }
 
     return false;
 }
 
-bool
-HeapTypeSet::HasObjectFlags(JSContext *cx, TypeObject *object, TypeObjectFlags flags)
-{
-    if (object->hasAnyFlags(flags))
-        return true;
-
-    HeapTypeSet *types = object->getProperty(cx, JSID_EMPTY);
-    if (!types)
-        return true;
-    types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>(
-                      cx->compartment()->types.compiledInfo, flags), false);
-    return false;
-}
-
 static inline void
 ObjectStateChange(ExclusiveContext *cxArg, TypeObject *object, bool markingUnknown, bool force)
 {
     if (object->unknownProperties())
         return;
 
     /* All constraints listening to state changes are on the empty id. */
     TypeSet *types = object->maybeGetProperty(cxArg, JSID_EMPTY);
@@ -758,59 +1003,73 @@ ObjectStateChange(ExclusiveContext *cxAr
                 constraint = constraint->next;
             }
         } else {
             JS_ASSERT(!types->constraintList);
         }
     }
 }
 
-void
-HeapTypeSet::WatchObjectStateChange(JSContext *cx, TypeObject *obj)
-{
-    JS_ASSERT(!obj->unknownProperties());
-    HeapTypeSet *types = obj->getProperty(cx, JSID_EMPTY);
-    if (!types)
-        return;
-
-    /*
-     * Use a constraint which triggers recompilation when markStateChange is
-     * called, which will set 'force' to true.
-     */
-    types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>(
-                     cx->compartment()->types.compiledInfo,
-                     0));
-}
+static void
+CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun);
 
 namespace {
 
-class TypeConstraintFreezeConfiguredProperty : public TypeConstraint
+class ConstraintDataFreezeConfiguredProperty
 {
   public:
-    RecompileInfo info;
-
-    TypeConstraintFreezeConfiguredProperty(RecompileInfo info)
-      : info(info)
+    TypeObjectKey *object;
+
+    ConstraintDataFreezeConfiguredProperty(TypeObjectKey *object)
+      : object(object)
     {}
 
-    const char *kind() { return "freezeOwnProperty"; }
-
-    void newType(JSContext *cx, TypeSet *source, Type type) {}
-
-    void newPropertyState(JSContext *cx, TypeSet *source)
+    const char *kind() { return "freezeConfiguredProperty"; }
+
+    bool invalidateOnNewType(Type type) { return false; }
+    bool invalidateOnNewPropertyState(TypeSet *property) {
+        return property->configuredProperty();
+    }
+    bool invalidateOnNewObjectState(TypeObject *object, bool force) { return false; }
+
+    bool constraintHolds(JSContext *cx,
+                         const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
-        if (source->configuredProperty())
-            cx->compartment()->types.addPendingRecompile(cx, info);
+        // Everywhere compiled code depends on definite properties associated
+        // with a type object's newScript, we need to make sure there are
+        // constraints in place which will mark those properties as configured
+        // should the definite properties be invalidated.
+        TypeObject *type = object->isSingleObject()
+                           ? object->asSingleObject()->type()
+                           : object->asTypeObject();
+        if (type->flags & OBJECT_FLAG_NEW_SCRIPT_REGENERATE) {
+            type->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
+            if (type->hasNewScript()) {
+                CheckNewScriptProperties(cx, type, type->newScript()->fun);
+            } else {
+                JS_ASSERT(type->flags & OBJECT_FLAG_ADDENDUM_CLEARED);
+                type->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
+            }
+        }
+
+        return !property.actualTypes->configuredProperty();
     }
 };
 
 } /* anonymous namespace */
 
-static void
-CheckNewScriptProperties(JSContext *cx, HandleTypeObject type, HandleFunction fun);
+bool
+HeapTypeSetKey::configured(CompilerConstraintList *constraints, TypeObjectKey *type)
+{
+    if (actualTypes->configuredProperty())
+        return true;
+
+    constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeConfiguredProperty> >(*this, ConstraintDataFreezeConfiguredProperty(type)));
+    return false;
+}
 
 bool
 TypeObject::incrementTenureCount()
 {
     uint32_t count = tenureCount();
     JS_ASSERT(count <= OBJECT_FLAG_TENURE_COUNT_LIMIT);
 
     if (count >= OBJECT_FLAG_TENURE_COUNT_LIMIT)
@@ -818,56 +1077,16 @@ TypeObject::incrementTenureCount()
 
     flags = (flags & ~OBJECT_FLAG_TENURE_COUNT_MASK)
           | ((count + 1) << OBJECT_FLAG_TENURE_COUNT_SHIFT);
 
     return count >= MaxJITAllocTenures;
 }
 
 bool
-HeapTypeSet::isConfiguredProperty(JSContext *cx, TypeObject *object)
-{
-    /*
-     * Everywhere compiled code depends on definite properties associated with
-     * a type object's newScript, we need to make sure there are constraints
-     * in place which will mark those properties as configured should the
-     * definite properties be invalidated.
-     */
-    if (object->flags & OBJECT_FLAG_NEW_SCRIPT_REGENERATE) {
-        object->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
-        if (object->hasNewScript()) {
-            Rooted<TypeObject*> typeObj(cx, object);
-            RootedFunction fun(cx, object->newScript()->fun);
-            CheckNewScriptProperties(cx, typeObj, fun);
-        } else {
-            JS_ASSERT(object->flags & OBJECT_FLAG_ADDENDUM_CLEARED);
-            object->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
-        }
-    }
-
-    if (configuredProperty())
-        return true;
-
-    add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeConfiguredProperty>(
-                cx->compartment()->types.compiledInfo), false);
-    return false;
-}
-
-bool
-HeapTypeSet::knownNonEmpty(JSContext *cx)
-{
-    if (baseFlags() != 0 || baseObjectCount() != 0)
-        return true;
-
-    addFreeze(cx);
-
-    return false;
-}
-
-bool
 TemporaryTypeSet::filtersType(const TemporaryTypeSet *other, Type filteredType) const
 {
     if (other->unknown())
         return unknown();
 
     for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) {
         Type type = Type::PrimitiveType(TypeFlagPrimitive(flag));
         if (type != filteredType && other->hasType(type) && !hasType(type))
@@ -885,63 +1104,55 @@ TemporaryTypeSet::filtersType(const Temp
                 return false;
         }
     }
 
     return true;
 }
 
 TemporaryTypeSet::DoubleConversion
-TemporaryTypeSet::convertDoubleElements(JSContext *cx)
+TemporaryTypeSet::convertDoubleElements(CompilerConstraintList *constraints)
 {
     if (unknownObject() || !getObjectCount())
         return AmbiguousDoubleConversion;
 
     bool alwaysConvert = true;
     bool maybeConvert = false;
     bool dontConvert = false;
 
     for (unsigned i = 0; i < getObjectCount(); i++) {
-        TypeObject *type = getTypeObject(i);
-        if (!type) {
-            if (JSObject *obj = getSingleObject(i)) {
-                type = obj->getType(cx);
-                if (!type)
-                    return AmbiguousDoubleConversion;
-            } else {
-                continue;
-            }
-        }
+        TypeObjectKey *type = getObject(i);
+        if (!type)
+            continue;
 
         if (type->unknownProperties()) {
             alwaysConvert = false;
             continue;
         }
 
-        HeapTypeSet *types = type->getProperty(cx, JSID_VOID);
-        if (!types)
-            return AmbiguousDoubleConversion;
-
-        types->addFreeze(cx);
+        HeapTypeSetKey property = type->property(JSID_VOID);
+        property.freeze(constraints);
 
         // We can't convert to double elements for objects which do not have
         // double in their element types (as the conversion may render the type
         // information incorrect), nor for non-array objects (as their elements
         // may point to emptyObjectElements, which cannot be converted).
-        if (!types->hasType(Type::DoubleType()) || type->clasp != &ArrayObject::class_) {
+        if (!property.actualTypes->hasType(Type::DoubleType()) ||
+            type->clasp() != &ArrayObject::class_)
+        {
             dontConvert = true;
             alwaysConvert = false;
             continue;
         }
 
         // Only bother with converting known packed arrays whose possible
         // element types are int or double. Other arrays require type tests
         // when elements are accessed regardless of the conversion.
-        if (types->getKnownTypeTag(cx) == JSVAL_TYPE_DOUBLE &&
-            !HeapTypeSet::HasObjectFlags(cx, type, OBJECT_FLAG_NON_PACKED))
+        if (property.knownTypeTag(constraints) == JSVAL_TYPE_DOUBLE &&
+            !type->hasFlags(constraints, OBJECT_FLAG_NON_PACKED))
         {
             maybeConvert = true;
         } else {
             alwaysConvert = false;
         }
     }
 
     JS_ASSERT_IF(alwaysConvert, maybeConvert);
@@ -950,27 +1161,16 @@ TemporaryTypeSet::convertDoubleElements(
         return AmbiguousDoubleConversion;
     if (alwaysConvert)
         return AlwaysConvertToDoubles;
     if (maybeConvert)
         return MaybeConvertToDoubles;
     return DontConvertToDoubles;
 }
 
-bool
-HeapTypeSet::knownSubset(JSContext *cx, HeapTypeSet *other)
-{
-    if (!isSubset(other))
-        return false;
-
-    addFreeze(cx);
-
-    return true;
-}
-
 const Class *
 TemporaryTypeSet::getKnownClass()
 {
     if (unknownObject())
         return NULL;
 
     const Class *clasp = NULL;
     unsigned count = getObjectCount();
@@ -1081,71 +1281,33 @@ TemporaryTypeSet::getCommonPrototype()
                 return NULL;
             proto = nproto.toObject();
         }
     }
 
     return proto;
 }
 
-JSObject *
-TemporaryTypeSet::getSingleton()
-{
-    if (baseFlags() != 0 || baseObjectCount() != 1)
-        return NULL;
-
-    return getSingleObject(0);
-}
-
-JSObject *
-HeapTypeSet::getSingleton(JSContext *cx)
+bool
+TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList *constraints, jsid id)
 {
-    if (baseFlags() != 0 || baseObjectCount() != 1)
-        return NULL;
-
-    RootedObject obj(cx, getSingleObject(0));
-
-    if (obj)
-        addFreeze(cx);
-
-    return obj;
-}
-
-bool
-HeapTypeSet::needsBarrier(JSContext *cx)
-{
-    bool result = unknownObject()
-               || getObjectCount() > 0
-               || hasAnyFlag(TYPE_FLAG_STRING);
-    if (!result)
-        addFreeze(cx);
-    return result;
-}
-
-bool
-TemporaryTypeSet::propertyNeedsBarrier(JSContext *cx, jsid id)
-{
-    RootedId typeId(cx, IdToTypeId(id));
-
     if (unknownObject())
         return true;
 
     for (unsigned i = 0; i < getObjectCount(); i++) {
-        if (getSingleObject(i))
+        TypeObjectKey *type = getObject(i);
+        if (!type)
+            continue;
+
+        if (type->unknownProperties())
             return true;
 
-        if (types::TypeObject *otype = getTypeObject(i)) {
-            if (otype->unknownProperties())
-                return true;
-
-            if (types::HeapTypeSet *propTypes = otype->maybeGetProperty(cx, typeId)) {
-                if (propTypes->needsBarrier(cx))
-                    return true;
-            }
-        }
+        HeapTypeSetKey property = type->property(id);
+        if (property.needsBarrier(constraints))
+            return true;
     }
 
     return false;
 }
 
 /*
  * Force recompilation of any jitcode for the script, or of any other script
  * which this script was inlined into.
@@ -1188,17 +1350,16 @@ class TypeConstraintFreezeStack : public
 
 /////////////////////////////////////////////////////////////////////
 // TypeCompartment
 /////////////////////////////////////////////////////////////////////
 
 TypeCompartment::TypeCompartment()
 {
     PodZero(this);
-    compiledInfo.outputIndex = RecompileInfo::NoCompilerRunning;
 }
 
 void
 TypeZone::init(JSContext *cx)
 {
     if (!cx ||
         !cx->hasOption(JSOPTION_TYPE_INFERENCE) ||
         !cx->runtime()->jitSupportsFloatingPoint)
@@ -1466,67 +1627,64 @@ types::UseNewTypeForInitializer(JSScript
 static inline bool
 ClassCanHaveExtraProperties(const Class *clasp)
 {
     JS_ASSERT(clasp->resolve);
     return clasp->resolve != JS_ResolveStub || clasp->ops.lookupGeneric || clasp->ops.getGeneric;
 }
 
 static inline bool
-PrototypeHasIndexedProperty(JSContext *cx, JSObject *obj)
+PrototypeHasIndexedProperty(CompilerConstraintList *constraints, JSObject *obj)
 {
     do {
-        TypeObject *type = obj->getType(cx);
-        if (!type)
-            return true;
-        if (ClassCanHaveExtraProperties(type->clasp))
+        TypeObjectKey *type = TypeObjectKey::get(obj);
+        if (ClassCanHaveExtraProperties(type->clasp()))
             return true;
         if (type->unknownProperties())
             return true;
-        HeapTypeSet *indexTypes = type->getProperty(cx, JSID_VOID);
-        if (!indexTypes || indexTypes->isConfiguredProperty(cx, type) || indexTypes->knownNonEmpty(cx))
+        HeapTypeSetKey index = type->property(JSID_VOID);
+        if (index.configured(constraints, type) || index.notEmpty(constraints))
             return true;
         obj = obj->getProto();
     } while (obj);
 
     return false;
 }
 
 bool
-types::ArrayPrototypeHasIndexedProperty(JSContext *cx, HandleScript script)
+types::ArrayPrototypeHasIndexedProperty(CompilerConstraintList *constraints,
+                                        HandleScript script)
 {
-    if (!cx->typeInferenceEnabled() || !script->compileAndGo)
-        return true;
-
-    JSObject *proto = script->global().getOrCreateArrayPrototype(cx);
+    JSObject *proto = script->global().getOrCreateArrayPrototype(jit::GetIonContext()->cx);
     if (!proto)
         return true;
 
-    return PrototypeHasIndexedProperty(cx, proto);
+    return PrototypeHasIndexedProperty(constraints, proto);
 }
 
 bool
-types::TypeCanHaveExtraIndexedProperties(JSContext *cx, TemporaryTypeSet *types)
+types::TypeCanHaveExtraIndexedProperties(CompilerConstraintList *constraints,
+                                         TemporaryTypeSet *types)
 {
     const Class *clasp = types->getKnownClass();
 
     // Note: typed arrays have indexed properties not accounted for by type
     // information, though these are all in bounds and will be accounted for
     // by JIT paths.
     if (!clasp || (ClassCanHaveExtraProperties(clasp) && !IsTypedArrayClass(clasp)))
         return true;
 
-    if (types->hasObjectFlags(cx, types::OBJECT_FLAG_SPARSE_INDEXES))
+    if (types->hasObjectFlags(constraints, types::OBJECT_FLAG_SPARSE_INDEXES))
         return true;
 
     JSObject *proto = types->getCommonPrototype();
     if (!proto)
         return true;
 
-    return PrototypeHasIndexedProperty(cx, proto);
+    return PrototypeHasIndexedProperty(constraints, proto);
 }
 
 bool
 TypeCompartment::growPendingArray(JSContext *cx)
 {
     unsigned newCapacity = js::Max(unsigned(100), pendingCapacity * 2);
     PendingWork *newArray = js_pod_calloc<PendingWork>(newCapacity);
     if (!newArray) {
@@ -1617,68 +1775,36 @@ TypeZone::nukeTypes(FreeOp *fop)
 
     pendingNukeTypes = false;
 }
 
 void
 TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
 {
     CompilerOutput *co = info.compilerOutput(cx);
-    if (!co)
-        return;
-
-    if (co->pendingRecompilation)
+    if (!co || !co->isValid() || co->pendingInvalidation())
         return;
 
-    if (co->isValid())
-        CancelOffThreadIonCompile(cx->compartment(), co->script);
-
-    if (compiledInfo.outputIndex == info.outputIndex) {
-        /* Tell Ion to discard generated code when it's done. */
-        JS_ASSERT(compiledInfo.outputIndex != RecompileInfo::NoCompilerRunning);
-        JS_ASSERT(co->kind() == CompilerOutput::Ion || co->kind() == CompilerOutput::ParallelIon);
-        co->invalidate();
-        return;
-    }
-
-    if (!co->isValid()) {
-        JS_ASSERT(co->script == NULL);
-        return;
-    }
-
-#if defined(JS_ION)
-    if (!co->script->hasAnyIonScript()) {
-        /* Scripts which haven't been compiled yet don't need to be recompiled. */
-        return;
-    }
-#endif
-
     if (!pendingRecompiles) {
         pendingRecompiles = cx->new_< Vector<RecompileInfo> >(cx);
         if (!pendingRecompiles) {
             cx->compartment()->types.setPendingNukeTypes(cx);
             return;
         }
     }
 
-#if DEBUG
-    for (size_t i = 0; i < pendingRecompiles->length(); i++) {
-        RecompileInfo pr = (*pendingRecompiles)[i];
-        JS_ASSERT(info.outputIndex != pr.outputIndex);
-    }
-#endif
-
     if (!pendingRecompiles->append(info)) {
         cx->compartment()->types.setPendingNukeTypes(cx);
         return;
     }
 
-    InferSpew(ISpewOps, "addPendingRecompile: %p:%s:%d", co->script, co->script->filename(), co->script->lineno);
-
-    co->setPendingRecompilation();
+    InferSpew(ISpewOps, "addPendingRecompile: %p:%s:%d",
+              co->script(), co->script()->filename(), co->script()->lineno);
+
+    co->setPendingInvalidation();
 }
 
 void
 TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script)
 {
     JS_ASSERT(script);
     if (!constrainedOutputs)
         return;
@@ -1692,40 +1818,19 @@ TypeCompartment::addPendingRecompile(JSC
 
     if (script->hasIonScript())
         addPendingRecompile(cx, script->ionScript()->recompileInfo());
 
     if (script->hasParallelIonScript())
         addPendingRecompile(cx, script->parallelIonScript()->recompileInfo());
 #endif
 
-    /*
-     * Remind Ion not to save the compile code if generating type
-     * inference information mid-compilation causes an invalidation of the
-     * script being compiled.
-     */
-    if (compiledInfo.outputIndex != RecompileInfo::NoCompilerRunning) {
-        CompilerOutput *co = compiledInfo.compilerOutput(cx);
-        if (!co) {
-            if (script->compartment() != cx->compartment())
-                MOZ_CRASH();
-            return;
-        }
-
-        JS_ASSERT(co->kind() == CompilerOutput::Ion || co->kind() == CompilerOutput::ParallelIon);
-
-        if (co->script == script)
-            co->invalidate();
-    }
-
-    /*
-     * When one script is inlined into another the caller listens to state
-     * changes on the callee's script, so trigger these to force recompilation
-     * of any such callers.
-     */
+    // When one script is inlined into another the caller listens to state
+    // changes on the callee's script, so trigger these to force recompilation
+    // of any such callers.
     if (script->function() && !script->function()->hasLazyType())
         ObjectStateChange(cx, script->function()->type(), false, true);
 }
 
 void
 TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target)
 {
     JS_ASSERT(this == &cx->compartment()->types);
@@ -2776,18 +2881,20 @@ types::AddClearDefiniteFunctionUsesInScr
 }
 
 /*
  * Either make the newScript information for type when it is constructed
  * by the specified script, or regenerate the constraints for an existing
  * newScript on the type after they were cleared by a GC.
  */
 static void
-CheckNewScriptProperties(JSContext *cx, HandleTypeObject type, HandleFunction fun)
+CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun)
 {
+    JS_ASSERT(cx->compartment()->activeAnalysis);
+
 #ifdef JS_ION
     if (type->unknownProperties())
         return;
 
     /* Strawman object to add properties to and watch for duplicates. */
     RootedObject baseobj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, gc::FINALIZE_OBJECT16));
     if (!baseobj)
         return;
@@ -2827,18 +2934,19 @@ CheckNewScriptProperties(JSContext *cx, 
 
     TypeNewScript::Initializer done(TypeNewScript::Initializer::DONE, 0);
 
     /*
      * The base object may have been created with a different finalize kind
      * than we will use for subsequent new objects. Generate an object with the
      * appropriate final shape.
      */
+    Rooted<TypeObject *> rootedType(cx, type);
     RootedShape shape(cx, baseobj->lastProperty());
-    baseobj = NewReshapedObject(cx, type, baseobj->getParent(), kind, shape);
+    baseobj = NewReshapedObject(cx, rootedType, baseobj->getParent(), kind, shape);
     if (!baseobj ||
         !type->addDefiniteProperties(cx, baseobj) ||
         !initializerList.append(done))
     {
         cx->compartment()->types.setPendingNukeTypes(cx);
         return;
     }
 
@@ -3701,18 +3809,16 @@ TypeCompartment::sweep(FreeOp *fop)
      * The pending array is reset on GC, it can grow large (75+ KB) and is easy
      * to reallocate if the compartment becomes active again.
      */
     if (pendingArray)
         fop->free_(pendingArray);
 
     pendingArray = NULL;
     pendingCapacity = 0;
-
-    sweepCompilerOutputs(fop, true);
 }
 
 void
 TypeCompartment::sweepShapes(FreeOp *fop)
 {
     /*
      * Sweep any weak shape references that may be finalized even if a GC is
      * preserving type information.
@@ -3727,46 +3833,25 @@ TypeCompartment::sweepShapes(FreeOp *fop
                 fop->free_(entry.types);
                 e.removeFront();
             }
         }
     }
 }
 
 void
-TypeCompartment::sweepCompilerOutputs(FreeOp *fop, bool discardConstraints)
+TypeCompartment::clearCompilerOutputs(FreeOp *fop)
 {
     if (constrainedOutputs) {
-        if (discardConstraints) {
-            JS_ASSERT(compiledInfo.outputIndex == RecompileInfo::NoCompilerRunning);
-#if DEBUG
-            for (unsigned i = 0; i < constrainedOutputs->length(); i++) {
-                CompilerOutput &co = (*constrainedOutputs)[i];
-                JS_ASSERT(!co.isValid());
-            }
-#endif
-
-            fop->delete_(constrainedOutputs);
-            constrainedOutputs = NULL;
-        } else {
-            // Constraints have captured an index to the constrained outputs
-            // vector.  Thus, we invalidate all compilations except the one
-            // which is potentially running now.
-            size_t len = constrainedOutputs->length();
-            for (unsigned i = 0; i < len; i++) {
-                if (i != compiledInfo.outputIndex) {
-                    CompilerOutput &co = (*constrainedOutputs)[i];
-                    JS_ASSERT(!co.isValid());
-                    co.invalidate();
-                }
-            }
-        }
+        fop->delete_(constrainedOutputs);
+        constrainedOutputs = NULL;
     }
 
     if (pendingRecompiles) {
+        JS_ASSERT(pendingRecompiles->length() == 0);
         fop->delete_(pendingRecompiles);
         pendingRecompiles = NULL;
     }
 }
 
 void
 JSCompartment::sweepNewTypeObjectTable(TypeObjectSet &table)
 {
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -99,32 +99,42 @@ class RootedBase<TaggedProto> : public T
         return static_cast<const Rooted<TaggedProto> *>(this)->address();
     }
 };
 
 class CallObject;
 
 namespace jit {
     struct IonScript;
+    class IonAllocPolicy;
+
+    enum ExecutionMode {
+        // Normal JavaScript execution
+        SequentialExecution,
+
+        // JavaScript code to be executed in parallel worker threads,
+        // e.g. by ParallelArray
+        ParallelExecution,
+
+        // MIR analysis performed when invoking 'new' on a script, to determine
+        // definite properties
+        DefinitePropertiesAnalysis
+    };
 }
 
 namespace analyze {
     class ScriptAnalysis;
 }
 
 namespace types {
 
 class TypeCompartment;
 class TypeSet;
 
-/* Type set entry for either a JSObject with singleton type or a non-singleton TypeObject. */
-struct TypeObjectKey {
-    static intptr_t keyBits(TypeObjectKey *obj) { return (intptr_t) obj; }
-    static TypeObjectKey *getKey(TypeObjectKey *obj) { return obj; }
-};
+struct TypeObjectKey;
 
 /*
  * Information about a single concrete type. We pack this into a single word,
  * where small values are particular primitive or other singleton types, and
  * larger values are either specific JS objects or type objects.
  */
 class Type
 {
@@ -255,17 +265,17 @@ public:
     /* Debugging name for this kind of constraint. */
     virtual const char *kind() = 0;
 
     /* Register a new type for the set this constraint is listening to. */
     virtual void newType(JSContext *cx, TypeSet *source, Type type) = 0;
 
     /*
      * For constraints attached to an object property's type set, mark the
-     * property as having been configured or received an own property.
+     * property as having been configured.
      */
     virtual void newPropertyState(JSContext *cx, TypeSet *source) {}
 
     /*
      * For constraints attached to the JSID_EMPTY type set on an object, mark a
      * change in one of the object's dynamic property flags. If force is set,
      * recompilation is always triggered.
      */
@@ -548,57 +558,19 @@ class StackTypeSet : public TypeSet
   public:
     StackTypeSet() { flags |= TYPE_FLAG_STACK_SET; }
 };
 
 class HeapTypeSet : public TypeSet
 {
   public:
     HeapTypeSet() { flags |= TYPE_FLAG_HEAP_SET; }
-
-    /* Completely freeze the contents of this type set. */
-    void addFreeze(JSContext *cx);
-
-    /*
-     * Watch for a generic object state change on a type object. This currently
-     * includes reallocations of slot pointers for global objects, and changes
-     * to newScript data on types.
-     */
-    static void WatchObjectStateChange(JSContext *cx, TypeObject *object);
-
-    /* Whether an object has any of a set of flags. */
-    static bool HasObjectFlags(JSContext *cx, TypeObject *object, TypeObjectFlags flags);
+};
 
-    /*
-     * For type sets on a property, return true if the property has any 'own'
-     * values assigned. If configurable is set, return 'true' if the property
-     * has additionally been reconfigured as non-configurable, non-enumerable
-     * or non-writable (this only applies to properties that have changed after
-     * having been created, not to e.g. properties non-writable on creation).
-     */
-    bool isConfiguredProperty(JSContext *cx, TypeObject *object);
-
-    /* Get whether this type set is non-empty. */
-    bool knownNonEmpty(JSContext *cx);
-
-    /* Get whether this type set is known to be a subset of other. */
-    bool knownSubset(JSContext *cx, HeapTypeSet *other);
-
-    /* Get the single value which can appear in this type set, otherwise NULL. */
-    JSObject *getSingleton(JSContext *cx);
-
-    /*
-     * Whether a location with this TypeSet needs a write barrier (i.e., whether
-     * it can hold GC things). The type set is frozen if no barrier is needed.
-     */
-    bool needsBarrier(JSContext *cx);
-
-    /* Get any type tag which all values in this set must have. */
-    JSValueType getKnownTypeTag(JSContext *cx);
-};
+class CompilerConstraintList;
 
 class TemporaryTypeSet : public TypeSet
 {
   public:
     TemporaryTypeSet() {}
     TemporaryTypeSet(Type type);
 
     TemporaryTypeSet(uint32_t flags, TypeObjectKey **objectSet) {
@@ -639,17 +611,17 @@ class TemporaryTypeSet : public TypeSet
         TypeFlags flags = TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL | TYPE_FLAG_ANYOBJECT;
         if (baseFlags() & (~flags & TYPE_FLAG_BASE_MASK))
             return false;
 
         return hasAnyFlag(TYPE_FLAG_ANYOBJECT) || baseObjectCount() > 0;
     }
 
     /* Whether the type set contains objects with any of a set of flags. */
-    bool hasObjectFlags(JSContext *cx, TypeObjectFlags flags);
+    bool hasObjectFlags(CompilerConstraintList *constraints, TypeObjectFlags flags);
 
     /* Get the class shared by all objects in this set, or NULL. */
     const Class *getKnownClass();
 
     /* Get the prototype shared by all objects in this set, or NULL. */
     JSObject *getCommonPrototype();
 
     /* Get the typed array type of all objects in this set, or TypedArrayObject::TYPE_MAX. */
@@ -663,17 +635,17 @@ class TemporaryTypeSet : public TypeSet
 
     /* Whether clasp->emulatesUndefined() is true for one or more objects in this set. */
     bool maybeEmulatesUndefined();
 
     /* Get the single value which can appear in this type set, otherwise NULL. */
     JSObject *getSingleton();
 
     /* Whether any objects in the type set needs a barrier on id. */
-    bool propertyNeedsBarrier(JSContext *cx, jsid id);
+    bool propertyNeedsBarrier(CompilerConstraintList *constraints, jsid id);
 
     /*
      * Whether this set contains all types in other, except (possibly) the
      * specified type.
      */
     bool filtersType(const TemporaryTypeSet *other, Type type) const;
 
     enum DoubleConversion {
@@ -689,17 +661,17 @@ class TemporaryTypeSet : public TypeSet
         /* Some types should use eager double conversion, others cannot. */
         AmbiguousDoubleConversion
     };
 
     /*
      * Whether known double optimizations are possible for element accesses on
      * objects in this type set.
      */
-    DoubleConversion convertDoubleElements(JSContext *cx);
+    DoubleConversion convertDoubleElements(CompilerConstraintList *constraints);
 };
 
 inline StackTypeSet *
 TypeSet::toStackSet()
 {
     JS_ASSERT(isStackSet());
     return (StackTypeSet *) this;
 }
@@ -988,25 +960,16 @@ struct TypeObject : gc::BarrieredCell<Ty
     inline HeapTypeSet *getProperty(ExclusiveContext *cx, jsid id);
 
     /* Get a property only if it already exists. */
     inline HeapTypeSet *maybeGetProperty(ExclusiveContext *cx, jsid id);
 
     inline unsigned getPropertyCount();
     inline Property *getProperty(unsigned i);
 
-    /* Get the typed array element type if clasp is a typed array. */
-    inline int getTypedArrayType();
-
-    /*
-     * Get the global object which all objects of this type are parented to,
-     * or NULL if there is none known.
-     */
-    //inline JSObject *getGlobal();
-
     /* Tenure counter management. */
 
     /*
      * When an object allocation site generates objects that are long lived
      * enough to frequently be tenured during minor collections, we mark the
      * site as long lived and allocate directly into the tenured generation.
      */
     const static uint32_t MaxJITAllocTenures = OBJECT_FLAG_TENURE_COUNT_LIMIT - 2;
@@ -1114,21 +1077,21 @@ UseNewType(JSContext *cx, JSScript *scri
 bool
 UseNewTypeForClone(JSFunction *fun);
 
 /*
  * Whether Array.prototype, or an object on its proto chain, has an
  * indexed property.
  */
 bool
-ArrayPrototypeHasIndexedProperty(JSContext *cx, HandleScript script);
+ArrayPrototypeHasIndexedProperty(CompilerConstraintList *constraints, HandleScript script);
 
 /* Whether obj or any of its prototypes have an indexed property. */
 bool
-TypeCanHaveExtraIndexedProperties(JSContext *cx, TemporaryTypeSet *types);
+TypeCanHaveExtraIndexedProperties(CompilerConstraintList *constraints, TemporaryTypeSet *types);
 
 /* Persistent type information for a script, retained across GCs. */
 class TypeScript
 {
     friend class ::JSScript;
 
     /* Analysis information for the script, cleared on each GC. */
     analyze::ScriptAnalysis *analysis;
@@ -1199,66 +1162,126 @@ typedef HashMap<ArrayTableKey,ReadBarrie
 
 struct ObjectTableKey;
 struct ObjectTableEntry;
 typedef HashMap<ObjectTableKey,ObjectTableEntry,ObjectTableKey,SystemAllocPolicy> ObjectTypeTable;
 
 struct AllocationSiteKey;
 typedef HashMap<AllocationSiteKey,ReadBarriered<TypeObject>,AllocationSiteKey,SystemAllocPolicy> AllocationSiteTable;
 
+class HeapTypeSetKey;
+
+/* Type set entry for either a JSObject with singleton type or a non-singleton TypeObject. */
+struct TypeObjectKey {
+    static intptr_t keyBits(TypeObjectKey *obj) { return (intptr_t) obj; }
+    static TypeObjectKey *getKey(TypeObjectKey *obj) { return obj; }
+
+    static TypeObjectKey *get(JSObject *obj) {
+        JS_ASSERT(obj);
+        return (TypeObjectKey *) (uintptr_t(obj) | 1);
+    }
+    static TypeObjectKey *get(TypeObject *obj) {
+        JS_ASSERT(obj);
+        return (TypeObjectKey *) obj;
+    }
+
+    bool isTypeObject() {
+        return (uintptr_t(this) & 1) == 0;
+    }
+    bool isSingleObject() {
+        return (uintptr_t(this) & 1) != 0;
+    }
+
+    TypeObject *asTypeObject() {
+        JS_ASSERT(isTypeObject());
+        return (TypeObject *) this;
+    }
+    JSObject *asSingleObject() {
+        JS_ASSERT(isSingleObject());
+        return (JSObject *) (uintptr_t(this) & ~1);
+    }
+
+    const Class *clasp();
+    TaggedProto proto();
+    JSObject *singleton();
+    TypeNewScript *newScript();
+
+    bool unknownProperties();
+    bool hasFlags(CompilerConstraintList *constraints, TypeObjectFlags flags);
+    void watchStateChange(CompilerConstraintList *constraints);
+    HeapTypeSetKey property(jsid id);
+};
+
+class HeapTypeSetKey
+{
+  public:
+    HeapTypeSet *actualTypes;
+
+    void freeze(CompilerConstraintList *constraints);
+    JSValueType knownTypeTag(CompilerConstraintList *constraints);
+    bool configured(CompilerConstraintList *constraints, TypeObjectKey *type);
+    bool notEmpty(CompilerConstraintList *constraints);
+    bool knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other);
+    JSObject *singleton(CompilerConstraintList *constraints);
+    bool needsBarrier(CompilerConstraintList *constraints);
+};
+
 /*
  * Information about the result of the compilation of a script.  This structure
  * stored in the TypeCompartment is indexed by the RecompileInfo. This
- * indirection enable the invalidation of all constraints related to the same
- * compilation. The compiler output is build by the AutoEnterCompilation.
+ * indirection enables the invalidation of all constraints related to the same
+ * compilation.
  */
-struct CompilerOutput
+class CompilerOutput
 {
-    enum Kind {
-        Ion,
-        ParallelIon
-    };
+    // If this compilation has not been invalidated, the associated script and
+    // kind of compilation being performed.
+    JSScript *script_;
+    unsigned mode_ : 2;
 
-    JSScript *script;
+    // Whether this compilation is about to be invalidated.
+    bool pendingInvalidation_ : 1;
 
-    // This integer will always be a member of CompilerOutput::Kind,
-    // but, for portability, bitfields are limited to bool, int, and
-    // unsigned int.  You should really use the accessor below.
-    unsigned kindInt : 2;
-    bool pendingRecompilation : 1;
-
-    CompilerOutput();
+  public:
+    CompilerOutput()
+      : script_(NULL), mode_(0), pendingInvalidation_(false)
+    {}
 
-    Kind kind() const { return static_cast<Kind>(kindInt); }
-    void setKind(Kind k) { kindInt = k; }
-
-    jit::IonScript *ion() const;
+    CompilerOutput(JSScript *script, jit::ExecutionMode mode)
+      : script_(script), mode_(mode), pendingInvalidation_(false)
+    {}
 
-    bool isValid() const;
+    JSScript *script() const { return script_; }
+    inline jit::ExecutionMode mode() const { return static_cast<jit::ExecutionMode>(mode_); }
 
-    void setPendingRecompilation() {
-        pendingRecompilation = true;
+    inline jit::IonScript *ion() const;
+
+    bool isValid() const {
+        return script_ != NULL;
     }
     void invalidate() {
-        script = NULL;
+        script_ = NULL;
     }
-    bool isInvalidated() const {
-        return script == NULL;
+
+    void setPendingInvalidation() {
+        pendingInvalidation_ = true;
+    }
+    bool pendingInvalidation() {
+        return pendingInvalidation_;
     }
 };
 
-struct RecompileInfo
+class RecompileInfo
 {
-    static const uint32_t NoCompilerRunning = uint32_t(-1);
     uint32_t outputIndex;
 
-    RecompileInfo()
-      : outputIndex(NoCompilerRunning)
-    {
-    }
+  public:
+    RecompileInfo(uint32_t outputIndex = uint32_t(-1))
+      : outputIndex(outputIndex)
+    {}
 
     bool operator == (const RecompileInfo &o) const {
         return outputIndex == o.outputIndex;
     }
     CompilerOutput *compilerOutput(TypeCompartment &types) const;
     CompilerOutput *compilerOutput(JSContext *cx) const;
 };
 
@@ -1288,23 +1311,16 @@ struct TypeCompartment
     unsigned scriptCount;
 
     /* Valid & Invalid script referenced by type constraints. */
     Vector<CompilerOutput> *constrainedOutputs;
 
     /* Pending recompilations to perform before execution of JIT code can resume. */
     Vector<RecompileInfo> *pendingRecompiles;
 
-    /*
-     * Script currently being compiled. All constraints which look for type
-     * changes inducing recompilation are keyed to this script. Note: script
-     * compilation is not reentrant.
-     */
-    RecompileInfo compiledInfo;
-
     /* Table for referencing types of objects keyed to an allocation site. */
     AllocationSiteTable *allocationSiteTable;
 
     /* Tables for determining types of singleton/JSON objects. */
 
     ArrayTypeTable *arrayTypeTable;
     ObjectTypeTable *objectTypeTable;
 
@@ -1354,17 +1370,17 @@ struct TypeCompartment
     void addPendingRecompile(JSContext *cx, const RecompileInfo &info);
     void addPendingRecompile(JSContext *cx, JSScript *script);
 
     /* Mark any type set containing obj as having a generic object type. */
     void markSetsUnknown(JSContext *cx, TypeObject *obj);
 
     void sweep(FreeOp *fop);
     void sweepShapes(FreeOp *fop);
-    void sweepCompilerOutputs(FreeOp *fop, bool discardConstraints);
+    void clearCompilerOutputs(FreeOp *fop);
 
     void finalizeObjects();
 
     void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
                                 size_t *pendingArrays,
                                 size_t *allocationSiteTables,
                                 size_t *arrayTypeTables,
                                 size_t *objectTypeTables);
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -11,16 +11,17 @@
 
 #include "jsinfer.h"
 
 #include "mozilla/PodOperations.h"
 
 #include "jsanalyze.h"
 
 #include "builtin/ParallelArray.h"
+#include "jit/ExecutionModeInlines.h"
 #include "vm/ArrayObject.h"
 #include "vm/BooleanObject.h"
 #include "vm/NumberObject.h"
 #include "vm/StringObject.h"
 #include "vm/TypedArrayObject.h"
 
 #include "jsanalyzeinlines.h"
 #include "jscntxtinlines.h"
@@ -82,73 +83,31 @@ js::TaggedProtoOperations<Outer>::toObje
 
 namespace js {
 namespace types {
 
 /////////////////////////////////////////////////////////////////////
 // CompilerOutput & RecompileInfo
 /////////////////////////////////////////////////////////////////////
 
-inline
-CompilerOutput::CompilerOutput()
-  : script(NULL),
-    kindInt(Ion),
-    pendingRecompilation(false)
-{
-}
-
 inline jit::IonScript *
 CompilerOutput::ion() const
 {
 #ifdef JS_ION
+    // Note: If type constraints are generated before compilation has finished
+    // (i.e. after IonBuilder but before CodeGenerator::link) then a valid
+    // CompilerOutput may not yet have an associated IonScript.
     JS_ASSERT(isValid());
-    switch (kind()) {
-      case Ion: return script->ionScript();
-      case ParallelIon: return script->parallelIonScript();
-    }
+    jit::IonScript *ion = jit::GetIonScript(script(), mode());
+    JS_ASSERT(ion != ION_COMPILING_SCRIPT);
+    return ion;
 #endif
     MOZ_ASSUME_UNREACHABLE("Invalid kind of CompilerOutput");
 }
 
-inline bool
-CompilerOutput::isValid() const
-{
-    if (!script)
-        return false;
-
-#if defined(DEBUG) && defined(JS_ION)
-    TypeCompartment &types = script->compartment()->types;
-#endif
-
-    switch (kind()) {
-      case Ion:
-#ifdef JS_ION
-        if (script->hasIonScript()) {
-            JS_ASSERT(this == script->ionScript()->recompileInfo().compilerOutput(types));
-            return true;
-        }
-        if (script->isIonCompilingOffThread())
-            return true;
-#endif
-        return false;
-
-      case ParallelIon:
-#ifdef JS_ION
-        if (script->hasParallelIonScript()) {
-            JS_ASSERT(this == script->parallelIonScript()->recompileInfo().compilerOutput(types));
-            return true;
-        }
-        if (script->isParallelIonCompilingOffThread())
-            return true;
-#endif
-        return false;
-    }
-    return false;
-}
-
 inline CompilerOutput*
 RecompileInfo::compilerOutput(TypeCompartment &types) const
 {
     if (!types.constrainedOutputs || outputIndex >= types.constrainedOutputs->length())
         return NULL;
     return &(*types.constrainedOutputs)[outputIndex];
 }
 
@@ -344,85 +303,16 @@ struct AutoEnterAnalysis
     void init(FreeOp *fop, JSCompartment *comp) {
         freeOp = fop;
         compartment = comp;
         oldActiveAnalysis = compartment->activeAnalysis;
         compartment->activeAnalysis = true;
     }
 };
 
-/*
- * Structure marking the currently compiled script, for constraints which can
- * trigger recompilation.
- */
-struct AutoEnterCompilation
-{
-    JSContext *cx;
-    RecompileInfo &info;
-    CompilerOutput::Kind kind;
-
-    AutoEnterCompilation(JSContext *cx, CompilerOutput::Kind kind)
-      : cx(cx),
-        info(cx->compartment()->types.compiledInfo),
-        kind(kind)
-    {
-        JS_ASSERT(cx->compartment()->activeAnalysis);
-        JS_ASSERT(info.outputIndex == RecompileInfo::NoCompilerRunning);
-    }
-
-    bool init(JSScript *script)
-    {
-        CompilerOutput co;
-        co.script = script;
-        co.setKind(kind);
-
-        JS_ASSERT(!co.isValid());
-        TypeCompartment &types = cx->compartment()->types;
-        if (!types.constrainedOutputs) {
-            types.constrainedOutputs = cx->new_< Vector<CompilerOutput> >(cx);
-            if (!types.constrainedOutputs) {
-                types.setPendingNukeTypes(cx);
-                return false;
-            }
-        }
-
-        info.outputIndex = types.constrainedOutputs->length();
-        // I hope we GC before we reach 64k of compilation attempts.
-        if (info.outputIndex >= RecompileInfo::NoCompilerRunning)
-            return false;
-
-        if (!types.constrainedOutputs->append(co)) {
-            info.outputIndex = RecompileInfo::NoCompilerRunning;
-            return false;
-        }
-        return true;
-    }
-
-    void initExisting(RecompileInfo oldInfo)
-    {
-        // Initialize the active compilation index from that produced during a
-        // previous compilation, for finishing an off thread compilation.
-        info = oldInfo;
-    }
-
-    ~AutoEnterCompilation()
-    {
-        // Handle failure cases of init.
-        if (info.outputIndex >= RecompileInfo::NoCompilerRunning)
-            return;
-
-        JS_ASSERT(info.outputIndex < cx->compartment()->types.constrainedOutputs->length());
-        CompilerOutput *co = info.compilerOutput(cx);
-        if (!co->isValid())
-            co->invalidate();
-
-        info.outputIndex = RecompileInfo::NoCompilerRunning;
-    }
-};
-
 /////////////////////////////////////////////////////////////////////
 // Interface functions
 /////////////////////////////////////////////////////////////////////
 
 /*
  * These functions check whether inference is enabled before performing some
  * action on the type state. To avoid checking cx->typeInferenceEnabled()
  * everywhere, it is generally preferred to use one of these functions or
@@ -1310,24 +1200,24 @@ TypeSet::getObject(unsigned i) const
     }
     return objectSet[i];
 }
 
 inline JSObject *
 TypeSet::getSingleObject(unsigned i) const
 {
     TypeObjectKey *key = getObject(i);
-    return (uintptr_t(key) & 1) ? (JSObject *)(uintptr_t(key) ^ 1) : NULL;
+    return (key && key->isSingleObject()) ? key->asSingleObject() : NULL;
 }
 
 inline TypeObject *
 TypeSet::getTypeObject(unsigned i) const
 {
     TypeObjectKey *key = getObject(i);
-    return (key && !(uintptr_t(key) & 1)) ? (TypeObject *) key : NULL;
+    return (key && key->isTypeObject()) ? key->asTypeObject() : NULL;
 }
 
 inline bool
 TypeSet::getTypeOrSingleObject(JSContext *cx, unsigned i, TypeObject **result) const
 {
     JS_ASSERT(result);
     JS_ASSERT(cx->compartment()->activeAnalysis);
 
@@ -1465,24 +1355,16 @@ TypeObject::getProperty(unsigned i)
     JS_ASSERT(i < getPropertyCount());
     if (basePropertyCount() == 1) {
         JS_ASSERT(i == 0);
         return (Property *) propertySet;
     }
     return propertySet[i];
 }
 
-inline int
-TypeObject::getTypedArrayType()
-{
-    if (IsTypedArrayClass(clasp))
-        return clasp - &TypedArrayObject::classes[0];
-    return ScalarTypeRepresentation::TYPE_MAX;
-}
-
 inline void
 TypeObjectAddendum::writeBarrierPre(TypeObjectAddendum *type)
 {
 #ifdef JSGC_INCREMENTAL
     if (!type)
         return;
 
     switch (type->kind) {
@@ -1505,16 +1387,54 @@ TypeNewScript::writeBarrierPre(TypeNewSc
     JS::Zone *zone = newScript->fun->zone();
     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, jit::ExecutionMode executionMode,
+                  CompilerConstraintList *constraints, RecompileInfo *precompileInfo);
+
+class CompilerConstraint;
+class CompilerConstraintList
+{
+    // Generated constraints.
+    Vector<CompilerConstraint *, 0, jit::IonAllocPolicy> constraints;
+
+    // OOM during generation of some constraint.
+    bool failed_;
+
+  public:
+    CompilerConstraintList()
+      : failed_(false)
+    {}
+
+    void add(CompilerConstraint *constraint);
+
+    size_t length() {
+        return constraints.length();
+    }
+    CompilerConstraint *get(size_t i) {
+        return constraints[i];
+    }
+
+    bool failed() {
+        return failed_;
+    }
+    void setFailed() {
+        failed_ = true;
+    }
+};
+
 } } /* namespace js::types */
 
 inline bool
 JSScript::ensureHasTypes(JSContext *cx)
 {
     return types || makeTypes(cx);
 }