[JAEGER] Simplify double arithmetic paths.
authorDavid Anderson <danderson@mozilla.com>
Thu, 22 Jul 2010 17:34:26 -0700
changeset 53152 51ed7672df50fbc43858f1235b448160425111f6
parent 53151 35c51fe530b6fea305f4c93ff39dabf3ee2914ee
child 53153 87e07ff8196caff03a2d480da82645d944d0f283
push id15660
push userrsayre@mozilla.com
push dateSat, 11 Sep 2010 19:16:24 +0000
treeherdermozilla-central@f1bd314e64ac [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone2.0b2pre
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
[JAEGER] Simplify double arithmetic paths.
js/src/assembler/assembler/X86Assembler.h
js/src/methodjit/Compiler.h
js/src/methodjit/FrameState-inl.h
js/src/methodjit/FrameState.cpp
js/src/methodjit/FrameState.h
js/src/methodjit/nunbox/FastArithmetic.cpp
js/src/methodjit/nunbox/FastOps.cpp
js/src/trace-test/tests/basic/testIntOverflow.js
js/src/trace-test/tests/jaeger/subCommutativity.js
--- a/js/src/assembler/assembler/X86Assembler.h
+++ b/js/src/assembler/assembler/X86Assembler.h
@@ -587,17 +587,18 @@ public:
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_AND, addr);
             m_formatter.immediate32(imm);
         }
     }
 #endif
 
     void negl_r(RegisterID dst)
     {
-        FIXME_INSN_PRINTING;
+        js::JaegerSpew(js::JSpew_Insns,
+                       IPFX "negl       %s\n", MAYBE_PAD, nameIReg(4,dst));
         m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NEG, dst);
     }
 
     void negl_m(int offset, RegisterID base)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NEG, base, offset);
     }
@@ -932,17 +933,18 @@ public:
             m_formatter.oneByteOp64(OP_GROUP2_EvIb, GROUP2_OP_SHL, dst);
             m_formatter.immediate8(imm);
         }
     }
 #endif
 
     void imull_rr(RegisterID src, RegisterID dst)
     {
-        FIXME_INSN_PRINTING;
+        js::JaegerSpew(js::JSpew_Insns,
+                       IPFX "imull       %s, %s\n", MAYBE_PAD, nameIReg(4,src), nameIReg(4, dst));
         m_formatter.twoByteOp(OP2_IMUL_GvEv, dst, src);
     }
 
     void imull_mr(int offset, RegisterID base, RegisterID dst)
     {
         FIXME_INSN_PRINTING;
         m_formatter.twoByteOp(OP2_IMUL_GvEv, dst, base, offset);
     }
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -140,42 +140,24 @@ class Compiler
 
     struct Defs {
         Defs(uint32 ndefs)
           : ndefs(ndefs)
         { }
         uint32 ndefs;
     };
 
-    class MaybeRegisterID {
-      public:
-        MaybeRegisterID()
-          : reg(Registers::ReturnReg), set(false)
-        { }
-
-        MaybeRegisterID(RegisterID reg)
-          : reg(reg), set(true)
-        { }
-
-        inline RegisterID getReg() const { JS_ASSERT(set); return reg; }
-        inline void setReg(const RegisterID r) { reg = r; set = true; }
-        inline bool isSet() const { return set; }
-
-      private:
-        RegisterID reg;
-        bool set;
-    };
-
     class MaybeJump {
       public:
         MaybeJump()
           : set(false)
         { }
 
         inline Jump getJump() const { JS_ASSERT(set); return jump; }
+        inline Jump get() const { JS_ASSERT(set); return jump; }
         inline void setJump(const Jump &j) { jump = j; set = true; }
         inline bool isSet() const { return set; }
 
         inline MaybeJump &operator=(Jump j) { setJump(j); return *this; }
 
       private:
         Jump jump;
         bool set;
@@ -229,16 +211,17 @@ class Compiler
     JSC::ExecutablePool *getExecPool(size_t size);
     bool compareTwoValues(JSContext *cx, JSOp op, const Value &lhs, const Value &rhs);
 
     /* Emitting helpers. */
     void restoreFrameRegs(Assembler &masm);
     void emitStubCmpOp(BoolStub stub, jsbytecode *target, JSOp fused);
     void iterNext();
     void iterMore();
+    MaybeJump loadDouble(FrameEntry *fe, FPRegisterID fpReg);
 
     /* Opcode handlers. */
     void jumpAndTrace(Jump j, jsbytecode *target);
     void jsop_bindname(uint32 index);
     void jsop_setglobal(uint32 index);
     void jsop_getglobal(uint32 index);
     void jsop_getprop_slow();
     void jsop_getarg(uint32 index);
@@ -267,19 +250,19 @@ class Compiler
     bool jsop_callprop_obj(JSAtom *atom);
     bool jsop_callprop_str(JSAtom *atom);
     bool jsop_callprop_generic(JSAtom *atom);
     void jsop_instanceof();
     void jsop_name(JSAtom *atom);
 
     /* Fast arithmetic. */
     void jsop_binary(JSOp op, VoidStub stub);
-    void jsop_binary_intmath(JSOp op, RegisterID *returnReg,
-                             MaybeJump &jmpOverflow);
-    void jsop_binary_dblmath(JSOp op, FPRegisterID rfp, FPRegisterID lfp);
+    void jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op, VoidStub stub);
+    void jsop_binary_full_simple(FrameEntry *fe, JSOp op, VoidStub stub);
+    void jsop_binary_double(FrameEntry *lhs, FrameEntry *rhs, JSOp op, VoidStub stub);
     void slowLoadConstantDouble(Assembler &masm, FrameEntry *fe,
                                 FPRegisterID fpreg);
     void maybeJumpIfNotInt32(Assembler &masm, MaybeJump &mj, FrameEntry *fe,
                              MaybeRegisterID &mreg);
     void maybeJumpIfNotDouble(Assembler &masm, MaybeJump &mj, FrameEntry *fe,
                               MaybeRegisterID &mreg);
 
     /* Fast opcodes. */
--- a/js/src/methodjit/FrameState-inl.h
+++ b/js/src/methodjit/FrameState-inl.h
@@ -328,33 +328,16 @@ FrameState::pushUntypedPayload(JSValueTy
     fe->data.unsync();
     fe->setNotCopied();
     fe->setCopyOf(NULL);
     fe->data.setRegister(payload);
     regstate[payload] = RegisterState(fe, RematInfo::DATA);
 }
 
 inline JSC::MacroAssembler::RegisterID
-FrameState::predictRegForType(FrameEntry *fe)
-{
-    JS_ASSERT(!fe->type.isConstant());
-    if (fe->isCopy())
-        fe = fe->copyOf();
-
-    if (fe->type.inRegister())
-        return fe->type.reg();
-
-    /* :XXX: X64 */
-
-    RegisterID reg = allocReg(fe, RematInfo::TYPE);
-    fe->type.setRegister(reg);
-    return reg;
-}
-
-inline JSC::MacroAssembler::RegisterID
 FrameState::tempRegForType(FrameEntry *fe, RegisterID fallback)
 {
     JS_ASSERT(regstate[fallback].fe == NULL);
     if (fe->isCopy())
         fe = fe->copyOf();
 
     JS_ASSERT(!fe->type.isConstant());
 
@@ -736,13 +719,44 @@ FrameState::giveOwnRegs(FrameEntry *fe)
         pushTypedPayload(type, data);
     } else {
         RegisterID type = copyTypeIntoReg(fe);
         pop();
         pushRegs(type, data);
     }
 }
 
+inline void
+FrameState::loadDouble(FrameEntry *fe, FPRegisterID fpReg, Assembler &masm) const
+{
+    if (fe->type.synced() && fe->data.synced()) {
+        masm.loadDouble(addressOf(fe), fpReg);
+        return;
+    }
+
+    if (fe->isCopy()) {
+        FrameEntry *backing = fe->copyOf();
+        if (backing->type.synced() && backing->data.synced()) {
+            masm.loadDouble(addressOf(backing), fpReg);
+            return;
+        }
+        fe = backing;
+    }
+
+    Address address = addressOf(fe);
+    do {
+        if (!fe->data.synced()) {
+            syncData(fe, address, masm);
+            if (fe->isConstant())
+                break;
+        }
+        if (!fe->type.synced())
+            syncType(fe, address, masm);
+    } while (0);
+
+    masm.loadDouble(address, fpReg);
+}
+
 } /* namspace mjit */
 } /* namspace js */
 
 #endif /* include */
 
