[INFER] Decompose property inc/dec ops for JM and TI, bug 647624.
authorBrian Hackett <bhackett1024@gmail.com>
Sat, 16 Jul 2011 08:25:22 -0700
changeset 77357 3273738a165ed11764226c01fb035b07e28e1853
parent 77356 77b0c25be7fbd3cb871f627cfccc3e8fb1e16b9a
child 77358 ce4c49e8575f3429be96e57f1b96c6b87fd91d7c
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs647624
milestone8.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
[INFER] Decompose property inc/dec ops for JM and TI, bug 647624.
js/src/jit-test/tests/basic/testIncElem4.js
js/src/jsanalyze.cpp
js/src/jsemit.cpp
js/src/jsinfer.cpp
js/src/jsinterp.cpp
js/src/jsopcode.cpp
js/src/jsopcode.h
js/src/jsopcode.tbl
js/src/jstracer.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/StubCalls.cpp
js/src/methodjit/StubCalls.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/testIncElem4.js
@@ -0,0 +1,9 @@
+
+var counter = 0;
+var x = { toString: function() { counter++; } };
+var y = {};
+
+for (var i = 0; i < 50; i++)
+  ++y[x];
+
+assertEq(counter, 50);
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -458,22 +458,28 @@ ScriptAnalysis::analyzeBytecode(JSContex
                              definedLocals[local] <= offset);
                 if (definedLocals[local] == LOCAL_CONDITIONALLY_DEFINED)
                     setLocal(local, offset);
             }
             defineArray = code->defineArray = NULL;
             defineCount = code->defineCount = 0;
         }
 
-        unsigned nuses = GetUseCount(script, offset);
-        unsigned ndefs = GetDefCount(script, offset);
+        /*
+         * Treat decompose ops as no-ops which do not adjust the stack. We will
+         * pick up the stack depths as we go through the decomposed version.
+         */
+        if (!(js_CodeSpec[op].format & JOF_DECOMPOSE)) {
+            unsigned nuses = GetUseCount(script, offset);
+            unsigned ndefs = GetDefCount(script, offset);
 
-        JS_ASSERT(stackDepth >= nuses);
-        stackDepth -= nuses;
-        stackDepth += ndefs;
+            JS_ASSERT(stackDepth >= nuses);
+            stackDepth -= nuses;
+            stackDepth += ndefs;
+        }
 
         switch (op) {
 
           case JSOP_RETURN:
           case JSOP_STOP:
             numReturnSites_++;
             break;
 
@@ -483,20 +489,16 @@ ScriptAnalysis::analyzeBytecode(JSContex
             isInlineable = false;
             break;
 
           case JSOP_NAME:
           case JSOP_CALLNAME:
           case JSOP_BINDNAME:
           case JSOP_SETNAME:
           case JSOP_DELNAME:
-          case JSOP_INCNAME:
-          case JSOP_DECNAME:
-          case JSOP_NAMEINC:
-          case JSOP_NAMEDEC:
           case JSOP_FORNAME:
           case JSOP_QNAMEPART:
           case JSOP_QNAMECONST:
             checkAliasedName(cx, pc);
             usesScope = true;
             isInlineable = false;
             break;
 
@@ -1369,16 +1371,23 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
                 if (code->fallthrough || code->jumpFallthrough)
                     mergeValue(cx, offset, values[v.slot], &v);
                 mergeBranchTarget(cx, values[v.slot], v.slot, branchTargets);
                 values[v.slot] = v.value;
             }
             freezeNewValues(cx, offset);
         }
 
+        JSOp op = (JSOp)*pc;
+
+        if (js_CodeSpec[op].format & JOF_DECOMPOSE) {
+            offset = successorOffset;
+            continue;
+        }
+
         unsigned nuses = GetUseCount(script, offset);
         unsigned ndefs = GetDefCount(script, offset);
         JS_ASSERT(stackDepth >= nuses);
 
         unsigned xuses = ExtendedUse(pc) ? nuses + 1 : nuses;
 
         if (xuses) {
             code->poppedValues = (SSAValue *)ArenaArray<SSAValue>(pool, xuses);
@@ -1436,17 +1445,16 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
                 setOOM(cx);
                 return;
             }
             PodZero(code->pushedUses, xdefs);
         }
 
         stackDepth += ndefs;
 
