[INFER] Use PICs on monitored SETNAME and SETPROP, bug 621937.
authorBrian Hackett <bhackett1024@gmail.com>
Fri, 11 Mar 2011 16:29:38 -0800
changeset 74759 bcf148dbce2f3de31d37f393b4b0e5297f06c565
parent 74758 816e5118734b8c397a82d590d3bd37a31b2fd8f8
child 74760 ce1accd11d7adff7b97884b542b21a703feb0de9
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
bugs621937
milestone2.0b13pre
[INFER] Use PICs on monitored SETNAME and SETPROP, bug 621937.
js/src/jsapi.cpp
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsemit.h
js/src/jsfun.cpp
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsparse.cpp
js/src/jsparse.h
js/src/jsscope.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsscriptinlines.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/PolyIC.cpp
js/src/methodjit/PolyIC.h
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5043,27 +5043,20 @@ CompileUCFunctionForPrincipalsCommon(JSC
         }
     }
 
     fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, obj, funAtom, NULL, NULL);
     if (!fun)
         goto out2;
 
     {
-        EmptyShape *emptyCallShape = EmptyShape::getEmptyCallShape(cx);
-        if (!emptyCallShape) {
-            fun = NULL;
-            goto out2;
-        }
-        AutoShapeRooter shapeRoot(cx, emptyCallShape);
-
         AutoObjectRooter tvr(cx, FUN_OBJECT(fun));
         MUST_FLOW_THROUGH("out");
 
-        Bindings bindings(cx, emptyCallShape);
+        Bindings bindings(cx);
         AutoBindingsRooter root(cx, bindings);
         for (i = 0; i < nargs; i++) {
             argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]), 0);
             if (!argAtom) {
                 fun = NULL;
                 goto out2;
             }
 
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -70,17 +70,16 @@ JSCompartment::JSCompartment(JSRuntime *
     data(NULL),
     active(false),
 #ifdef JS_METHODJIT
     jaegerCompartment(NULL),
 #endif
     propertyTree(thisForCtor()),
     emptyArgumentsShape(NULL),
     emptyBlockShape(NULL),
-    emptyCallShape(NULL),
     emptyDeclEnvShape(NULL),
     emptyEnumeratorShape(NULL),
     emptyWithShape(NULL),
     debugMode(rt->debugMode),
 #if ENABLE_YARR_JIT
     regExpAllocator(NULL),
 #endif
     mathCache(NULL)
@@ -495,29 +494,39 @@ JSCompartment::sweep(JSContext *cx, uint
         }
     }
 
     /* Remove dead empty shapes. */
     if (emptyArgumentsShape && !emptyArgumentsShape->marked())
         emptyArgumentsShape = NULL;
     if (emptyBlockShape && !emptyBlockShape->marked())
         emptyBlockShape = NULL;
-    if (emptyCallShape && !emptyCallShape->marked())
-        emptyCallShape = NULL;
     if (emptyDeclEnvShape && !emptyDeclEnvShape->marked())
         emptyDeclEnvShape = NULL;
     if (emptyEnumeratorShape && !emptyEnumeratorShape->marked())
         emptyEnumeratorShape = NULL;
     if (emptyWithShape && !emptyWithShape->marked())
         emptyWithShape = NULL;
 
 #ifdef JS_TRACER
     traceMonitor.sweep(cx);
 #endif
 
+# if defined JS_POLYIC
+    /*
+     * Purge all PICs in the compartment. These can reference type data and
+     * need to know which types are pending collection.
+     */
+    for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
+        JSScript *script = reinterpret_cast<JSScript *>(cursor);
+        if (script->hasJITCode())
+            mjit::ic::PurgePICs(cx, script);
+    }
+# endif
+
     if (!types.inferenceDepth && types.inferenceEnabled) {
         for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
             JSScript *script = reinterpret_cast<JSScript *>(cursor);
             script->condenseTypes(cx);
         }
 
         types.condense(cx);
     }
