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 111740 e4204b2b987cbf9a996f56952b6b9f5bf7df6c7f
parent 111739 4826ed659d3199bb5dc6b9adde5902399fa73cfe
child 111741 ea35621ef12ae0d033e3cc3ab95f7495fef52a87
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersbhackett
bugs804636
milestone19.0a1
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;