-        JSOp op = (JSOp)*pc;
         switch (op) {
           case JSOP_SETARG:
           case JSOP_SETLOCAL:
           case JSOP_SETLOCALPOP:
           case JSOP_DEFLOCALFUN:
           case JSOP_DEFLOCALFUN_FC:
           case JSOP_FORARG:
           case JSOP_FORLOCAL:
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -192,54 +192,28 @@ EmitCheck(JSContext *cx, JSCodeGenerator
 }
 
 static void
 UpdateDepth(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t target)
 {
     jsbytecode *pc;
     JSOp op;
     const JSCodeSpec *cs;
-    uintN extra, depth, nuses;
+    uintN extra, nuses;
     intN ndefs;
 
     pc = CG_CODE(cg, target);
     op = (JSOp) *pc;
     cs = &js_CodeSpec[op];
 #ifdef JS_TRACER
     extern uint8 js_opcode2extra[];
     extra = js_opcode2extra[op];
 #else
     extra = 0;
 #endif
-    if ((cs->format & JOF_TMPSLOT_MASK) || extra) {
-        depth = (uintN) cg->stackDepth +
-                ((cs->format & JOF_TMPSLOT_MASK) >> JOF_TMPSLOT_SHIFT) +
-                extra;
-        /* :TODO: hack - remove later. */
-        switch (op) {
-          case JSOP_PROPINC:
-          case JSOP_PROPDEC:
-            depth += 1;
-            break;
-          case JSOP_NAMEINC:
-          case JSOP_NAMEDEC:
-          case JSOP_INCNAME:
-          case JSOP_DECNAME:
-          case JSOP_GNAMEINC:
-          case JSOP_GNAMEDEC:
-          case JSOP_INCGNAME:
-          case JSOP_DECGNAME:
-            depth += 2;
-            break;
-          default:
-            break;
-        }
-        if (depth > cg->maxStackDepth)
-            cg->maxStackDepth = depth;
-    }
 
     nuses = js_GetStackUses(cs, op, pc);
     cg->stackDepth -= nuses;
     JS_ASSERT(cg->stackDepth >= 0);
     if (cg->stackDepth < 0) {
         char numBuf[12];
         TokenStream *ts;
 
@@ -265,16 +239,32 @@ UpdateDepth(JSContext *cx, JSCodeGenerat
         OBJ_SET_BLOCK_DEPTH(cx, blockObj, cg->stackDepth);
         ndefs = OBJ_BLOCK_COUNT(cx, blockObj);
     }
     cg->stackDepth += ndefs;
     if ((uintN)cg->stackDepth > cg->maxStackDepth)
         cg->maxStackDepth = cg->stackDepth;
 }
 
+/*
+ * Table of decomposed lengths for each bytecode, initially zeroed. Every
+ * opcode always has the same decomposed code generated for it, and rather than
+ * track/maintain this info in a static table like jsopcode.tbl, we just update
+ * this table for opcodes which have actually been emitted.
+ */
+uint8 js_decomposeLengthTable[256] = {};
+
+static inline void
+SetDecomposeLength(JSOp op, uintN length)
+{
+    JS_ASSERT(length < 256);
+    JS_ASSERT_IF(js_decomposeLengthTable[op], js_decomposeLengthTable[op] == length);
+    js_decomposeLengthTable[op] = length;
+}
+
 ptrdiff_t
 js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op)
 {
     ptrdiff_t offset = EmitCheck(cx, cg, op, 1);
 
     if (offset >= 0) {
         *CG_NEXT(cg)++ = (jsbytecode)op;
         UpdateDepth(cx, cg, offset);
@@ -2917,16 +2907,115 @@ EmitPropOp(JSContext *cx, JSParseNode *p
     if (js_NewSrcNote2(cx, cg, SRC_PCBASE,
                        CG_OFFSET(cg) - pn2->pn_offset) < 0) {
         return JS_FALSE;
     }
 
     return EmitAtomOp(cx, pn, op, cg);
 }
 
+static bool
+EmitPropIncDec(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
+{
+    if (!EmitPropOp(cx, pn, op, cg, false))
+        return false;
+
+    /*
+     * The stack is the same depth before/after INCPROP, so no balancing to do
+     * before the decomposed version.
+     */
+    int start = CG_OFFSET(cg);
+
+    const JSCodeSpec *cs = &js_CodeSpec[op];
+    JS_ASSERT(cs->format & JOF_PROP);
+    JS_ASSERT(cs->format & (JOF_INC | JOF_DEC));
+
+    bool post = (cs->format & JOF_POST);
+    JSOp binop = (cs->format & JOF_INC) ? JSOP_ADD : JSOP_SUB;
+
+                                                   // OBJ
+    if (js_Emit1(cx, cg, JSOP_DUP) < 0)            // OBJ OBJ
+        return false;
+    if (!EmitAtomOp(cx, pn, JSOP_GETPROP, cg))     // OBJ V
+        return false;
+    if (js_Emit1(cx, cg, JSOP_POS) < 0)            // OBJ N
+        return false;
+    if (post && js_Emit1(cx, cg, JSOP_DUP) < 0)    // OBJ N? N
+        return false;
+    if (js_Emit1(cx, cg, JSOP_ONE) < 0)            // OBJ N? N 1
+        return false;
+    if (js_Emit1(cx, cg, binop) < 0)               // OBJ N? N+1
+        return false;
+
+    if (post) {
+        if (js_Emit2(cx, cg, JSOP_PICK, (jsbytecode)2) < 0) // N? N+1 OBJ
+            return false;
+        if (js_Emit1(cx, cg, JSOP_SWAP) < 0)                // N? OBJ N+1
+            return false;
+    }
+
+    if (!EmitAtomOp(cx, pn, JSOP_SETPROP, cg))     // N? N+1
+        return false;
+    if (post && js_Emit1(cx, cg, JSOP_POP) < 0)    // RESULT
+        return false;
+
+    SetDecomposeLength(op, CG_OFFSET(cg) - start);
+
+    return true;
+}
+
+static bool
+EmitNameIncDec(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
+{
+    if (!EmitAtomOp(cx, pn, op, cg))
+        return false;
+
+    /* Remove the result to restore the stack depth before the INCNAME. */
+    cg->stackDepth--;
+
+    int start = CG_OFFSET(cg);
+
+    const JSCodeSpec *cs = &js_CodeSpec[op];
+    JS_ASSERT((cs->format & JOF_NAME) || (cs->format & JOF_GNAME));
+    JS_ASSERT(cs->format & (JOF_INC | JOF_DEC));
+
+    bool global = (cs->format & JOF_GNAME);
+    bool post = (cs->format & JOF_POST);
+    JSOp binop = (cs->format & JOF_INC) ? JSOP_ADD : JSOP_SUB;
+
+    if (!EmitAtomOp(cx, pn, global ? JSOP_BINDGNAME : JSOP_BINDNAME, cg))  // OBJ
+        return false;
+    if (!EmitAtomOp(cx, pn, global ? JSOP_GETGNAME : JSOP_NAME, cg))       // OBJ V
+        return false;
+    if (js_Emit1(cx, cg, JSOP_POS) < 0)            // OBJ N
+        return false;
+    if (post && js_Emit1(cx, cg, JSOP_DUP) < 0)    // OBJ N? N
+        return false;
+    if (js_Emit1(cx, cg, JSOP_ONE) < 0)            // OBJ N? N 1
+        return false;
+    if (js_Emit1(cx, cg, binop) < 0)               // OBJ N? N+1
+        return false;
+
+    if (post) {
+        if (js_Emit2(cx, cg, JSOP_PICK, (jsbytecode)2) < 0) // N? N+1 OBJ
+            return false;
+        if (js_Emit1(cx, cg, JSOP_SWAP) < 0)                // N? OBJ N+1
+            return false;
+    }
+
+    if (!EmitAtomOp(cx, pn, JSOP_SETPROP, cg))     // N? N+1
+        return false;
+    if (post && js_Emit1(cx, cg, JSOP_POP) < 0)    // RESULT
+        return false;
+
+    SetDecomposeLength(op, CG_OFFSET(cg) - start);
+
+    return true;
+}
+
 static JSBool
 EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
 {
     ptrdiff_t top;
     JSParseNode *left, *right, *next, ltmp, rtmp;
     int32_t slot;
 
     top = CG_OFFSET(cg);
@@ -3048,16 +3137,73 @@ EmitElemOp(JSContext *cx, JSParseNode *p
               right->pn_op == JSOP_QNAMEPART);
     if (!js_EmitTree(cx, cg, right))
         return JS_FALSE;
     if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0)
         return JS_FALSE;
     return EmitElemOpBase(cx, cg, op);
 }
 
+static bool
+EmitElemIncDec(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
+{
+    if (!EmitElemOp(cx, pn, op, cg))
+        return false;
+
+    /* INCELEM pops two values and pushes one, so restore the initial depth. */
+    cg->stackDepth++;
+
+    int start = CG_OFFSET(cg);
+
+    const JSCodeSpec *cs = &js_CodeSpec[op];
+    JS_ASSERT(cs->format & JOF_ELEM);
+    JS_ASSERT(cs->format & (JOF_INC | JOF_DEC));
+
+    bool post = (cs->format & JOF_POST);
+    JSOp binop = (cs->format & JOF_INC) ? JSOP_ADD : JSOP_SUB;
+
+    /*
+     * We need to convert the key to an object id first, so that we do not do
+     * it inside both the GETELEM and the SETELEM.
+     */
+                                                 // OBJ KEY*
+    if (js_Emit1(cx, cg, JSOP_TOID) < 0)         // OBJ KEY
+        return false;
+    if (js_Emit1(cx, cg, JSOP_DUP2) < 0)         // OBJ KEY OBJ KEY
+        return false;
+    if (!EmitElemOpBase(cx, cg, JSOP_GETELEM))   // OBJ KEY V
+        return false;
+    if (js_Emit1(cx, cg, JSOP_POS) < 0)          // OBJ KEY N
+        return false;
+    if (post && js_Emit1(cx, cg, JSOP_DUP) < 0)  // OBJ KEY N? N
+        return false;
+    if (js_Emit1(cx, cg, JSOP_ONE) < 0)          // OBJ KEY N? N 1
+        return false;
+    if (js_Emit1(cx, cg, binop) < 0)             // OBJ KEY N? N+1
+        return false;
+
+    if (post) {
+        if (js_Emit2(cx, cg, JSOP_PICK, (jsbytecode)3) < 0)  // KEY N N+1 OBJ
+            return false;
+        if (js_Emit2(cx, cg, JSOP_PICK, (jsbytecode)3) < 0)  // N N+1 OBJ KEY
+            return false;
+        if (js_Emit2(cx, cg, JSOP_PICK, (jsbytecode)2) < 0)  // N OBJ KEY N+1
+            return false;
+    }
+
+    if (!EmitElemOpBase(cx, cg, JSOP_SETELEM))   // N? N+1
+        return false;
+    if (post && js_Emit1(cx, cg, JSOP_POP) < 0)  // RESULT
+        return false;
+
+    SetDecomposeLength(op, CG_OFFSET(cg) - start);
+
+    return true;
+}
+
 static JSBool
 EmitNumberOp(JSContext *cx, jsdouble dval, JSCodeGenerator *cg)
 {
     int32_t ival;
     uint32 u;
     ptrdiff_t off;
     jsbytecode *pc;
 
@@ -6514,17 +6660,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
             if (op == JSOP_CALLEE) {
                 if (js_Emit1(cx, cg, op) < 0)
                     return JS_FALSE;
             } else if (!pn2->pn_cookie.isFree()) {
                 atomIndex = pn2->pn_cookie.asInteger();
                 EMIT_UINT16_IMM_OP(op, atomIndex);
             } else {
                 JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
-                if (!EmitAtomOp(cx, pn2, op, cg))
+                if (!EmitNameIncDec(cx, pn2, op, cg))
                     return JS_FALSE;
                 break;
             }
             if (pn2->isConst()) {
                 if (js_Emit1(cx, cg, JSOP_POS) < 0)
                     return JS_FALSE;
                 op = PN_OP(pn);
                 if (!(js_CodeSpec[op].format & JOF_POST)) {
@@ -6532,21 +6678,21 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
                         return JS_FALSE;
                     op = (js_CodeSpec[op].format & JOF_INC) ? JSOP_ADD : JSOP_SUB;
                     if (js_Emit1(cx, cg, op) < 0)
                         return JS_FALSE;
                 }
             }
             break;
           case TOK_DOT:
-            if (!EmitPropOp(cx, pn2, op, cg, JS_FALSE))
+            if (!EmitPropIncDec(cx, pn2, op, cg))
                 return JS_FALSE;
             break;
           case TOK_LB:
-            if (!EmitElemOp(cx, pn2, op, cg))
+            if (!EmitElemIncDec(cx, pn2, op, cg))
                 return JS_FALSE;
             break;
           case TOK_LP:
             if (!js_EmitTree(cx, cg, pn2))
                 return JS_FALSE;
             if (js_NewSrcNote2(cx, cg, SRC_PCBASE,
                                CG_OFFSET(cg) - pn2->pn_offset) < 0) {
                 return JS_FALSE;
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -525,20 +525,17 @@ public:
     /* Property being accessed. */
     jsid id;
 
     TypeConstraintProp(JSScript *script, jsbytecode *pc,
                        TypeSet *target, jsid id, bool assign)
         : TypeConstraint("prop"), script(script), pc(pc),
           assign(assign), target(target), id(id)
     {
-        JS_ASSERT(script && pc);
-
-        /* If the target is NULL, this is as an inc/dec on the property. */
-        JS_ASSERT_IF(!target, assign);
+        JS_ASSERT(script && pc && target);
     }
 
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
 TypeSet::addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
                         TypeSet *target, jsid id)
@@ -974,18 +971,16 @@ MarkPropertyAccessUnknown(JSContext *cx,
 /*
  * Handle a property access on a specific object. All property accesses go through
  * here, whether via x.f, x[f], or global name accesses.
  */
 static inline void
 PropertyAccess(JSContext *cx, JSScript *script, jsbytecode *pc, TypeObject *object,
                bool assign, TypeSet *target, jsid id)
 {
-    JS_ASSERT_IF(!target, assign);
-
     /* Monitor assigns on the 'prototype' property. */
     if (assign && id == id_prototype(cx)) {
         cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
         return;
     }
 
     /* Monitor accesses on other properties with special behavior we don't keep track of. */
     if (id == id___proto__(cx) || id == id_constructor(cx) || id == id_caller(cx)) {
@@ -999,33 +994,25 @@ PropertyAccess(JSContext *cx, JSScript *
     /* Reads from objects with unknown properties are unknown, writes to such objects are ignored. */
     if (object->unknownProperties()) {
         if (!assign)
             MarkPropertyAccessUnknown(cx, script, pc, target);
         return;
     }
 
     /* Capture the effects of a standard property access. */
-    if (target) {
-        TypeSet *types = object->getProperty(cx, id, assign);
-        if (!types)
-            return;
-        if (assign)
-            target->addSubset(cx, types);
-        else if (UsePropertyTypeBarrier(pc))
-            types->addSubsetBarrier(cx, script, pc, target);
-        else
-            types->addSubset(cx, target);
-    } else {
-        TypeSet *readTypes = object->getProperty(cx, id, false);
-        TypeSet *writeTypes = object->getProperty(cx, id, true);
-        if (!readTypes || !writeTypes)
-            return;
-        readTypes->addArith(cx, writeTypes);
-    }
+    TypeSet *types = object->getProperty(cx, id, assign);
+    if (!types)
+        return;
+    if (assign)
+        target->addSubset(cx, types);
+    else if (UsePropertyTypeBarrier(pc))
+        types->addSubsetBarrier(cx, script, pc, target);
+    else
+        types->addSubset(cx, target);
 }
 
 /* Whether the JSObject/TypeObject referent of an access on type cannot be determined. */
 static inline bool
 UnknownPropertyAccess(JSScript *script, Type type)
 {
     return type.isUnknown()
         || type.isAnyObject()
@@ -2171,24 +2158,16 @@ TypeCompartment::monitorBytecode(JSConte
     Bytecode &code = analysis->getCode(pc);
 
     if (returnOnly ? code.monitoredTypesReturn : code.monitoredTypes)
         return;
 
     InferSpew(ISpewOps, "addMonitorNeeded:%s #%u:%05u",
               returnOnly ? " returnOnly" : "", script->id(), offset);
 
-    /*
-     * When monitoring side effects for incops, mark the result of the opcode
-     * as unknown. These bytecodes are not JOF_TYPESET so there is no place to
-     * add type barriers at.
-     */
-    if (js_CodeSpec[*pc].format & (JOF_INC | JOF_DEC))
-        analysis->addPushedType(cx, offset, 0, Type::UnknownType());
-
     /* Dynamically monitor this call to keep track of its result types. */
     if (js_CodeSpec[*pc].format & JOF_INVOKE)
         code.monitoredTypesReturn = true;
 
     if (!returnOnly)
         code.monitoredTypes = true;
 
     cx->compartment->types.addPendingRecompile(cx, script);
@@ -2245,16 +2224,18 @@ TypeCompartment::markSetsUnknown(JSConte
         }
         MarkTypeObjectListSetsUnknown(cx, script->types.typeObjects, target);
         if (script->hasAnalysis() && script->analysis(cx)->ranInference()) {
             for (unsigned i = 0; i < script->length; i++) {
                 if (!script->analysis(cx)->maybeCode(i))
                     continue;
                 jsbytecode *pc = script->code + i;
                 UntrapOpcode untrap(cx, script, pc);
+                if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
+                    continue;
                 unsigned defCount = GetDefCount(script, i);
                 if (ExtendedDef(pc))
                     defCount++;
                 for (unsigned j = 0; j < defCount; j++) {
                     TypeSet *types = script->analysis(cx)->pushedTypes(pc, j);
                     if (types->hasType(Type::ObjectType(target)))
                         types->addType(cx, Type::AnyObjectType());
                 }
@@ -3368,26 +3349,16 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
             pushed[0].addPropagateThis(cx, script, pc, Type::UnknownType());
         }
 
         if (CheckNextTest(pc))
             pushed[0].addType(cx, Type::UndefinedType());
         break;
       }
 
-      case JSOP_INCGNAME:
-      case JSOP_DECGNAME:
-      case JSOP_GNAMEINC:
-      case JSOP_GNAMEDEC: {
-        jsid id = GetAtomId(cx, script, pc, 0);
-        PropertyAccess(cx, script, pc, script->global()->type(), true, NULL, id);
-        PropertyAccess(cx, script, pc, script->global()->type(), false, &pushed[0], id);
-        break;
-      }
-
       case JSOP_NAME:
       case JSOP_CALLNAME: {
         /*
          * The first value pushed by NAME/CALLNAME must always be added to the
          * bytecode types, we don't model these opcodes with inference.
          */
         TypeSet *seen = script->types.bytecodeTypes(pc);
         addTypeBarrier(cx, pc, seen, Type::UnknownType());
@@ -3412,23 +3383,16 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
       }
 
       case JSOP_SETNAME:
       case JSOP_SETCONST:
         cx->compartment->types.monitorBytecode(cx, script, offset);
         poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
 
-      case JSOP_INCNAME:
-      case JSOP_DECNAME:
-      case JSOP_NAMEINC:
-      case JSOP_NAMEDEC:
-        cx->compartment->types.monitorBytecode(cx, script, offset);
-        break;
-
       case JSOP_GETXPROP: {
         TypeSet *seen = script->types.bytecodeTypes(pc);
         addTypeBarrier(cx, pc, seen, Type::UnknownType());
         seen->addSubset(cx, &pushed[0]);
         break;
       }
 
       case JSOP_GETFCSLOT:
@@ -3544,25 +3508,16 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
         seen->addSubset(cx, &pushed[0]);
         if (op == JSOP_CALLPROP)
             poppedTypes(pc, 0)->addFilterPrimitives(cx, &pushed[1], true);
         if (CheckNextTest(pc))
             pushed[0].addType(cx, Type::UndefinedType());
         break;
       }
 
-      case JSOP_INCPROP:
-      case JSOP_DECPROP:
-      case JSOP_PROPINC:
-      case JSOP_PROPDEC: {
-        jsid id = GetAtomId(cx, script, pc, 0);
-        poppedTypes(pc, 0)->addGetProperty(cx, script, pc, &pushed[0], id);
-        break;
-      }
-
       /*
        * We only consider ELEM accesses on integers below. Any element access
        * which is accessing a non-integer property must be monitored.
        */
 
       case JSOP_GETELEM:
       case JSOP_CALLELEM: {
         TypeSet *seen = script->types.bytecodeTypes(pc);
@@ -3574,29 +3529,30 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
         seen->addSubset(cx, &pushed[0]);
         if (op == JSOP_CALLELEM)
             poppedTypes(pc, 1)->addFilterPrimitives(cx, &pushed[1], true);
         if (CheckNextTest(pc))
             pushed[0].addType(cx, Type::UndefinedType());
         break;
       }
 
-      case JSOP_INCELEM:
-      case JSOP_DECELEM:
-      case JSOP_ELEMINC:
-      case JSOP_ELEMDEC:
-        poppedTypes(pc, 1)->addGetProperty(cx, script, pc, &pushed[0], JSID_VOID);
-        break;
-
       case JSOP_SETELEM:
       case JSOP_SETHOLE:
         poppedTypes(pc, 2)->addSetProperty(cx, script, pc, poppedTypes(pc, 0), JSID_VOID);
         poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
 
+      case JSOP_TOID:
+        /*
+         * This is only used for element inc/dec ops; any id produced which
+         * is not an integer must be monitored.
+         */
+        pushed[0].addType(cx, Type::Int32Type());
+        break;
+
       case JSOP_THIS:
         script->types.thisTypes()->addTransformThis(cx, script, &pushed[0]);
         break;
 
       case JSOP_RETURN:
       case JSOP_SETRVAL:
         if (script->fun)
             poppedTypes(pc, 0)->addSubset(cx, script->types.returnTypes());
@@ -4001,19 +3957,21 @@ ScriptAnalysis::analyzeTypes(JSContext *
 
     unsigned offset = 0;
     while (offset < script->length) {
         Bytecode *code = maybeCode(offset);
 
         jsbytecode *pc = script->code + offset;
         UntrapOpcode untrap(cx, script, pc);
 
-        if (code && !analyzeTypesBytecode(cx, offset, state)) {
-            cx->compartment->types.setPendingNukeTypes(cx);
-            return;
+        if (code && !(js_CodeSpec[*pc].format & JOF_DECOMPOSE)) {
+            if (!analyzeTypesBytecode(cx, offset, state)) {
+                cx->compartment->types.setPendingNukeTypes(cx);
+                return;
+            }
         }
 
         offset += GetBytecodeLength(pc);
     }
 
     for (unsigned i = 0; i < state.phiNodes.length(); i++) {
         SSAPhiNode *node = state.phiNodes[i];
         for (unsigned j = 0; j < node->length; j++) {
@@ -4582,16 +4540,19 @@ ScriptAnalysis::printTypes(JSContext *cx
      */
     for (unsigned offset = 0; offset < script->length; offset++) {
         if (!maybeCode(offset))
             continue;
 
         jsbytecode *pc = script->code + offset;
         UntrapOpcode untrap(cx, script, pc);
 
+        if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
+            continue;
+
         unsigned defCount = GetDefCount(script, offset);
         if (!defCount)
             continue;
 
         for (unsigned i = 0; i < defCount; i++) {
             TypeSet *types = pushedTypes(offset, i);
 
             if (types->unknown()) {
@@ -4664,16 +4625,19 @@ ScriptAnalysis::printTypes(JSContext *cx
         if (!maybeCode(offset))
             continue;
 
         jsbytecode *pc = script->code + offset;
         UntrapOpcode untrap(cx, script, pc);
 
         PrintBytecode(cx, script, pc);
 
+        if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
+            continue;
+
         if (js_CodeSpec[*pc].format & JOF_TYPESET) {
             TypeSet *types = script->types.bytecodeTypes(pc);
             printf("  typeset %d:", (int) (types - script->types.typeArray));
             types->print(cx);
             printf("\n");
         }
 
         unsigned defCount = GetDefCount(script, offset);
@@ -4827,39 +4791,22 @@ TypeDynamicResult(JSContext *cx, JSScrip
         }
         return;
     }
 
     /*
      * For inc/dec ops, we need to go back and reanalyze the affected opcode
      * taking the overflow into account. We won't see an explicit adjustment
      * of the type of the thing being inc/dec'ed, nor will adding TYPE_DOUBLE to
-     * the pushed value affect that type. We only handle inc/dec operations
-     * that do not have an object lvalue; INCNAME/INCPROP/INCELEM and friends
-     * should call addTypeProperty to reflect the property change.
+     * the pushed value affect that type.
      */
     JSOp op = JSOp(*pc);
     const JSCodeSpec *cs = &js_CodeSpec[op];
     if (cs->format & (JOF_INC | JOF_DEC)) {
         switch (op) {
-          case JSOP_INCGNAME:
-          case JSOP_DECGNAME:
-          case JSOP_GNAMEINC:
-          case JSOP_GNAMEDEC: {
-            jsid id = GetAtomId(cx, script, pc, 0);
-            TypeObject *global = script->global()->type();
-            if (!global->unknownProperties()) {
-                TypeSet *types = global->getProperty(cx, id, true);
-                if (!types)
-                    break;
-                types->addType(cx, type);
-            }
-            break;
-          }
-
           case JSOP_INCLOCAL:
           case JSOP_DECLOCAL:
           case JSOP_LOCALINC:
           case JSOP_LOCALDEC:
           case JSOP_INCARG:
           case JSOP_DECARG:
           case JSOP_ARGINC:
           case JSOP_ARGDEC: {
@@ -4928,17 +4875,17 @@ TypeDynamicResult(JSContext *cx, JSScrip
         ObjectStateChange(cx, script->fun->type(), false, true);
 }
 
 void
 TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval)
 {
     UntrapOpcode untrap(cx, script, pc);
 
-    /* Allow the non-TYPESET scenario to simplify stubs invoked by INC* ops. Yuck. */
+    /* Allow the non-TYPESET scenario to simplify stubs used in compound opcodes. */
     if (!(js_CodeSpec[*pc].format & JOF_TYPESET))
         return;
 
     Type type = GetValueType(cx, rval);
     TypeSet *types = script->types.bytecodeTypes(pc);
     if (types->hasType(type))
         return;
 
@@ -5006,19 +4953,21 @@ IgnorePushed(const jsbytecode *pc, unsig
       case JSOP_FORARG:
       case JSOP_FORPROP:
       case JSOP_FORELEM:
       case JSOP_ITER:
       case JSOP_MOREITER:
       case JSOP_ENDITER:
         return true;
 
-      /* DUP can be applied to values pushed by other opcodes we don't model. */
+      /* Ops which can manipulate values pushed by opcodes we don't model. */
       case JSOP_DUP:
       case JSOP_DUP2:
+      case JSOP_SWAP:
+      case JSOP_PICK:
         return true;
 
       /* We don't keep track of state indicating whether there is a pending exception. */
       case JSOP_FINALLY:
         return true;
 
       /*
        * We don't treat GETLOCAL immediately followed by a pop as a use-before-def,
@@ -5119,16 +5068,19 @@ JSScript::typeSetFunction(JSContext *cx,
 #ifdef DEBUG
 
 void
 TypeScript::checkBytecode(JSContext *cx, jsbytecode *pc, const js::Value *sp)
 {
     AutoEnterTypeInference enter(cx);
     UntrapOpcode untrap(cx, script(), pc);
 
+    if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
+        return;
+
     if (!script()->hasAnalysis() || !script()->analysis(cx)->ranInference())
         return;
     ScriptAnalysis *analysis = script()->analysis(cx);
 
     int defCount = GetDefCount(script(), pc - script()->code);
 
     for (int i = 0; i < defCount; i++) {
         const js::Value &val = sp[-defCount + i];
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -3373,75 +3373,106 @@ BEGIN_CASE(JSOP_DELELEM)
     /* Get or set the element. */
     if (!obj->deleteProperty(cx, id, &regs.sp[-2], script->strictModeCode))
         goto error;
 
     regs.sp--;
 }
 END_CASE(JSOP_DELELEM)
 
+BEGIN_CASE(JSOP_TOID)
+{
+    /*
+     * There must be an object value below the id, which will not be popped
+     * but is necessary in interning the id for XML.
+     */
+    JSObject *obj;
+    FETCH_OBJECT(cx, -2, obj);
+
+    jsid id;
+    FETCH_ELEMENT_ID(obj, -1, id);
+
+    if (!JSID_IS_INT(id))
+        script->types.monitorUnknown(cx, regs.pc);
+}
+END_CASE(JSOP_TOID)
+
 BEGIN_CASE(JSOP_TYPEOFEXPR)
 BEGIN_CASE(JSOP_TYPEOF)
 {
     const Value &ref = regs.sp[-1];
     JSType type = JS_TypeOfValue(cx, Jsvalify(ref));
     JSAtom *atom = rt->atomState.typeAtoms[type];
     regs.sp[-1].setString(atom);
 }
 END_CASE(JSOP_TYPEOF)
 
 BEGIN_CASE(JSOP_VOID)
     regs.sp[-1].setUndefined();
 END_CASE(JSOP_VOID)
 
 {
+    /*
+     * Property incops are followed by an equivalent decomposed version,
+     * and we have the option of running either. If type inference is enabled
+     * we run the decomposed version to accumulate observed types and
+     * overflows which inference can process, otherwise we run the fat opcode
+     * as doing so is faster and is what the tracer needs while recording.
+     */
     JSObject *obj;
     JSAtom *atom;
     jsid id;
     jsint i;
 
 BEGIN_CASE(JSOP_INCELEM)
 BEGIN_CASE(JSOP_DECELEM)
 BEGIN_CASE(JSOP_ELEMINC)
 BEGIN_CASE(JSOP_ELEMDEC)
 
+    if (cx->typeInferenceEnabled())
+        DO_NEXT_OP(JSOP_INCELEM_LENGTH);
+
     /*
      * Delay fetching of id until we have the object to ensure the proper
      * evaluation order. See bug 372331.
      */
     id = JSID_VOID;
     i = -2;
     goto fetch_incop_obj;
 
 BEGIN_CASE(JSOP_INCPROP)
 BEGIN_CASE(JSOP_DECPROP)
 BEGIN_CASE(JSOP_PROPINC)
 BEGIN_CASE(JSOP_PROPDEC)
+
+    if (cx->typeInferenceEnabled())
+        DO_NEXT_OP(JSOP_INCPROP_LENGTH);
+
     LOAD_ATOM(0, atom);
     id = ATOM_TO_JSID(atom);
     i = -1;
 
   fetch_incop_obj:
     FETCH_OBJECT(cx, i, obj);
-    if (JSID_IS_VOID(id)) {
+    if (JSID_IS_VOID(id))
         FETCH_ELEMENT_ID(obj, -1, id);
-        if (!JSID_IS_INT(id))
-            script->types.monitorUnknown(cx, regs.pc);
-    }
     goto do_incop;
 
 BEGIN_CASE(JSOP_INCNAME)
 BEGIN_CASE(JSOP_DECNAME)
 BEGIN_CASE(JSOP_NAMEINC)
 BEGIN_CASE(JSOP_NAMEDEC)
 BEGIN_CASE(JSOP_INCGNAME)
 BEGIN_CASE(JSOP_DECGNAME)
 BEGIN_CASE(JSOP_GNAMEINC)
 BEGIN_CASE(JSOP_GNAMEDEC)
 {
+    if (cx->typeInferenceEnabled())
+        DO_NEXT_OP(JSOP_INCNAME_LENGTH);
+
     obj = &regs.fp()->scopeChain();
 
     bool global = (js_CodeSpec[op].format & JOF_GNAME);
     if (global)
         obj = obj->getGlobal();
 
     JSObject *obj2;
     PropertyCacheEntry *entry;
@@ -3453,17 +3484,17 @@ BEGIN_CASE(JSOP_GNAMEDEC)
             Value &rref = obj->nativeGetSlotRef(slot);
             int32_t tmp;
             if (JS_LIKELY(rref.isInt32() && CanIncDecWithoutOverflow(tmp = rref.toInt32()))) {
                 int32_t inc = tmp + ((js_CodeSpec[op].format & JOF_INC) ? 1 : -1);
                 if (!(js_CodeSpec[op].format & JOF_POST))
                     tmp = inc;
                 rref.getInt32Ref() = inc;
                 PUSH_INT32(tmp);
-                len = JSOP_INCNAME_LENGTH;
+                len = JSOP_INCNAME_LENGTH + GetDecomposeLength(op);
                 DO_NEXT_OP(len);
             }
         }
         LOAD_ATOM(0, atom);
     }
 
     id = ATOM_TO_JSID(atom);
     JSProperty *prop;
@@ -3480,24 +3511,16 @@ do_incop:
     /*
      * We need a root to store the value to leave on the stack until
      * we have done with obj->setProperty.
      */
     PUSH_NULL();
     if (!obj->getProperty(cx, id, &regs.sp[-1]))
         goto error;
 
-    /*
-     * Add undefined to the object itself when read out during an incop.
-     * The getProperty can produce undefined without being accounted for by
-     * type information, and types.monitor will not be update the object itself.
-     */
-    if (regs.sp[-1].isUndefined())
-        AddTypePropertyId(cx, obj, id, types::Type::UndefinedType());
-
     const JSCodeSpec *cs = &js_CodeSpec[op];
     JS_ASSERT(cs->ndefs == 1);
     JS_ASSERT((cs->format & JOF_TMPSLOT_MASK) >= JOF_TMPSLOT2);
 
     uint32 format = cs->format;
     uint32 setPropFlags = (JOF_MODE(format) == JOF_NAME)
                           ? JSRESOLVE_ASSIGNING
                           : JSRESOLVE_ASSIGNING | JSRESOLVE_QUALIFIED;
@@ -3523,34 +3546,32 @@ do_incop:
          */
         ref.setInt32(tmp);
     } else {
         /* We need an extra root for the result. */
         PUSH_NULL();
         if (!js_DoIncDec(cx, cs, &regs.sp[-2], &regs.sp[-1]))
             goto error;
 
-        script->types.monitorOverflow(cx, regs.pc);
-
         {
             JSAutoResolveFlags rf(cx, setPropFlags);
             if (!obj->setProperty(cx, id, &regs.sp[-1], script->strictModeCode))
                 goto error;
         }
 
         regs.sp--;
     }
 
     if (cs->nuses == 0) {
         /* regs.sp[-1] already contains the result of name increment. */
     } else {
         regs.sp[-1 - cs->nuses] = regs.sp[-1];
         regs.sp -= cs->nuses;
     }
-    len = cs->length;
+    len = cs->length + GetDecomposeLength(op);
     DO_NEXT_OP(len);
 }
 }
 
 {
     int incr, incr2;
     uint32 slot;
     Value *vp;
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -4788,16 +4788,19 @@ Decompile(SprintStack *ss, jsbytecode *p
         }
 
         if (cs->format & JOF_CALLOP) {
             todo = Sprint(&ss->sprinter, "");
             if (todo < 0 || !PushOff(ss, todo, saveop))
                 return NULL;
         }
 
+        if (cs->format & JOF_DECOMPOSE)
+            pc += GetDecomposeLength(op);
+
         pc += len;
     }
 
 /*
  * Undefine local macros.
  */
 #undef inXML
 #undef DECOMPILE_CODE
@@ -5490,16 +5493,19 @@ ReconstructPCStack(JSContext *cx, JSScri
     ptrdiff_t oplen;
     for (; pc < target; pc += oplen) {
         JSOp op = js_GetOpcode(cx, script, pc);
         const JSCodeSpec *cs = &js_CodeSpec[op];
         oplen = cs->length;
         if (oplen < 0)
             oplen = js_GetVariableBytecodeLength(pc);
 
+        if (cs->format & JOF_DECOMPOSE)
+            continue;
+
         /*
          * A (C ? T : E) expression requires skipping either T (if target is in
          * E) or both T and E (if target is after the whole expression) before
          * adjusting pcdepth based on the JSOP_IFEQ or JSOP_IFEQX at pc that
          * tests condition C.  We know that the stack depth can't change from
          * what it was with C on top of stack.
          */
         jssrcnote *sn = js_GetSrcNote(script, pc);
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -138,16 +138,18 @@ typedef enum JSOp {
 #define JOF_TMPSLOT_MASK  (JS_BITMASK(2) << JOF_TMPSLOT_SHIFT)
 
 #define JOF_SHARPSLOT    (1U<<24) /* first immediate is uint16 stack slot no.
                                      that needs fixup when in global code (see
                                      Compiler::compileScript) */
 #define JOF_GNAME        (1U<<25) /* predicted global name */
 #define JOF_TYPESET      (1U<<26) /* has a trailing 2 byte immediate indexing
                                      the set of observed types */
+#define JOF_DECOMPOSE    (1U<<27) /* followed by an equivalent decomposed
+                                   * version of the opcode */
 
 /* Shorthands for type from format and type from opcode. */
 #define JOF_TYPE(fmt)   ((fmt) & JOF_TYPEMASK)
 #define JOF_OPTYPE(op)  JOF_TYPE(js_CodeSpec[op].format)
 
 /* Shorthands for mode from format and mode from opcode. */
 #define JOF_MODE(fmt)   ((fmt) & JOF_MODEMASK)
 #define JOF_OPMODE(op)  JOF_MODE(js_CodeSpec[op].format)
@@ -516,16 +518,26 @@ extern ptrdiff_t
 SprintString(Sprinter *sp, JSString *str);
 
 extern ptrdiff_t
 Sprint(Sprinter *sp, const char *format, ...);
 
 extern bool
 CallResultEscapes(jsbytecode *pc);
 
+static inline uintN
+GetDecomposeLength(JSOp op)
+{
+    /* Table of lengths, updated on demand. See SetDecomposeLength. */
+    extern uint8 js_decomposeLengthTable[];
+    JS_ASSERT(js_CodeSpec[op].format & JOF_DECOMPOSE);
+    JS_ASSERT(js_decomposeLengthTable[op]);
+    return js_decomposeLengthTable[op];
+}
+
 }
 #endif
 
 #if defined(DEBUG) && defined(__cplusplus)
 /*
  * Disassemblers, for debugging only.
  */
 extern JS_FRIEND_API(JSBool)
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -155,28 +155,28 @@ OPDEF(JSOP_BITNOT,    33, "bitnot",     
 OPDEF(JSOP_NEG,       34, "neg",        "- ",         1,  1,  1, 15,  JOF_BYTE)
 OPDEF(JSOP_POS,       35, "pos",        "+ ",         1,  1,  1, 15,  JOF_BYTE)
 OPDEF(JSOP_DELNAME,   36, "delname",    NULL,         3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_DEL)
 OPDEF(JSOP_DELPROP,   37, "delprop",    NULL,         3,  1,  1, 15,  JOF_ATOM|JOF_PROP|JOF_DEL)
 OPDEF(JSOP_DELELEM,   38, "delelem",    NULL,         1,  2,  1, 15,  JOF_BYTE |JOF_ELEM|JOF_DEL)
 OPDEF(JSOP_TYPEOF,    39, js_typeof_str,NULL,         1,  1,  1, 15,  JOF_BYTE|JOF_DETECTING)
 OPDEF(JSOP_VOID,      40, js_void_str,  NULL,         1,  1,  1, 15,  JOF_BYTE)
 
-OPDEF(JSOP_INCNAME,   41, "incname",    NULL,         3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_INC|JOF_TMPSLOT3)
-OPDEF(JSOP_INCPROP,   42, "incprop",    NULL,         3,  1,  1, 15,  JOF_ATOM|JOF_PROP|JOF_INC|JOF_TMPSLOT3)
-OPDEF(JSOP_INCELEM,   43, "incelem",    NULL,         1,  2,  1, 15,  JOF_BYTE |JOF_ELEM|JOF_INC|JOF_TMPSLOT2)
-OPDEF(JSOP_DECNAME,   44, "decname",    NULL,         3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_DEC|JOF_TMPSLOT3)
-OPDEF(JSOP_DECPROP,   45, "decprop",    NULL,         3,  1,  1, 15,  JOF_ATOM|JOF_PROP|JOF_DEC|JOF_TMPSLOT3)
-OPDEF(JSOP_DECELEM,   46, "decelem",    NULL,         1,  2,  1, 15,  JOF_BYTE |JOF_ELEM|JOF_DEC|JOF_TMPSLOT2)
-OPDEF(JSOP_NAMEINC,   47, "nameinc",    NULL,         3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3)
-OPDEF(JSOP_PROPINC,   48, "propinc",    NULL,         3,  1,  1, 15,  JOF_ATOM|JOF_PROP|JOF_INC|JOF_POST|JOF_TMPSLOT3)
-OPDEF(JSOP_ELEMINC,   49, "eleminc",    NULL,         1,  2,  1, 15,  JOF_BYTE |JOF_ELEM|JOF_INC|JOF_POST|JOF_TMPSLOT2)
-OPDEF(JSOP_NAMEDEC,   50, "namedec",    NULL,         3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3)
-OPDEF(JSOP_PROPDEC,   51, "propdec",    NULL,         3,  1,  1, 15,  JOF_ATOM|JOF_PROP|JOF_DEC|JOF_POST|JOF_TMPSLOT3)
-OPDEF(JSOP_ELEMDEC,   52, "elemdec",    NULL,         1,  2,  1, 15,  JOF_BYTE |JOF_ELEM|JOF_DEC|JOF_POST|JOF_TMPSLOT2)
+OPDEF(JSOP_INCNAME,   41, "incname",    NULL,         3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_INC|JOF_TMPSLOT3|JOF_DECOMPOSE)
+OPDEF(JSOP_INCPROP,   42, "incprop",    NULL,         3,  1,  1, 15,  JOF_ATOM|JOF_PROP|JOF_INC|JOF_TMPSLOT3|JOF_DECOMPOSE)
+OPDEF(JSOP_INCELEM,   43, "incelem",    NULL,         1,  2,  1, 15,  JOF_BYTE |JOF_ELEM|JOF_INC|JOF_TMPSLOT2|JOF_DECOMPOSE)
+OPDEF(JSOP_DECNAME,   44, "decname",    NULL,         3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_DEC|JOF_TMPSLOT3|JOF_DECOMPOSE)
+OPDEF(JSOP_DECPROP,   45, "decprop",    NULL,         3,  1,  1, 15,  JOF_ATOM|JOF_PROP|JOF_DEC|JOF_TMPSLOT3|JOF_DECOMPOSE)
+OPDEF(JSOP_DECELEM,   46, "decelem",    NULL,         1,  2,  1, 15,  JOF_BYTE |JOF_ELEM|JOF_DEC|JOF_TMPSLOT2|JOF_DECOMPOSE)
+OPDEF(JSOP_NAMEINC,   47, "nameinc",    NULL,         3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3|JOF_DECOMPOSE)
+OPDEF(JSOP_PROPINC,   48, "propinc",    NULL,         3,  1,  1, 15,  JOF_ATOM|JOF_PROP|JOF_INC|JOF_POST|JOF_TMPSLOT3|JOF_DECOMPOSE)
+OPDEF(JSOP_ELEMINC,   49, "eleminc",    NULL,         1,  2,  1, 15,  JOF_BYTE |JOF_ELEM|JOF_INC|JOF_POST|JOF_TMPSLOT2|JOF_DECOMPOSE)
+OPDEF(JSOP_NAMEDEC,   50, "namedec",    NULL,         3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3|JOF_DECOMPOSE)
+OPDEF(JSOP_PROPDEC,   51, "propdec",    NULL,         3,  1,  1, 15,  JOF_ATOM|JOF_PROP|JOF_DEC|JOF_POST|JOF_TMPSLOT3|JOF_DECOMPOSE)
+OPDEF(JSOP_ELEMDEC,   52, "elemdec",    NULL,         1,  2,  1, 15,  JOF_BYTE |JOF_ELEM|JOF_DEC|JOF_POST|JOF_TMPSLOT2|JOF_DECOMPOSE)
 
 OPDEF(JSOP_GETPROP,   53, "getprop",    NULL,         5,  1,  1, 18,  JOF_ATOM|JOF_PROP|JOF_TYPESET)
 OPDEF(JSOP_SETPROP,   54, "setprop",    NULL,         3,  2,  1,  3,  JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
 OPDEF(JSOP_GETELEM,   55, "getelem",    NULL,         3,  2,  1, 18,  JOF_BYTE |JOF_ELEM|JOF_TYPESET|JOF_LEFTASSOC)
 OPDEF(JSOP_SETELEM,   56, "setelem",    NULL,         1,  3,  1,  3,  JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING)
 OPDEF(JSOP_CALLNAME,  57, "callname",   NULL,         5,  0,  2, 19,  JOF_ATOM|JOF_NAME|JOF_TYPESET|JOF_CALLOP)
 OPDEF(JSOP_CALL,      58, "call",       NULL,         5, -1,  1, 18,  JOF_UINT16|JOF_INVOKE|JOF_TYPESET)
 OPDEF(JSOP_NAME,      59, "name",       NULL,         5,  0,  1, 19,  JOF_ATOM|JOF_NAME|JOF_TYPESET)
@@ -217,17 +217,17 @@ OPDEF(JSOP_SETCALL,   74, "setcall",    
  * JSOP_ENDITER cleans up after the loop. It uses the slot above the iterator
  * for temporary GC rooting.
  */
 OPDEF(JSOP_ITER,      75, "iter",       NULL,         2,  1,  1,  0,  JOF_UINT8)
 OPDEF(JSOP_MOREITER,  76, "moreiter",   NULL,         1,  1,  2,  0,  JOF_BYTE)
 OPDEF(JSOP_ENDITER,   77, "enditer",    NULL,         1,  1,  0,  0,  JOF_BYTE)
 
 OPDEF(JSOP_FUNAPPLY,  78, "funapply",   NULL,         5, -1,  1, 18,  JOF_UINT16|JOF_INVOKE|JOF_TYPESET)
-OPDEF(JSOP_SWAP,      79, "swap",       NULL,         1,  2,  2,  0,  JOF_BYTE)
+OPDEF(JSOP_SWAP,      79, "swap",       NULL,         1,  2,  2,  0,  JOF_BYTE|JOF_TMPSLOT2)
 
 /* Push object literal: either an XML object or initialiser object. */
 OPDEF(JSOP_OBJECT,    80, "object",     NULL,         3,  0,  1, 19,  JOF_OBJECT)
 
 /* Pop value and discard it. */
 OPDEF(JSOP_POP,       81, "pop",        NULL,         1,  1,  0,  2,  JOF_BYTE)
 
 /* Call a function as a constructor; operand is argc. */
@@ -347,17 +347,17 @@ OPDEF(JSOP_CALLEE,    131, "callee",    
 
 /*
  * Like JSOP_SETLOCAL, but specialized to avoid requiring JSOP_POP immediately
  * after to throw away the exception value.
  */
 OPDEF(JSOP_SETLOCALPOP, 132, "setlocalpop", NULL,     3,  1,  0,  3,  JOF_LOCAL|JOF_NAME|JOF_SET)
 
 /* Pick an element from the stack. */
-OPDEF(JSOP_PICK,        133, "pick",      NULL,       2,  0,  0,  0,  JOF_UINT8)
+OPDEF(JSOP_PICK,        133, "pick",      NULL,       2,  0,  0,  0,  JOF_UINT8|JOF_TMPSLOT2)
 
 /*
  * Exception handling no-op, for more economical byte-coding than SRC_TRYFIN
  * srcnote-annotated JSOP_NOPs and to simply stack balance handling.
  */
 OPDEF(JSOP_TRY,         134,"try",        NULL,       1,  0,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_FINALLY,     135,"finally",    NULL,       1,  0,  2,  0,  JOF_BYTE)
 
@@ -406,20 +406,20 @@ OPDEF(JSOP_THROWING,      153,"throwing"
 
 /* Set and get return value pseudo-register in stack frame. */
 OPDEF(JSOP_SETRVAL,       154,"setrval",  NULL,       1,  1,  0,  2,  JOF_BYTE)
 OPDEF(JSOP_RETRVAL,       155,"retrval",  NULL,       1,  0,  0,  0,  JOF_BYTE)
 
 /* Free variable references that must either be found on the global or a ReferenceError */
 OPDEF(JSOP_GETGNAME,      156,"getgname",  NULL,       5,  0,  1, 19,  JOF_ATOM|JOF_NAME|JOF_TYPESET|JOF_GNAME)
 OPDEF(JSOP_SETGNAME,      157,"setgname",  NULL,       3,  2,  1,  3,  JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING|JOF_GNAME)
-OPDEF(JSOP_INCGNAME,      158,"incgname",  NULL,       3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_INC|JOF_TMPSLOT3|JOF_GNAME)
-OPDEF(JSOP_DECGNAME,      159,"decgname",  NULL,       3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_DEC|JOF_TMPSLOT3|JOF_GNAME)
-OPDEF(JSOP_GNAMEINC,      160,"gnameinc",  NULL,       3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3|JOF_GNAME)
-OPDEF(JSOP_GNAMEDEC,      161,"gnamedec",  NULL,       3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3|JOF_GNAME)
+OPDEF(JSOP_INCGNAME,      158,"incgname",  NULL,       3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_INC|JOF_TMPSLOT3|JOF_GNAME|JOF_DECOMPOSE)
+OPDEF(JSOP_DECGNAME,      159,"decgname",  NULL,       3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_DEC|JOF_TMPSLOT3|JOF_GNAME|JOF_DECOMPOSE)
+OPDEF(JSOP_GNAMEINC,      160,"gnameinc",  NULL,       3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3|JOF_GNAME|JOF_DECOMPOSE)
+OPDEF(JSOP_GNAMEDEC,      161,"gnamedec",  NULL,       3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3|JOF_GNAME|JOF_DECOMPOSE)
 
 /* Regular expression literal requiring special "fork on exec" handling. */
 OPDEF(JSOP_REGEXP,        162,"regexp",   NULL,       3,  0,  1, 19,  JOF_REGEXP)
 
 /* XML (ECMA-357, a.k.a. "E4X") support. */
 OPDEF(JSOP_DEFXMLNS,      163,"defxmlns",   NULL,     1,  1,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_ANYNAME,       164,"anyname",    NULL,     1,  0,  1, 19,  JOF_BYTE|JOF_XMLNAME)
 OPDEF(JSOP_QNAMEPART,     165,"qnamepart",  NULL,     3,  0,  1, 19,  JOF_ATOM|JOF_XMLNAME)
@@ -598,8 +598,11 @@ OPDEF(JSOP_CALLGLOBAL,    233,"callgloba
 
 /* Like JSOP_FUNAPPLY but for f.call instead of f.apply. */
 OPDEF(JSOP_FUNCALL,       234,"funcall",       NULL,  5, -1,  1, 18,  JOF_UINT16|JOF_INVOKE|JOF_TYPESET)
 
 OPDEF(JSOP_FORGNAME,      235,"forgname",      NULL,  3,  1,  1, 19,  JOF_ATOM|JOF_GNAME|JOF_FOR|JOF_TMPSLOT3)
 
 /* Substituted for JSOP_SETELEM to indicate opcodes which have written holes in dense arrays. */
 OPDEF(JSOP_SETHOLE,       236, "sethole",      NULL,  1,  3,  1,  3,  JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING)
+
+/* Pop the stack, convert to a jsid (int or string), and push back. */
+OPDEF(JSOP_TOID,          237, "toid",         NULL,  1,  1,  1,  0,  JOF_BYTE)
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -15469,16 +15469,22 @@ TraceRecorder::record_JSOP_DEFAULT()
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_EVAL()
 {
     return ARECORD_STOP;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
+TraceRecorder::record_JSOP_TOID()
+{
+    return ARECORD_STOP;
+}
+
+JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_ENUMELEM()
 {
     /*
      * To quote from jsinterp.cpp's JSOP_ENUMELEM case:
      * Funky: the value to set is under the [obj, id] pair.
      */
     return setElem(-2, -1, -3);
 }
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -1533,16 +1533,22 @@ mjit::Compiler::generateMethod()
             prepareStubCall(Uses(0));
             masm.move(Imm32(trap), Registers::ArgReg1);
             Call cl = emitStubCall(JS_FUNC_TO_DATA_PTR(void *, stubs::Trap), NULL);
             InternalCallSite site(masm.callReturnOffset(cl), a->inlineIndex, PC,
                                   REJOIN_TRAP, false);
             addCallSite(site);
         }
 
+        /* Don't compile fat opcodes, run the decomposed version instead. */
+        if (js_CodeSpec[op].format & JOF_DECOMPOSE) {
+            PC += js_CodeSpec[op].length;
+            continue;
+        }
+
     /**********************
      * BEGIN COMPILER OPS *
      **********************/ 
 
         jsbytecode *oldPC = PC;
 
         switch (op) {
           BEGIN_CASE(JSOP_NOP)
@@ -1665,16 +1671,49 @@ mjit::Compiler::generateMethod()
           BEGIN_CASE(JSOP_DUP)
             frame.dup();
           END_CASE(JSOP_DUP)
 
           BEGIN_CASE(JSOP_DUP2)
             frame.dup2();
           END_CASE(JSOP_DUP2)
 
+          BEGIN_CASE(JSOP_SWAP)
+            frame.dup2();
+            frame.shift(-3);
+            frame.shift(-1);
+          END_CASE(JSOP_SWAP)
+
+          BEGIN_CASE(JSOP_PICK)
+          {
+            int32 amt = GET_INT8(PC);
+
+            // Push -(amt + 1), say amt == 2
+            // Stack before: X3 X2 X1
+            // Stack after:  X3 X2 X1 X3
+            frame.dupAt(-(amt + 1));
+
+            // For each item X[i...1] push it then move it down.
+            // The above would transition like so:
+            //   X3 X2 X1 X3 X2 (dupAt)
+            //   X2 X2 X1 X3    (shift)
+            //   X2 X2 X1 X3 X1 (dupAt)
+            //   X2 X1 X1 X3    (shift)
+            for (int32 i = -amt; i < 0; i++) {
+                frame.dupAt(i - 1);
+                frame.shift(i - 2);
+            }
+
+            // The stack looks like:
+            // Xn ... X1 X1 X{n+1}
+            // So shimmy the last value down.
+            frame.shimmy(1);
+          }
+          END_CASE(JSOP_PICK)
+
           BEGIN_CASE(JSOP_BITOR)
           BEGIN_CASE(JSOP_BITXOR)
           BEGIN_CASE(JSOP_BITAND)
             jsop_bitop(op);
           END_CASE(JSOP_BITAND)
 
           BEGIN_CASE(JSOP_LT)
           BEGIN_CASE(JSOP_LE)
@@ -1899,127 +1938,31 @@ mjit::Compiler::generateMethod()
             jsop_typeof();
           END_CASE(JSOP_TYPEOF)
 
           BEGIN_CASE(JSOP_VOID)
             frame.pop();
             frame.push(UndefinedValue());
           END_CASE(JSOP_VOID)
 
-          BEGIN_CASE(JSOP_INCNAME)
-          {
-            CompileStatus status = jsop_nameinc(op, STRICT_VARIANT(stubs::IncName), fullAtomIndex(PC));
-            if (status != Compile_Okay)
-                return status;
-          }
-          END_CASE(JSOP_INCNAME)
-
-          BEGIN_CASE(JSOP_INCGNAME)
-            if (!jsop_gnameinc(op, STRICT_VARIANT(stubs::IncGlobalName), fullAtomIndex(PC)))
-                return Compile_Retry;
-          END_CASE(JSOP_INCGNAME)
-
-          BEGIN_CASE(JSOP_INCPROP)
-          {
-            CompileStatus status = jsop_propinc(op, STRICT_VARIANT(stubs::IncProp), fullAtomIndex(PC));
-            if (status != Compile_Okay)
-                return status;
-          }
-          END_CASE(JSOP_INCPROP)
-
-          BEGIN_CASE(JSOP_INCELEM)
-            jsop_eleminc(op, STRICT_VARIANT(stubs::IncElem));
-          END_CASE(JSOP_INCELEM)
-
-          BEGIN_CASE(JSOP_DECNAME)
-          {
-            CompileStatus status = jsop_nameinc(op, STRICT_VARIANT(stubs::DecName), fullAtomIndex(PC));
-            if (status != Compile_Okay)
-                return status;
-          }
-          END_CASE(JSOP_DECNAME)
-
-          BEGIN_CASE(JSOP_DECGNAME)
-            if (!jsop_gnameinc(op, STRICT_VARIANT(stubs::DecGlobalName), fullAtomIndex(PC)))
-                return Compile_Retry;
-          END_CASE(JSOP_DECGNAME)
-
-          BEGIN_CASE(JSOP_DECPROP)
-          {
-            CompileStatus status = jsop_propinc(op, STRICT_VARIANT(stubs::DecProp), fullAtomIndex(PC));
-            if (status != Compile_Okay)
-                return status;
-          }
-          END_CASE(JSOP_DECPROP)
-
-          BEGIN_CASE(JSOP_DECELEM)
-            jsop_eleminc(op, STRICT_VARIANT(stubs::DecElem));
-          END_CASE(JSOP_DECELEM)
-
-          BEGIN_CASE(JSOP_NAMEINC)
-          {
-            CompileStatus status = jsop_nameinc(op, STRICT_VARIANT(stubs::NameInc), fullAtomIndex(PC));
-            if (status != Compile_Okay)
-                return status;
-          }
-          END_CASE(JSOP_NAMEINC)
-
-          BEGIN_CASE(JSOP_GNAMEINC)
-            if (!jsop_gnameinc(op, STRICT_VARIANT(stubs::GlobalNameInc), fullAtomIndex(PC)))
-                return Compile_Retry;
-          END_CASE(JSOP_GNAMEINC)
-
-          BEGIN_CASE(JSOP_PROPINC)
-          {
-            CompileStatus status = jsop_propinc(op, STRICT_VARIANT(stubs::PropInc), fullAtomIndex(PC));
-            if (status != Compile_Okay)
-                return status;
-          }
-          END_CASE(JSOP_PROPINC)
-
-          BEGIN_CASE(JSOP_ELEMINC)
-            jsop_eleminc(op, STRICT_VARIANT(stubs::ElemInc));
-          END_CASE(JSOP_ELEMINC)
-
-          BEGIN_CASE(JSOP_NAMEDEC)
-          {
-            CompileStatus status = jsop_nameinc(op, STRICT_VARIANT(stubs::NameDec), fullAtomIndex(PC));
-            if (status != Compile_Okay)
-                return status;
-          }
-          END_CASE(JSOP_NAMEDEC)
-
-          BEGIN_CASE(JSOP_GNAMEDEC)
-            if (!jsop_gnameinc(op, STRICT_VARIANT(stubs::GlobalNameDec), fullAtomIndex(PC)))
-                return Compile_Retry;
-          END_CASE(JSOP_GNAMEDEC)
-
-          BEGIN_CASE(JSOP_PROPDEC)
-          {
-            CompileStatus status = jsop_propinc(op, STRICT_VARIANT(stubs::PropDec), fullAtomIndex(PC));
-            if (status != Compile_Okay)
-                return status;
-          }
-          END_CASE(JSOP_PROPDEC)
-
-          BEGIN_CASE(JSOP_ELEMDEC)
-            jsop_eleminc(op, STRICT_VARIANT(stubs::ElemDec));
-          END_CASE(JSOP_ELEMDEC)
-
           BEGIN_CASE(JSOP_GETPROP)
           BEGIN_CASE(JSOP_LENGTH)
             if (!jsop_getprop(script->getAtom(fullAtomIndex(PC)), knownPushedType(0)))
                 return Compile_Error;
           END_CASE(JSOP_GETPROP)
 
           BEGIN_CASE(JSOP_GETELEM)
             if (!jsop_getelem(false))
                 return Compile_Error;
           END_CASE(JSOP_GETELEM)
 
+          BEGIN_CASE(JSOP_TOID)
+            jsop_toid();
+          END_CASE(JSOP_TOID)
+
           BEGIN_CASE(JSOP_SETELEM)
           BEGIN_CASE(JSOP_SETHOLE)
           {
             jsbytecode *next = &PC[JSOP_SETELEM_LENGTH];
             bool pop = (JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next));
             if (!jsop_setelem(pop))
                 return Compile_Error;
           }
@@ -4284,20 +4227,19 @@ mjit::Compiler::jsop_getprop(JSAtom *ato
         }
     }
 
     /*
      * Check if we are accessing a known type which always has the property
      * in a particular inline slot. Get the property directly in this case,
      * without using an IC.
      */
-    JSOp op = JSOp(*PC);
     jsid id = ATOM_TO_JSID(atom);
     types::TypeSet *types = frame.extra(top).types;
-    if (op == JSOP_GETPROP && types && !types->unknownObject() &&
+    if (types && !types->unknownObject() &&
         types->getObjectCount() == 1 &&
         types->getTypeObject(0) != NULL &&
         !types->getTypeObject(0)->unknownProperties() &&
         id == types::MakeTypeId(cx, id)) {
         JS_ASSERT(usePropCache);
         types::TypeObject *object = types->getTypeObject(0);
         types::TypeSet *propertyTypes = object->getProperty(cx, id, false);
         if (!propertyTypes)
@@ -5426,198 +5368,16 @@ mjit::Compiler::jsop_this()
             frame.pushThis();
         }
 
         JS_ASSERT(thisFe->isType(JSVAL_TYPE_OBJECT));
     }
 }
 
 bool
-mjit::Compiler::jsop_gnameinc(JSOp op, VoidStubAtom stub, uint32 index)
-{
-    JSAtom *atom = script->getAtom(index);
-
-#if defined JS_MONOIC
-    int amt = (op == JSOP_GNAMEINC || op == JSOP_INCGNAME) ? 1 : -1;
-
-    jsop_bindgname();
-    // OBJ
-
-    jsop_getgname(index);
-    // OBJ V
-
-    if (!analysis->incrementInitialValueObserved(PC)) {
-        frame.push(Int32Value(-amt));
-        // OBJ V 1
-
-        /* Use sub since it calls ToNumber instead of string concat. */
-        if (!jsop_binary(JSOP_SUB, stubs::Sub, knownPushedType(0), pushedTypeSet(0)))
-            return false;
-        // OBJ N+1
-
-        jsop_setgname(atom, false, analysis->popGuaranteed(PC));
-        // N+1
-    } else {
-        jsop_pos();
-        // OBJ N
-
-        frame.swap();
-        // N OBJ
-
-        frame.dupAt(-2);
-        // N OBJ N
-
-        frame.push(Int32Value(amt));
-        // N OBJ N 1
-
-        if (!jsop_binary(JSOP_ADD, stubs::Add, knownPushedType(0), pushedTypeSet(0)))
-            return false;
-        // N OBJ N+1
-
-        jsop_setgname(atom, false, true);
-        // N N+1
-
-        frame.pop();
-        // N
-    }
-#else
-    prepareStubCall(Uses(0));
-    masm.move(ImmPtr(atom), Registers::ArgReg1);
-    INLINE_STUBCALL(stub, REJOIN_FALLTHROUGH);
-    frame.pushSynced(knownPushedType(0));
-#endif
-
-    return true;
-}
-
-CompileStatus
-mjit::Compiler::jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index)
-{
-    JSAtom *atom = script->getAtom(index);
-#if defined JS_POLYIC
-    int amt = (op == JSOP_NAMEINC || op == JSOP_INCNAME) ? 1 : -1;
-
-    jsop_bindname(atom, false);
-    // OBJ
-
-    jsop_name(atom, JSVAL_TYPE_UNKNOWN, false);
-    // OBJ V
-
-    if (!analysis->incrementInitialValueObserved(PC)) {
-        frame.push(Int32Value(-amt));
-        // OBJ V 1
-
-        /* Use sub since it calls ToNumber instead of string concat. */
-        frame.syncAt(-3);
-        if (!jsop_binary(JSOP_SUB, stubs::Sub, knownPushedType(0), pushedTypeSet(0)))
-            return Compile_Retry;
-        // OBJ N+1
-
-        if (!jsop_setprop(atom, false, analysis->popGuaranteed(PC)))
-            return Compile_Error;
-        // N+1
-    } else {
-        jsop_pos();
-        // OBJ N
-
-        frame.swap();
-        // N OBJ
-
-        frame.dupAt(-2);
-        // N OBJ N
-
-        frame.push(Int32Value(amt));
-        // N OBJ N 1
-
-        frame.syncAt(-3);
-        if (!jsop_binary(JSOP_ADD, stubs::Add, knownPushedType(0), pushedTypeSet(0)))
-            return Compile_Retry;
-        // N OBJ N+1
-
-        if (!jsop_setprop(atom, false, true))
-            return Compile_Error;
-        // N N+1
-
-        frame.pop();
-        // N
-    }
-#else
-    prepareStubCall(Uses(0));
-    masm.move(ImmPtr(atom), Registers::ArgReg1);
-    INLINE_STUBCALL(stub);
-    frame.pushSynced(knownPushedType(0));
-#endif
-
-    return Compile_Okay;
-}
-
-CompileStatus
-mjit::Compiler::jsop_propinc(JSOp op, VoidStubAtom stub, uint32 index)
-{
-    JSAtom *atom = script->getAtom(index);
-#if defined JS_POLYIC
-    int amt = (op == JSOP_PROPINC || op == JSOP_INCPROP) ? 1 : -1;
-
-    frame.dup();
-    // OBJ OBJ
-
-    if (!jsop_getprop(atom, JSVAL_TYPE_UNKNOWN))
-        return Compile_Error;
-    // OBJ V
-
-    if (!analysis->incrementInitialValueObserved(PC)) {
-        frame.push(Int32Value(-amt));
-        // OBJ V 1
-
-        /* Use sub since it calls ToNumber instead of string concat. */
-        frame.syncAt(-3);
-        if (!jsop_binary(JSOP_SUB, stubs::Sub, knownPushedType(0), pushedTypeSet(0)))
-            return Compile_Retry;
-        // OBJ N+1
-
-        if (!jsop_setprop(atom, false, analysis->popGuaranteed(PC)))
-            return Compile_Error;
-        // N+1
-    } else {
-        jsop_pos();
-        // OBJ N
-
-        frame.swap();
-        // N OBJ
-
-        frame.dupAt(-2);
-        // N OBJ N
-
-        frame.push(Int32Value(amt));
-        // N OBJ N 1
-
-        frame.syncAt(-3);
-        if (!jsop_binary(JSOP_ADD, stubs::Add, knownPushedType(0), pushedTypeSet(0)))
-            return Compile_Retry;
-        // N OBJ N+1
-
-        if (!jsop_setprop(atom, false, true))
-            return Compile_Error;
-        // N N+1
-
-        frame.pop();
-        // N
-    }
-#else
-    prepareStubCall(Uses(1));
-    masm.move(ImmPtr(atom), Registers::ArgReg1);
-    INLINE_STUBCALL(stub);
-    frame.pop();
-    pushSyncedEntry(0);
-#endif
-
-    return Compile_Okay;
-}
-
-bool
 mjit::Compiler::iter(uintN flags)
 {
     FrameEntry *fe = frame.peek(-1);
 
     /*
      * Stub the call if this is not a simple 'for in' loop or if the iterated
      * value is known to not be an object.
      */
@@ -5868,25 +5628,16 @@ mjit::Compiler::iterEnd()
     OOL_STUBCALL(stubs::EndIter, REJOIN_FALLTHROUGH);
 
     frame.pop();
 
     stubcc.rejoin(Changes(1));
 }
 
 void
-mjit::Compiler::jsop_eleminc(JSOp op, VoidStub stub)
-{
-    prepareStubCall(Uses(2));
-    INLINE_STUBCALL(stub, REJOIN_FALLTHROUGH);
-    frame.popn(2);
-    pushSyncedEntry(0);
-}
-
-void
 mjit::Compiler::jsop_getgname_slow(uint32 index)
 {
     prepareStubCall(Uses(0));
     INLINE_STUBCALL(stubs::GetGlobalName, REJOIN_GETTER);
     frame.pushSynced(JSVAL_TYPE_UNKNOWN);
 }
 
 void
@@ -5924,50 +5675,42 @@ mjit::Compiler::jsop_getgname(uint32 ind
 
     /* Optimize singletons like Math for JSOP_CALLPROP. */
     JSObject *obj = pushedSingleton(0);
     if (obj && !hasTypeBarriers(PC) && testSingletonProperty(globalObj, ATOM_TO_JSID(atom))) {
         frame.push(ObjectValue(*obj));
         return;
     }
 
-    /* Get the type of the global. */
     jsid id = ATOM_TO_JSID(atom);
-    JSValueType type = JSVAL_TYPE_UNKNOWN;
+    JSValueType type = knownPushedType(0);
     if (cx->typeInferenceEnabled() && globalObj->isGlobal() && id == types::MakeTypeId(cx, id) &&
         !globalObj->type()->unknownProperties()) {
-        /*
-         * Get the type tag for the global either straight off it (in case this
-         * is an INCGNAME op, and the pushed type set is wrong) or from the
-         * pushed type set (if this is a GETGNAME op, and the property may have
-         * a type barrier on it).
-         */
         types::TypeSet *propertyTypes = globalObj->type()->getProperty(cx, id, false);
         if (!propertyTypes)
             return;
-        types::TypeSet *types = (JSOp(*PC) == JSOP_GETGNAME || JSOp(*PC) == JSOP_CALLGNAME)
-            ? pushedTypeSet(0)
-            : propertyTypes;
-        type = types->getKnownTypeTag(cx);
-
+
+        /*
+         * If we are accessing a defined global which is a normal data property
+         * then bake its address into the jitcode and guard against future
+         * reallocation of the global object's slots.
+         */
         const js::Shape *shape = globalObj->nativeLookup(ATOM_TO_JSID(atom));
         if (shape && shape->hasDefaultGetterOrIsMethod() && shape->hasSlot()) {
             Value *value = &globalObj->getSlotRef(shape->slot);
             if (!value->isUndefined() && !propertyTypes->isOwnProperty(cx, true)) {
                 watchGlobalReallocation();
                 RegisterID reg = frame.allocReg();
                 masm.move(ImmPtr(value), reg);
 
                 BarrierState barrier = pushAddressMaybeBarrier(Address(reg), type, true);
                 finishBarrier(barrier, REJOIN_GETTER, 0);
                 return;
             }
         }
-        if (knownPushedType(0) != type)
-            type = JSVAL_TYPE_UNKNOWN;
     }
 
 #if defined JS_MONOIC
     jsop_bindgname();
 
     FrameEntry *fe = frame.peek(-1);
     JS_ASSERT(fe->isTypeKnown() && fe->getKnownType() == JSVAL_TYPE_OBJECT);
 
@@ -6978,16 +6721,51 @@ mjit::Compiler::jsop_forgname(JSAtom *at
     // After:  ITER VALUE
     jsop_setgname(atom, false, true);
 
     // Before: ITER VALUE
     // After:  ITER
     frame.pop();
 }
 
+void
+mjit::Compiler::jsop_toid()
+{
+    /*
+     * Leave integers alone, stub everything else. This doesn't quite match the
+     * interpreter's semantics as integers that do not fit in a jsid will be
+     * left alone, but this difference does not break downstream assumptions.
+     */
+    FrameEntry *top = frame.peek(-1);
+
+    if (top->isType(JSVAL_TYPE_INT32))
+        return;
+
+    if (top->isNotType(JSVAL_TYPE_INT32)) {
+        prepareStubCall(Uses(2));
+        INLINE_STUBCALL(stubs::ToId, REJOIN_FALLTHROUGH);
+        frame.pop();
+        pushSyncedEntry(0);
+        return;
+    }
+
+    frame.syncAt(-1);
+
+    Jump j = frame.testInt32(Assembler::NotEqual, top);
+    stubcc.linkExit(j, Uses(2));
+
+    stubcc.leave();
+    OOL_STUBCALL(stubs::ToId, REJOIN_FALLTHROUGH);
+
+    frame.pop();
+    pushSyncedEntry(0);
+
+    stubcc.rejoin(Changes(1));
+}
+
 /*
  * For any locals or args which we know to be integers but are treated as
  * doubles by the type inference, convert to double. These will be assumed to be
  * doubles at control flow join points. This function must be called before
  * branching to another opcode.
  *
  * We can only carry entries as doubles when we can track all incoming edges to
  * a join point (no try blocks etc.) and when we can track all writes to the
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -620,20 +620,16 @@ class Compiler : public BaseCompiler
     void emitUncachedCall(uint32 argc, bool callingNew);
     void checkCallApplySpeculation(uint32 callImmArgc, uint32 speculatedArgc,
                                    FrameEntry *origCallee, FrameEntry *origThis,
                                    MaybeRegisterID origCalleeType, RegisterID origCalleeData,
                                    MaybeRegisterID origThisType, RegisterID origThisData,
                                    Jump *uncachedCallSlowRejoin, CallPatchInfo *uncachedCallPatch);
     bool inlineCallHelper(uint32 argc, bool callingNew, FrameSize &callFrameSize);
     void fixPrimitiveReturn(Assembler *masm, FrameEntry *fe);
-    bool jsop_gnameinc(JSOp op, VoidStubAtom stub, uint32 index);
-    CompileStatus jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index);
-    CompileStatus jsop_propinc(JSOp op, VoidStubAtom stub, uint32 index);
-    void jsop_eleminc(JSOp op, VoidStub);
     void jsop_getgname(uint32 index);
     void jsop_getgname_slow(uint32 index);
     void jsop_callgname_epilogue();
     void jsop_setgname(JSAtom *atom, bool usePropertyCache, bool popGuaranteed);
     void jsop_setgname_slow(JSAtom *atom, bool usePropertyCache);
     void jsop_bindgname();
     void jsop_setelem_slow();
     void jsop_getelem_slow();
@@ -711,16 +707,17 @@ class Compiler : public BaseCompiler
 #endif
     bool jsop_setelem(bool popGuaranteed);
     bool jsop_getelem(bool isCall);
     void jsop_getelem_dense(bool isPacked);
     void jsop_getelem_args();
 #ifdef JS_METHODJIT_TYPED_ARRAY
     void jsop_getelem_typed(int atype);
 #endif
+    void jsop_toid();
     bool isCacheableBaseAndIndex(FrameEntry *obj, FrameEntry *id);
     void jsop_stricteq(JSOp op);
     bool jsop_equality(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused);
     bool jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused);
     void jsop_pos();
 
     static inline Assembler::Condition
     GetCompareCondition(JSOp op, JSOp fused)
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -1165,69 +1165,16 @@ FinishVarIncOp(VMFrame &f, RejoinState r
         if (!nv.setNumber(d + N))
             f.script()->types.monitorOverflow(cx, f.pc());
     }
 
     *var = nv;
     *vp = (cs->format & JOF_POST) ? ov : nv;
 }
 
-static bool
-FinishObjIncOp(VMFrame &f, RejoinState rejoin, Value objv, Value ov, Value nv, Value *vp)
-{
-    /*
-     * Finish a property access increment operation on a GNAME, NAME or PROP. We don't need
-     * to handle ELEM as these are always stubbed.
-     */
-    JS_ASSERT(rejoin == REJOIN_BINDNAME || rejoin == REJOIN_GETTER ||
-              rejoin == REJOIN_POS || rejoin == REJOIN_BINARY);
-
-    JSContext *cx = f.cx;
-
-    JSObject *obj = ValueToObject(cx, &objv);
-    if (!obj)
-        return false;
-
-    JSOp op = js_GetOpcode(cx, f.script(), f.pc());
-    const JSCodeSpec *cs = &js_CodeSpec[op];
-    JS_ASSERT(JOF_TYPE(cs->format) == JOF_ATOM);
-
-    jsid id = ATOM_TO_JSID(f.script()->getAtom(GET_SLOTNO(f.pc())));
-
-    if (rejoin == REJOIN_BINDNAME && !obj->getProperty(cx, id, &ov))
-        return false;
-
-    if (rejoin == REJOIN_BINDNAME || rejoin == REJOIN_GETTER) {
-        double d;
-        if (!ToNumber(cx, ov, &d))
-            return false;
-        ov.setNumber(d);
-    }
-
-    if (rejoin == REJOIN_BINDNAME || rejoin == REJOIN_GETTER || rejoin == REJOIN_POS) {
-        double d = ov.toNumber();
-        double N = (cs->format & JOF_INC) ? 1 : -1;
-        if (!nv.setNumber(d + N))
-            f.script()->types.monitorOverflow(cx, f.pc());
-    }
-
-    uint32 setPropFlags = (cs->format & JOF_NAME)
-                          ? JSRESOLVE_ASSIGNING
-                          : JSRESOLVE_ASSIGNING | JSRESOLVE_QUALIFIED;
-
-    {
-        JSAutoResolveFlags rf(cx, setPropFlags);
-        if (!obj->setProperty(cx, id, &nv, f.script()->strictModeCode))
-            return false;
-    }
-
-    *vp = (cs->format & JOF_POST) ? ov : nv;
-    return true;
-}
-
 extern "C" void *
 js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VMFrame &f)
 {
     JSRejoinState jsrejoin = f.fp()->rejoin();
     RejoinState rejoin;
     if (jsrejoin & 0x1) {
         /* Rejoin after a scripted call finished. Restore f.regs.pc and f.regs.inlined (NULL) */
         uint32 pcOffset = jsrejoin >> 1;
@@ -1277,69 +1224,36 @@ js_InternalInterpret(void *returnData, v
     JaegerSpew(JSpew_Recompile, "interpreter rejoin (file \"%s\") (line \"%d\") (op %s) (opline \"%d\")\n",
                script->filename, script->lineno, OpcodeNames[op], js_PCToLineNumber(cx, script, pc));
 #endif
 
     uint32 nextDepth = uint32(-1);
     bool skipTrap = false;
 
     if ((cs->format & (JOF_INC | JOF_DEC)) &&
-        rejoin != REJOIN_FALLTHROUGH &&
-        rejoin != REJOIN_RESUME &&
-        rejoin != REJOIN_THIS_PROTOTYPE &&
-        rejoin != REJOIN_CHECK_ARGUMENTS) {
-        /* We may reenter the interpreter while finishing the INC/DEC operation. */
+        (rejoin == REJOIN_POS || rejoin == REJOIN_BINARY)) {
+        /*
+         * We may reenter the interpreter while finishing the INC/DEC operation
+         * on a local or arg (property INC/DEC operations will rejoin into the
+         * decomposed version of the op.
+         */
+        JS_ASSERT(cs->format & (JOF_LOCAL | JOF_QARG));
+
         nextDepth = analysis->getCode(nextpc).stackDepth;
         untrap.retrap();
         enter.leave();
 
-        switch (op) {
-          case JSOP_INCLOCAL:
-          case JSOP_DECLOCAL:
-          case JSOP_LOCALINC:
-          case JSOP_LOCALDEC:
-          case JSOP_INCARG:
-          case JSOP_DECARG:
-          case JSOP_ARGINC:
-          case JSOP_ARGDEC:
-            if (rejoin != REJOIN_BINARY || !analysis->incrementInitialValueObserved(pc)) {
-                /* Stack layout is 'V', 'N' or 'N+1' (only if the N is not needed) */
-                FinishVarIncOp(f, rejoin, nextsp[-1], nextsp[-1], &nextsp[-1]);
-            } else {
-                /* Stack layout is 'N N+1' */
-                FinishVarIncOp(f, rejoin, nextsp[-1], nextsp[0], &nextsp[-1]);
-            }
-            break;
+        if (rejoin != REJOIN_BINARY || !analysis->incrementInitialValueObserved(pc)) {
+            /* Stack layout is 'V', 'N' or 'N+1' (only if the N is not needed) */
+            FinishVarIncOp(f, rejoin, nextsp[-1], nextsp[-1], &nextsp[-1]);
+        } else {
+            /* Stack layout is 'N N+1' */
+            FinishVarIncOp(f, rejoin, nextsp[-1], nextsp[0], &nextsp[-1]);
+        }
 
-          case JSOP_INCGNAME:
-          case JSOP_DECGNAME:
-          case JSOP_GNAMEINC:
-          case JSOP_GNAMEDEC:
-          case JSOP_INCNAME:
-          case JSOP_DECNAME:
-          case JSOP_NAMEINC:
-          case JSOP_NAMEDEC:
-          case JSOP_INCPROP:
-          case JSOP_DECPROP:
-          case JSOP_PROPINC:
-          case JSOP_PROPDEC:
-            if (rejoin != REJOIN_BINARY || !analysis->incrementInitialValueObserved(pc)) {
-                /* Stack layout is 'OBJ V', 'OBJ N' or 'OBJ N+1' (only if the N is not needed) */
-                if (!FinishObjIncOp(f, rejoin, nextsp[-1], nextsp[0], nextsp[0], &nextsp[-1]))
-                    return js_InternalThrow(f);
-            } else {
-                /* Stack layout is 'N OBJ N+1' */
-                if (!FinishObjIncOp(f, rejoin, nextsp[0], nextsp[-1], nextsp[1], &nextsp[-1]))
-                    return js_InternalThrow(f);
-            }
-            break;
-
-          default:
-            JS_NOT_REACHED("Bad op");
-        }
         rejoin = REJOIN_FALLTHROUGH;
     }
 
     switch (rejoin) {
       case REJOIN_SCRIPTED: {
 #ifdef JS_NUNBOX32
         uint64 rvalBits = ((uint64)returnType << 32) | (uint32)returnData;
 #elif JS_PUNBOX64
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -621,16 +621,34 @@ stubs::SetElem(VMFrame &f)
      * this logic can then be handled in Compiler.cpp. */
     regs.sp[-3] = regs.sp[-1];
 }
 
 template void JS_FASTCALL stubs::SetElem<true>(VMFrame &f);
 template void JS_FASTCALL stubs::SetElem<false>(VMFrame &f);
 
 void JS_FASTCALL
+stubs::ToId(VMFrame &f)
+{
+    Value &objval = f.regs.sp[-2];
+    Value &idval  = f.regs.sp[-1];
+
+    JSObject *obj = ValueToObject(f.cx, &objval);
+    if (!obj)
+        THROW();
+
+    jsid id;
+    if (!FetchElementId(f, obj, idval, id, &idval))
+        THROW();
+
+    if (!JSID_IS_INT(id))
+        f.script()->types.monitorUnknown(f.cx, f.pc());
+}
+
+void JS_FASTCALL
 stubs::CallName(VMFrame &f)
 {
     JSObject *obj = NameOp(f, &f.fp()->scopeChain(), true);
     if (!obj)
         THROW();
 }
 
 /*
@@ -1017,56 +1035,16 @@ stubs::Equal(VMFrame &f)
 JSBool JS_FASTCALL
 stubs::NotEqual(VMFrame &f)
 {
     if (!StubEqualityOp<JS_FALSE, true>(f))
         THROWV(JS_FALSE);
     return f.regs.sp[-2].toBoolean();
 }
 
-static inline void
-MonitorArithmeticOverflow(VMFrame &f, const Value &v)
-{
-    JSContext *cx = f.cx;
-
-    JS_ASSERT(v.isDouble());
-    f.script()->types.monitorOverflow(cx, f.pc());
-
-    /*
-     * Monitoring the overflow is not enough for fused INC operations on NAME/PROP,
-     * as modifying the pushed stack types does not affect the object itself.
-     * The method JIT fuses these opcodes (unlike the interpreter, which has a case
-     * to modify the object directly on overflow), so we have to detect that the
-     * current operation is fused and determine the object to update --- it must be
-     * synced and at a particular slot. This is a gross hack.
-     */
-
-    Value ov;
-
-    switch (JSOp(*f.pc())) {
-      case JSOP_INCPROP:
-      case JSOP_DECPROP:
-      case JSOP_PROPINC:
-      case JSOP_PROPDEC:
-        ov = f.regs.sp[-3];
-        break;
-
-      default:
-        return;
-    }
-
-    JSObject *obj = ValueToObject(cx, &ov);
-    if (!obj)
-        return;
-    JSAtom *atom;
-    GET_ATOM_FROM_BYTECODE(f.script(), f.pc(), 0, atom);
-
-    AddTypePropertyId(cx, obj, ATOM_TO_JSID(atom), Type::DoubleType());
-}
-
 void JS_FASTCALL
 stubs::Add(VMFrame &f)
 {
     JSContext *cx = f.cx;
     FrameRegs &regs = f.regs;
     Value rval = regs.sp[-1];
     Value lval = regs.sp[-2];
 
@@ -1119,17 +1097,17 @@ stubs::Add(VMFrame &f)
 
         } else {
             double l, r;
             if (!ToNumber(cx, lval, &l) || !ToNumber(cx, rval, &r))
                 THROW();
             l += r;
             if (!regs.sp[-2].setNumber(l) &&
                 (lIsObject || rIsObject || (!lval.isDouble() && !rval.isDouble()))) {
-                MonitorArithmeticOverflow(f, regs.sp[-2]);
+                f.script()->types.monitorOverflow(cx, f.pc());
             }
         }
     }
     return;
 
   string_concat:
     JSString *str = js_ConcatStrings(cx, lstr, rstr);
     if (!str)
@@ -1144,17 +1122,17 @@ stubs::Sub(VMFrame &f)
 {
     JSContext *cx = f.cx;
     FrameRegs &regs = f.regs;
     double d1, d2;
     if (!ToNumber(cx, regs.sp[-2], &d1) || !ToNumber(cx, regs.sp[-1], &d2))
         THROW();
     double d = d1 - d2;
     if (!regs.sp[-2].setNumber(d))
-        MonitorArithmeticOverflow(f, regs.sp[-2]);
+        f.script()->types.monitorOverflow(cx, f.pc());
 }
 
 void JS_FASTCALL
 stubs::Mul(VMFrame &f)
 {
     JSContext *cx = f.cx;
     FrameRegs &regs = f.regs;
     double d1, d2;
@@ -1584,362 +1562,16 @@ stubs::Lambda(VMFrame &f, JSFunction *fu
 
     obj = CloneFunctionObject(f.cx, fun, parent, true);
     if (!obj)
         THROWV(NULL);
 
     return obj;
 }
 
-/* Test whether v is an int in the range [-2^31 + 1, 2^31 - 2] */
-static JS_ALWAYS_INLINE bool
-CanIncDecWithoutOverflow(int32_t i)
-{
-    return (i > JSVAL_INT_MIN) && (i < JSVAL_INT_MAX);
-}
-
-template <int32 N, bool POST, JSBool strict, bool qualified>
-static inline bool
-ObjIncOp(VMFrame &f, JSObject *obj, jsid id)
-{
-    JSContext *cx = f.cx;
-
-    f.regs.sp[0].setNull();
-    f.regs.sp++;
-    if (!obj->getProperty(cx, id, &f.regs.sp[-1]))
-        return false;
-
-    uint32 setPropFlags = qualified
-                          ? JSRESOLVE_ASSIGNING
-                          : JSRESOLVE_ASSIGNING | JSRESOLVE_QUALIFIED;
-
-    Value &ref = f.regs.sp[-1];
-    int32_t tmp;
-    if (JS_LIKELY(ref.isInt32() && CanIncDecWithoutOverflow(tmp = ref.toInt32()))) {
-        if (POST)
-            ref.getInt32Ref() = tmp + N;
-        else
-            ref.getInt32Ref() = tmp += N;
-
-        {
-            JSAutoResolveFlags rf(cx, setPropFlags);
-            if (!obj->setProperty(cx, id, &ref, strict))
-                return false;
-        }
-
-        /*
-         * We must set regs.sp[-1] to tmp for both post and pre increments
-         * as the setter overwrites regs.sp[-1].
-         */
-        ref.setInt32(tmp);
-    } else {
-        Value v;
-        double d;
-        if (!ToNumber(cx, ref, &d))
-            return false;
-        if (POST) {
-            ref.setNumber(d);
-            d += N;
-        } else {
-            d += N;
-            ref.setNumber(d);
-        }
-
-        v.setNumber(d);
-        f.script()->types.monitorOverflow(cx, f.pc());
-
-        {
-            JSAutoResolveFlags rf(cx, setPropFlags);
-            if (!obj->setProperty(cx, id, &v, strict))
-                return false;
-        }
-    }
-
-    return true;
-}
-
-template <int32 N, bool POST, JSBool strict>
-static inline bool
-NameIncDec(VMFrame &f, JSObject *obj, JSAtom *origAtom)
-{
-    JSContext *cx = f.cx;
-
-    JSAtom *atom;
-    JSObject *obj2;
-    JSProperty *prop;
-    PropertyCacheEntry *entry;
-    JS_PROPERTY_CACHE(cx).test(cx, f.pc(), obj, obj2, entry, atom);
-    if (!atom) {
-        if (obj == obj2 && entry->vword.isSlot()) {
-            uint32 slot = entry->vword.toSlot();
-            Value &rref = obj->nativeGetSlotRef(slot);
-            int32_t tmp;
-            if (JS_LIKELY(rref.isInt32() && CanIncDecWithoutOverflow(tmp = rref.toInt32()))) {
-                int32_t inc = tmp + N;
-                if (!POST)
-                    tmp = inc;
-                rref.getInt32Ref() = inc;
-                f.regs.sp[0].setInt32(tmp);
-                return true;
-            }
-        }
-        atom = origAtom;
-    }
-
-    jsid id = ATOM_TO_JSID(atom);
-    bool global = (js_CodeSpec[*f.pc()].format & JOF_GNAME);
-    if (!js_FindPropertyHelper(cx, id, true, global, &obj, &obj2, &prop))
-        return false;
-    if (!prop) {
-        ReportAtomNotDefined(cx, atom);
-        return false;
-    }
-    return ObjIncOp<N, POST, strict, false>(f, obj, id);
-}
-
-template<JSBool strict>
-void JS_FASTCALL
-stubs::PropInc(VMFrame &f, JSAtom *atom)
-{
-    JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
-    if (!obj)
-        THROW();
-    if (!ObjIncOp<1, true, strict, true>(f, obj, ATOM_TO_JSID(atom)))
-        THROW();
-    f.regs.sp[-2] = f.regs.sp[-1];
-}
-
-template void JS_FASTCALL stubs::PropInc<true>(VMFrame &f, JSAtom *atom);
-template void JS_FASTCALL stubs::PropInc<false>(VMFrame &f, JSAtom *atom);
-
-template<JSBool strict>
-void JS_FASTCALL
-stubs::PropDec(VMFrame &f, JSAtom *atom)
-{
-    JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
-    if (!obj)
-        THROW();
-    if (!ObjIncOp<-1, true, strict, true>(f, obj, ATOM_TO_JSID(atom)))
-        THROW();
-    f.regs.sp[-2] = f.regs.sp[-1];
-}
-
-template void JS_FASTCALL stubs::PropDec<true>(VMFrame &f, JSAtom *atom);
-template void JS_FASTCALL stubs::PropDec<false>(VMFrame &f, JSAtom *atom);
-
-template<JSBool strict>
-void JS_FASTCALL
-stubs::IncProp(VMFrame &f, JSAtom *atom)
-{
-    JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
-    if (!obj)
-        THROW();
-    if (!ObjIncOp<1, false, strict, true>(f, obj, ATOM_TO_JSID(atom)))
-        THROW();
-    f.regs.sp[-2] = f.regs.sp[-1];
-}
-
-template void JS_FASTCALL stubs::IncProp<true>(VMFrame &f, JSAtom *atom);
-template void JS_FASTCALL stubs::IncProp<false>(VMFrame &f, JSAtom *atom);
-
-template<JSBool strict>
-void JS_FASTCALL
-stubs::DecProp(VMFrame &f, JSAtom *atom)
-{
-    JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
-    if (!obj)
-        THROW();
-    if (!ObjIncOp<-1, false, strict, true>(f, obj, ATOM_TO_JSID(atom)))
-        THROW();
-    f.regs.sp[-2] = f.regs.sp[-1];
-}
-
-template void JS_FASTCALL stubs::DecProp<true>(VMFrame &f, JSAtom *atom);
-template void JS_FASTCALL stubs::DecProp<false>(VMFrame &f, JSAtom *atom);
-
-template<JSBool strict>
-void JS_FASTCALL
-stubs::ElemInc(VMFrame &f)
-{
-    JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
-    if (!obj)
-        THROW();
-    jsid id;
-    if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
-        THROW();
-    if (!ObjIncOp<1, true, strict, true>(f, obj, id))
-        THROW();
-    f.regs.sp[-3] = f.regs.sp[-1];
-
-    if (!JSID_IS_INT(id))
-        f.script()->types.monitorUnknown(f.cx, f.pc());
-}
-
-template void JS_FASTCALL stubs::ElemInc<true>(VMFrame &f);
-template void JS_FASTCALL stubs::ElemInc<false>(VMFrame &f);
-
-template<JSBool strict>
-void JS_FASTCALL
-stubs::ElemDec(VMFrame &f)
-{
-    JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
-    if (!obj)
-        THROW();
-    jsid id;
-    if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
-        THROW();
-    if (!ObjIncOp<-1, true, strict, true>(f, obj, id))
-        THROW();
-    f.regs.sp[-3] = f.regs.sp[-1];
-
-    if (!JSID_IS_INT(id))
-        f.script()->types.monitorUnknown(f.cx, f.pc());
-}
-
-template void JS_FASTCALL stubs::ElemDec<true>(VMFrame &f);
-template void JS_FASTCALL stubs::ElemDec<false>(VMFrame &f);
-
-template<JSBool strict>
-void JS_FASTCALL
-stubs::IncElem(VMFrame &f)
-{
-    JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
-    if (!obj)
-        THROW();
-    jsid id;
-    if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
-        THROW();
-    if (!ObjIncOp<1, false, strict, true>(f, obj, id))
-        THROW();
-    f.regs.sp[-3] = f.regs.sp[-1];
-
-    if (!JSID_IS_INT(id))
-        f.script()->types.monitorUnknown(f.cx, f.pc());
-}
-
-template void JS_FASTCALL stubs::IncElem<true>(VMFrame &f);
-template void JS_FASTCALL stubs::IncElem<false>(VMFrame &f);
-
-template<JSBool strict>
-void JS_FASTCALL
-stubs::DecElem(VMFrame &f)
-{
-    JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
-    if (!obj)
-        THROW();
-    jsid id;
-    if (!FetchElementId(f, obj, f.regs.sp[-1], id, &f.regs.sp[-1]))
-        THROW();
-    if (!ObjIncOp<-1, false, strict, true>(f, obj, id))
-        THROW();
-    f.regs.sp[-3] = f.regs.sp[-1];
-
-    if (!JSID_IS_INT(id))
-        f.script()->types.monitorUnknown(f.cx, f.pc());
-}
-
-template void JS_FASTCALL stubs::DecElem<true>(VMFrame &f);
-template void JS_FASTCALL stubs::DecElem<false>(VMFrame &f);
-
-template<JSBool strict>
-void JS_FASTCALL
-stubs::NameInc(VMFrame &f, JSAtom *atom)
-{
-    JSObject *obj = &f.fp()->scopeChain();
-    if (!NameIncDec<1, true, strict>(f, obj, atom))
-        THROW();
-}
-
-template void JS_FASTCALL stubs::NameInc<true>(VMFrame &f, JSAtom *atom);
-template void JS_FASTCALL stubs::NameInc<false>(VMFrame &f, JSAtom *atom);
-
-template<JSBool strict>
-void JS_FASTCALL
-stubs::NameDec(VMFrame &f, JSAtom *atom)
-{
-    JSObject *obj = &f.fp()->scopeChain();
-    if (!NameIncDec<-1, true, strict>(f, obj, atom))
-        THROW();
-}
-
-template void JS_FASTCALL stubs::NameDec<true>(VMFrame &f, JSAtom *atom);
-template void JS_FASTCALL stubs::NameDec<false>(VMFrame &f, JSAtom *atom);
-
-template<JSBool strict>
-void JS_FASTCALL
-stubs::IncName(VMFrame &f, JSAtom *atom)
-{
-    JSObject *obj = &f.fp()->scopeChain();
-    if (!NameIncDec<1, false, strict>(f, obj, atom))
-        THROW();
-}
-
-template void JS_FASTCALL stubs::IncName<true>(VMFrame &f, JSAtom *atom);
-template void JS_FASTCALL stubs::IncName<false>(VMFrame &f, JSAtom *atom);
-
-template<JSBool strict>
-void JS_FASTCALL
-stubs::DecName(VMFrame &f, JSAtom *atom)
-{
-    JSObject *obj = &f.fp()->scopeChain();
-    if (!NameIncDec<-1, false, strict>(f, obj, atom))
-        THROW();
-}
-
-template void JS_FASTCALL stubs::DecName<true>(VMFrame &f, JSAtom *atom);
-template void JS_FASTCALL stubs::DecName<false>(VMFrame &f, JSAtom *atom);
-
-template<JSBool strict>
-void JS_FASTCALL
-stubs::GlobalNameInc(VMFrame &f, JSAtom *atom)
-{
-    JSObject *obj = f.fp()->scopeChain().getGlobal();
-    if (!NameIncDec<1, true, strict>(f, obj, atom))
-        THROW();
-}
-
-template void JS_FASTCALL stubs::GlobalNameInc<true>(VMFrame &f, JSAtom *atom);
-template void JS_FASTCALL stubs::GlobalNameInc<false>(VMFrame &f, JSAtom *atom);
-
-template<JSBool strict>
-void JS_FASTCALL
-stubs::GlobalNameDec(VMFrame &f, JSAtom *atom)
-{
-    JSObject *obj = f.fp()->scopeChain().getGlobal();
-    if (!NameIncDec<-1, true, strict>(f, obj, atom))
-        THROW();
-}
-
-template void JS_FASTCALL stubs::GlobalNameDec<true>(VMFrame &f, JSAtom *atom);
-template void JS_FASTCALL stubs::GlobalNameDec<false>(VMFrame &f, JSAtom *atom);
-
-template<JSBool strict>
-void JS_FASTCALL
-stubs::IncGlobalName(VMFrame &f, JSAtom *atom)
-{
-    JSObject *obj = f.fp()->scopeChain().getGlobal();
-    if (!NameIncDec<1, false, strict>(f, obj, atom))
-        THROW();
-}
-
-template void JS_FASTCALL stubs::IncGlobalName<true>(VMFrame &f, JSAtom *atom);
-template void JS_FASTCALL stubs::IncGlobalName<false>(VMFrame &f, JSAtom *atom);
-
-template<JSBool strict>
-void JS_FASTCALL
-stubs::DecGlobalName(VMFrame &f, JSAtom *atom)
-{
-    JSObject *obj = f.fp()->scopeChain().getGlobal();
-    if (!NameIncDec<-1, false, strict>(f, obj, atom))
-        THROW();
-}
-
-template void JS_FASTCALL stubs::DecGlobalName<true>(VMFrame &f, JSAtom *atom);
-template void JS_FASTCALL stubs::DecGlobalName<false>(VMFrame &f, JSAtom *atom);
-
 static bool JS_ALWAYS_INLINE
 InlineGetProp(VMFrame &f)
 {
     JSContext *cx = f.cx;
     FrameRegs &regs = f.regs;
 
     Value *vp = &f.regs.sp[-1];
 
--- a/js/src/methodjit/StubCalls.h
+++ b/js/src/methodjit/StubCalls.h
@@ -132,37 +132,22 @@ template<JSBool strict> void JS_FASTCALL
 template<JSBool strict> void JS_FASTCALL SetGlobalName(VMFrame &f, JSAtom *atom);
 template<JSBool strict> void JS_FASTCALL SetGlobalNameNoCache(VMFrame &f, JSAtom *atom);
 void JS_FASTCALL Name(VMFrame &f);
 void JS_FASTCALL GetProp(VMFrame &f);
 void JS_FASTCALL GetPropNoCache(VMFrame &f, JSAtom *atom);
 void JS_FASTCALL GetElem(VMFrame &f);
 void JS_FASTCALL CallElem(VMFrame &f);
 template<JSBool strict> void JS_FASTCALL SetElem(VMFrame &f);
+void JS_FASTCALL ToId(VMFrame &f);
 void JS_FASTCALL CallName(VMFrame &f);
 void JS_FASTCALL PushImplicitThisForGlobal(VMFrame &f);
 void JS_FASTCALL GetUpvar(VMFrame &f, uint32 index);
 void JS_FASTCALL GetGlobalName(VMFrame &f);
 
-template<JSBool strict> void JS_FASTCALL NameInc(VMFrame &f, JSAtom *atom);
-template<JSBool strict> void JS_FASTCALL NameDec(VMFrame &f, JSAtom *atom);
-template<JSBool strict> void JS_FASTCALL IncName(VMFrame &f, JSAtom *atom);
-template<JSBool strict> void JS_FASTCALL DecName(VMFrame &f, JSAtom *atom);
-template<JSBool strict> void JS_FASTCALL GlobalNameInc(VMFrame &f, JSAtom *atom);
-template<JSBool strict> void JS_FASTCALL GlobalNameDec(VMFrame &f, JSAtom *atom);
-template<JSBool strict> void JS_FASTCALL IncGlobalName(VMFrame &f, JSAtom *atom);
-template<JSBool strict> void JS_FASTCALL DecGlobalName(VMFrame &f, JSAtom *atom);
-template<JSBool strict> void JS_FASTCALL PropInc(VMFrame &f, JSAtom *atom);
-template<JSBool strict> void JS_FASTCALL PropDec(VMFrame &f, JSAtom *atom);
-template<JSBool strict> void JS_FASTCALL IncProp(VMFrame &f, JSAtom *atom);
-template<JSBool strict> void JS_FASTCALL DecProp(VMFrame &f, JSAtom *atom);
-template<JSBool strict> void JS_FASTCALL ElemInc(VMFrame &f);
-template<JSBool strict> void JS_FASTCALL ElemDec(VMFrame &f);
-template<JSBool strict> void JS_FASTCALL IncElem(VMFrame &f);
-template<JSBool strict> void JS_FASTCALL DecElem(VMFrame &f);
 void JS_FASTCALL CallProp(VMFrame &f, JSAtom *atom);
 template <JSBool strict> void JS_FASTCALL DelProp(VMFrame &f, JSAtom *atom);
 template <JSBool strict> void JS_FASTCALL DelElem(VMFrame &f);
 void JS_FASTCALL DelName(VMFrame &f, JSAtom *atom);
 JSBool JS_FASTCALL In(VMFrame &f);
 
 void JS_FASTCALL DefVarOrConst(VMFrame &f, JSAtom *atom);
 void JS_FASTCALL SetConst(VMFrame &f, JSAtom *atom);