@@ -594,19 +603,16 @@ JSCompartment::purge(JSContext *cx)
         traceMonitor.needFlush = JS_TRUE;
 #endif
 
 #ifdef JS_METHODJIT
     for (JSScript *script = (JSScript *)scripts.next;
          &script->links != &scripts;
          script = (JSScript *)script->links.next) {
         if (script->hasJITCode()) {
-# if defined JS_POLYIC
-            mjit::ic::PurgePICs(cx, script);
-# endif
 # if defined JS_MONOIC
             /*
              * MICs do not refer to data which can be GC'ed and do not generate stubs
              * which might need to be discarded, but are sensitive to shape regeneration.
              */
             if (cx->runtime->gcRegenShapes)
                 mjit::ic::PurgeMICs(cx, script);
 # endif
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -420,21 +420,20 @@ struct JS_FRIEND_API(JSCompartment) {
     jsrefcount                   livePropTreeNodes;
     jsrefcount                   totalPropTreeNodes;
     jsrefcount                   propTreeKidsChunks;
     jsrefcount                   liveDictModeNodes;
 #endif
 
     /*
      * Runtime-shared empty scopes for well-known built-in objects that lack
-     * class prototypes (the usual locus of an emptyShape). Mnemonic: ABCDEW
+     * class prototypes (the usual locus of an emptyShape). Mnemonic: ABDEW
      */
     js::EmptyShape               *emptyArgumentsShape;
     js::EmptyShape               *emptyBlockShape;
-    js::EmptyShape               *emptyCallShape;
     js::EmptyShape               *emptyDeclEnvShape;
     js::EmptyShape               *emptyEnumeratorShape;
     js::EmptyShape               *emptyWithShape;
 
     typedef js::HashSet<js::EmptyShape *,
                         js::DefaultHasher<js::EmptyShape *>,
                         js::SystemAllocPolicy> EmptyShapeSet;
 
--- a/js/src/jsemit.h
+++ b/js/src/jsemit.h
@@ -345,17 +345,17 @@ struct JSTreeContext {              /* t
 #endif
 
     void trace(JSTracer *trc);
 
     JSTreeContext(js::Parser *prs)
       : flags(0), bodyid(0), blockidGen(0), topStmt(NULL), topScopeStmt(NULL),
         blockChainBox(NULL), blockNode(NULL), parser(prs), scopeChain_(NULL),
         parent(prs->tc), staticLevel(0), funbox(NULL), functionList(NULL),
-        innermostWith(NULL), bindings(prs->context, prs->emptyCallShape),
+        innermostWith(NULL), bindings(prs->context),
         sharpSlotBase(-1)
     {
         prs->tc = this;
         JS_SCOPE_DEPTH_METERING(scopeDepth = maxScopeDepth = 0);
     }
 
     /*
      * For functions the tree context is constructed and destructed a second
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -2514,22 +2514,17 @@ Function(JSContext *cx, uintN argc, Valu
      * Report errors via CSP is done in the script security manager.
      * js_CheckContentSecurityPolicy is defined in jsobj.cpp
      */
     if (!js_CheckContentSecurityPolicy(cx, parent)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_FUNCTION);
         return JS_FALSE;
     }
 
-    EmptyShape *emptyCallShape = EmptyShape::getEmptyCallShape(cx);
-    if (!emptyCallShape)
-        return JS_FALSE;
-    AutoShapeRooter shapeRoot(cx, emptyCallShape);
-
-    Bindings bindings(cx, emptyCallShape);
+    Bindings bindings(cx);
     AutoBindingsRooter root(cx, bindings);
 
     Value *argv = vp + 2;
     uintN n = argc ? argc - 1 : 0;
     if (n > 0) {
         enum { OK, BAD, BAD_FORMAL } state;
 
         /*
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -1277,16 +1277,77 @@ TypeConstraintCondensed::newType(JSConte
 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
+{
+public:
+    /* Whether a second type has already been added, triggering recompilation. */
+    bool typeAdded;
+
+    TypeConstraintFreezeSingleType(JSScript *script)
+        : TypeConstraint("freezeSingleType", 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)
+{
+    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;
+    }
+}
+
+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;
+}
+
 /*
  * Constraint which triggers recompilation of a script if a possible new JSValueType
  * tag is realized for a type set.
  */
 class TypeConstraintFreezeTypeTag : public TypeConstraint
 {
 public:
     /*
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -92,24 +92,24 @@ const jstype TYPE_UNKNOWN = 7;
 /*
  * Test whether a type is an primitive or an object.  Object types can be
  * cast into a TypeObject*.
  */
 
 static inline bool
 TypeIsPrimitive(jstype type)
 {
-    JS_ASSERT(type && type != TYPE_UNKNOWN);
+    JS_ASSERT(type);
     return type < TYPE_UNKNOWN;
 }
 
 static inline bool
 TypeIsObject(jstype type)
 {
-    JS_ASSERT(type && type != TYPE_UNKNOWN);
+    JS_ASSERT(type);
     return type > TYPE_UNKNOWN;
 }
 
 /* Get the type of a jsval, or zero for an unknown special value. */
 inline jstype GetValueType(JSContext *cx, const Value &val);
 
 /*
  * Type inference memory management overview.
@@ -294,23 +294,27 @@ struct TypeSet
     void addCondensed(JSContext *cx, JSScript *script);
 
     /*
      * Make an intermediate type set with the specified debugging name,
      * not embedded in another structure.
      */
     static inline TypeSet* make(JSContext *cx, const char *name);
 
-    /* Methods for JIT compilation. */
+    /*
+     * 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 any type tag which all values in this set must have.  Should this type
-     * set change in the future so that another type tag is possible, mark script
-     * 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);
 
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -562,36 +562,45 @@ JSScript::typeSetNewCalled(JSContext *cx
         js::types::AnalyzeScriptNew(cx, this);
         if (!cx->compartment->types.checkPendingRecompiles(cx))
             return false;
     }
     return true;
 }
 
 inline bool
-JSScript::typeSetLocal(JSContext *cx, unsigned local, const js::Value &value)
+JSScript::typeSetLocal(JSContext *cx, unsigned local, js::types::jstype type)
 {
     if (!cx->typeInferenceEnabled())
         return true;
     if (!ensureVarTypes(cx))
         return false;
-    js::types::jstype type = js::types::GetValueType(cx, value);
     if (!localTypes(local)->hasType(type)) {
         js::types::AutoEnterTypeInference enter(cx);
 
         js::types::InferSpew(js::types::ISpewDynamic, "SetLocal: #%u %u: %s",
                              id(), local, js::types::TypeString(type));
         localTypes(local)->addType(cx, type);
 
         return compartment->types.checkPendingRecompiles(cx);
     }
     return true;
 }
 
 inline bool
+JSScript::typeSetLocal(JSContext *cx, unsigned local, const js::Value &value)
+{
+    if (cx->typeInferenceEnabled()) {
+        js::types::jstype type = js::types::GetValueType(cx, value);
+        return typeSetLocal(cx, local, type);
+    }
+    return true;
+}
+
+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);
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -191,32 +191,28 @@ Parser::Parser(JSContext *cx, JSPrincipa
     tokenStream(cx),
     principals(NULL),
     callerFrame(cfp),
     callerVarObj(cfp ? &cfp->varobj(cx->containingSegment(cfp)) : NULL),
     nodeList(NULL),
     functionCount(0),
     traceListHead(NULL),
     tc(NULL),
-    emptyCallShape(NULL),
     keepAtoms(cx->runtime)
 {
     js::PodArrayZero(tempFreeList);
     setPrincipals(prin);
     JS_ASSERT_IF(cfp, cfp->isScriptFrame());
 }
 
 bool
 Parser::init(const jschar *base, size_t length, const char *filename, uintN lineno,
              JSVersion version)
 {
     JSContext *cx = context;
-    emptyCallShape = EmptyShape::getEmptyCallShape(cx);
-    if (!emptyCallShape)
-        return false;
     tempPoolMark = JS_ARENA_MARK(&cx->tempPool);
     if (!tokenStream.init(base, length, filename, lineno, version)) {
         JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
         return false;
     }
     return true;
 }
 
@@ -289,17 +285,17 @@ Parser::newFunctionBox(JSObject *obj, JS
     funbox->isFunctionBox = true;
     funbox->node = fn;
     funbox->siblings = tc->functionList;
     tc->functionList = funbox;
     ++tc->parser->functionCount;
     funbox->kids = NULL;
     funbox->parent = tc->funbox;
     funbox->methods = NULL;
-    new (&funbox->bindings) Bindings(context, emptyCallShape);
+    new (&funbox->bindings) Bindings(context);
     funbox->queued = false;
     funbox->inLoop = false;
     for (JSStmtInfo *stmt = tc->topStmt; stmt; stmt = stmt->down) {
         if (STMT_IS_LOOP(stmt)) {
             funbox->inLoop = true;
             break;
         }
     }
--- a/js/src/jsparse.h
+++ b/js/src/jsparse.h
@@ -1047,17 +1047,16 @@ struct Parser : private js::AutoGCRooter
     void                *tempPoolMark;  /* initial JSContext.tempPool mark */
     JSPrincipals        *principals;    /* principals associated with source */
     JSStackFrame *const callerFrame;    /* scripted caller frame for eval and dbgapi */
     JSObject     *const callerVarObj;   /* callerFrame's varObj */
     JSParseNode         *nodeList;      /* list of recyclable parse-node structs */
     uint32              functionCount;  /* number of functions in current unit */
     JSObjectBox         *traceListHead; /* list of parsed object for GC tracing */
     JSTreeContext       *tc;            /* innermost tree context (stack-allocated) */
-    js::EmptyShape      *emptyCallShape;/* empty shape for Call objects */
 
     /* Root atoms and objects allocated for the parsed tree. */
     js::AutoKeepAtoms   keepAtoms;
 
     Parser(JSContext *cx, JSPrincipals *prin = NULL, JSStackFrame *cfp = NULL);
     ~Parser();
 
     friend void js::AutoGCRooter::trace(JSTracer *trc);
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -671,20 +671,16 @@ struct EmptyShape : public js::Shape
     static EmptyShape *getEmptyArgumentsShape(JSContext *cx) {
         return ensure(cx, &js_ArgumentsClass, &cx->compartment->emptyArgumentsShape);
     }
 
     static EmptyShape *getEmptyBlockShape(JSContext *cx) {
         return ensure(cx, &js_BlockClass, &cx->compartment->emptyBlockShape);
     }
 
-    static EmptyShape *getEmptyCallShape(JSContext *cx) {
-        return ensure(cx, &js_CallClass, &cx->compartment->emptyCallShape);
-    }
-
     static EmptyShape *getEmptyDeclEnvShape(JSContext *cx) {
         return ensure(cx, &js_DeclEnvClass, &cx->compartment->emptyDeclEnvShape);
     }
 
     static EmptyShape *getEmptyEnumeratorShape(JSContext *cx) {
         return ensure(cx, &js_IteratorClass, &cx->compartment->emptyEnumeratorShape);
     }
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -74,17 +74,18 @@
 using namespace js;
 using namespace js::gc;
 
 namespace js {
 
 BindingKind
 Bindings::lookup(JSContext *cx, JSAtom *name, uintN *indexp) const
 {
-    JS_ASSERT(lastBinding);
+    if (!lastBinding)
+        return NONE;
 
     Shape *shape =
         SHAPE_FETCH(Shape::search(cx->runtime, const_cast<Shape **>(&lastBinding),
                     ATOM_TO_JSID(name)));
     if (!shape)
         return NONE;
 
     if (indexp)
@@ -96,17 +97,18 @@ Bindings::lookup(JSContext *cx, JSAtom *
         return UPVAR;
 
     return shape->writable() ? VARIABLE : CONSTANT;
 }
 
 bool
 Bindings::add(JSContext *cx, JSAtom *name, BindingKind kind)
 {
-    JS_ASSERT(lastBinding);
+    if (!ensureShape(cx))
+        return false;
 
     /*
      * We still follow 10.2.3 of ES3 and make argument and variable properties
      * of the Call objects enumerable. ES5 reformulated all of its Clause 10 to
      * avoid objects as activations, something we should do too.
      */
     uintN attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED;
 
@@ -372,22 +374,17 @@ js_XDRScript(JSXDRState *xdr, JSScript *
         nvars = argsVars & 0xFFFF;
         JS_ASSERT((paddingUpvars >> 16) == 0);
         nupvars = paddingUpvars & 0xFFFF;
     }
     JS_ASSERT(nargs != Bindings::BINDING_COUNT_LIMIT);
     JS_ASSERT(nvars != Bindings::BINDING_COUNT_LIMIT);
     JS_ASSERT(nupvars != Bindings::BINDING_COUNT_LIMIT);
 
-    EmptyShape *emptyCallShape = EmptyShape::getEmptyCallShape(cx);
-    if (!emptyCallShape)
-        return false;
-    AutoShapeRooter shapeRoot(cx, emptyCallShape);
-
-    Bindings bindings(cx, emptyCallShape);
+    Bindings bindings(cx);
     AutoBindingsRooter rooter(cx, bindings);
     uint32 nameCount = nargs + nvars + nupvars;
     if (nameCount > 0) {
         struct AutoMark {
           JSArenaPool * const pool;
           void * const mark;
           AutoMark(JSArenaPool *pool) : pool(pool), mark(JS_ARENA_MARK(pool)) { }
           ~AutoMark() {
@@ -465,18 +462,21 @@ js_XDRScript(JSXDRState *xdr, JSScript *
                                       ? CONSTANT
                                       : VARIABLE)
                                    : UPVAR;
                 if (!bindings.add(cx, name, kind))
                     return false;
             }
         }
 
-        if (xdr->mode == JSXDR_DECODE)
+        if (xdr->mode == JSXDR_DECODE) {
+            if (!bindings.ensureShape(cx))
+                return false;
             bindings.makeImmutable();
+        }
     }
 
     if (xdr->mode == JSXDR_ENCODE)
         length = script->length;
     if (!JS_XDRUint32(xdr, &length))
         return JS_FALSE;
 
     if (xdr->mode == JSXDR_ENCODE) {
@@ -1201,21 +1201,16 @@ JS_STATIC_ASSERT(sizeof(JSObjectArray) +
 JS_STATIC_ASSERT(JSScript::INVALID_OFFSET <= 255);
 
 JSScript *
 JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms,
                     uint32 nobjects, uint32 nupvars, uint32 nregexps,
                     uint32 ntrynotes, uint32 nconsts, uint32 nglobals,
                     uint16 nClosedArgs, uint16 nClosedVars, JSVersion version)
 {
-    EmptyShape *emptyCallShape = EmptyShape::getEmptyCallShape(cx);
-    if (!emptyCallShape)
-        return NULL;
-    AutoShapeRooter shapeRoot(cx, emptyCallShape);
-
     size_t size, vectorSize;
     JSScript *script;
     uint8 *cursor;
     unsigned constPadding = 0;
 
     uint32 totalClosed = nClosedArgs + nClosedVars;
 
     size = sizeof(JSScript) +
@@ -1249,17 +1244,17 @@ JSScript::NewScript(JSContext *cx, uint3
 
     script = (JSScript *) cx->malloc(size);
     if (!script)
         return NULL;
 
     PodZero(script);
     script->length = length;
     script->version = version;
-    new (&script->bindings) Bindings(cx, emptyCallShape);
+    new (&script->bindings) Bindings(cx);
 
     uint8 *scriptEnd = reinterpret_cast<uint8 *>(script + 1);
 
     cursor = scriptEnd;
     if (nobjects != 0) {
         script->objectsOffset = (uint8)(cursor - scriptEnd);
         cursor += sizeof(JSObjectArray);
     } else {
@@ -1507,16 +1502,18 @@ JSScript::NewScriptFromCG(JSContext *cx,
 
     if (script->nClosedArgs)
         memcpy(script->closedSlots, &cg->closedArgs[0], script->nClosedArgs * sizeof(uint32));
     if (script->nClosedVars) {
         memcpy(&script->closedSlots[script->nClosedArgs], &cg->closedVars[0],
                script->nClosedVars * sizeof(uint32));
     }
 
+    if (!cg->bindings.ensureShape(cx))
+        goto bad;
     cg->bindings.makeImmutable();
     script->bindings.transfer(cx, &cg->bindings);
 
     /*
      * We initialize fun->u.script to be the script constructed above
      * so that the debugger has a valid FUN_SCRIPT(fun).
      */
     fun = NULL;
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -171,17 +171,17 @@ enum BindingKind { NONE, ARGUMENT, VARIA
  */
 class Bindings {
     js::Shape *lastBinding;
     uint16 nargs;
     uint16 nvars;
     uint16 nupvars;
 
   public:
-    inline Bindings(JSContext *cx, EmptyShape *emptyCallShape);
+    inline Bindings(JSContext *cx);
 
     /*
      * Transfers ownership of bindings data from bindings into this fresh
      * Bindings instance. Once such a transfer occurs, the old bindings must
      * not be used again.
      */
     inline void transfer(JSContext *cx, Bindings *bindings);
 
@@ -198,16 +198,19 @@ class Bindings {
 
     uintN countArgsAndVars() const { return nargs + nvars; }
 
     uintN countLocalNames() const { return nargs + nvars + nupvars; }
 
     bool hasUpvars() const { return nupvars > 0; }
     bool hasLocalNames() const { return countLocalNames() > 0; }
 
+    /* Ensure these bindings have a shape lineage. */
+    inline bool ensureShape(JSContext *cx);
+
     /* Returns the shape lineage generated for these bindings. */
     inline const js::Shape *lastShape() const;
 
     enum {
         /*
          * A script may have no more than this many arguments, variables, or
          * upvars.
          */
@@ -519,16 +522,17 @@ 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 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 typeSetArgument(JSContext *cx, unsigned arg, js::types::jstype type);
     inline bool typeSetArgument(JSContext *cx, unsigned arg, const js::Value &value);
     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.
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -46,58 +46,73 @@
 #include "jsopcode.h"
 #include "jsregexp.h"
 #include "jsscript.h"
 #include "jsscope.h"
 
 namespace js {
 
 inline
-Bindings::Bindings(JSContext *cx, EmptyShape *emptyCallShape)
-  : lastBinding(emptyCallShape), nargs(0), nvars(0), nupvars(0)
+Bindings::Bindings(JSContext *cx)
+  : lastBinding(NULL), nargs(0), nvars(0), nupvars(0)
 {
 }
 
 inline void
 Bindings::transfer(JSContext *cx, Bindings *bindings)
 {
-    JS_ASSERT(lastBinding == cx->compartment->emptyCallShape);
+    JS_ASSERT(!lastBinding);
 
     *this = *bindings;
 #ifdef DEBUG
     bindings->lastBinding = NULL;
 #endif
 
     /* Preserve back-pointer invariants across the lastBinding transfer. */
-    if (lastBinding->inDictionary())
+    if (lastBinding && lastBinding->inDictionary())
         lastBinding->listp = &this->lastBinding;
 }
 
 inline void
 Bindings::clone(JSContext *cx, Bindings *bindings)
 {
-    JS_ASSERT(lastBinding == cx->compartment->emptyCallShape);
+    JS_ASSERT(!lastBinding);
 
     /*
      * Non-dictionary bindings are fine to share, as are dictionary bindings if
      * they're copy-on-modification.
      */
-    JS_ASSERT(!bindings->lastBinding->inDictionary() || bindings->lastBinding->frozen());
+    JS_ASSERT(!bindings->lastBinding ||
+              !bindings->lastBinding->inDictionary() ||
+              bindings->lastBinding->frozen());
 
     *this = *bindings;
 }
 
 const Shape *
 Bindings::lastShape() const
 {
     JS_ASSERT(lastBinding);
     JS_ASSERT_IF(lastBinding->inDictionary(), lastBinding->frozen());
     return lastBinding;
 }
 
+bool
+Bindings::ensureShape(JSContext *cx)
+{
+    if (!lastBinding) {
+        lastBinding = EmptyShape::create(cx, &js_CallClass);
+        if (!lastBinding) {
+            js_ReportOutOfMemory(cx);
+            return false;
+        }
+    }
+    return true;
+}
+
 } // namespace js
 
 inline JSFunction *
 JSScript::getFunction(size_t index)
 {
     JSObject *funobj = getObject(index);
     JS_ASSERT(funobj->isFunction());
     JS_ASSERT(funobj == (JSObject *) funobj->getPrivate());
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -3753,29 +3753,38 @@ mjit::Compiler::jsop_callprop(JSAtom *at
 
 bool
 mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache)
 {
     FrameEntry *lhs = frame.peek(-2);
     FrameEntry *rhs = frame.peek(-1);
 
     /* If the incoming type will never PIC, take slow path. */
-    if (monitored(PC) || (lhs->isTypeKnown() && lhs->getKnownType() != JSVAL_TYPE_OBJECT)) {
+    if (lhs->isTypeKnown() && lhs->getKnownType() != JSVAL_TYPE_OBJECT) {
         jsop_setprop_slow(atom, usePropCache);
         return true;
     }
 
     JSOp op = JSOp(*PC);
 
     ic::PICInfo::Kind kind = (op == JSOP_SETMETHOD)
                              ? ic::PICInfo::SETMETHOD
                              : ic::PICInfo::SET;
     PICGenInfo pic(kind, op, usePropCache);
     pic.atom = atom;
 
+    if (monitored(PC)) {
+        types::TypeSet *types = rhs->getTypeSet();
+        pic.typeMonitored = true;
+        pic.knownType = types ? types->getSingleType(cx, script) : types::TYPE_UNKNOWN;
+    } else {
+        pic.typeMonitored = false;
+        pic.knownType = types::TYPE_UNKNOWN;
+    }
+
     RESERVE_IC_SPACE(masm);
     RESERVE_OOL_SPACE(stubcc.masm);
 
     /* Guard that the type is an object. */
     Jump typeCheck;
     if (!lhs->isTypeKnown()) {
         RegisterID reg = frame.tempRegForType(lhs);
         pic.typeReg = reg;
@@ -4855,16 +4864,17 @@ mjit::Compiler::jsop_setgname_slow(JSAto
     frame.popn(2);
     pushSyncedEntry(0);
 }
 
 void
 mjit::Compiler::jsop_setgname(JSAtom *atom, bool usePropertyCache)
 {
     if (monitored(PC)) {
+        /* Global accesses are monitored only for a few names like __proto__. */
         jsop_setgname_slow(atom, usePropertyCache);
         return;
     }
 
 #if defined JS_MONOIC
     FrameEntry *objFe = frame.peek(-2);
     FrameEntry *fe = frame.peek(-1);
     JS_ASSERT_IF(objFe->isTypeKnown(), objFe->getKnownType() == JSVAL_TYPE_OBJECT);
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -224,16 +224,18 @@ class Compiler : public BaseCompiler
         RegisterID shapeReg;
         RegisterID objReg;
         RegisterID typeReg;
         bool usePropCache;
         Label shapeGuard;
         jsbytecode *pc;
         JSAtom *atom;
         bool hasTypeCheck;
+        bool typeMonitored;
+        types::jstype knownType;
         ValueRemat vr;
 #ifdef JS_HAS_IC_LABELS
         union {
             ic::GetPropLabels getPropLabels_;
             ic::SetPropLabels setPropLabels_;
             ic::BindNameLabels bindNameLabels_;
             ic::ScopeNameLabels scopeNameLabels_;
         };
@@ -280,16 +282,18 @@ class Compiler : public BaseCompiler
             ic.atom = atom;
             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;
 #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/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -195,16 +195,19 @@ 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)
     {
+        if (types::TypeIsObject(pic.knownType) && !((types::TypeObject*)pic.knownType)->marked)
+            pic.knownType = types::TYPE_UNKNOWN;
+
         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);
 
         FunctionPtr target(JS_FUNC_TO_DATA_PTR(void *, ic::SetProp));
@@ -592,35 +595,66 @@ class SetPropCompiler : public PICStubCo
              * Doing this would cause us to walk down this same update path
              * every time a reallocation is needed, however, which will
              * 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))
+                    return error();
+                if (f.jit()->recompilations != recompilations)
+                    return Lookup_Uncacheable;
+            }
+
             return generateStub(initialShape, shape, true, !obj->hasSlotsArray());
         }
 
         const Shape *shape = (const Shape *) prop;
         if (pic.kind == ic::PICInfo::SETMETHOD && !shape->isMethod())
             return disable("set method on non-method shape");
         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))
+                    return error();
+                if (f.jit()->recompilations != recompilations)
+                    return Lookup_Uncacheable;
+            }
         } else {
             if (shape->hasSetterValue())
                 return disable("scripted setter");
             if (shape->setterOp() != SetCallArg &&
                 shape->setterOp() != SetCallVar) {
                 return disable("setter");
             }
+            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))
+                        return error();
+                } else {
+                    if (!script->typeSetLocal(cx, slot, pic.knownType))
+                        return error();
+                }
+                if (f.jit()->recompilations != recompilations)
+                    return Lookup_Uncacheable;
+            }
         }
 
         JS_ASSERT(obj == holder);
         if (!pic.inlinePathPatched &&
             !obj->brandedOrHasMethodBarrier() &&
             shape->hasDefaultSetter() &&
             !obj->isDenseArray()) {
             return patchInline(shape, !obj->hasSlotsArray());
@@ -1731,33 +1765,39 @@ ic::SetProp(VMFrame &f, ic::PICInfo *pic
 
     JSScript *script = f.fp()->script();
     JS_ASSERT(pic->isSet());
 
     VoidStubPIC stub = pic->usePropCache
                        ? STRICT_VARIANT(DisabledSetPropIC)
                        : STRICT_VARIANT(DisabledSetPropICNoCache);
 
+    // Save this in case the compiler triggers a recompilation of this script.
+    JSAtom *atom = pic->atom;
+    VoidStubAtom nstub = pic->usePropCache
+                         ? STRICT_VARIANT(stubs::SetName)
+                         : STRICT_VARIANT(stubs::SetPropNoCache);
+
     //
     // Important: We update the PIC before looking up the property so that the
     // PIC is updated only if the property already exists. The PIC doesn't try
     // to optimize adding new properties; that is for the slow case.
     //
     // Also note, we can't use SetName for PROPINC PICs because the property
     // cache can't handle a GET and SET from the same scripted PC.
     if (pic->shouldUpdate(f.cx)) {
 
-        SetPropCompiler cc(f, script, obj, *pic, pic->atom, stub);
+        SetPropCompiler cc(f, script, obj, *pic, atom, stub);
         LookupStatus status = cc.update();
         if (status == Lookup_Error)
             THROW();
     }
     
     Value rval = f.regs.sp[-1];
-    stub(f, pic);
+    nstub(f, atom);
 }
 
 static void JS_FASTCALL
 DisabledCallPropIC(VMFrame &f, ic::PICInfo *pic)
 {
     stubs::CallProp(f, pic->atom);
 }
 
--- a/js/src/methodjit/PolyIC.h
+++ b/js/src/methodjit/PolyIC.h
@@ -442,18 +442,24 @@ struct PICInfo : public BasePolyIC {
     bool usePropCache : 1;
 
     // State flags.
     bool inlinePathPatched : 1;     // inline path has been patched
 
     RegisterID shapeReg : 5;        // also the out type reg
     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;
     
     inline bool isSet() const {
         return kind == SET || kind == SETMETHOD;
     }
     inline bool isGet() const {
         return kind == GET || kind == CALL;
     }
     inline bool isBind() const {