Bug 804636 part 1 - Decompose LOCAL/ARG inc/dec ops. r=bhackett
authorJan de Mooij <jdemooij@mozilla.com>
Sat, 27 Oct 2012 14:16:17 +0200
changeset 111605 e4204b2b987cbf9a996f56952b6b9f5bf7df6c7f
parent 111604 4826ed659d3199bb5dc6b9adde5902399fa73cfe
child 111606 ea35621ef12ae0d033e3cc3ab95f7495fef52a87
push id17153
push userjandemooij@gmail.com
push dateSat, 27 Oct 2012 12:18:43 +0000
treeherdermozilla-inbound@e4204b2b987c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs804636
milestone19.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 804636 part 1 - Decompose LOCAL/ARG inc/dec ops. r=bhackett
js/src/frontend/BytecodeEmitter.cpp
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
js/src/jsanalyze.cpp
js/src/jsanalyze.h
js/src/jsinfer.cpp
js/src/jsinterp.cpp
js/src/jsopcode.cpp
js/src/jsopcode.tbl
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/FastArithmetic.cpp
js/src/methodjit/FastOps.cpp
js/src/methodjit/FrameState.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/LoopState.cpp
js/src/methodjit/MethodJIT.h
js/src/vm/Xdr.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -969,64 +969,59 @@ EmitVarOp(JSContext *cx, ParseNode *pn, 
         JS_ASSERT_IF(pn->isDefn(), pn->pn_cookie.level() == bce->script->staticLevel);
         return EmitUnaliasedVarOp(cx, op, pn->pn_cookie.slot(), bce);
     }
 
     switch (op) {
       case JSOP_GETARG: case JSOP_GETLOCAL: op = JSOP_GETALIASEDVAR; break;
       case JSOP_SETARG: case JSOP_SETLOCAL: op = JSOP_SETALIASEDVAR; break;
       case JSOP_CALLARG: case JSOP_CALLLOCAL: op = JSOP_CALLALIASEDVAR; break;
+      case JSOP_INCARG: case JSOP_INCLOCAL: op = JSOP_INCALIASEDVAR; break;
+      case JSOP_ARGINC: case JSOP_LOCALINC: op = JSOP_ALIASEDVARINC; break;
+      case JSOP_DECARG: case JSOP_DECLOCAL: op = JSOP_DECALIASEDVAR; break;
+      case JSOP_ARGDEC: case JSOP_LOCALDEC: op = JSOP_ALIASEDVARDEC; break;
       default: JS_NOT_REACHED("unexpected var op");
     }
 
     return EmitAliasedVarOp(cx, op, pn, bce);
 }
 
 static bool
 EmitVarIncDec(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
 {
     JS_ASSERT(pn->isKind(PNK_NAME));
     JS_ASSERT(IsArgOp(op) || IsLocalOp(op));
     JS_ASSERT(js_CodeSpec[op].format & (JOF_INC | JOF_DEC));
     JS_ASSERT(!pn->pn_cookie.isFree());
 
-    if (!bce->isAliasedName(pn))
-        return EmitUnaliasedVarOp(cx, op, pn->pn_cookie.slot(), bce);
-
-    switch (op) {
-      case JSOP_INCARG: case JSOP_INCLOCAL: op = JSOP_INCALIASEDVAR; break;
-      case JSOP_ARGINC: case JSOP_LOCALINC: op = JSOP_ALIASEDVARINC; break;
-      case JSOP_DECARG: case JSOP_DECLOCAL: op = JSOP_DECALIASEDVAR; break;
-      case JSOP_ARGDEC: case JSOP_LOCALDEC: op = JSOP_ALIASEDVARDEC; break;
-      default: JS_NOT_REACHED("unexpected var op");
-    }
-
-    if (!EmitAliasedVarOp(cx, op, pn, bce))
+    if (!EmitVarOp(cx, pn, op, bce))
         return false;
 
     /* Remove the result to restore the stack depth before the INCALIASEDVAR. */
     bce->stackDepth--;
 
     int start = bce->offset();
 
     const JSCodeSpec &cs = js_CodeSpec[op];
     bool post = (cs.format & JOF_POST);
     JSOp binop = (cs.format & JOF_INC) ? JSOP_ADD : JSOP_SUB;
-
-    if (!EmitAliasedVarOp(cx, JSOP_GETALIASEDVAR, pn, bce))  // V
+    JSOp getOp = IsLocalOp(op) ? JSOP_GETLOCAL : JSOP_GETARG;
+    JSOp setOp = IsLocalOp(op) ? JSOP_SETLOCAL : JSOP_SETARG;
+
+    if (!EmitVarOp(cx, pn, getOp, bce))                      // V
         return false;
     if (Emit1(cx, bce, JSOP_POS) < 0)                        // N
         return false;
     if (post && Emit1(cx, bce, JSOP_DUP) < 0)                // N? N
         return false;
     if (Emit1(cx, bce, JSOP_ONE) < 0)                        // N? N 1
         return false;
     if (Emit1(cx, bce, binop) < 0)                           // N? N+1
         return false;
-    if (!EmitAliasedVarOp(cx, JSOP_SETALIASEDVAR, pn, bce))  // N? N+1
+    if (!EmitVarOp(cx, pn, setOp, bce))                      // N? N+1
         return false;
     if (post && Emit1(cx, bce, JSOP_POP) < 0)                // RESULT
         return false;
 
     UpdateDecomposeLength(bce, start);
     return true;
 }
 
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -810,38 +810,26 @@ IonBuilder::inspectOpcode(JSOp op)
       case JSOP_AND:
       case JSOP_OR:
         return jsop_andor(op);
 
       case JSOP_DEFVAR:
       case JSOP_DEFCONST:
         return jsop_defvar(GET_UINT32_INDEX(pc));
 
-      case JSOP_LOCALINC:
-      case JSOP_INCLOCAL:
-      case JSOP_LOCALDEC:
-      case JSOP_DECLOCAL:
-        return jsop_localinc(op);
-
       case JSOP_EQ:
       case JSOP_NE:
       case JSOP_STRICTEQ:
       case JSOP_STRICTNE:
       case JSOP_LT:
       case JSOP_LE:
       case JSOP_GT:
       case JSOP_GE:
         return jsop_compare(op);
 
-      case JSOP_ARGINC:
-      case JSOP_INCARG:
-      case JSOP_ARGDEC:
-      case JSOP_DECARG:
-        return jsop_arginc(op);
-
       case JSOP_DOUBLE:
         return pushConstant(info().getConst(pc));
 
       case JSOP_STRING:
         return pushConstant(StringValue(info().getAtom(pc)));
 
       case JSOP_ZERO:
         return pushConstant(Int32Value(0));
@@ -3836,72 +3824,16 @@ bool
 IonBuilder::makeCall(HandleFunction target, uint32 argc, bool constructing)
 {
     types::StackTypeSet *barrier;
     types::StackTypeSet *types = oracle->returnTypeSet(script_, pc, &barrier);
     return makeCallBarrier(target, argc, constructing, types, barrier);
 }
 
 bool
-IonBuilder::jsop_incslot(JSOp op, uint32 slot)
-{
-    int32 amt = (js_CodeSpec[op].format & JOF_INC) ? 1 : -1;
-    bool post = !!(js_CodeSpec[op].format & JOF_POST);
-    TypeOracle::BinaryTypes types = oracle->incslot(script_, pc);
-
-    // Grab the value at the local slot, and convert it to a number. Currently,
-    // we use ToInt32 or ToNumber which are fallible but idempotent. This whole
-    // operation must be idempotent because we cannot resume in the middle of
-    // an INC op.
-    current->pushSlot(slot);
-    MDefinition *value = current->pop();
-    MInstruction *lhs;
-
-    JSValueType knownType = types.lhsTypes->getKnownTypeTag();
-    if (knownType == JSVAL_TYPE_INT32) {
-        lhs = MToInt32::New(value);
-    } else if (knownType == JSVAL_TYPE_DOUBLE) {
-        lhs = MToDouble::New(value);
-    } else {
-        // Don't compile effectful incslot ops.
-        return abort("INCSLOT non-int/double lhs");
-    }
-    current->add(lhs);
-
-    // If this is a post operation, save the original value.
-    if (post)
-        current->push(lhs);
-
-    MConstant *rhs = MConstant::New(Int32Value(amt));
-    current->add(rhs);
-
-    MAdd *result = MAdd::New(lhs, rhs);
-    current->add(result);
-    result->infer(cx, types);
-    current->push(result);
-    current->setSlot(slot);
-
-    if (post)
-        current->pop();
-    return true;
-}
-
-bool
-IonBuilder::jsop_localinc(JSOp op)
-{
-    return jsop_incslot(op, info().localSlot(GET_SLOTNO(pc)));
-}
-
-bool
-IonBuilder::jsop_arginc(JSOp op)
-{
-    return jsop_incslot(op, info().argSlot(GET_SLOTNO(pc)));
-}
-
-bool
 IonBuilder::jsop_compare(JSOp op)
 {
     MDefinition *right = current->pop();
     MDefinition *left = current->pop();
 
     MCompare *ins = MCompare::New(left, right, op);
     current->add(ins);
     current->push(ins);
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -314,19 +314,16 @@ class IonBuilder : public MIRGenerator
     bool jsop_notearg();
     bool jsop_funcall(uint32 argc);
     bool jsop_funapply(uint32 argc);
     bool jsop_call(uint32 argc, bool constructing);
     bool jsop_ifeq(JSOp op);
     bool jsop_andor(JSOp op);
     bool jsop_dup2();
     bool jsop_loophead(jsbytecode *pc);
-    bool jsop_incslot(JSOp op, uint32 slot);
-    bool jsop_localinc(JSOp op);
-    bool jsop_arginc(JSOp op);
     bool jsop_compare(JSOp op);
     bool jsop_getgname(HandlePropertyName name);
     bool jsop_setgname(HandlePropertyName name);
     bool jsop_getname(HandlePropertyName name);
     bool jsop_bindname(PropertyName *name);
     bool jsop_getelem();
     bool jsop_getelem_dense();
     bool jsop_getelem_typed(int arrayType);
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -449,34 +449,26 @@ ScriptAnalysis::analyzeBytecode(JSContex
                     localsAliasStack_ = true;
                     break;
                 }
             }
             break;
           }
 
           case JSOP_CALLLOCAL:
