[INFER] Track multiple types when monitoring SETPROP/SETNAME and calls, bug 641714.
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 15 Mar 2011 23:50:44 -0700
changeset 74778 763f423cce2c736d520a95bf14adc1279117482f
parent 74775 eb9047c955c89949453aca74d8494722e2778fed
child 74779 9de854ef345f0646d5acc0aa0a9dc25161c3dbc4
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
bugs641714
milestone2.0b13pre
[INFER] Track multiple types when monitoring SETPROP/SETNAME and calls, bug 641714.
js/src/jscntxt.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsscript.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/MonoIC.cpp
js/src/methodjit/MonoIC.h
js/src/methodjit/PolyIC.cpp
js/src/methodjit/PolyIC.h
js/src/methodjit/StubCalls.h
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -2168,16 +2168,17 @@ public:
     /* Monitor an assignment made to a property by a script. */
     inline bool typeMonitorAssign(JSObject *obj, jsid id, const js::Value &value);
 
     /* Add a possible value for the named property of obj. */
     inline bool addTypeProperty(js::types::TypeObject *obj, const char *name, js::types::jstype type);
     inline bool addTypeProperty(js::types::TypeObject *obj, const char *name, const js::Value &value);
     inline bool addTypePropertyId(js::types::TypeObject *obj, jsid id, js::types::jstype type);
     inline bool addTypePropertyId(js::types::TypeObject *obj, jsid id, const js::Value &value);
+    inline bool addTypePropertyId(js::types::TypeObject *obj, jsid id, js::types::ClonedTypeSet *types);
 
     /* Get the type to use for objects with no prototype. */
     inline js::types::TypeObject *getTypeEmpty();
 
     /* Alias two properties in the type information for obj. */
     inline bool aliasTypeProperties(js::types::TypeObject *obj, jsid first, jsid second);
 
     /* Mark an array type as being not packed and, possibly, not dense. */
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -359,16 +359,37 @@ struct AnalyzeState {
         return stack[stackDepth - 1 - i];
     }
 };
 
 /////////////////////////////////////////////////////////////////////
 // TypeSet
 /////////////////////////////////////////////////////////////////////
 