--- a/js/src/methodjit/FrameState.cpp
+++ b/js/src/methodjit/FrameState.cpp
@@ -956,8 +956,193 @@ void
 FrameState::shift(int32 n)
 {
     JS_ASSERT(n < 0);
     JS_ASSERT(sp + n - 1 >= spBase);
     storeLocal(uint32(&sp[n - 1] - locals), true);
     pop();
 }
 
+static inline bool
+AllocHelper(RematInfo &info, MaybeRegisterID &maybe)
+{
+    if (info.inRegister()) {
+        maybe = info.reg();
+        return true;
+    }
+    return false;
+}
+
+void
+FrameState::allocForSameBinary(FrameEntry *fe, JSOp op, BinaryAlloc &alloc)
+{
+    JS_ASSERT(fe->isCopy());
+
+    if (!fe->isTypeKnown()) {
+        alloc.lhsType = tempRegForType(fe);
+        pinReg(alloc.lhsType.reg());
+    }
+
+    alloc.lhsData = tempRegForData(fe);
+
+    if (!freeRegs.empty()) {
+        alloc.result = allocReg();
+        masm.move(alloc.lhsData.reg(), alloc.result);
+        alloc.lhsNeedsRemat = false;
+    } else {
+        alloc.result = alloc.lhsData.reg();
+        takeReg(alloc.result);
+        alloc.lhsNeedsRemat = true;
+    }
+
+    if (alloc.lhsType.isSet())
+        unpinReg(alloc.lhsType.reg());
+}
+
+void
+FrameState::allocForBinary(FrameEntry *lhs, FrameEntry *rhs, JSOp op, BinaryAlloc &alloc)
+{
+    FrameEntry *backingLeft = lhs;
+    FrameEntry *backingRight = rhs;
+
+    if (backingLeft->isCopy())
+        backingLeft = backingLeft->copyOf();
+    if (backingRight->isCopy())
+        backingRight = backingRight->copyOf();
+
+    /*
+     * For each remat piece of both FEs, if a register is assigned, get it now
+     * and pin it. This is safe - constants and known types will be avoided.
+     */
+    if (AllocHelper(backingLeft->type, alloc.lhsType))
+        pinReg(alloc.lhsType.reg());
+    if (AllocHelper(backingLeft->data, alloc.lhsData))
+        pinReg(alloc.lhsData.reg());
+    if (AllocHelper(backingRight->type, alloc.rhsType))
+        pinReg(alloc.rhsType.reg());
+    if (AllocHelper(backingRight->data, alloc.rhsData))
+        pinReg(alloc.rhsData.reg());
+
+    /* For each type without a register, give it a register if needed. */
+    if (!alloc.lhsType.isSet() && backingLeft->type.inMemory()) {
+        alloc.lhsType = tempRegForType(lhs);
+        pinReg(alloc.lhsType.reg());
+    }
+    if (!alloc.rhsType.isSet() && backingRight->type.inMemory()) {
+        alloc.rhsType = tempRegForType(rhs);
+        pinReg(alloc.rhsType.reg());
+    }
+
+    bool commu;
+    switch (op) {
+      case JSOP_ADD:
+      case JSOP_MUL:
+      case JSOP_SUB:
+        commu = true;
+        break;
+
+      case JSOP_DIV:
+        commu = false;
+        break;
+
+      default:
+        JS_NOT_REACHED("unknown op");
+        return;
+    }
+
+    /*
+     * Data is a little more complicated. If the op is MUL, not all CPUs
+     * have multiplication on immediates, so a register is needed. Also,
+     * if the op is not commutative, the LHS _must_ be in a register.
+     */
+    JS_ASSERT_IF(lhs->isConstant(), !rhs->isConstant());
+    JS_ASSERT_IF(rhs->isConstant(), !lhs->isConstant());
+
+    if (!alloc.lhsData.isSet()) {
+        if (backingLeft->data.inMemory()) {
+            alloc.lhsData = tempRegForData(lhs);
+            pinReg(alloc.lhsData.reg());
+        } else if (op == JSOP_MUL || !commu) {
+            JS_ASSERT(lhs->isConstant());
+            alloc.lhsData = allocReg();
+            alloc.extraFree = alloc.lhsData;
+            masm.move(Imm32(lhs->getValue().toInt32()), alloc.lhsData.reg());
+        }
+    }
+    if (!alloc.rhsData.isSet()) {
+        if (backingRight->data.inMemory()) {
+            alloc.rhsData = tempRegForData(rhs);
+            pinReg(alloc.rhsData.reg());
+        } else if (op == JSOP_MUL) {
+            JS_ASSERT(rhs->isConstant());
+            alloc.rhsData = allocReg();
+            alloc.extraFree = alloc.rhsData;
+            masm.move(Imm32(rhs->getValue().toInt32()), alloc.rhsData.reg());
+        }
+    }
+
+    alloc.lhsNeedsRemat = false;
+    alloc.rhsNeedsRemat = false;
+
+    /*
+     * Now a result register is needed. It must contain a mutable copy of the
+     * LHS. For commutative operations, we can opt to use the RHS instead. At
+     * this point, if for some reason either must be in a register, that has
+     * already been guaranteed at this point.
+     */
+    if (!freeRegs.empty()) {
+        /* Free reg - just grab it. */
+        alloc.result = allocReg();
+        if (!alloc.lhsData.isSet()) {
+            JS_ASSERT(alloc.rhsData.isSet());
+            JS_ASSERT(commu);
+            masm.move(alloc.rhsData.reg(), alloc.result);
+            alloc.resultHasRhs = true;
+        } else {
+            masm.move(alloc.lhsData.reg(), alloc.result);
+            alloc.resultHasRhs = false;
+        }
+    } else {
+        /*
+         * No free regs. Find a good candidate to re-use. Best candidates don't
+         * require syncs on the inline path.
+         */
+        bool leftInReg = backingLeft->data.inRegister();
+        bool rightInReg = backingRight->data.inRegister();
+        bool leftSynced = backingLeft->data.synced();
+        bool rightSynced = backingRight->data.synced();
+        if (!commu || (leftInReg && (leftSynced || (!rightInReg || !rightSynced)))) {
+            JS_ASSERT(backingLeft->data.inRegister() || !commu);
+            JS_ASSERT_IF(backingLeft->data.inRegister(),
+                         backingLeft->data.reg() == alloc.lhsData.reg());
+            if (backingLeft->data.inRegister()) {
+                alloc.result = backingLeft->data.reg();
+                unpinReg(alloc.result);
+                takeReg(alloc.result);
+                alloc.lhsNeedsRemat = true;
+            } else {
+                /* For now, just spill... */
+                alloc.result = allocReg();
+                masm.move(alloc.lhsData.reg(), alloc.result);
+            }
+            alloc.resultHasRhs = false;
+        } else {
+            JS_ASSERT(commu);
+            JS_ASSERT(!leftInReg || (rightInReg && rightSynced));
+            alloc.result = backingRight->data.reg();
+            unpinReg(alloc.result);
+            takeReg(alloc.result);
+            alloc.resultHasRhs = true;
+            alloc.rhsNeedsRemat = true;
+        }
+    }
+
+    /* Unpin everything that was pinned. */
+    if (backingLeft->type.inRegister())
+        unpinReg(backingLeft->type.reg());
+    if (backingRight->type.inRegister())
+        unpinReg(backingRight->type.reg());
+    if (backingLeft->data.inRegister())
+        unpinReg(backingLeft->data.reg());
+    if (backingRight->data.inRegister())
+        unpinReg(backingRight->data.reg());
+}
+
--- a/js/src/methodjit/FrameState.h
+++ b/js/src/methodjit/FrameState.h
@@ -67,16 +67,48 @@ struct Uses {
 
 struct Changes {
     explicit Changes(uint32 nchanges)
       : nchanges(nchanges)
     { }
     uint32 nchanges;
 };
 
+class MaybeRegisterID {
+    typedef JSC::MacroAssembler::RegisterID RegisterID;
+
+  public:
+    MaybeRegisterID()
+      : reg_(Registers::ReturnReg), set(false)
+    { }
+
+    MaybeRegisterID(RegisterID reg)
+      : reg_(reg), set(true)
+    { }
+
+    inline RegisterID reg() const { JS_ASSERT(set); return reg_; }
+    inline void setReg(const RegisterID r) { reg_ = r; set = true; }
+    inline bool isSet() const { return set; }
+
+    MaybeRegisterID & operator =(const MaybeRegisterID &other) {
+        set = other.set;
+        reg_ = other.reg_;
+        return *this;
+    }
+
+    MaybeRegisterID & operator =(RegisterID r) {
+        setReg(r);
+        return *this;
+    }
+
+  private:
+    RegisterID reg_;
+    bool set;
+};
+
 /*
  * The FrameState keeps track of values on the frame during compilation.
  * The compiler can query FrameState for information about arguments, locals,
  * and stack slots (all hereby referred to as "slots"). Slot information can
  * be requested in constant time. For each slot there is a FrameEntry *. If
  * this is non-NULL, it contains valid information and can be returned.
  *
  * The register allocator keeps track of registers as being in one of two
@@ -237,23 +269,16 @@ class FrameState
     inline void leaveBlock(uint32 n);
 
     /*
      * Pushes a copy of a local variable.
      */
     void pushLocal(uint32 n);
 
     /*
-     * Allocates a temporary register for a FrameEntry's type, but does not
-     * output the bytecode to load the value into the register. The semantics
-     * are the same as with tempRegForType().
-     */
-    inline RegisterID predictRegForType(FrameEntry *fe);
-
-    /*
      * Allocates a temporary register for a FrameEntry's type. The register
      * can be spilled or clobbered by the frame. The compiler may only operate
      * on it temporarily, and must take care not to clobber it.
      */
     inline RegisterID tempRegForType(FrameEntry *fe);
 
     /*
      * Try to use a register already allocated for fe's type, but if one
@@ -342,16 +367,51 @@ class FrameState
      * Returns a register that contains the constant Int32 value of the
      * frame entry's data payload.
      * Since the register is not bound to a FrameEntry,
      * it MUST be explicitly freed with freeReg().
      */
     RegisterID copyInt32ConstantIntoReg(FrameEntry *fe);
     RegisterID copyInt32ConstantIntoReg(Assembler &masm, FrameEntry *fe);
 
+    struct BinaryAlloc {
+        MaybeRegisterID lhsType;
+        MaybeRegisterID lhsData;
+        MaybeRegisterID rhsType;
+        MaybeRegisterID rhsData;
+        MaybeRegisterID extraFree;
+        RegisterID result;  // mutable result reg
+        bool resultHasRhs;  // whether the result has the RHS instead of the LHS
+        bool lhsNeedsRemat; // whether LHS needs memory remat
+        bool rhsNeedsRemat; // whether RHS needs memory remat
+    };
+
+    /*
+     * Ensures that the two given FrameEntries have registers for both their
+     * type and data. The register allocations are returned in a struct.
+     *
+     * One mutable register is allocated as well, holding the LHS payload. If
+     * this would cause a spill that could be avoided by using a mutable RHS,
+     * and the operation is commutative, then the resultHasRhs is set to true.
+     */
+    void allocForBinary(FrameEntry *lhs, FrameEntry *rhs, JSOp op, BinaryAlloc &alloc);
+
+    /*
+     * Similar to allocForBinary, except works when the LHS and RHS have the
+     * same backing FE. Only a reduced subset of BinaryAlloc is used:
+     *   lhsType
+     *   lhsData
+     *   result
+     *   lhsNeedsRemat
+     */
+    void allocForSameBinary(FrameEntry *fe, JSOp op, BinaryAlloc &alloc);
+
+    /* Loads an FE into an fp reg . */
+    inline void loadDouble(FrameEntry *fe, FPRegisterID fpReg, Assembler &masm) const;
+
     /*
      * Types don't always have to be in registers, sometimes the compiler
      * can use addresses and avoid spilling. If this FrameEntry has a synced
      * address and no register, this returns true.
      */
     inline bool shouldAvoidTypeRemat(FrameEntry *fe);
 
     /*
--- a/js/src/methodjit/nunbox/FastArithmetic.cpp
+++ b/js/src/methodjit/nunbox/FastArithmetic.cpp
@@ -44,16 +44,18 @@
 #include "methodjit/MethodJIT.h"
 #include "methodjit/Compiler.h"
 #include "methodjit/StubCalls.h"
 #include "methodjit/FrameState-inl.h"
 
 using namespace js;
 using namespace js::mjit;
 
+typedef JSC::MacroAssembler::FPRegisterID FPRegisterID;
+
 static inline bool
 JSOpBinaryTryConstantFold(JSContext *cx, FrameState &frame, JSOp op, FrameEntry *lhs, FrameEntry *rhs)
 {
     if (!lhs->isConstant() || !rhs->isConstant())
         return false;
 
     const Value &L = lhs->getValue();
     const Value &R = rhs->getValue();
@@ -109,121 +111,16 @@ JSOpBinaryTryConstantFold(JSContext *cx,
     Value v;
     v.setNumber(dL);
     frame.popn(2);
     frame.push(v);
 
     return true;
 }
 
-void
-mjit::Compiler::jsop_binary_intmath(JSOp op, RegisterID *returnReg, MaybeJump &jmpOverflow)
-{
-    FrameEntry *rhs = frame.peek(-1);
-    FrameEntry *lhs = frame.peek(-2);
-
-    /* 
-     * One of the values should not be a constant.
-     * If there is a constant, force it to be in rhs.
-     */
-    bool swapped = false;
-    if (lhs->isConstant()) {
-        JS_ASSERT(!rhs->isConstant());
-        swapped = true;
-        FrameEntry *tmp = lhs;
-        lhs = rhs;
-        rhs = tmp;
-    }
-
-    RegisterID reg = Registers::ReturnReg;
-    reg = frame.copyDataIntoReg(lhs);
-    if (swapped && op == JSOP_SUB) {
-        masm.neg32(reg);
-        op = JSOP_ADD;
-    }
-
-    Jump fail;
-    switch(op) {
-      case JSOP_ADD:
-        if (rhs->isConstant()) {
-            fail = masm.branchAdd32(Assembler::Overflow,
-                                    Imm32(rhs->getValue().toInt32()), reg);
-        } else if (frame.shouldAvoidDataRemat(rhs)) {
-            fail = masm.branchAdd32(Assembler::Overflow,
-                                    frame.addressOf(rhs), reg);
-        } else {
-            RegisterID rhsReg = frame.tempRegForData(rhs);
-            fail = masm.branchAdd32(Assembler::Overflow,
-                                    rhsReg, reg);
-        }
-        break;
-
-      case JSOP_SUB:
-        if (rhs->isConstant()) {
-            fail = masm.branchSub32(Assembler::Overflow,
-                                    Imm32(rhs->getValue().toInt32()), reg);
-        } else if (frame.shouldAvoidDataRemat(rhs)) {
-            fail = masm.branchSub32(Assembler::Overflow,
-                                    frame.addressOf(rhs), reg);
-        } else {
-            RegisterID rhsReg = frame.tempRegForData(rhs);
-            fail = masm.branchSub32(Assembler::Overflow,
-                                    rhsReg, reg);
-        }
-        break;
-
-#if !defined(JS_CPU_ARM)
-      case JSOP_MUL:
-        if (rhs->isConstant()) {
-            RegisterID rhsReg = frame.copyInt32ConstantIntoReg(rhs);
-            fail = masm.branchMul32(Assembler::Overflow,
-                                    rhsReg, reg);
-            frame.freeReg(rhsReg);
-        } else if (frame.shouldAvoidDataRemat(rhs)) {
-            fail = masm.branchMul32(Assembler::Overflow,
-                                    frame.addressOf(rhs), reg);
-        } else {
-            RegisterID rhsReg = frame.tempRegForData(rhs);
-            fail = masm.branchMul32(Assembler::Overflow,
-                                    rhsReg, reg);
-        }
-        break;
-#endif /* JS_CPU_ARM */
-
-      default:
-        JS_NOT_REACHED("unhandled int32 op.");
-        break;
-    }
-    
-    *returnReg = reg;
-    jmpOverflow.setJump(fail);
-}
-
-void
-mjit::Compiler::jsop_binary_dblmath(JSOp op, FPRegisterID rfp, FPRegisterID lfp)
-{
-    switch (op) {
-      case JSOP_ADD:
-        stubcc.masm.addDouble(rfp, lfp);
-        break;
-      case JSOP_SUB:
-        stubcc.masm.subDouble(rfp, lfp);
-        break;
-      case JSOP_MUL:
-        stubcc.masm.mulDouble(rfp, lfp);
-        break;
-      case JSOP_DIV:
-        stubcc.masm.divDouble(rfp, lfp);
-        break;
-      default:
-        JS_NOT_REACHED("unhandled double op.");
-        break;
-    }
-}
-
 /*
  * TODO: Replace with fast constant loading.
  * This is not part of the FrameState because fast constant loading
  * is inherently a Compiler task, and this code should not be used
  * except temporarily.
  */
 void
 mjit::Compiler::slowLoadConstantDouble(Assembler &masm,
@@ -240,45 +137,43 @@ mjit::Compiler::slowLoadConstantDouble(A
 }
 
 void
 mjit::Compiler::maybeJumpIfNotInt32(Assembler &masm, MaybeJump &mj, FrameEntry *fe,
                                     MaybeRegisterID &mreg)
 {
     if (!fe->isTypeKnown()) {
         if (mreg.isSet())
-            mj.setJump(masm.testInt32(Assembler::NotEqual, mreg.getReg()));
+            mj.setJump(masm.testInt32(Assembler::NotEqual, mreg.reg()));
         else
             mj.setJump(masm.testInt32(Assembler::NotEqual, frame.addressOf(fe)));
     } else if (fe->getKnownType() != JSVAL_TYPE_INT32) {
         mj.setJump(masm.jump());
     }
 }
 
 void
 mjit::Compiler::maybeJumpIfNotDouble(Assembler &masm, MaybeJump &mj, FrameEntry *fe,
                                     MaybeRegisterID &mreg)
 {
     if (!fe->isTypeKnown()) {
         if (mreg.isSet())
-            mj.setJump(masm.testDouble(Assembler::NotEqual, mreg.getReg()));
+            mj.setJump(masm.testDouble(Assembler::NotEqual, mreg.reg()));
         else
             mj.setJump(masm.testDouble(Assembler::NotEqual, frame.addressOf(fe)));
     } else if (fe->getKnownType() != JSVAL_TYPE_DOUBLE) {
         mj.setJump(masm.jump());
     }
 }
 
 void
 mjit::Compiler::jsop_binary(JSOp op, VoidStub stub)
 {
     FrameEntry *rhs = frame.peek(-1);
     FrameEntry *lhs = frame.peek(-2);
-    /* Predict the address of the returned FrameEntry. */
-    FrameEntry *returnFe = lhs;
 
     if (JSOpBinaryTryConstantFold(cx, frame, op, lhs, rhs))
         return;
 
     /*
      * Bail out if there are unhandled types or ops.
      * This is temporary while ops are still being implemented.
      */
@@ -303,259 +198,466 @@ mjit::Compiler::jsop_binary(JSOp op, Voi
         return;
     }
 
     /* Can do int math iff there is no double constant and the op is not division. */
     bool canDoIntMath = op != JSOP_DIV &&
                         !((rhs->isTypeKnown() && rhs->getKnownType() == JSVAL_TYPE_DOUBLE) ||
                           (lhs->isTypeKnown() && lhs->getKnownType() == JSVAL_TYPE_DOUBLE));
 
-    frame.syncAllRegs(Registers::AvailRegs);
+    if (canDoIntMath)
+        jsop_binary_full(lhs, rhs, op, stub);
+    else
+        jsop_binary_double(lhs, rhs, op, stub);
+}
 
-    /*
-     * Since both frame-syncing/stub-call code and the double
-     * fast-path are emitted into the secondary buffer, we attempt
-     * to minimize the number of jumps by forcing all fast-path exits
-     * to be synced prior to generating any code for doubles.
-     * 
-     * The required registers for type-checking are 'pre-allocated' here
-     * by invoking register allocation logic. This logic works only if
-     * the registers are used in the order that they are allocated,
-     * and only if success is guaranteed after the type-checking step.
-     *
-     * Fast-paths for ints and doubles cannot use type information:
-     * ints can be converted to doubles in case of overflow, and
-     * doubles can be converted to ints in case of roundness.
-     * It is therefore not possible to optimize away a type check
-     * unless a value's type is known to be constant.
-     */
-    MaybeRegisterID rhsTypeReg;
-    bool rhsTypeRegNeedsLoad = false;
-    if (!rhs->isTypeKnown() && !frame.shouldAvoidTypeRemat(rhs)) {
-        rhsTypeRegNeedsLoad = !frame.peekTypeInRegister(rhs);
-        rhsTypeReg.setReg(frame.predictRegForType(rhs));
-    }
-    Label rhsSyncTarget = stubcc.syncExitAndJump(Uses(2));
-    
-    MaybeRegisterID lhsTypeReg;
-    bool lhsTypeRegNeedsLoad = false;
-    if (!lhs->isTypeKnown() && !frame.shouldAvoidTypeRemat(lhs) &&
-        !frame.haveSameBacking(lhs, rhs)) {
-        lhsTypeRegNeedsLoad = !frame.peekTypeInRegister(lhs);
-        lhsTypeReg.setReg(frame.predictRegForType(lhs));
-    }
-    Label lhsSyncTarget = rhsSyncTarget;
-    if (!rhsTypeReg.isSet() || lhsTypeReg.isSet())
-        lhsSyncTarget = stubcc.syncExitAndJump(Uses(2));
+static void
+EmitDoubleOp(JSOp op, FPRegisterID fpRight, FPRegisterID fpLeft, Assembler &masm)
+{
+    switch (op) {
+      case JSOP_ADD:
+        masm.addDouble(fpRight, fpLeft);
+        break;
 
+      case JSOP_SUB:
+        masm.subDouble(fpRight, fpLeft);
+        break;
 
-    /* Reassigned by jsop_binary_intmath() */
-    RegisterID returnReg = Registers::ReturnReg;
+      case JSOP_MUL:
+        masm.mulDouble(fpRight, fpLeft);
+        break;
+
+      case JSOP_DIV:
+        masm.divDouble(fpRight, fpLeft);
+        break;
 
-    /*
-     * The conversion path logic is slightly redundant because
-     * we hold the invariant that the lhsTypeReg's type cannot be
-     * loaded until all uses of the rhsTypeReg are finished.
-     * This invariant helps prevent reloading type data
-     * in the case of rhsTypeReg == lhsTypeReg.
-     */
-    FPRegisterID rfp = FPRegisters::First;
-    FPRegisterID lfp = FPRegisters::Second;
+      default:
+        JS_NOT_REACHED("unrecognized binary op");
+    }
+}
 
-    /*
-     * Conversion Path 2: rhs known double; lhs not double.
-     * Jumped to from double path if lhs not double.
-     * Partially jumped to from above conversion path.
-     */
-    MaybeJump jmpCvtPath2;
-    MaybeJump jmpCvtPath2NotInt;
-    Label lblCvtPath2 = stubcc.masm.label();
-    {
-        /* Don't bother emitting double conversion for a known double. */
-        if (!lhs->isTypeKnown() || lhs->getKnownType() != JSVAL_TYPE_DOUBLE) {
-            maybeJumpIfNotInt32(stubcc.masm, jmpCvtPath2NotInt, lhs, lhsTypeReg);
+mjit::Compiler::MaybeJump
+mjit::Compiler::loadDouble(FrameEntry *fe, FPRegisterID fpReg)
+{
+    MaybeJump notNumber;
 
-            if (!lhs->isConstant())
-                frame.convertInt32ToDouble(stubcc.masm, lhs, lfp);
-            else
-                slowLoadConstantDouble(stubcc.masm, lhs, lfp);
-
-            jmpCvtPath2.setJump(stubcc.masm.jump());
-        }
-    }
-    
-    /*
-     * Conversion Path 3: rhs int; lhs not int.
-     * Jumped to from int path if lhs not int.
-     */
-    MaybeJump jmpCvtPath3;
-    MaybeJump jmpCvtPath3NotDbl;
-    Label lblCvtPath3 = stubcc.masm.label();
-    {
-        /* Don't bother emitting double checking code for a known int. */
-        if (!lhs->isTypeKnown() || lhs->getKnownType() != JSVAL_TYPE_INT32) {
-            maybeJumpIfNotDouble(stubcc.masm, jmpCvtPath3NotDbl, lhs, lhsTypeReg);
-
-            if (!rhs->isConstant())
-                frame.convertInt32ToDouble(stubcc.masm, rhs, rfp);
-            else
-                slowLoadConstantDouble(stubcc.masm, rhs, rfp);
-
-            frame.copyEntryIntoFPReg(stubcc.masm, lhs, lfp);
-            jmpCvtPath3.setJump(stubcc.masm.jump());
-        }
+    if (fe->isConstant()) {
+        slowLoadConstantDouble(masm, fe, fpReg);
+    } else if (!fe->isTypeKnown()) {
+        //frame.tempRegForType(fe);
+        Jump j = frame.testDouble(Assembler::Equal, fe);
+        notNumber = frame.testInt32(Assembler::NotEqual, fe);
+        frame.convertInt32ToDouble(masm, fe, fpReg);
+        Jump converted = masm.jump();
+        j.linkTo(masm.label(), &masm);
+        frame.loadDouble(fe, fpReg, masm);
+        converted.linkTo(masm.label(), &masm);
+    } else if (fe->getKnownType() == JSVAL_TYPE_INT32) {
+        frame.convertInt32ToDouble(masm, fe, fpReg);
+    } else {
+        JS_ASSERT(fe->getKnownType() == JSVAL_TYPE_DOUBLE);
+        frame.loadDouble(fe, fpReg, masm);
     }
 
-    /* Double exits */
-    MaybeJump jmpRhsNotDbl;
-    MaybeJump jmpLhsNotDbl;
-    Jump jmpDblRejoin;
-    /* Double joins */
-    Label lblDblRhsTest = stubcc.masm.label();
-    Label lblDblDoMath;
-    {
-        /* Try a double fastpath. */
-
-        /* rhsTypeReg must have already been loaded by the int path. */
-        maybeJumpIfNotDouble(stubcc.masm, jmpRhsNotDbl, rhs, rhsTypeReg);
-        frame.copyEntryIntoFPReg(stubcc.masm, rhs, rfp);
-
-        if (lhsTypeRegNeedsLoad)
-            frame.emitLoadTypeTag(stubcc.masm, lhs, lhsTypeReg.getReg());
-        maybeJumpIfNotDouble(stubcc.masm, jmpLhsNotDbl, lhs, lhsTypeReg);
-        frame.copyEntryIntoFPReg(stubcc.masm, lhs, lfp);
-
-        lblDblDoMath = stubcc.masm.label();
-        jsop_binary_dblmath(op, rfp, lfp);
-
-        /*
-         * Double syncing/rejoining code is emitted after the integer path,
-         * so that returnReg is set in the FrameState. This is a hack.
-         */
-    }
-
+    return notNumber;
+}
 
-    /*
-     * Integer code must come after double code: tempRegForType(),
-     * used by jsop_binary_intmath(), changes compiler state.
-     */
-
-    /* Integer exits */
-    MaybeJump jmpRhsNotInt;
-    MaybeJump jmpLhsNotInt;
-    MaybeJump jmpOverflow;
-    MaybeJump jmpIntDiv;
-    {
-        /* Try an integer fastpath. */
-        if (rhsTypeRegNeedsLoad)
-            frame.emitLoadTypeTag(rhs, rhsTypeReg.getReg());
-        maybeJumpIfNotInt32(masm, jmpRhsNotInt, rhs, rhsTypeReg);
+/*
+ * This function emits a single fast-path for handling numerical arithmetic.
+ * Unlike jsop_binary_full(), all integers are converted to doubles.
+ */
+void
+mjit::Compiler::jsop_binary_double(FrameEntry *lhs, FrameEntry *rhs, JSOp op, VoidStub stub)
+{
+    FPRegisterID fpLeft = FPRegisters::First;
+    FPRegisterID fpRight = FPRegisters::Second;
 
-        if (lhsTypeRegNeedsLoad)
-            frame.emitLoadTypeTag(lhs, lhsTypeReg.getReg());
-        maybeJumpIfNotInt32(masm, jmpLhsNotInt, lhs, lhsTypeReg);
-
-        /*
-         * Only perform integer math if there is no constant
-         * double value forcing type conversion.
-         */
-        if (canDoIntMath)
-            jsop_binary_intmath(op, &returnReg, jmpOverflow);
-
-        if (op == JSOP_DIV)
-           jmpIntDiv.setJump(masm.jump()); 
-    }
-    
+    MaybeJump lhsNotNumber = loadDouble(lhs, fpLeft);
 
-    /* Sync/rejoin double path. */
-    {
-        /*
-         * Since there does not exist a floating-point register allocator,
-         * the calculated value must be synced to memory.
-         */
-        stubcc.masm.storeDouble(lfp, frame.addressOf(returnFe));
-        if (canDoIntMath)
-            stubcc.masm.loadPayload(frame.addressOf(returnFe), returnReg);
-
-        jmpDblRejoin = stubcc.masm.jump();
-    }
-
-    /*
-     * Conversion Path 1: rhs known int; lhs known int.
-     * Jumped to on overflow, or for division conversion.
-     *
-     * TODO: We can reuse instructions from Conversion Path 2
-     * to handle conversion for the lhs. However, this involves
-     * fighting the FrameState, which is currently unadvised.
-     */
-    MaybeJump jmpCvtPath1;
-    Label lblCvtPath1 = stubcc.masm.label();
-    {
-        if (canDoIntMath || op == JSOP_DIV) {
-            /*
-             * TODO: Constants should be handled by rip/PC-relative
-             * addressing on x86_64/ARM, and absolute addressing
-             * on x86. This requires coercing masm into emitting
-             * constants into the instruction stream.
-             * Such an approach is safe because each conversion
-             * path is directly jumped to: no code can fall through.
-             */
-            if (!lhs->isConstant())
-                frame.convertInt32ToDouble(stubcc.masm, lhs, lfp);
-            else
-                slowLoadConstantDouble(stubcc.masm, lhs, lfp);
+    MaybeJump rhsNotNumber;
+    if (frame.haveSameBacking(lhs, rhs))
+        masm.moveDouble(fpLeft, fpRight);
+    else
+        rhsNotNumber = loadDouble(rhs, fpRight);
 
-            if (!rhs->isConstant())
-                frame.convertInt32ToDouble(stubcc.masm, rhs, rfp);
-            else
-                slowLoadConstantDouble(stubcc.masm, rhs, rfp);
-
-            jmpCvtPath1.setJump(stubcc.masm.jump());
-        }
-    }
-
+    EmitDoubleOp(op, fpRight, fpLeft, masm);
+    masm.storeDouble(fpLeft, frame.addressOf(lhs));
 
-    /* Patch up jumps. */
-    if (jmpRhsNotInt.isSet())
-        stubcc.linkExitDirect(jmpRhsNotInt.getJump(), lblDblRhsTest);
-    if (jmpLhsNotInt.isSet())
-        stubcc.linkExitDirect(jmpLhsNotInt.getJump(), lblCvtPath3);
-    if (jmpOverflow.isSet())
-        stubcc.linkExitDirect(jmpOverflow.getJump(), lblCvtPath1);
-    if (jmpIntDiv.isSet())
-        stubcc.linkExitDirect(jmpIntDiv.getJump(), lblCvtPath1);
+    if (lhsNotNumber.isSet())
+        stubcc.linkExit(lhsNotNumber.get(), Uses(2));
+    if (rhsNotNumber.isSet())
+        stubcc.linkExit(rhsNotNumber.get(), Uses(2));
 
-    if (jmpRhsNotDbl.isSet())
-        jmpRhsNotDbl.getJump().linkTo(rhsSyncTarget, &stubcc.masm);
-    if (jmpLhsNotDbl.isSet())
-        jmpLhsNotDbl.getJump().linkTo(lblCvtPath2, &stubcc.masm);
-
-    if (jmpCvtPath1.isSet())
-        jmpCvtPath1.getJump().linkTo(lblDblDoMath, &stubcc.masm);
-    if (jmpCvtPath2.isSet())
-        jmpCvtPath2.getJump().linkTo(lblDblDoMath, &stubcc.masm);
-    if (jmpCvtPath2NotInt.isSet())
-        jmpCvtPath2NotInt.getJump().linkTo(lhsSyncTarget, &stubcc.masm);
-    if (jmpCvtPath3.isSet())
-        jmpCvtPath3.getJump().linkTo(lblDblDoMath, &stubcc.masm);
-    if (jmpCvtPath3NotDbl.isSet())
-        jmpCvtPath3NotDbl.getJump().linkTo(lhsSyncTarget, &stubcc.masm);
-    
-        
-    /* Leave. */
     stubcc.leave();
     stubcc.call(stub);
 
     frame.popn(2);
-    if (canDoIntMath)
-        frame.pushUntypedPayload(JSVAL_TYPE_INT32, returnReg);
-    else
+    frame.pushSynced();
+
+    stubcc.rejoin(Changes(1));
+}
+
+/*
+ * Simpler version of jsop_binary_full() for when lhs == rhs.
+ */
+void
+mjit::Compiler::jsop_binary_full_simple(FrameEntry *fe, JSOp op, VoidStub stub)
+{
+    FrameEntry *lhs = frame.peek(-2);
+
+    /* Easiest case: known double. Don't bother conversion back yet? */
+    if (fe->isTypeKnown() && fe->getKnownType() == JSVAL_TYPE_DOUBLE) {
+        loadDouble(fe, FPRegisters::First);
+        EmitDoubleOp(op, FPRegisters::First, FPRegisters::First, masm);
+        frame.popn(2);
         frame.pushSynced();
+        return;
+    }
 
-    stubcc.crossJump(jmpDblRejoin, masm.label());
+    /* Allocate all registers up-front. */
+    FrameState::BinaryAlloc regs;
+    frame.allocForSameBinary(fe, op, regs);
+
+    MaybeJump notNumber;
+    MaybeJump doublePathDone;
+    if (!fe->isTypeKnown()) {
+        Jump notInt = masm.testInt32(Assembler::NotEqual, regs.lhsType.reg());
+        stubcc.linkExitDirect(notInt, stubcc.masm.label());
+
+        notNumber = stubcc.masm.testDouble(Assembler::NotEqual, regs.lhsType.reg());
+        frame.loadDouble(fe, FPRegisters::First, stubcc.masm);
+        EmitDoubleOp(op, FPRegisters::First, FPRegisters::First, stubcc.masm);
+
+        /* Force the double back to memory. */
+        Address result = frame.addressOf(lhs);
+        stubcc.masm.storeDouble(FPRegisters::First, result);
+
+        /* Load the payload into the result reg so the rejoin is safe. */
+        stubcc.masm.loadPayload(result, regs.result);
+
+        doublePathDone = stubcc.masm.jump();
+    }
+
+    /* Okay - good to emit the integer fast-path. */
+    MaybeJump overflow;
+    switch (op) {
+      case JSOP_ADD:
+        overflow = masm.branchAdd32(Assembler::Overflow, regs.result, regs.result);
+        break;
+
+      case JSOP_SUB:
+        overflow = masm.branchSub32(Assembler::Overflow, regs.result, regs.result);
+        break;
+
+#if !defined(JS_CPU_ARM)
+      case JSOP_MUL:
+        overflow = masm.branchMul32(Assembler::Overflow, regs.result, regs.result);
+        break;
+#endif
+
+      default:
+        JS_NOT_REACHED("unrecognized op");
+    }
+    
+    JS_ASSERT(overflow.isSet());
+
+    /*
+     * Integer overflow path. Separate from the first double path, since we
+     * know never to try and convert back to integer.
+     */
+    MaybeJump overflowDone;
+    stubcc.linkExitDirect(overflow.get(), stubcc.masm.label());
+    {
+        if (regs.lhsNeedsRemat) {
+            Address address = masm.payloadOf(frame.addressOf(lhs));
+            stubcc.masm.convertInt32ToDouble(address, FPRegisters::First);
+        } else if (!lhs->isConstant()) {
+            stubcc.masm.convertInt32ToDouble(regs.lhsData.reg(), FPRegisters::First);
+        } else {
+            slowLoadConstantDouble(stubcc.masm, lhs, FPRegisters::First);
+        }
+
+        EmitDoubleOp(op, FPRegisters::First, FPRegisters::First, stubcc.masm);
+
+        Address address = frame.addressOf(lhs);
+        stubcc.masm.storeDouble(FPRegisters::First, address);
+        stubcc.masm.loadPayload(address, regs.result);
+
+        overflowDone = stubcc.masm.jump();
+    }
+
+    /* Slow paths funnel here. */
+    if (notNumber.isSet())
+        notNumber.get().linkTo(stubcc.masm.label(), &stubcc.masm);
+    overflowDone.get().linkTo(stubcc.masm.label(), &stubcc.masm);
+
+    /* Slow call. */
+    stubcc.syncExit(Uses(2));
+    stubcc.leave();
+    stubcc.call(stub);
+
+    /* Finish up stack operations. */
+    frame.popn(2);
+    frame.pushUntypedPayload(JSVAL_TYPE_INT32, regs.result);
+
+    /* Merge back OOL double paths. */
+    if (doublePathDone.isSet())
+        stubcc.linkRejoin(doublePathDone.get());
+    stubcc.linkRejoin(overflowDone.get());
+
+    stubcc.rejoin(Changes(1));
+}
+
+/*
+ * This function emits multiple fast-paths for handling numerical arithmetic.
+ * Currently, it handles only ADD, SUB, and MUL, where both LHS and RHS are
+ * known not to be doubles.
+ *
+ * The control flow of the emitted code depends on which types are known.
+ * Given both types are unknown, the full spread looks like:
+ *
+ * Inline                              OOL
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Is LHS Int32?  ------ No -------->  Is LHS Double?  ----- No -------,
+ *                                     Sync LHS                        |
+ *                                     Load LHS into XMM1              |
+ *                                     Is RHS Double? ---- Yes --,     |
+ *                                       Is RHS Int32? ---- No --|-----|
+ *                                       Convert RHS into XMM0   |     |
+ *                                     Else  <-------------------'     |
+ *                                       Sync RHS                      |
+ *                                       Load RHS into XMM0            |
+ *                                     [Add,Sub,Mul] XMM0,XMM1         |
+ *                                     Jump ---------------------,     |
+ *                                                               |     |
+ * Is RHS Int32?  ------ No ------->   Is RHS Double? ----- No --|-----|
+ *                                     Sync RHS                  |     |
+ *                                     Load RHS into XMM0        |     |
+ *                                     Convert LHS into XMM1     |     |
+ *                                     [Add,Sub,Mul] XMM0,XMM1   |     |
+ *                                     Jump ---------------------|   Slow Call
+ *                                                               |
+ * [Add,Sub,Mul] RHS, LHS                                        |
+ * Overflow      ------ Yes ------->   Convert RHS into XMM0     |
+ *                                     Convert LHS into XMM1     |
+ *                                     [Add,Sub,Mul] XMM0,XMM1   |
+ *                                     Sync XMM1 to stack    <---'
+ *  <--------------------------------- Rejoin
+ */
+void
+mjit::Compiler::jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op, VoidStub stub)
+{
+    if (frame.haveSameBacking(lhs, rhs)) {
+        jsop_binary_full_simple(lhs, op, stub);
+        return;
+    }
+
+    /* Allocate all registers up-front. */
+    FrameState::BinaryAlloc regs;
+    frame.allocForBinary(lhs, rhs, op, regs);
+
+    /* Quick-test some invariants. */
+    JS_ASSERT_IF(lhs->isTypeKnown(), lhs->getKnownType() == JSVAL_TYPE_INT32);
+    JS_ASSERT_IF(rhs->isTypeKnown(), rhs->getKnownType() == JSVAL_TYPE_INT32);
+
+    FPRegisterID fpLeft = FPRegisters::First;
+    FPRegisterID fpRight = FPRegisters::Second;
+
+    MaybeJump lhsNotDouble;
+    MaybeJump rhsNotNumber;
+    MaybeJump lhsUnknownDone;
+    if (!lhs->isTypeKnown()) {
+        /* If the LHS is not a 32-bit integer, take OOL path. */
+        Jump lhsNotInt32 = masm.testInt32(Assembler::NotEqual, regs.lhsType.reg());
+        stubcc.linkExitDirect(lhsNotInt32, stubcc.masm.label());
+
+        /* OOL path for LHS as a double - first test LHS is double. */
+        lhsNotDouble = stubcc.masm.testDouble(Assembler::NotEqual, regs.lhsType.reg());
+
+        /* Ensure the RHS is a number. */
+        MaybeJump rhsIsDouble;
+        if (!rhs->isTypeKnown()) {
+            rhsIsDouble = stubcc.masm.testDouble(Assembler::Equal, regs.rhsType.reg());
+            rhsNotNumber = stubcc.masm.testInt32(Assembler::NotEqual, regs.rhsType.reg());
+        }
+
+        /* If RHS is constant, convert now. */
+        if (rhs->isConstant())
+            slowLoadConstantDouble(stubcc.masm, rhs, fpRight);
+        else
+            stubcc.masm.convertInt32ToDouble(regs.rhsData.reg(), fpRight);
+
+        if (!rhs->isTypeKnown()) {
+            /* Jump past double load, bind double type check. */
+            Jump converted = stubcc.masm.jump();
+            rhsIsDouble.get().linkTo(stubcc.masm.label(), &stubcc.masm);
+
+            /* Load the double. */
+            frame.loadDouble(rhs, fpRight, stubcc.masm);
+
+            converted.linkTo(stubcc.masm.label(), &stubcc.masm);
+        }
+
+        /* Load the LHS. */
+        frame.loadDouble(lhs, fpLeft, stubcc.masm);
+        lhsUnknownDone = stubcc.masm.jump();
+    }
+
+    MaybeJump rhsNotNumber2;
+    if (!rhs->isTypeKnown()) {
+        /* If the RHS is not a double, take OOL path. */
+        Jump notInt32 = masm.testInt32(Assembler::NotEqual, regs.rhsType.reg());
+        stubcc.linkExitDirect(notInt32, stubcc.masm.label());
+
+        /* Now test if RHS is a double. */
+        rhsNotNumber2 = stubcc.masm.testDouble(Assembler::NotEqual, regs.rhsType.reg());
+
+        /* We know LHS is an integer. */
+        if (lhs->isConstant())
+            slowLoadConstantDouble(stubcc.masm, lhs, fpLeft);
+        else
+            stubcc.masm.convertInt32ToDouble(regs.lhsData.reg(), fpLeft);
+
+        /* Load the RHS. */
+        frame.loadDouble(rhs, fpRight, stubcc.masm);
+    }
+
+    /* Perform the double addition. */
+    MaybeJump doublePathDone;
+    if (!rhs->isTypeKnown() || lhsUnknownDone.isSet()) {
+        /* If the LHS type was not known, link its path here. */
+        if (lhsUnknownDone.isSet())
+            lhsUnknownDone.get().linkTo(stubcc.masm.label(), &stubcc.masm);
+        
+        /* Perform the double operation. */
+        EmitDoubleOp(op, fpRight, fpLeft, stubcc.masm);
+
+        /* Force the double back to memory. */
+        Address result = frame.addressOf(lhs);
+        stubcc.masm.storeDouble(fpLeft, result);
+
+        /* Load the payload into the result reg so the rejoin is safe. */
+        stubcc.masm.loadPayload(result, regs.result);
+
+        /* We'll link this back up later, at the bottom of the op. */
+        doublePathDone = stubcc.masm.jump();
+    }
+
+    /* Time to do the integer path. Figure out the immutable side. */
+    int32 value = 0;
+    JSOp origOp = op;
+    MaybeRegisterID reg;
+    if (!regs.resultHasRhs) {
+        if (!regs.rhsData.isSet())
+            value = rhs->getValue().toInt32();
+        else
+            reg = regs.rhsData.reg();
+    } else {
+        if (!regs.lhsData.isSet())
+            value = lhs->getValue().toInt32();
+        else
+            reg = regs.lhsData.reg();
+        if (op == JSOP_SUB) {
+            masm.neg32(regs.result);
+            op = JSOP_ADD;
+        }
+    }
+
+    /* Okay - good to emit the integer fast-path. */
+    MaybeJump overflow;
+    switch (op) {
+      case JSOP_ADD:
+        if (reg.isSet())
+            overflow = masm.branchAdd32(Assembler::Overflow, reg.reg(), regs.result);
+        else
+            overflow = masm.branchAdd32(Assembler::Overflow, Imm32(value), regs.result);
+        break;
+
+      case JSOP_SUB:
+        if (reg.isSet())
+            overflow = masm.branchSub32(Assembler::Overflow, reg.reg(), regs.result);
+        else
+            overflow = masm.branchSub32(Assembler::Overflow, Imm32(value), regs.result);
+        break;
+
+#if !defined(JS_CPU_ARM)
+      case JSOP_MUL:
+        JS_ASSERT(reg.isSet());
+        overflow = masm.branchMul32(Assembler::Overflow, reg.reg(), regs.result);
+        break;
+#endif
+
+      default:
+        JS_NOT_REACHED("unrecognized op");
+    }
+    op = origOp;
+    
+    JS_ASSERT(overflow.isSet());
+
+    /*
+     * Integer overflow path. Separate from the first double path, since we
+     * know never to try and convert back to integer.
+     */
+    MaybeJump overflowDone;
+    stubcc.linkExitDirect(overflow.get(), stubcc.masm.label());
+    {
+        if (regs.lhsNeedsRemat) {
+            Address address = masm.payloadOf(frame.addressOf(lhs));
+            stubcc.masm.convertInt32ToDouble(address, fpLeft);
+        } else if (!lhs->isConstant()) {
+            stubcc.masm.convertInt32ToDouble(regs.lhsData.reg(), fpLeft);
+        } else {
+            slowLoadConstantDouble(stubcc.masm, lhs, fpLeft);
+        }
+
+        if (regs.rhsNeedsRemat) {
+            Address address = masm.payloadOf(frame.addressOf(rhs));
+            stubcc.masm.convertInt32ToDouble(address, fpRight);
+        } else if (!rhs->isConstant()) {
+            stubcc.masm.convertInt32ToDouble(regs.rhsData.reg(), fpRight);
+        } else {
+            slowLoadConstantDouble(stubcc.masm, rhs, fpRight);
+        }
+
+        EmitDoubleOp(op, fpRight, fpLeft, stubcc.masm);
+
+        Address address = frame.addressOf(lhs);
+        stubcc.masm.storeDouble(fpLeft, address);
+        stubcc.masm.loadPayload(address, regs.result);
+
+        overflowDone = stubcc.masm.jump();
+    }
+
+    /* The register allocator creates at most one temporary. */
+    if (regs.extraFree.isSet())
+        frame.freeReg(regs.extraFree.reg());
+
+    /* Slow paths funnel here. */
+    if (lhsNotDouble.isSet()) {
+        lhsNotDouble.get().linkTo(stubcc.masm.label(), &stubcc.masm);
+        if (rhsNotNumber.isSet())
+            rhsNotNumber.get().linkTo(stubcc.masm.label(), &stubcc.masm);
+    }
+    if (rhsNotNumber2.isSet())
+        rhsNotNumber2.get().linkTo(stubcc.masm.label(), &stubcc.masm);
+
+    /* Slow call. */
+    stubcc.syncExit(Uses(2));
+    stubcc.leave();
+    stubcc.call(stub);
+
+    /* Finish up stack operations. */
+    frame.popn(2);
+    frame.pushUntypedPayload(JSVAL_TYPE_INT32, regs.result);
+
+    /* Merge back OOL double paths. */
+    if (doublePathDone.isSet())
+        stubcc.linkRejoin(doublePathDone.get());
+    stubcc.linkRejoin(overflowDone.get());
+
     stubcc.rejoin(Changes(1));
 }
 
 static const uint64 DoubleNegMask = 0x8000000000000000ULL;
 
 void
 mjit::Compiler::jsop_neg()
 {
@@ -575,17 +677,17 @@ mjit::Compiler::jsop_neg()
      * Load type information into register.
      */
     MaybeRegisterID feTypeReg;
     if (!fe->isTypeKnown() && !frame.shouldAvoidTypeRemat(fe)) {
         /* Safe because only one type is loaded. */
         feTypeReg.setReg(frame.tempRegForType(fe));
 
         /* Don't get clobbered by copyDataIntoReg(). */
-        frame.pinReg(feTypeReg.getReg());
+        frame.pinReg(feTypeReg.reg());
     }
 
     /*
      * If copyDataIntoReg() is called here, syncAllRegs() is not required.
      * If called from the int path, the double and int states would
      * need to be explicitly synced in a currently unsupported manner.
      *
      * This function call can call allocReg() and clobber any register.
@@ -630,17 +732,17 @@ mjit::Compiler::jsop_neg()
         stubcc.masm.storePayload(reg, frame.addressOf(fe));
         stubcc.masm.storeTypeTag(ImmType(JSVAL_TYPE_INT32), frame.addressOf(fe));
 
         jmpIntRejoin.setJump(stubcc.masm.jump());
     }
 
     frame.freeReg(reg);
     if (feTypeReg.isSet())
-        frame.unpinReg(feTypeReg.getReg());
+        frame.unpinReg(feTypeReg.reg());
 
     stubcc.leave();
     stubcc.call(stubs::Neg);
 
     frame.pop();
     frame.pushSynced();
 
     /* Link jumps. */
--- a/js/src/methodjit/nunbox/FastOps.cpp
+++ b/js/src/methodjit/nunbox/FastOps.cpp
@@ -804,17 +804,17 @@ mjit::Compiler::booleanJumpScript(JSOp o
 {
     FrameEntry *fe = frame.peek(-1);
 
     MaybeRegisterID type;
     MaybeRegisterID data;
 
     if (!fe->isTypeKnown() && !frame.shouldAvoidTypeRemat(fe)) {
         type.setReg(frame.tempRegForType(fe));
-        frame.pinReg(type.getReg());
+        frame.pinReg(type.reg());
     }
     data.setReg(frame.tempRegForData(fe));
 
     /* :FIXME: Can something more lightweight be used? */
     frame.forgetEverything();
 
     Assembler::Condition cond = (op == JSOP_IFNE || op == JSOP_OR)
                                 ? Assembler::NonZero
@@ -822,31 +822,31 @@ mjit::Compiler::booleanJumpScript(JSOp o
     Assembler::Condition ncond = (op == JSOP_IFNE || op == JSOP_OR)
                                  ? Assembler::Zero
                                  : Assembler::NonZero;
 
     /* Inline path: Boolean guard + call script. */
     MaybeJump jmpNotBool;
     MaybeJump jmpNotExecScript;
     if (type.isSet()) {
-        jmpNotBool.setJump(masm.testBoolean(Assembler::NotEqual, type.getReg()));
+        jmpNotBool.setJump(masm.testBoolean(Assembler::NotEqual, type.reg()));
     } else {
         if (!fe->isTypeKnown()) {
             jmpNotBool.setJump(masm.testBoolean(Assembler::NotEqual,
                                                 frame.addressOf(fe)));
         } else if (fe->getKnownType() != JSVAL_TYPE_BOOLEAN) {
             jmpNotBool.setJump(masm.jump());
         }
     }
 
     /* 
      * TODO: We don't need the second jump if
      * jumpInScript() can go from ool path to inline path.
      */
-    jmpNotExecScript.setJump(masm.branchTest32(ncond, data.getReg(), data.getReg()));
+    jmpNotExecScript.setJump(masm.branchTest32(ncond, data.reg(), data.reg()));
     Label lblExecScript = masm.label();
     Jump j = masm.jump();
 
 
     /* OOL path: Conversion to boolean. */
     MaybeJump jmpCvtExecScript;
     MaybeJump jmpCvtRejoin;
     Label lblCvtPath = stubcc.masm.label();
@@ -1171,22 +1171,22 @@ mjit::Compiler::jsop_getelem_dense(Frame
         stubcc.linkExit(notHole, Uses(2));
 
         /* Load slot address into regs. */
         masm.loadTypeTag(slot, tmpReg);
         masm.loadPayload(slot, objReg);
     } else {
         JS_ASSERT(idReg.isSet());
         Jump inRange = masm.branch32(Assembler::AboveOrEqual,
-                                     idReg.getReg(),
+                                     idReg.reg(),
                                      masm.payloadOf(Address(objReg, -int(sizeof(Value)))));
         stubcc.linkExit(inRange, Uses(2));
 
         /* guard not a hole */
-        BaseIndex slot(objReg, idReg.getReg(), Assembler::JSVAL_SCALE);
+        BaseIndex slot(objReg, idReg.reg(), Assembler::JSVAL_SCALE);
         Jump notHole = masm.branch32(Assembler::Equal, masm.tagOf(slot), ImmType(JSVAL_TYPE_MAGIC));
         stubcc.linkExit(notHole, Uses(2));
 
         masm.loadTypeTag(slot, tmpReg);
         masm.loadPayload(slot, objReg);
     }
     /* Postcondition: type must be in tmpReg, data must be in objReg. */
 
@@ -1207,17 +1207,17 @@ mjit::Compiler::jsop_getelem_known_type(
 
         /* Meat. */
         jsop_getelem_dense(obj, id, objReg, idReg, tmpReg);
         stubcc.leave();
         stubcc.call(stubs::GetElem);
 
         /* Epilogue. */
         if (idReg.isSet())
-            frame.freeReg(idReg.getReg());
+            frame.freeReg(idReg.reg());
         frame.popn(2);
         frame.pushRegs(tmpReg, objReg);
         stubcc.rejoin(Changes(1));
         return;
       }
 #ifdef JS_POLYIC
       case JSVAL_TYPE_STRING:
       {
@@ -1262,21 +1262,21 @@ mjit::Compiler::jsop_getelem_with_pic(Fr
     intGuard.linkTo(masm.label(), &masm);
     Jump stringGuard = masm.testString(Assembler::NotEqual, typeReg);
     stubcc.linkExit(stringGuard, Uses(2)); /* Neither int nor string at this point. */
 
     stubcc.leave();
     stubcc.call(stubs::GetElem);
     Jump toFinalMerge = stubcc.masm.jump();
 
-    jsop_getelem_pic(obj, id, objReg, idReg.getReg(), tmpReg);
+    jsop_getelem_pic(obj, id, objReg, idReg.reg(), tmpReg);
     performedDense.linkTo(masm.label(), &masm);
     frame.popn(2);
     frame.pushRegs(tmpReg, objReg);
-    frame.freeReg(idReg.getReg());
+    frame.freeReg(idReg.reg());
     toFinalMerge.linkTo(stubcc.masm.label(), &stubcc.masm);
     stubcc.rejoin(Changes(1));
 }
 #endif
 
 void
 mjit::Compiler::jsop_getelem_nopic(FrameEntry *obj, FrameEntry *id, RegisterID tmpReg)
 {
@@ -1288,17 +1288,17 @@ mjit::Compiler::jsop_getelem_nopic(Frame
     stubcc.linkExit(intGuard, Uses(2));
 
     /* Meat. */
     jsop_getelem_dense(obj, id, objReg, idReg, tmpReg);
     stubcc.leave();
     stubcc.call(stubs::GetElem);
 
     /* Epilogue. */
-    frame.freeReg(idReg.getReg());
+    frame.freeReg(idReg.reg());
     frame.popn(2);
     frame.pushRegs(tmpReg, objReg);
     stubcc.rejoin(Changes(1));
 }
 
 void
 mjit::Compiler::jsop_getelem()
 {
--- a/js/src/trace-test/tests/basic/testIntOverflow.js
+++ b/js/src/trace-test/tests/basic/testIntOverflow.js
@@ -1,15 +1,15 @@
 function testIntOverflow() {
     // int32_max - 7
     var ival = 2147483647 - 7;
     for (var i = 0; i < 30; i++) {
-        ival += 2;
+        ival += 30;
     }
     return (ival < 2147483647);
 }
 assertEq(testIntOverflow(), false);
 checkStats({
-    recorderStarted: 2,
+    recorderStarted: 1,
     recorderAborted: 0,
-    traceCompleted: 2,
-    traceTriggered: 2,
+    traceCompleted: 1,
+    traceTriggered: 1,
 });
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/jaeger/subCommutativity.js
@@ -0,0 +1,4 @@
+assertEq(6 - ((void 0) ^ 0x80000005), 2147483649);
+
+var x = ((void 0) ^ 0x80000005);
+assertEq(6 - x, 2147483649);