-          case JSOP_INCLOCAL:
-          case JSOP_DECLOCAL:
-          case JSOP_LOCALINC:
-          case JSOP_LOCALDEC:
           case JSOP_SETLOCAL: {
             uint32_t local = GET_SLOTNO(pc);
             if (local >= script_->nfixed) {
                 localsAliasStack_ = true;
                 break;
             }
             break;
           }
 
           case JSOP_SETARG:
-          case JSOP_INCARG:
-          case JSOP_DECARG:
-          case JSOP_ARGINC:
-          case JSOP_ARGDEC:
             modifiesArguments_ = true;
             isInlineable = false;
             break;
 
           case JSOP_GETPROP:
           case JSOP_CALLPROP:
           case JSOP_LENGTH:
           case JSOP_GETELEM:
@@ -785,32 +777,16 @@ ScriptAnalysis::analyzeLifetimes(JSConte
           case JSOP_SETARG:
           case JSOP_SETLOCAL: {
             uint32_t slot = GetBytecodeSlot(script_, pc);
             if (!slotEscapes(slot))
                 killVariable(cx, lifetimes[slot], offset, saved, savedCount);
             break;
           }
 
-          case JSOP_INCARG:
-          case JSOP_DECARG:
-          case JSOP_ARGINC:
-          case JSOP_ARGDEC:
-          case JSOP_INCLOCAL:
-          case JSOP_DECLOCAL:
-          case JSOP_LOCALINC:
-          case JSOP_LOCALDEC: {
-            uint32_t slot = GetBytecodeSlot(script_, pc);
-            if (!slotEscapes(slot)) {
-                killVariable(cx, lifetimes[slot], offset, saved, savedCount);
-                addVariable(cx, lifetimes[slot], offset, saved, savedCount);
-            }
-            break;
-          }
-
           case JSOP_LOOKUPSWITCH:
           case JSOP_TABLESWITCH:
             /* Restore all saved variables. :FIXME: maybe do this precisely. */
             for (unsigned i = 0; i < savedCount; i++) {
                 LifetimeVariable &var = *saved[i];
                 var.lifetime = alloc.new_<Lifetime>(offset, var.savedEnd, var.saved);
                 if (!var.lifetime) {
                     js_free(saved);
@@ -1398,18 +1374,18 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
             }
             for (unsigned i = 0; i < nuses; i++) {
                 SSAValue &v = stack[stackDepth - 1 - i].v;
                 code->poppedValues[i] = v;
                 v.clear();
             }
             if (xuses > nuses) {
                 /*
-                 * For SETLOCAL, INCLOCAL, etc. opcodes, add an extra popped
-                 * value holding the value of the local before the op.
+                 * For SETLOCAL, etc. opcodes, add an extra popped value
+                 * holding the value of the local before the op.
                  */
                 uint32_t slot = GetBytecodeSlot(script_, pc);
                 if (trackSlot(slot))
                     code->poppedValues[nuses] = values[slot].v;
                 else
                     code->poppedValues[nuses].clear();
             }
 
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -372,59 +372,37 @@ static inline uint32_t StackSlot(JSScrip
 
 static inline uint32_t GetBytecodeSlot(JSScript *script, jsbytecode *pc)
 {
     switch (JSOp(*pc)) {
 
       case JSOP_GETARG:
       case JSOP_CALLARG:
       case JSOP_SETARG:
-      case JSOP_INCARG:
-      case JSOP_DECARG:
-      case JSOP_ARGINC:
-      case JSOP_ARGDEC:
         return ArgSlot(GET_SLOTNO(pc));
 
       case JSOP_GETLOCAL:
       case JSOP_CALLLOCAL:
       case JSOP_SETLOCAL:
-      case JSOP_INCLOCAL:
-      case JSOP_DECLOCAL:
-      case JSOP_LOCALINC:
-      case JSOP_LOCALDEC:
         return LocalSlot(script, GET_SLOTNO(pc));
 
       case JSOP_THIS:
         return ThisSlot();
 
       default:
         JS_NOT_REACHED("Bad slot opcode");
         return 0;
     }
 }
 
 /* Slot opcodes which update SSA information. */
 static inline bool
 BytecodeUpdatesSlot(JSOp op)
 {
-    switch (op) {
-      case JSOP_SETARG:
-      case JSOP_SETLOCAL:
-      case JSOP_INCARG:
-      case JSOP_DECARG:
-      case JSOP_ARGINC:
-      case JSOP_ARGDEC:
-      case JSOP_INCLOCAL:
-      case JSOP_DECLOCAL:
-      case JSOP_LOCALINC:
-      case JSOP_LOCALDEC:
-        return true;
-      default:
-        return false;
-    }
+    return (op == JSOP_SETARG || op == JSOP_SETLOCAL);
 }
 
 static inline int32_t
 GetBytecodeInteger(jsbytecode *pc)
 {
     switch (JSOp(*pc)) {
       case JSOP_ZERO:   return 0;
       case JSOP_ONE:    return 1;
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -3895,37 +3895,16 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
          */
         bytecodeTypes(pc)->addSubset(cx, &pushed[0]);
         break;
 
       case JSOP_SETALIASEDVAR:
         poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
 
-      case JSOP_INCARG:
-      case JSOP_DECARG:
-      case JSOP_ARGINC:
-      case JSOP_ARGDEC:
-      case JSOP_INCLOCAL:
-      case JSOP_DECLOCAL:
-      case JSOP_LOCALINC:
-      case JSOP_LOCALDEC: {
-        uint32_t slot = GetBytecodeSlot(script_, pc);
-        if (trackSlot(slot)) {
-            poppedTypes(pc, 0)->addArith(cx, script, pc, &pushed[0]);
-        } else if (slot < TotalSlots(script_)) {
-            StackTypeSet *types = TypeScript::SlotTypes(script_, slot);
-            types->addArith(cx, script, pc, types);
-            types->addSubset(cx, &pushed[0]);
-        } else {
-            pushed[0].addType(cx, Type::UnknownType());
-        }
-        break;
-      }
-
       case JSOP_ARGUMENTS:
         /* Compute a precise type only when we know the arguments won't escape. */
         if (script_->needsArgsObj())
             pushed[0].addType(cx, Type::UnknownType());
         else
             pushed[0].addType(cx, Type::MagicArgType());
         break;
 
@@ -4499,35 +4478,16 @@ ScriptAnalysis::analyzeTypes(JSContext *
 }
 
 bool
 ScriptAnalysis::integerOperation(jsbytecode *pc)
 {
     JS_ASSERT(uint32_t(pc - script_->code) < script_->length);
 
     switch (JSOp(*pc)) {
-
-      case JSOP_INCARG:
-      case JSOP_DECARG:
-      case JSOP_ARGINC:
-      case JSOP_ARGDEC:
-      case JSOP_INCLOCAL:
-      case JSOP_DECLOCAL:
-      case JSOP_LOCALINC:
-      case JSOP_LOCALDEC: {
-        if (pushedTypes(pc, 0)->getKnownTypeTag() != JSVAL_TYPE_INT32)
-            return false;
-        uint32_t slot = GetBytecodeSlot(script_, pc);
-        if (trackSlot(slot)) {
-            if (poppedTypes(pc, 0)->getKnownTypeTag() != JSVAL_TYPE_INT32)
-                return false;
-        }
-        return true;
-      }
-
       case JSOP_ADD:
       case JSOP_SUB:
       case JSOP_MUL:
       case JSOP_DIV:
         if (pushedTypes(pc, 0)->getKnownTypeTag() != JSVAL_TYPE_INT32)
             return false;
         if (poppedTypes(pc, 0)->getKnownTypeTag() != JSVAL_TYPE_INT32)
             return false;
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -856,48 +856,16 @@ TryNoteIter::settle()
          * depth exceeding the current one and this condition is what we use to
          * filter them out.
          */
         if (tn->stackDepth <= regs.stackDepth())
             break;
     }
 }
 
-/*
- * Increment/decrement the value 'v'. The resulting value is stored in *slot.
- * The result of the expression (taking into account prefix/postfix) is stored
- * in *expr.
- */
-static bool
-DoIncDec(JSContext *cx, HandleScript script, jsbytecode *pc, const Value &v, Value *slot, Value *expr)
-{
-    const JSCodeSpec &cs = js_CodeSpec[*pc];
-
-    if (v.isInt32()) {
-        int32_t i = v.toInt32();
-        if (i > JSVAL_INT_MIN && i < JSVAL_INT_MAX) {
-            int32_t sum = i + (cs.format & JOF_INC ? 1 : -1);
-            *slot = Int32Value(sum);
-            *expr = (cs.format & JOF_POST) ? Int32Value(i) : *slot;
-            return true;
-        }
-    }
-
-    double d;
-    if (!ToNumber(cx, v, &d))
-        return false;
-
-    double sum = d + (cs.format & JOF_INC ? 1 : -1);
-    *slot = NumberValue(sum);
-    *expr = (cs.format & JOF_POST) ? NumberValue(d) : *slot;
-
-    TypeScript::MonitorOverflow(cx, script, pc);
-    return true;
-}
-
 #define PUSH_COPY(v)             do { *regs.sp++ = v; assertSameCompartment(cx, regs.sp[-1]); } while (0)
 #define PUSH_COPY_SKIP_CHECK(v)  *regs.sp++ = v
 #define PUSH_NULL()              regs.sp++->setNull()
 #define PUSH_UNDEFINED()         regs.sp++->setUndefined()
 #define PUSH_BOOLEAN(b)          regs.sp++->setBoolean(b)
 #define PUSH_DOUBLE(d)           regs.sp++->setDouble(d)
 #define PUSH_INT32(i)            regs.sp++->setInt32(i)
 #define PUSH_STRING(s)           do { regs.sp++->setString(s); assertSameCompartment(cx, regs.sp[-1]); } while (0)
@@ -2196,42 +2164,26 @@ BEGIN_CASE(JSOP_ALIASEDVARINC)
     /* No-op */
 END_CASE(JSOP_ALIASEDVARINC)
 
 BEGIN_CASE(JSOP_DECARG)
 BEGIN_CASE(JSOP_ARGDEC)
 BEGIN_CASE(JSOP_INCARG)
 BEGIN_CASE(JSOP_ARGINC)
 {
-    unsigned i = GET_ARGNO(regs.pc);
-    if (script->argsObjAliasesFormals()) {
-        const Value &arg = regs.fp()->argsObj().arg(i);
-        Value v;
-        if (!DoIncDec(cx, script, regs.pc, arg, &v, &regs.sp[0]))
-            goto error;
-        regs.fp()->argsObj().setArg(i, v);
-    } else {
-        Value &arg = regs.fp()->unaliasedFormal(i);
-        if (!DoIncDec(cx, script, regs.pc, arg, &arg, &regs.sp[0]))
-            goto error;
-    }
-    regs.sp++;
+    /* No-op */
 }
 END_CASE(JSOP_ARGINC);
 
 BEGIN_CASE(JSOP_DECLOCAL)
 BEGIN_CASE(JSOP_LOCALDEC)
 BEGIN_CASE(JSOP_INCLOCAL)
 BEGIN_CASE(JSOP_LOCALINC)
 {
-    unsigned i = GET_SLOTNO(regs.pc);
-    Value &local = regs.fp()->unaliasedLocal(i);
-    if (!DoIncDec(cx, script, regs.pc, local, &local, &regs.sp[0]))
-        goto error;
-    regs.sp++;
+    /* No-op */
 }
 END_CASE(JSOP_LOCALINC)
 
 BEGIN_CASE(JSOP_THIS)
     if (!ComputeThis(cx, regs.fp()))
         goto error;
     PUSH_COPY(regs.fp()->thisValue());
 END_CASE(JSOP_THIS)
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -3594,28 +3594,42 @@ Decompile(SprintStack *ss, jsbytecode *p
                 if (IsVarSlot(jp, pc, &atom, &i))
                     goto do_setname;
                 lval = GetLocal(ss, i);
                 rval = PopStrDupe(ss, op, &rvalpc);
                 goto do_setlval;
 
               case JSOP_INCALIASEDVAR:
               case JSOP_DECALIASEDVAR:
+                if (IsVarSlot(jp, pc, &atom, &i))
+                    goto do_incatom;
+                lval = GetLocal(ss, i);
+                goto do_inclval;
+
               case JSOP_INCLOCAL:
               case JSOP_DECLOCAL:
-                if (IsVarSlot(jp, pc, &atom, &i))
+                /* INCLOCAL/DECLOCAL are followed by GETLOCAL containing the slot. */
+                JS_ASSERT(pc[JSOP_INCLOCAL_LENGTH] == JSOP_GETLOCAL);
+                if (IsVarSlot(jp, pc + JSOP_INCLOCAL_LENGTH, &atom, &i))
                     goto do_incatom;
                 lval = GetLocal(ss, i);
                 goto do_inclval;
 
               case JSOP_ALIASEDVARINC:
               case JSOP_ALIASEDVARDEC:
+                if (IsVarSlot(jp, pc, &atom, &i))
+                    goto do_atominc;
+                lval = GetLocal(ss, i);
+                goto do_lvalinc;
+
               case JSOP_LOCALINC:
               case JSOP_LOCALDEC:
-                if (IsVarSlot(jp, pc, &atom, &i))
+                /* LOCALINC/LOCALDEC are followed by GETLOCAL containing the slot. */
+                JS_ASSERT(pc[JSOP_LOCALINC_LENGTH] == JSOP_GETLOCAL);
+                if (IsVarSlot(jp, pc + JSOP_LOCALINC_LENGTH, &atom, &i))
                     goto do_atominc;
                 lval = GetLocal(ss, i);
                 goto do_lvalinc;
 
               case JSOP_RETRVAL:
                 todo = -2;
                 break;
 
@@ -4408,17 +4422,19 @@ Decompile(SprintStack *ss, jsbytecode *p
                 todo = ss->sprinter.getOffset();
                 Sprint(&ss->sprinter, "%s ", prefix);
                 SprintOpcode(ss, rval, rvalpc, pc, todo);
                 break;
               }
 
               case JSOP_INCARG:
               case JSOP_DECARG:
-                atom = GetArgOrVarAtom(jp, GET_ARGNO(pc));
+                /* INCARG/DECARG are followed by GETARG containing the slot. */
+                JS_ASSERT(pc[JSOP_INCARG_LENGTH] == JSOP_GETARG);
+                atom = GetArgOrVarAtom(jp, GET_ARGNO(pc + JSOP_INCARG_LENGTH));
                 LOCAL_ASSERT(atom);
                 goto do_incatom;
 
               case JSOP_INCNAME:
               case JSOP_DECNAME:
               case JSOP_INCGNAME:
               case JSOP_DECGNAME:
                 LOAD_ATOM(0);
@@ -4468,17 +4484,19 @@ Decompile(SprintStack *ss, jsbytecode *p
                     todo = Sprint(&ss->sprinter, ss_format,
                                   js_incop_strs[!(cs->format & JOF_INC)], lval);
                 }
                 len += GetDecomposeLength(pc, JSOP_INCELEM_LENGTH);
                 break;
 
               case JSOP_ARGINC:
               case JSOP_ARGDEC:
-                atom = GetArgOrVarAtom(jp, GET_ARGNO(pc));
+                /* ARGINC/ARGDEC are followed by GETARG containing the slot. */
+                JS_ASSERT(pc[JSOP_ARGINC_LENGTH] == JSOP_GETARG);
+                atom = GetArgOrVarAtom(jp, GET_ARGNO(pc + JSOP_ARGINC_LENGTH));
                 LOCAL_ASSERT(atom);
                 goto do_atominc;
 
               case JSOP_NAMEINC:
               case JSOP_NAMEDEC:
               case JSOP_GNAMEINC:
               case JSOP_GNAMEDEC:
                 LOAD_ATOM(0);
@@ -5611,17 +5629,17 @@ js_DecompileFunction(JSPrinter *jp)
 
     if (fun->isNative() || fun->isSelfHostedBuiltin()) {
         js_printf(jp, ") {\n");
         jp->indent += 4;
         js_printf(jp, native_code_str);
         jp->indent -= 4;
         js_printf(jp, "\t}");
     } else {
-        JSScript *script = fun->script();
+        RootedScript script(cx, fun->script());
 #if JS_HAS_DESTRUCTURING
         SprintStack ss(cx);
 #endif
 
         /* Print the parameters. */
         jsbytecode *pc = script->main();
         jsbytecode *endpc = pc + script->length;
         JSBool ok = JS_TRUE;
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -224,25 +224,25 @@ OPDEF(JSOP_NEWARRAY,  90, "newarray",   
 OPDEF(JSOP_NEWOBJECT, 91, "newobject",  NULL,         5,  0,  1, 19,  JOF_OBJECT|JOF_TYPESET)
 OPDEF(JSOP_ENDINIT,   92, "endinit",    NULL,         1,  0,  0, 19,  JOF_BYTE)
 OPDEF(JSOP_INITPROP,  93, "initprop",   NULL,         5,  2,  1,  3,  JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
 OPDEF(JSOP_INITELEM,  94, "initelem",   NULL,         1,  3,  1,  3,  JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING)
 OPDEF(JSOP_INITELEM_INC,95, "initelem_inc", NULL,     1,  3,  2,  3,  JOF_BYTE|JOF_ELEM|JOF_SET)
 OPDEF(JSOP_UNUSED15,  96, "unused15",   NULL,         1,  0,  0,  0,  JOF_BYTE)
 
 /* Fast inc/dec ops for args and locals. */
-OPDEF(JSOP_INCARG,    97, "incarg",     NULL,         3,  0,  1, 15,  JOF_QARG |JOF_NAME|JOF_INC|JOF_TMPSLOT3)
-OPDEF(JSOP_DECARG,    98, "decarg",     NULL,         3,  0,  1, 15,  JOF_QARG |JOF_NAME|JOF_DEC|JOF_TMPSLOT3)
-OPDEF(JSOP_ARGINC,    99, "arginc",     NULL,         3,  0,  1, 15,  JOF_QARG |JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3)
-OPDEF(JSOP_ARGDEC,   100, "argdec",     NULL,         3,  0,  1, 15,  JOF_QARG |JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3)
+OPDEF(JSOP_INCARG,    97, "incarg",     NULL,         3,  0,  1, 15,  JOF_QARG |JOF_NAME|JOF_INC|JOF_TMPSLOT3|JOF_DECOMPOSE)
+OPDEF(JSOP_DECARG,    98, "decarg",     NULL,         3,  0,  1, 15,  JOF_QARG |JOF_NAME|JOF_DEC|JOF_TMPSLOT3|JOF_DECOMPOSE)
+OPDEF(JSOP_ARGINC,    99, "arginc",     NULL,         3,  0,  1, 15,  JOF_QARG |JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3|JOF_DECOMPOSE)
+OPDEF(JSOP_ARGDEC,   100, "argdec",     NULL,         3,  0,  1, 15,  JOF_QARG |JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3|JOF_DECOMPOSE)
 
-OPDEF(JSOP_INCLOCAL,  101,"inclocal",   NULL,         3,  0,  1, 15,  JOF_LOCAL|JOF_NAME|JOF_INC|JOF_TMPSLOT3)
-OPDEF(JSOP_DECLOCAL,  102,"declocal",   NULL,         3,  0,  1, 15,  JOF_LOCAL|JOF_NAME|JOF_DEC|JOF_TMPSLOT3)
-OPDEF(JSOP_LOCALINC,  103,"localinc",   NULL,         3,  0,  1, 15,  JOF_LOCAL|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3)
-OPDEF(JSOP_LOCALDEC,  104,"localdec",   NULL,         3,  0,  1, 15,  JOF_LOCAL|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3)
+OPDEF(JSOP_INCLOCAL,  101,"inclocal",   NULL,         3,  0,  1, 15,  JOF_LOCAL|JOF_NAME|JOF_INC|JOF_TMPSLOT3|JOF_DECOMPOSE)
+OPDEF(JSOP_DECLOCAL,  102,"declocal",   NULL,         3,  0,  1, 15,  JOF_LOCAL|JOF_NAME|JOF_DEC|JOF_TMPSLOT3|JOF_DECOMPOSE)
+OPDEF(JSOP_LOCALINC,  103,"localinc",   NULL,         3,  0,  1, 15,  JOF_LOCAL|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3|JOF_DECOMPOSE)
+OPDEF(JSOP_LOCALDEC,  104,"localdec",   NULL,         3,  0,  1, 15,  JOF_LOCAL|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3|JOF_DECOMPOSE)
 
 /* Leave a for-let-in block leaving its storage pushed (to be popped after enditer). */
 OPDEF(JSOP_LEAVEFORLETIN, 105,"leaveforletin",NULL,   1,  0,  0,  0,  JOF_BYTE)
 
 /* The argument is the offset to the next statement and is used by IonMonkey. */
 OPDEF(JSOP_LABEL,     106,"label",     NULL,          5,  0,  0,  0,  JOF_JUMP)
 OPDEF(JSOP_UNUSED3,   107,"unused3",   NULL,          1,  0,  0,  0,  JOF_BYTE)
 
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -3029,58 +3029,16 @@ mjit::Compiler::generateMethod()
             frame.pop();
           END_CASE(JSOP_INITPROP)
 
           BEGIN_CASE(JSOP_INITELEM)
             jsop_initelem();
             frame.popn(2);
           END_CASE(JSOP_INITELEM)
 
-          BEGIN_CASE(JSOP_INCARG)
-          BEGIN_CASE(JSOP_DECARG)
-          BEGIN_CASE(JSOP_ARGINC)
-          BEGIN_CASE(JSOP_ARGDEC)
-            if (script_->hasScriptCounts) {
-                restoreVarType();
-                FrameEntry *fe = frame.getArg(GET_SLOTNO(PC));
-                if (fe->isTypeKnown())
-                    arithFirstUseType = fe->getKnownType();
-            }
-
-            if (!jsop_arginc(op, GET_SLOTNO(PC)))
-                return Compile_Retry;
-
-            if (script_->hasScriptCounts) {
-                FrameEntry *fe = frame.getArg(GET_SLOTNO(PC));
-                updateArithCounts(PC, fe, arithFirstUseType, JSVAL_TYPE_INT32);
-                arithUpdated = true;
-            }
-          END_CASE(JSOP_ARGDEC)
-
-          BEGIN_CASE(JSOP_INCLOCAL)
-          BEGIN_CASE(JSOP_DECLOCAL)
-          BEGIN_CASE(JSOP_LOCALINC)
-          BEGIN_CASE(JSOP_LOCALDEC)
-            if (script_->hasScriptCounts) {
-                restoreVarType();
-                FrameEntry *fe = frame.getLocal(GET_SLOTNO(PC));
-                if (fe->isTypeKnown())
-                    arithFirstUseType = fe->getKnownType();
-            }
-
-            if (!jsop_localinc(op, GET_SLOTNO(PC)))
-                return Compile_Retry;
-
-            if (script_->hasScriptCounts) {
-                FrameEntry *fe = frame.getLocal(GET_SLOTNO(PC));
-                updateArithCounts(PC, fe, arithFirstUseType, JSVAL_TYPE_INT32);
-                arithUpdated = true;
-            }
-          END_CASE(JSOP_LOCALDEC)
-
           BEGIN_CASE(JSOP_BINDNAME)
             jsop_bindname(script_->getName(GET_UINT32_INDEX(PC)));
           END_CASE(JSOP_BINDNAME)
 
           BEGIN_CASE(JSOP_SETPROP)
           {
             jsbytecode *next = &PC[JSOP_SETPROP_LENGTH];
             bool pop = JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next);
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -698,18 +698,16 @@ private:
     bool jsop_mod();
     void jsop_neg();
     void jsop_bitnot();
     void jsop_not();
     void jsop_typeof();
     bool booleanJumpScript(JSOp op, jsbytecode *target);
     bool jsop_ifneq(JSOp op, jsbytecode *target);
     bool jsop_andor(JSOp op, jsbytecode *target);
-    bool jsop_arginc(JSOp op, uint32_t slot);
-    bool jsop_localinc(JSOp op, uint32_t slot);
     bool jsop_newinit();
     bool jsop_regexp();
     void jsop_initmethod();
     void jsop_initprop();
     void jsop_initelem();
     void jsop_setelem_dense();
 #ifdef JS_METHODJIT_TYPED_ARRAY
     void jsop_setelem_typed(int atype);
--- a/js/src/methodjit/FastArithmetic.cpp
+++ b/js/src/methodjit/FastArithmetic.cpp
@@ -145,17 +145,17 @@ bool
 mjit::Compiler::jsop_binary_slow(JSOp op, VoidStub stub, JSValueType type,
                                  FrameEntry *lhs, FrameEntry *rhs)
 {
     bool isStringResult = (op == JSOP_ADD) &&
                           (lhs->isType(JSVAL_TYPE_STRING) || rhs->isType(JSVAL_TYPE_STRING));
     JS_ASSERT_IF(isStringResult && type != JSVAL_TYPE_UNKNOWN, type == JSVAL_TYPE_STRING);
 
     prepareStubCall(Uses(2));
-    INLINE_STUBCALL(stub, REJOIN_BINARY);
+    INLINE_STUBCALL(stub, REJOIN_FALLTHROUGH);
     frame.popn(2);
     frame.pushSynced(isStringResult ? JSVAL_TYPE_STRING : type);
     return true;
 }
 
 bool
 mjit::Compiler::jsop_binary(JSOp op, VoidStub stub, JSValueType type, types::TypeSet *typeSet)
 {
@@ -362,17 +362,17 @@ mjit::Compiler::jsop_binary_double(Frame
         if (type != JSVAL_TYPE_DOUBLE)
             masm.storeDouble(fpLeft, frame.addressOf(lhs));
     }
 
     if (done.isSet())
         done.getJump().linkTo(masm.label(), &masm);
 
     stubcc.leave();
-    OOL_STUBCALL(stub, REJOIN_BINARY);
+    OOL_STUBCALL(stub, REJOIN_FALLTHROUGH);
 
     if (allocateRight)
         frame.freeReg(fpRight);
 
     frame.popn(2);
 
     if (type == JSVAL_TYPE_DOUBLE) {
         frame.pushDouble(fpLeft);
@@ -460,17 +460,17 @@ mjit::Compiler::jsop_binary_full_simple(
 
     /* Slow paths funnel here. */
     if (notNumber.isSet())
         notNumber.get().linkTo(stubcc.masm.label(), &stubcc.masm);
 
     /* Slow call - use frame.sync to avoid erroneous jump repatching in stubcc. */
     frame.sync(stubcc.masm, Uses(2));
     stubcc.leave();
-    OOL_STUBCALL(stub, REJOIN_BINARY);
+    OOL_STUBCALL(stub, REJOIN_FALLTHROUGH);
 
     /* Finish up stack operations. */
     frame.popn(2);
 
     if (type == JSVAL_TYPE_INT32)
         frame.pushTypedPayload(type, regs.result);
     else
         frame.pushNumber(regs.result, true);
@@ -727,17 +727,17 @@ mjit::Compiler::jsop_binary_full(FrameEn
             rhsNotNumber.get().linkTo(stubcc.masm.label(), &stubcc.masm);
     }
     if (rhsNotNumber2.isSet())
         rhsNotNumber2.get().linkTo(stubcc.masm.label(), &stubcc.masm);
 
     /* Slow call - use frame.sync to avoid erroneous jump repatching in stubcc. */
     frame.sync(stubcc.masm, Uses(2));
     stubcc.leave();
-    OOL_STUBCALL(stub, REJOIN_BINARY);
+    OOL_STUBCALL(stub, REJOIN_FALLTHROUGH);
 
     /* Finish up stack operations. */
     frame.popn(2);
 
     /*
      * Steal the result register if we remat the LHS/RHS by undoing the operation.
      * In this case the result register was still assigned to the corresponding
      * frame entry (so it is synced properly in OOL paths), so steal it back.
--- a/js/src/methodjit/FastOps.cpp
+++ b/js/src/methodjit/FastOps.cpp
@@ -886,155 +886,16 @@ mjit::Compiler::jsop_andor(JSOp op, jsby
 
         frame.pop();
         return true;
     }
 
     return booleanJumpScript(op, target);
 }
 
-bool
-mjit::Compiler::jsop_localinc(JSOp op, uint32_t slot)
-{
-    restoreVarType();
-
-    types::StackTypeSet *types = pushedTypeSet(0);
-    JSValueType type = types ? types->getKnownTypeTag() : JSVAL_TYPE_UNKNOWN;
-
-    int amt = (op == JSOP_LOCALINC || op == JSOP_INCLOCAL) ? 1 : -1;
-
-    if (!analysis->incrementInitialValueObserved(PC)) {
-        // Before:
-        // After:  V
-        frame.pushLocal(slot);
-
-        // Before: V
-        // After:  V 1
-        frame.push(Int32Value(-amt));
-
-        // Note, SUB will perform integer conversion for us.
-        // Before: V 1
-        // After:  N+1
-        if (!jsop_binary(JSOP_SUB, stubs::Sub, type, types))
-            return false;
-
-        // Before: N+1
-        // After:  N+1
-        frame.storeLocal(slot, analysis->popGuaranteed(PC));
-    } else {
-        // Before:
-        // After: V
-        frame.pushLocal(slot);
-
-        // Before: V
-        // After:  N
-        jsop_pos();
-
-        // Before: N
-        // After:  N N
-        frame.dup();
-
-        // Before: N N
-        // After:  N N 1
-        frame.push(Int32Value(amt));
-
-        // Before: N N 1
-        // After:  N N+1
-        if (!jsop_binary(JSOP_ADD, stubs::Add, type, types))
-            return false;
-
-        // Before: N N+1
-        // After:  N N+1
-        frame.storeLocal(slot, true);
-
-        // Before: N N+1
-        // After:  N
-        frame.pop();
-    }
-
-    updateVarType();
-    return true;
-}
-
-bool
-mjit::Compiler::jsop_arginc(JSOp op, uint32_t slot)
-{
-    restoreVarType();
-
-    types::StackTypeSet *types = pushedTypeSet(0);
-    JSValueType type = types ? types->getKnownTypeTag() : JSVAL_TYPE_UNKNOWN;
-
-    int amt = (op == JSOP_ARGINC || op == JSOP_INCARG) ? 1 : -1;
-
-    if (!analysis->incrementInitialValueObserved(PC)) {
-        // Before:
-        // After:  V
-        if (script_->argsObjAliasesFormals())
-            jsop_aliasedArg(slot, /* get = */ true);
-        else
-            frame.pushArg(slot);
-
-        // Before: V
-        // After:  V 1
-        frame.push(Int32Value(-amt));
-
-        // Note, SUB will perform integer conversion for us.
-        // Before: V 1
-        // After:  N+1
-        if (!jsop_binary(JSOP_SUB, stubs::Sub, type, types))
-            return false;
-
-        // Before: N+1
-        // After:  N+1
-        bool popGuaranteed = analysis->popGuaranteed(PC);
-        if (script_->argsObjAliasesFormals())
-            jsop_aliasedArg(slot, /* get = */ false, popGuaranteed);
-        else
-            frame.storeArg(slot, popGuaranteed);
-    } else {
-        // Before:
-        // After: V
-        if (script_->argsObjAliasesFormals())
-            jsop_aliasedArg(slot, /* get = */ true);
-        else
-            frame.pushArg(slot);
-
-        // Before: V
-        // After:  N
-        jsop_pos();
-
-        // Before: N
-        // After:  N N
-        frame.dup();
-
-        // Before: N N
-        // After:  N N 1
-        frame.push(Int32Value(amt));
-
-        // Before: N N 1
-        // After:  N N+1
-        if (!jsop_binary(JSOP_ADD, stubs::Add, type, types))
-            return false;
-
-        // Before: N N+1
-        // After:  N N+1
-        if (script_->argsObjAliasesFormals())
-            jsop_aliasedArg(slot, /* get = */ false, true);
-        else
-            frame.storeArg(slot, true);
-
-        // Before: N N+1
-        // After:  N
-        frame.pop();
-    }
-
-    updateVarType();
-    return true;
-}
-
 static inline bool
 IsCacheableSetElem(FrameEntry *obj, FrameEntry *id, FrameEntry *value)
 {
     if (obj->isNotType(JSVAL_TYPE_OBJECT))
         return false;
     if (id->isNotType(JSVAL_TYPE_INT32) && id->isNotType(JSVAL_TYPE_DOUBLE))
         return false;
     if (id->isConstant()) {
@@ -2653,33 +2514,33 @@ void
 mjit::Compiler::jsop_pos()
 {
     FrameEntry *top = frame.peek(-1);
 
     if (top->isTypeKnown()) {
         if (top->getKnownType() <= JSVAL_TYPE_INT32)
             return;
         prepareStubCall(Uses(1));
-        INLINE_STUBCALL(stubs::Pos, REJOIN_POS);
+        INLINE_STUBCALL(stubs::Pos, REJOIN_FALLTHROUGH);
         frame.pop();
         frame.pushSynced(knownPushedType(0));
         return;
     }
 
     frame.giveOwnRegs(top);
 
     Jump j;
     if (frame.shouldAvoidTypeRemat(top))
         j = masm.testNumber(Assembler::NotEqual, frame.addressOf(top));
     else
         j = masm.testNumber(Assembler::NotEqual, frame.tempRegForType(top));
     stubcc.linkExit(j, Uses(1));
 
     stubcc.leave();
-    OOL_STUBCALL(stubs::Pos, REJOIN_POS);
+    OOL_STUBCALL(stubs::Pos, REJOIN_FALLTHROUGH);
 
     stubcc.rejoin(Changes(1));
 }
 
 void
 mjit::Compiler::jsop_initprop()
 {
     FrameEntry *obj = frame.peek(-2);
--- a/js/src/methodjit/FrameState.cpp
+++ b/js/src/methodjit/FrameState.cpp
@@ -2525,40 +2525,23 @@ FrameState::ensureFullRegs(FrameEntry *f
 }
 
 inline bool
 FrameState::binaryEntryLive(FrameEntry *fe) const
 {
     /*
      * Compute whether fe is live after the binary operation performed at the current
      * bytecode. This is similar to variableLive except that it returns false for the
-     * top two stack entries and special cases LOCALINC/ARGINC and friends, which fuse
-     * a binary operation before writing over the local/arg.
+     * top two stack entries.
      */
     JS_ASSERT(cx->typeInferenceEnabled());
 
     if (deadEntry(fe, 2))
         return false;
 
-    switch (JSOp(*a->PC)) {
-      case JSOP_INCLOCAL:
-      case JSOP_DECLOCAL:
-      case JSOP_LOCALINC:
-      case JSOP_LOCALDEC:
-        if (fe - a->locals == (int) GET_SLOTNO(a->PC))
-            return false;
-      case JSOP_INCARG:
-      case JSOP_DECARG:
-      case JSOP_ARGINC:
-      case JSOP_ARGDEC:
-        if (fe - a->args == (int) GET_SLOTNO(a->PC))
-            return false;
-      default:;
-    }
-
     JS_ASSERT(fe != a->callee_);
 
     /* Arguments are always treated as live within inline frames, see bestEvictReg. */
     if (a->parent && fe < a->locals)
         return true;
 
     /* Caller must check that no copies are invalidated by rewriting the entry. */
     return fe >= a->spBase || variableLive(fe, a->PC);
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -725,51 +725,16 @@ JS_STATIC_ASSERT(JSOP_NOP == 0);
 #if defined(JS_METHODJIT_SPEW)
 static const char *OpcodeNames[] = {
 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) #name,
 # include "jsopcode.tbl"
 # undef OPDEF
 };
 #endif
 
-static void
-FinishVarIncOp(VMFrame &f, RejoinState rejoin, Value ov, Value nv, Value *vp)
-{
-    /* Finish an increment operation on a LOCAL or ARG. These do not involve property accesses. */
-    JS_ASSERT(rejoin == REJOIN_POS || rejoin == REJOIN_BINARY);
-
-    JSContext *cx = f.cx;
-
-    JSOp op = JSOp(*f.pc());
-    JS_ASSERT(op == JSOP_LOCALINC || op == JSOP_INCLOCAL ||
-              op == JSOP_LOCALDEC || op == JSOP_DECLOCAL ||
-              op == JSOP_ARGINC || op == JSOP_INCARG ||
-              op == JSOP_ARGDEC || op == JSOP_DECARG);
-    const JSCodeSpec *cs = &js_CodeSpec[op];
-
-    if (rejoin == REJOIN_POS) {
-        double d = ov.toNumber();
-        double N = (cs->format & JOF_INC) ? 1 : -1;
-        if (!nv.setNumber(d + N)) {
-            RootedScript fscript(cx, f.script());
-            types::TypeScript::MonitorOverflow(cx, fscript, f.pc());
-        }
-    }
-
-    unsigned i = GET_SLOTNO(f.pc());
-    if (JOF_TYPE(cs->format) == JOF_LOCAL)
-        f.fp()->unaliasedLocal(i) = nv;
-    else if (f.fp()->script()->argsObjAliasesFormals())
-        f.fp()->argsObj().setArg(i, nv);
-    else
-        f.fp()->unaliasedFormal(i) = nv;
-
-    *vp = (cs->format & JOF_POST) ? ov : nv;
-}
-
 extern "C" void *
 js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VMFrame &f)
 {
     FrameRejoinState 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_t pcOffset = jsrejoin >> 1;
@@ -782,17 +747,16 @@ js_InternalInterpret(void *returnData, v
 
     JSContext *cx = f.cx;
     StackFrame *fp = f.regs.fp();
     RootedScript script(cx, fp->script());
 
     jsbytecode *pc = f.regs.pc;
 
     JSOp op = JSOp(*pc);
-    const JSCodeSpec *cs = &js_CodeSpec[op];
 
     if (!script->ensureRanAnalysis(cx)) {
         js_ReportOutOfMemory(cx);
         return js_InternalThrow(f);
     }
 
     analyze::AutoEnterAnalysis enter(cx);
     analyze::ScriptAnalysis *analysis = script->analysis();
@@ -815,39 +779,16 @@ js_InternalInterpret(void *returnData, v
 #ifdef JS_METHODJIT_SPEW
     JaegerSpew(JSpew_Recompile, "interpreter rejoin (file \"%s\") (line \"%d\") (op %s) (opline \"%d\")\n",
                script->filename, script->lineno, OpcodeNames[op], PCToLineNumber(script, pc));
 #endif
 
     uint32_t nextDepth = UINT32_MAX;
     bool skipTrap = false;
 
-    if ((cs->format & (JOF_INC | JOF_DEC)) &&
-        (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;
-        enter.leave();
-
-        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]);
-        }
-
-        rejoin = REJOIN_FALLTHROUGH;
-    }
-
     switch (rejoin) {
       case REJOIN_SCRIPTED: {
         jsval_layout rval;
 #ifdef JS_NUNBOX32
         rval.asBits = ((uint64_t)returnType << 32) | (uint32_t)returnData;
 #elif JS_PUNBOX64
         rval.asBits = (uint64_t)returnType | (uint64_t)returnData;
 #else
@@ -1079,28 +1020,16 @@ js_InternalInterpret(void *returnData, v
           }
 
           default:
             f.regs.pc = nextpc;
             break;
         }
         break;
 
-      case REJOIN_POS:
-        /* Convert-to-number which might be part of an INC* op. */
-        JS_ASSERT(op == JSOP_POS);
-        f.regs.pc = nextpc;
-        break;
-
-      case REJOIN_BINARY:
-        /* Binary arithmetic op which might be part of an INC* op. */
-        JS_ASSERT(op == JSOP_ADD || op == JSOP_SUB || op == JSOP_MUL || op == JSOP_DIV);
-        f.regs.pc = nextpc;
-        break;
-
       case REJOIN_BRANCH: {
         /*
          * This must be an opcode fused with IFNE/IFEQ. Unfused IFNE/IFEQ are
          * implemented in terms of ValueToBoolean, which is infallible and
          * cannot trigger recompilation.
          */
         bool takeBranch = false;
         switch (JSOp(*nextpc)) {
--- a/js/src/methodjit/LoopState.cpp
+++ b/js/src/methodjit/LoopState.cpp
@@ -1439,55 +1439,19 @@ LoopState::getLoopTestAccess(const SSAVa
         if (outerAnalysis->liveness(slot).firstWrite(offset + 1, lifetime->backedge) != UINT32_MAX)
             return false;
         *pslot = slot;
         *pconstant = 0;
         return true;
     }
 
     jsbytecode *pc = outerScript->code + v.pushedOffset();
-
     JSOp op = JSOp(*pc);
-    const JSCodeSpec *cs = &js_CodeSpec[op];
-
-    /*
-     * If the pc is modifying a variable and the value tested is its earlier value
-     * (e.g. 'x++ < n'), we need to account for the modification --- at the start
-     * of the next iteration, the value compared will have been 'x - 1'.
-     * Note that we don't need to worry about other accesses to the variable
-     * in the condition like 'x++ < x', as loop tests where both operands are
-     * modified by the loop are rejected.
-     */
 
     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 (!outerAnalysis->integerOperation(pc))
-            return false;
-        uint32_t slot = GetBytecodeSlot(outerScript, pc);
-        if (outerAnalysis->slotEscapes(slot))
-            return false;
-
-        *pslot = slot;
-        if (cs->format & JOF_POST) {
-            if (cs->format & JOF_INC)
-                *pconstant = -1;
-            else
-                *pconstant = 1;
-        }
-        return true;
-      }
-
       case JSOP_ZERO:
       case JSOP_ONE:
       case JSOP_UINT16:
       case JSOP_UINT24:
       case JSOP_INT8:
       case JSOP_INT32:
         *pconstant = GetBytecodeInteger(pc);
         return true;
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -329,20 +329,18 @@ enum RejoinState {
      */
     REJOIN_CALL_PROLOGUE,
     REJOIN_CALL_PROLOGUE_LOWERED_CALL,
     REJOIN_CALL_PROLOGUE_LOWERED_APPLY,
 
     /* Triggered a recompilation while placing the arguments to an apply on the stack. */
     REJOIN_CALL_SPLAT,
 
-    /* FALLTHROUGH ops which can be implemented as part of an IncOp. */
+    /* Like REJOIN_FALLTHROUGH, but handles getprop used as part of JSOP_INSTANCEOF. */
     REJOIN_GETTER,
-    REJOIN_POS,
-    REJOIN_BINARY,
 
     /*
      * For an opcode fused with IFEQ/IFNE, call returns a boolean indicating
      * the result of the comparison and whether to take or not take the branch.
      */
     REJOIN_BRANCH
 };
 
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -20,17 +20,17 @@ namespace js {
  * Bytecode version number. Increment the subtrahend whenever JS bytecode
  * changes incompatibly.
  *
  * This version number is XDR'd near the front of xdr bytecode and
  * aborts deserialization if there is a mismatch between the current
  * and saved versions. If deserialization fails, the data should be
  * invalidated if possible.
  */
-static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 132);
+static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 133);
 
 class XDRBuffer {
   public:
     XDRBuffer(JSContext *cx)
       : context(cx), base(NULL), cursor(NULL), limit(NULL) { }
 
     JSContext *cx() const {
         return context;