+void
+TypeSet::addTypeSet(JSContext *cx, ClonedTypeSet *types)
+{
+    if (types->typeFlags & TYPE_FLAG_UNKNOWN) {
+        addType(cx, TYPE_UNKNOWN);
+        return;
+    }
+
+    for (jstype type = TYPE_UNDEFINED; type <= TYPE_STRING; type++) {
+        if (types->typeFlags & (1 << type))
+            addType(cx, type);
+    }
+
+    if (types->objectCount >= 2) {
+        for (unsigned i = 0; i < types->objectCount; i++)
+            addType(cx, (jstype) types->objectSet[i]);
+    } else if (types->objectCount == 1) {
+        addType(cx, (jstype) types->objectSet);
+    }
+}
+
 inline void
 TypeSet::add(JSContext *cx, TypeConstraint *constraint, bool callExisting)
 {
     JS_ASSERT_IF(!constraint->condensed(), cx->compartment->types.inferenceDepth);
     JS_ASSERT_IF(typeFlags & TYPE_FLAG_INTERMEDIATE_SET,
                  !constraint->baseSubset() && !constraint->condensed());
 
     if (!constraint) {
@@ -1272,74 +1293,69 @@ void
 TypeConstraintCondensed::arrayNotPacked(JSContext *cx, bool notDense)
 {
     if (script->types)
         return;
     AnalyzeScriptTypes(cx, script);
 }
 
 /* Constraint which triggers recompilation of a script if any type is added to a type set. */
-class TypeConstraintFreezeSingleType : public TypeConstraint
+class TypeConstraintFreeze : public TypeConstraint
 {
 public:
-    /* Whether a second type has already been added, triggering recompilation. */
+    /* Whether a new type has already been added, triggering recompilation. */
     bool typeAdded;
 
-    TypeConstraintFreezeSingleType(JSScript *script)
-        : TypeConstraint("freezeSingleType", script), typeAdded(false)
+    TypeConstraintFreeze(JSScript *script)
+        : TypeConstraint("freeze", script), typeAdded(false)
     {}
 
     void newType(JSContext *cx, TypeSet *source, jstype type)
     {
         if (typeAdded)
             return;
 
         typeAdded = true;
         cx->compartment->types.addPendingRecompile(cx, script);
     }
 };
 
-static inline jstype
-GetSingleTypeFromTypeFlags(TypeFlags flags)
+void
+TypeSet::Clone(JSContext *cx, JSScript *script, TypeSet *source, ClonedTypeSet *target)
 {
-    switch (flags) {
-      case TYPE_FLAG_UNDEFINED:
-        return TYPE_UNDEFINED;
-      case TYPE_FLAG_NULL:
-        return TYPE_NULL;
-      case TYPE_FLAG_BOOLEAN:
-        return TYPE_BOOLEAN;
-      case TYPE_FLAG_INT32:
-        return TYPE_INT32;
-      case (TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE):
-        return TYPE_DOUBLE;
-      case TYPE_FLAG_STRING:
-        return TYPE_STRING;
-      default:
-        return TYPE_UNKNOWN;
+    if (!source) {
+        target->typeFlags = TYPE_FLAG_UNKNOWN;
+        return;
     }
-}
-
-jstype
-TypeSet::getSingleType(JSContext *cx, JSScript *script)
-{
-    TypeFlags flags = typeFlags & ~TYPE_FLAG_INTERMEDIATE_SET;
-    jstype type;
-
-    if (objectCount >= 2)
-        type = TYPE_UNKNOWN;
-    else if (objectCount == 1)
-        type = flags ? TYPE_UNKNOWN : (jstype) objectSet;
-    else
-        type = GetSingleTypeFromTypeFlags(flags);
-
-    if (script && type != TYPE_UNKNOWN)
-        add(cx, ArenaNew<TypeConstraintFreezeSingleType>(cx->compartment->types.pool, script), false);
-
-    return type;
+
+    if (script && !source->unknown())
+        source->add(cx, ArenaNew<TypeConstraintFreeze>(cx->compartment->types.pool, script), false);
+
+    target->typeFlags = source->typeFlags & ~TYPE_FLAG_INTERMEDIATE_SET;
+    target->objectCount = source->objectCount;
+    if (source->objectCount >= 2) {
+        target->objectSet = (TypeObject **) ::js_malloc(sizeof(TypeObject*) * source->objectCount);
+        if (!target->objectSet) {
+            cx->compartment->types.setPendingNukeTypes(cx);
+            target->objectCount = 0;
+            return;
+        }
+        unsigned objectCapacity = HashSetCapacity(source->objectCount);
+        unsigned index = 0;
+        for (unsigned i = 0; i < objectCapacity; i++) {
+            TypeObject *object = source->objectSet[i];
+            if (object)
+                target->objectSet[index++] = object;
+        }
+        JS_ASSERT(index == source->objectCount);
+    } else if (source->objectCount == 1) {
+        target->objectSet = source->objectSet;
+    } else {
+        target->objectSet = NULL;
+    }
 }
 
 /*
  * Constraint which triggers recompilation of a script if a possible new JSValueType
  * tag is realized for a type set.
  */
 class TypeConstraintFreezeTypeTag : public TypeConstraint
 {
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -62,16 +62,17 @@ namespace js {
 namespace types {
 
 /* Forward declarations. */
 struct TypeSet;
 struct TypeCallsite;
 struct TypeObject;
 struct TypeFunction;
 struct TypeCompartment;
+struct ClonedTypeSet;
 
 /*
  * Information about a single concrete type.  This is a non-zero value whose
  * lower 3 bits indicate a particular primitive type below, and if those bits
  * are zero then a pointer to a type object.
  */
 typedef jsword jstype;
 
@@ -265,16 +266,19 @@ struct TypeSet
     bool unknown() { return typeFlags & TYPE_FLAG_UNKNOWN; }
 
     /*
      * Add a type to this set, calling any constraint handlers if this is a new
      * possible type.
      */
     inline void addType(JSContext *cx, jstype type);
 
+    /* Add all types in a cloned set to this set. */
+    void addTypeSet(JSContext *cx, ClonedTypeSet *types);
+
     /* Add specific kinds of constraints to this set. */
     inline void add(JSContext *cx, TypeConstraint *constraint, bool callExisting = true);
     void addSubset(JSContext *cx, JSScript *script, TypeSet *target);
     void addGetProperty(JSContext *cx, JSScript *script, const jsbytecode *pc,
                         TypeSet *target, jsid id);
     void addSetProperty(JSContext *cx, JSScript *script, const jsbytecode *pc,
                         TypeSet *target, jsid id);
     void addGetElem(JSContext *cx, JSScript *script, const jsbytecode *pc,
@@ -301,32 +305,43 @@ struct TypeSet
 
     /*
      * Methods for JIT compilation. Each of these takes a script argument indicating
      * which compiled code depends on the return value of these calls. Should that
      * returned value change in the future due to new type information, the script
      * will be marked for recompilation.
      */
 
-    /* Get the single type representing all values in this set, TYPE_UNKNOWN otherwise. */
-    jstype getSingleType(JSContext *cx, JSScript *script);
-
     /* Get any type tag which all values in this set must have. */
     JSValueType getKnownTypeTag(JSContext *cx, JSScript *script);
 
     /* Get information about the kinds of objects in this type set. */
     ObjectKind getKnownObjectKind(JSContext *cx, JSScript *script);
 
     /* Get whether this type set is non-empty. */
     bool knownNonEmpty(JSContext *cx, JSScript *script);
 
+    /*
+     * Clone this type set onto target; if any new types are added to this set
+     * in the future, the script will be recompiled.
+     */
+    static void Clone(JSContext *cx, JSScript *script, TypeSet *source, ClonedTypeSet *target);
+
   private:
     inline void markUnknown(JSContext *cx);
 };
 
+/* A type set captured for use by JIT compilers. */
+struct ClonedTypeSet
+{
+    TypeFlags typeFlags;
+    TypeObject **objectSet;
+    unsigned objectCount;
+};
+
 /* Type information about a property. */
 struct Property
 {
     /* Identifier for this property, JSID_VOID for the aggregate integer index property. */
     jsid id;
 
     /* Possible types for this property, including types inherited from prototypes. */
     TypeSet types;
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -317,16 +317,36 @@ JSContext::addTypePropertyId(js::types::
 inline bool
 JSContext::addTypePropertyId(js::types::TypeObject *obj, jsid id, const js::Value &value)
 {
     if (typeInferenceEnabled() && !obj->unknownProperties)
         return addTypePropertyId(obj, id, js::types::GetValueType(this, value));
     return true;
 }
 
+inline bool
+JSContext::addTypePropertyId(js::types::TypeObject *obj, jsid id, js::types::ClonedTypeSet *set)
+{
+    if (obj->unknownProperties)
+        return true;
+    id = js::types::MakeTypeId(this, id);
+
+    js::types::AutoEnterTypeInference enter(this);
+
+    js::types::TypeSet *types = obj->getProperty(this, id, true);
+    if (!types)
+        return compartment->types.checkPendingRecompiles(this);
+
+    js::types::InferSpew(js::types::ISpewOps, "externalType: property %s %s",
+                         obj->name(), js::types::TypeIdString(id));
+    types->addTypeSet(this, set);
+
+    return compartment->types.checkPendingRecompiles(this);
+}
+
 inline js::types::TypeObject *
 JSContext::getTypeEmpty()
 {
     return &compartment->types.typeEmpty;
 }
 
 inline bool
 JSContext::aliasTypeProperties(js::types::TypeObject *obj, jsid first, jsid second)
@@ -571,16 +591,29 @@ inline bool
 JSScript::typeSetThis(JSContext *cx, const js::Value &value)
 {
     if (cx->typeInferenceEnabled())
         return typeSetThis(cx, js::types::GetValueType(cx, value));
     return true;
 }
 
 inline bool
+JSScript::typeSetThis(JSContext *cx, js::types::ClonedTypeSet *set)
+{
+    if (!ensureVarTypes(cx))
+        return false;
+    js::types::AutoEnterTypeInference enter(cx);
+
+    js::types::InferSpew(js::types::ISpewOps, "externalType: setThis #%u: %s", id());
+    thisTypes()->addTypeSet(cx, set);
+
+    return cx->compartment->types.checkPendingRecompiles(cx);
+}
+
+inline bool
 JSScript::typeSetNewCalled(JSContext *cx)
 {
     if (!cx->typeInferenceEnabled() || calledWithNew)
         return true;
     calledWithNew = true;
 
     /*
      * Determining the 'this' type used when the script is invoked with 'new'
@@ -624,16 +657,29 @@ JSScript::typeSetLocal(JSContext *cx, un
     if (cx->typeInferenceEnabled()) {
         js::types::jstype type = js::types::GetValueType(cx, value);
         return typeSetLocal(cx, local, type);
     }
     return true;
 }
 
 inline bool
+JSScript::typeSetLocal(JSContext *cx, unsigned local, js::types::ClonedTypeSet *set)
+{
+    if (!ensureVarTypes(cx))
+        return false;
+    js::types::AutoEnterTypeInference enter(cx);
+
+    js::types::InferSpew(js::types::ISpewOps, "externalType: setLocal #%u %u", id(), local);
+    localTypes(local)->addTypeSet(cx, set);
+
+    return compartment->types.checkPendingRecompiles(cx);
+}
+
+inline bool
 JSScript::typeSetArgument(JSContext *cx, unsigned arg, js::types::jstype type)
 {
     if (!cx->typeInferenceEnabled())
         return true;
     if (!ensureVarTypes(cx))
         return false;
     if (!argTypes(arg)->hasType(type)) {
         js::types::AutoEnterTypeInference enter(cx);
@@ -653,16 +699,29 @@ JSScript::typeSetArgument(JSContext *cx,
     if (cx->typeInferenceEnabled()) {
         js::types::jstype type = js::types::GetValueType(cx, value);
         return typeSetArgument(cx, arg, type);
     }
     return true;
 }
 
 inline bool
+JSScript::typeSetArgument(JSContext *cx, unsigned arg, js::types::ClonedTypeSet *set)
+{
+    if (!ensureVarTypes(cx))
+        return false;
+    js::types::AutoEnterTypeInference enter(cx);
+
+    js::types::InferSpew(js::types::ISpewOps, "externalType: setArg #%u %u", id(), arg);
+    argTypes(arg)->addTypeSet(cx, set);
+
+    return cx->compartment->types.checkPendingRecompiles(cx);
+}
+
+inline bool
 JSScript::typeSetUpvar(JSContext *cx, unsigned upvar, const js::Value &value)
 {
     if (!cx->typeInferenceEnabled())
         return true;
     if (!ensureVarTypes(cx))
         return false;
     js::types::jstype type = js::types::GetValueType(cx, value);
     if (!upvarTypes(upvar)->hasType(type)) {
@@ -1162,17 +1221,35 @@ inline TypeObject::TypeObject(jsid name,
 
 inline TypeFunction::TypeFunction(jsid name, JSObject *proto)
     : TypeObject(name, proto), handler(NULL), script(NULL), isGeneric(false)
 {
     isFunction = true;
 }
 
 inline void
-SweepType(jstype *ptype)
+SweepClonedTypes(ClonedTypeSet *types)
 {
-    if (TypeIsObject(*ptype) && !((TypeObject*)*ptype)->marked)
-        *ptype = TYPE_UNKNOWN;
+    if (types->objectCount >= 2) {
+        for (unsigned i = 0; i < types->objectCount; i++) {
+            if (!types->objectSet[i]->marked)
+                types->objectSet[i--] = types->objectSet[--types->objectCount];
+        }
+        if (types->objectCount == 1) {
+            TypeObject *obj = (TypeObject *) types->objectSet;
+            ::js_free(types->objectSet);
+            types->objectSet = (TypeObject **) obj;
+        } else if (types->objectCount == 0) {
+            ::js_free(types->objectSet);
+            types->objectSet = NULL;
+        }
+    } else if (types->objectCount == 1) {
+        TypeObject *obj = (TypeObject *) types->objectSet;
+        if (!obj->marked) {
+            types->objectSet = NULL;
+            types->objectCount = 0;
+        }
+    }
 }
 
 } } /* namespace js::types */
 
 #endif // jsinferinlines_h___
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -522,21 +522,24 @@ struct JSScript {
     inline bool typeMonitorResult(JSContext *cx, const jsbytecode *pc, const js::Value &val);
     inline bool typeMonitorUndefined(JSContext *cx, const jsbytecode *pc);
     inline bool typeMonitorOverflow(JSContext *cx, const jsbytecode *pc);
     inline bool typeMonitorUnknown(JSContext *cx, const jsbytecode *pc);
 
     /* Add a type for a variable in this script. */
     inline bool typeSetThis(JSContext *cx, js::types::jstype type);
     inline bool typeSetThis(JSContext *cx, const js::Value &value);
+    inline bool typeSetThis(JSContext *cx, js::types::ClonedTypeSet *types);
     inline bool typeSetNewCalled(JSContext *cx);
     inline bool typeSetLocal(JSContext *cx, unsigned local, js::types::jstype type);
     inline bool typeSetLocal(JSContext *cx, unsigned local, const js::Value &value);
+    inline bool typeSetLocal(JSContext *cx, unsigned local, js::types::ClonedTypeSet *types);
     inline bool typeSetArgument(JSContext *cx, unsigned arg, js::types::jstype type);
     inline bool typeSetArgument(JSContext *cx, unsigned arg, const js::Value &value);
+    inline bool typeSetArgument(JSContext *cx, unsigned arg, js::types::ClonedTypeSet *types);
     inline bool typeSetUpvar(JSContext *cx, unsigned upvar, const js::Value &value);
 
     /*
      * Associates this script with a specific function, constructing a new type
      * object for the function.
      */
     bool typeSetFunction(JSContext *cx, JSFunction *fun);
 
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -3012,24 +3012,27 @@ mjit::Compiler::inlineCallHelper(uint32 
             callIC.frameSize.initStatic(frame.localSlots(), speculatedArgc);
         }
     }
 
     callIC.argTypes = NULL;
     callIC.typeMonitored = monitored(PC);
     if (callIC.typeMonitored && callIC.frameSize.isStatic()) {
         unsigned argc = callIC.frameSize.staticArgc();
-        callIC.argTypes = (types::jstype *) js_calloc((1 + argc) * sizeof(types::jstype));
-        if (!callIC.argTypes)
+        callIC.argTypes = (types::ClonedTypeSet *)
+            js_calloc((1 + argc) * sizeof(types::ClonedTypeSet));
+        if (!callIC.argTypes) {
+            js_ReportOutOfMemory(cx);
             return false;
+        }
         types::TypeSet *types = frame.getTypeSet(frame.peek(-(argc + 1)));
-        callIC.argTypes[0] = types ? types->getSingleType(cx, script) : types::TYPE_UNKNOWN;
+        types::TypeSet::Clone(cx, script, types, &callIC.argTypes[0]);
         for (unsigned i = 0; i < argc; i++) {
             types::TypeSet *types = frame.getTypeSet(frame.peek(-(argc - i)));
-            callIC.argTypes[i + 1] = types ? types->getSingleType(cx, script) : types::TYPE_UNKNOWN;
+            types::TypeSet::Clone(cx, script, types, &callIC.argTypes[i + 1]);
         }
     }
 
     /* Test the type if necessary. Failing this always takes a really slow path. */
     MaybeJump notObjectJump;
     if (icCalleeType.isSet())
         notObjectJump = masm.testObject(Assembler::NotEqual, icCalleeType.reg());
 
@@ -3829,20 +3832,25 @@ mjit::Compiler::jsop_setprop(JSAtom *ato
                              ? ic::PICInfo::SETMETHOD
                              : ic::PICInfo::SET;
     PICGenInfo pic(kind, op, usePropCache);
     pic.atom = atom;
 
     if (monitored(PC)) {
         types::TypeSet *types = frame.getTypeSet(rhs);
         pic.typeMonitored = true;
-        pic.knownType = types ? types->getSingleType(cx, script) : types::TYPE_UNKNOWN;
+        pic.rhsTypes = (types::ClonedTypeSet *) ::js_calloc(sizeof(types::ClonedTypeSet));
+        if (!pic.rhsTypes) {
+            js_ReportOutOfMemory(cx);
+            return false;
+        }
+        types::TypeSet::Clone(cx, script, types, pic.rhsTypes);
     } else {
         pic.typeMonitored = false;
-        pic.knownType = types::TYPE_UNKNOWN;
+        pic.rhsTypes = NULL;
     }
 
     RESERVE_IC_SPACE(masm);
     RESERVE_OOL_SPACE(stubcc.masm);
 
     /* Guard that the type is an object. */
     Jump typeCheck;
     if (!lhs->isTypeKnown()) {
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -150,17 +150,17 @@ class Compiler : public BaseCompiler
         DataLabelPtr addrLabel1;
         DataLabelPtr addrLabel2;
         Jump         oolJump;
         Label        icCall;
         RegisterID   funObjReg;
         RegisterID   funPtrReg;
         FrameSize    frameSize;
         bool         typeMonitored;
-        types::jstype *argTypes;
+        types::ClonedTypeSet *argTypes;
     };
 
   private:
 #endif
 
     /*
      * Writes of call return addresses which needs to be delayed until the final
      * absolute address of the join point is known.
@@ -214,30 +214,30 @@ class Compiler : public BaseCompiler
         Jump        claspGuard;
         Jump        holeGuard;
         Int32Key    key;
         uint32      volatileMask;
     };
 
     struct PICGenInfo : public BaseICInfo {
         PICGenInfo(ic::PICInfo::Kind kind, JSOp op, bool usePropCache)
-          : BaseICInfo(op), kind(kind), usePropCache(usePropCache)
+          : BaseICInfo(op), kind(kind), usePropCache(usePropCache), typeMonitored(false)
         { }
         ic::PICInfo::Kind kind;
         Label typeCheck;
         RegisterID shapeReg;
         RegisterID objReg;
         RegisterID typeReg;
         bool usePropCache;
         Label shapeGuard;
         jsbytecode *pc;
         JSAtom *atom;
         bool hasTypeCheck;
         bool typeMonitored;
-        types::jstype knownType;
+        types::ClonedTypeSet *rhsTypes;
         ValueRemat vr;
 #ifdef JS_HAS_IC_LABELS
         union {
             ic::GetPropLabels getPropLabels_;
             ic::SetPropLabels setPropLabels_;
             ic::BindNameLabels bindNameLabels_;
             ic::ScopeNameLabels scopeNameLabels_;
         };
@@ -285,17 +285,17 @@ class Compiler : public BaseCompiler
             ic.usePropCache = usePropCache;
             if (ic.isSet()) {
                 ic.u.vr = vr;
             } else if (ic.isGet()) {
                 ic.u.get.typeReg = typeReg;
                 ic.u.get.hasTypeCheck = hasTypeCheck;
             }
             ic.typeMonitored = typeMonitored;
-            ic.knownType = knownType;
+            ic.rhsTypes = rhsTypes;
 #ifdef JS_HAS_IC_LABELS
             if (ic.isGet())
                 ic.setLabels(getPropLabels());
             else if (ic.isSet())
                 ic.setLabels(setPropLabels());
             else if (ic.isBind())
                 ic.setLabels(bindNameLabels());
             else if (ic.isScopeName())
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -338,17 +338,18 @@ stubs::CompileFunction(VMFrame &f, uint3
 
     if (!ok)
         THROWV(NULL);
 
     return NULL;
 }
 
 static inline bool
-UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, bool *unjittable, uint32 argc, types::jstype *argTypes)
+UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, bool *unjittable, uint32 argc,
+                   types::ClonedTypeSet *argTypes)
 {
     JSContext *cx = f.cx;
     Value *vp = f.regs.sp - (argc + 2);
     JSObject &callee = vp->toObject();
     JSFunction *newfun = callee.getFunctionPrivate();
     JSScript *newscript = newfun->script();
 
     bool newType = (flags & JSFRAME_CONSTRUCTING) && cx->typeInferenceEnabled() &&
@@ -356,27 +357,30 @@ UncachedInlineCall(VMFrame &f, uint32 fl
 
     if (argTypes && argc == newfun->nargs) {
         /*
          * Use the space of all possible types being passed at this callsite if there
          * is a match between argc and nargs, so that the fastEntry can be subsequently
          * used without further type checking. If there is an argument count mismatch,
          * the callee's args will end up getting marked as unknown.
          */
+        types::AutoEnterTypeInference enter(cx);
         if (flags & JSFRAME_CONSTRUCTING) {
             if (!newscript->typeSetNewCalled(cx))
                 return false;
         } else {
-            if (!newscript->typeSetThis(cx, argTypes[0]))
+            if (!newscript->typeSetThis(cx, &argTypes[0]))
                 return false;
         }
         for (unsigned i = 0; i < argc; i++) {
-            if (!newscript->typeSetArgument(cx, i, argTypes[1 + i]))
+            if (!newscript->typeSetArgument(cx, i, &argTypes[1 + i]))
                 return false;
         }
+        if (!cx->compartment->types.checkPendingRecompiles(cx))
+            return false;
     } else {
         CallArgs args(vp + 2, argc);
         if (!cx->typeMonitorCall(args, flags & JSFRAME_CONSTRUCTING))
             return false;
     }
 
     /* Get pointer to new frame/slots, prepare arguments. */
     StackSpace &stack = cx->stack();
@@ -433,17 +437,18 @@ void * JS_FASTCALL
 stubs::UncachedNew(VMFrame &f, uint32 argc)
 {
     UncachedCallResult ucr;
     UncachedNewHelper(f, argc, NULL, &ucr);
     return ucr.codeAddr;
 }
 
 void
-stubs::UncachedNewHelper(VMFrame &f, uint32 argc, types::jstype *argTypes, UncachedCallResult *ucr)
+stubs::UncachedNewHelper(VMFrame &f, uint32 argc, types::ClonedTypeSet *argTypes,
+                         UncachedCallResult *ucr)
 {
     ucr->init();
 
     JSContext *cx = f.cx;
     Value *vp = f.regs.sp - (argc + 2);
 
     /* Try to do a fast inline call before the general Invoke path. */
     if (IsFunctionObject(*vp, &ucr->fun) && ucr->fun->isInterpreted()) {
@@ -476,17 +481,18 @@ stubs::Eval(VMFrame &f, uint32 argc)
     }
 
     JS_ASSERT(f.regs.fp == f.cx->fp());
     if (!DirectEval(f.cx, argc, vp))
         THROW();
 }
 
 void
-stubs::UncachedCallHelper(VMFrame &f, uint32 argc, types::jstype *argTypes, UncachedCallResult *ucr)
+stubs::UncachedCallHelper(VMFrame &f, uint32 argc, types::ClonedTypeSet *argTypes,
+                          UncachedCallResult *ucr)
 {
     ucr->init();
 
     JSContext *cx = f.cx;
     Value *vp = f.regs.sp - (argc + 2);
 
     if (IsFunctionObject(*vp, &ucr->callee)) {
         ucr->callee = &vp->toObject();
--- a/js/src/methodjit/MonoIC.cpp
+++ b/js/src/methodjit/MonoIC.cpp
@@ -1418,18 +1418,18 @@ JITScript::sweepCallICs(JSContext *cx, b
      * alive pools that we are trying to destroy (see JSCompartment::sweep).
      */
 
     ic::CallICInfo *callICs_ = callICs();
     for (uint32 i = 0; i < nCallICs; i++) {
         ic::CallICInfo &ic = callICs_[i];
 
         if (ic.argTypes) {
-            for (unsigned i = 0; i < ic.frameSize.staticArgc(); i++)
-                types::SweepType(&ic.argTypes[i]);
+            for (unsigned i = 0; i < ic.frameSize.staticArgc() + 1; i++)
+                types::SweepClonedTypes(&ic.argTypes[i]);
         }
 
         /*
          * If the object is unreachable, we're guaranteed not to be currently
          * executing a stub generated by a guard on that object. This lets us
          * precisely GC call ICs while keeping the identity guard safe.
          */
         bool fastFunDead = ic.fastGuardedObject &&
--- a/js/src/methodjit/MonoIC.h
+++ b/js/src/methodjit/MonoIC.h
@@ -251,17 +251,17 @@ struct CallICInfo {
     bool hit : 1;
     bool hasJsFunCheck : 1;
     bool typeMonitored : 1;
 
     /*
      * For monitored calls with static argc, the types of 'this' and arguments.
      * For calls through Function.prototype.call, this refers to the inner this/arguments.
      */
-    types::jstype *argTypes;
+    types::ClonedTypeSet *argTypes;
 
     inline void reset() {
         fastGuardedObject = NULL;
         fastGuardedNative = NULL;
         hit = false;
         hasJsFunCheck = false;
         pools[0] = pools[1] = pools[2] = NULL;
     }
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -195,17 +195,18 @@ class SetPropCompiler : public PICStubCo
     SetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSAtom *atom,
                     VoidStubPIC stub)
       : PICStubCompiler("setprop", f, script, pic, JS_FUNC_TO_DATA_PTR(void *, stub)),
         obj(obj), atom(atom), lastStubSecondShapeGuard(pic.secondShapeGuard)
     { }
 
     static void reset(Repatcher &repatcher, ic::PICInfo &pic)
     {
-        types::SweepType(&pic.knownType);
+        if (pic.rhsTypes)
+            types::SweepClonedTypes(pic.rhsTypes);
 
         SetPropLabels &labels = pic.setPropLabels();
         repatcher.repatchLEAToLoadPtr(labels.getDslotsLoad(pic.fastPathRejoin, pic.u.vr));
         repatcher.repatch(labels.getInlineShapeData(pic.fastPathStart, pic.shapeGuard),
                           int32(JSObjectMap::INVALID_SHAPE));
         repatcher.relink(labels.getInlineShapeJump(pic.fastPathStart.labelAtOffset(pic.shapeGuard)),
                          pic.slowPathStart);
 
@@ -596,17 +597,17 @@ class SetPropCompiler : public PICStubCo
              * usually be a slowdown even if there *are* other shapes that
              * don't realloc.
              */
             if (obj->numSlots() != slots)
                 return disable("insufficient slot capacity");
 
             if (pic.typeMonitored) {
                 uint32 recompilations = f.jit()->recompilations;
-                if (!cx->addTypePropertyId(obj->getType(), shape->id, pic.knownType))
+                if (!cx->addTypePropertyId(obj->getType(), shape->id, pic.rhsTypes))
                     return error();
                 if (f.jit()->recompilations != recompilations)
                     return Lookup_Uncacheable;
             }
 
             return generateStub(initialShape, shape, true, !obj->hasSlotsArray());
         }
 
@@ -616,17 +617,17 @@ class SetPropCompiler : public PICStubCo
         if (!shape->writable())
             return disable("readonly");
 
         if (shape->hasDefaultSetter()) {
             if (!shape->hasSlot())
                 return disable("invalid slot");
             if (pic.typeMonitored) {
                 uint32 recompilations = f.jit()->recompilations;
-                if (!cx->addTypePropertyId(obj->getType(), shape->id, pic.knownType))
+                if (!cx->addTypePropertyId(obj->getType(), shape->id, pic.rhsTypes))
                     return error();
                 if (f.jit()->recompilations != recompilations)
                     return Lookup_Uncacheable;
             }
         } else {
             if (shape->hasSetterValue())
                 return disable("scripted setter");
             if (shape->setterOp() != SetCallArg &&
@@ -635,20 +636,20 @@ class SetPropCompiler : public PICStubCo
             }
             if (pic.typeMonitored) {
                 uint32 recompilations = f.jit()->recompilations;
                 JSScript *script = obj->getCallObjCalleeFunction()->script();
                 uint16 slot = uint16(shape->shortid);
                 if (!script->ensureVarTypes(cx))
                     return error();
                 if (shape->setterOp() == SetCallArg) {
-                    if (!script->typeSetArgument(cx, slot, pic.knownType))
+                    if (!script->typeSetArgument(cx, slot, pic.rhsTypes))
                         return error();
                 } else {
-                    if (!script->typeSetLocal(cx, slot, pic.knownType))
+                    if (!script->typeSetLocal(cx, slot, pic.rhsTypes))
                         return error();
                 }
                 if (f.jit()->recompilations != recompilations)
                     return Lookup_Uncacheable;
             }
         }
 
         JS_ASSERT(obj == holder);
--- a/js/src/methodjit/PolyIC.h
+++ b/js/src/methodjit/PolyIC.h
@@ -448,18 +448,18 @@ struct PICInfo : public BasePolyIC {
     RegisterID objReg   : 5;        // also the out data reg
 
     // Whether type properties need to be updated to reflect generated stubs.
     bool typeMonitored : 1;
 
     // Offset from start of fast path to initial shape guard.
     uint32 shapeGuard;
 
-    // Exact known type of the RHS, for monitored PICs.
-    types::jstype knownType;
+    // Possible types of the RHS, for monitored SETPROP PICs.
+    types::ClonedTypeSet *rhsTypes;
     
     inline bool isSet() const {
         return kind == SET || kind == SETMETHOD;
     }
     inline bool isGet() const {
         return kind == GET || kind == CALL;
     }
     inline bool isBind() const {
@@ -539,16 +539,21 @@ struct PICInfo : public BasePolyIC {
 
     // Reset the data members to the state of a fresh PIC before any patching
     // or stub generation was done.
     void reset() {
         BasePolyIC::reset();
         inlinePathPatched = false;
         shapeRegHasBaseShape = true;
     }
+
+    ~PICInfo() {
+        if (typeMonitored)
+            js_free(rhsTypes);
+    }
 };
 
 #ifdef JS_POLYIC
 void PurgePICs(JSContext *cx, JSScript *script);
 void JS_FASTCALL GetProp(VMFrame &f, ic::PICInfo *);
 void JS_FASTCALL GetPropNoCache(VMFrame &f, ic::PICInfo *);
 void JS_FASTCALL SetProp(VMFrame &f, ic::PICInfo *);
 void JS_FASTCALL CallProp(VMFrame &f, ic::PICInfo *);
--- a/js/src/methodjit/StubCalls.h
+++ b/js/src/methodjit/StubCalls.h
@@ -99,18 +99,18 @@ struct UncachedCallResult {
     }        
 };
 
 /*
  * Helper functions for stubs and IC functions for calling functions.
  * These functions either execute the function, return a native code
  * pointer that can be used to call the function, or throw.
  */
-void UncachedCallHelper(VMFrame &f, uint32 argc, types::jstype *argTypes, UncachedCallResult *ucr);
-void UncachedNewHelper(VMFrame &f, uint32 argc, types::jstype *argTypes, UncachedCallResult *ucr);
+void UncachedCallHelper(VMFrame &f, uint32 argc, types::ClonedTypeSet *argTypes, UncachedCallResult *ucr);
+void UncachedNewHelper(VMFrame &f, uint32 argc, types::ClonedTypeSet *argTypes, UncachedCallResult *ucr);
 
 void JS_FASTCALL CreateThis(VMFrame &f, JSObject *proto);
 void JS_FASTCALL Throw(VMFrame &f);
 void JS_FASTCALL PutStrictEvalCallObject(VMFrame &f);
 void JS_FASTCALL PutActivationObjects(VMFrame &f);
 void JS_FASTCALL GetCallObject(VMFrame &f);
 #if JS_MONOIC
 void * JS_FASTCALL InvokeTracer(VMFrame &f, ic::TraceICInfo *tic);