Bug 1204194 - IonMonkey: MIPS: Split shareable code to mips-shared in MoveEmitter-mips32. r=nbp
authorHeiher <r@hev.cc>
Wed, 16 Sep 2015 17:01:24 +0800
changeset 295319 2a7153de17f7ef619722ebe1613724b2f4717b74
parent 295318 035471f81fb49d5b7f165dbad986d27d0fd814fb
child 295320 e7c0433c8dbe441064fef6a9a8288d9cd6c1634e
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs1204194
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1204194 - IonMonkey: MIPS: Split shareable code to mips-shared in MoveEmitter-mips32. r=nbp --- .../MoveEmitter-mips-shared.cpp} | 182 ++------------------ .../MoveEmitter-mips-shared.h} | 35 ++-- js/src/jit/mips32/MoveEmitter-mips32.cpp | 190 --------------------- js/src/jit/mips32/MoveEmitter-mips32.h | 42 +---- js/src/moz.build | 1 + 5 files changed, 39 insertions(+), 411 deletions(-) copy js/src/jit/{mips32/MoveEmitter-mips32.cpp => mips-shared/MoveEmitter-mips-shared.cpp} (45%) copy js/src/jit/{mips32/MoveEmitter-mips32.h => mips-shared/MoveEmitter-mips-shared.h} (65%)
js/src/jit/mips-shared/MoveEmitter-mips-shared.cpp
js/src/jit/mips-shared/MoveEmitter-mips-shared.h
js/src/jit/mips32/MoveEmitter-mips32.cpp
js/src/jit/mips32/MoveEmitter-mips32.h
js/src/moz.build
copy from js/src/jit/mips32/MoveEmitter-mips32.cpp
copy to js/src/jit/mips-shared/MoveEmitter-mips-shared.cpp
--- a/js/src/jit/mips32/MoveEmitter-mips32.cpp
+++ b/js/src/jit/mips-shared/MoveEmitter-mips-shared.cpp
@@ -1,187 +1,69 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "jit/mips32/MoveEmitter-mips32.h"
+#include "jit/mips-shared/MoveEmitter-mips-shared.h"
 
 #include "jit/MacroAssembler-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
-MoveEmitterMIPS::MoveEmitterMIPS(MacroAssembler& masm)
-  : inCycle_(0),
-    masm(masm),
-    pushedAtCycle_(-1),
-    pushedAtSpill_(-1),
-    spilledReg_(InvalidReg),
-    spilledFloatReg_(InvalidFloatReg)
-{
-    pushedAtStart_ = masm.framePushed();
-}
-
 void
-MoveEmitterMIPS::emit(const MoveResolver& moves)
+MoveEmitterMIPSShared::emit(const MoveResolver& moves)
 {
     if (moves.numCycles()) {
         // Reserve stack for cycle resolution
         masm.reserveStack(moves.numCycles() * sizeof(double));
         pushedAtCycle_ = masm.framePushed();
     }
 
     for (size_t i = 0; i < moves.numMoves(); i++)
         emit(moves.getMove(i));
 }
 
-MoveEmitterMIPS::~MoveEmitterMIPS()
-{
-    assertDone();
-}
-
 Address
-MoveEmitterMIPS::cycleSlot(uint32_t slot, uint32_t subslot) const
+MoveEmitterMIPSShared::cycleSlot(uint32_t slot, uint32_t subslot) const
 {
     int32_t offset = masm.framePushed() - pushedAtCycle_;
     MOZ_ASSERT(Imm16::IsInSignedRange(offset));
     return Address(StackPointer, offset + slot * sizeof(double) + subslot);
 }
 
 int32_t
-MoveEmitterMIPS::getAdjustedOffset(const MoveOperand& operand)
+MoveEmitterMIPSShared::getAdjustedOffset(const MoveOperand& operand)
 {
     MOZ_ASSERT(operand.isMemoryOrEffectiveAddress());
     if (operand.base() != StackPointer)
         return operand.disp();
 
     // Adjust offset if stack pointer has been moved.
     return operand.disp() + masm.framePushed() - pushedAtStart_;
 }
 
 Address
-MoveEmitterMIPS::getAdjustedAddress(const MoveOperand& operand)
+MoveEmitterMIPSShared::getAdjustedAddress(const MoveOperand& operand)
 {
     return Address(operand.base(), getAdjustedOffset(operand));
 }
 
 
 Register
-MoveEmitterMIPS::tempReg()
+MoveEmitterMIPSShared::tempReg()
 {
     spilledReg_ = SecondScratchReg;
     return SecondScratchReg;
 }
 
 void
-MoveEmitterMIPS::breakCycle(const MoveOperand& from, const MoveOperand& to,
-                            MoveOp::Type type, uint32_t slotId)
-{
-    // There is some pattern:
-    //   (A -> B)
-    //   (B -> A)
-    //
-    // This case handles (A -> B), which we reach first. We save B, then allow
-    // the original move to continue.
-    switch (type) {
-      case MoveOp::FLOAT32:
-        if (to.isMemory()) {
-            FloatRegister temp = ScratchFloat32Reg;
-            masm.loadFloat32(getAdjustedAddress(to), temp);
-            // Since it is uncertain if the load will be aligned or not
-            // just fill both of them with the same value.
-            masm.storeFloat32(temp, cycleSlot(slotId, 0));
-            masm.storeFloat32(temp, cycleSlot(slotId, 4));
-        } else {
-            // Just always store the largest possible size.
-            masm.storeDouble(to.floatReg().doubleOverlay(), cycleSlot(slotId, 0));
-        }
-        break;
-      case MoveOp::DOUBLE:
-        if (to.isMemory()) {
-            FloatRegister temp = ScratchDoubleReg;
-            masm.loadDouble(getAdjustedAddress(to), temp);
-            masm.storeDouble(temp, cycleSlot(slotId, 0));
-        } else {
-            masm.storeDouble(to.floatReg(), cycleSlot(slotId, 0));
-        }
-        break;
-      case MoveOp::INT32:
-        MOZ_ASSERT(sizeof(uintptr_t) == sizeof(int32_t));
-      case MoveOp::GENERAL:
-        if (to.isMemory()) {
-            Register temp = tempReg();
-            masm.loadPtr(getAdjustedAddress(to), temp);
-            masm.storePtr(temp, cycleSlot(0, 0));
-        } else {
-            // Second scratch register should not be moved by MoveEmitter.
-            MOZ_ASSERT(to.reg() != spilledReg_);
-            masm.storePtr(to.reg(), cycleSlot(0, 0));
-        }
-        break;
-      default:
-        MOZ_CRASH("Unexpected move type");
-    }
-}
-
-void
-MoveEmitterMIPS::completeCycle(const MoveOperand& from, const MoveOperand& to,
-                               MoveOp::Type type, uint32_t slotId)
-{
-    // There is some pattern:
-    //   (A -> B)
-    //   (B -> A)
-    //
-    // This case handles (B -> A), which we reach last. We emit a move from the
-    // saved value of B, to A.
-    switch (type) {
-      case MoveOp::FLOAT32:
-        if (to.isMemory()) {
-            FloatRegister temp = ScratchFloat32Reg;
-            masm.loadFloat32(cycleSlot(slotId, 0), temp);
-            masm.storeFloat32(temp, getAdjustedAddress(to));
-        } else {
-            uint32_t offset = 0;
-            if (from.floatReg().numAlignedAliased() == 1)
-                offset = sizeof(float);
-            masm.loadFloat32(cycleSlot(slotId, offset), to.floatReg());
-        }
-        break;
-      case MoveOp::DOUBLE:
-        if (to.isMemory()) {
-            FloatRegister temp = ScratchDoubleReg;
-            masm.loadDouble(cycleSlot(slotId, 0), temp);
-            masm.storeDouble(temp, getAdjustedAddress(to));
-        } else {
-            masm.loadDouble(cycleSlot(slotId, 0), to.floatReg());
-        }
-        break;
-      case MoveOp::INT32:
-        MOZ_ASSERT(sizeof(uintptr_t) == sizeof(int32_t));
-      case MoveOp::GENERAL:
-        MOZ_ASSERT(slotId == 0);
-        if (to.isMemory()) {
-            Register temp = tempReg();
-            masm.loadPtr(cycleSlot(0, 0), temp);
-            masm.storePtr(temp, getAdjustedAddress(to));
-        } else {
-            // Second scratch register should not be moved by MoveEmitter.
-            MOZ_ASSERT(to.reg() != spilledReg_);
-            masm.loadPtr(cycleSlot(0, 0), to.reg());
-        }
-        break;
-      default:
-        MOZ_CRASH("Unexpected move type");
-    }
-}
-
-void
-MoveEmitterMIPS::emitMove(const MoveOperand& from, const MoveOperand& to)
+MoveEmitterMIPSShared::emitMove(const MoveOperand& from, const MoveOperand& to)
 {
     if (from.isGeneralReg()) {
         // Second scratch register should not be moved by MoveEmitter.
         MOZ_ASSERT(from.reg() != spilledReg_);
 
         if (to.isGeneralReg())
             masm.movePtr(from.reg(), to.reg());
         else if (to.isMemory())
@@ -207,17 +89,17 @@ MoveEmitterMIPS::emitMove(const MoveOper
             MOZ_CRASH("Invalid emitMove arguments.");
         }
     } else {
         MOZ_CRASH("Invalid emitMove arguments.");
     }
 }
 
 void
-MoveEmitterMIPS::emitFloat32Move(const MoveOperand& from, const MoveOperand& to)
+MoveEmitterMIPSShared::emitFloat32Move(const MoveOperand& from, const MoveOperand& to)
 {
     // Ensure that we can use ScratchFloat32Reg in memory move.
     MOZ_ASSERT_IF(from.isFloatReg(), from.floatReg() != ScratchFloat32Reg);
     MOZ_ASSERT_IF(to.isFloatReg(), to.floatReg() != ScratchFloat32Reg);
 
     if (from.isFloatReg()) {
         if (to.isFloatReg()) {
             masm.moveFloat32(from.floatReg(), to.floatReg());
@@ -241,59 +123,17 @@ MoveEmitterMIPS::emitFloat32Move(const M
         MOZ_ASSERT(from.isMemory());
         MOZ_ASSERT(to.isMemory());
         masm.loadFloat32(getAdjustedAddress(from), ScratchFloat32Reg);
         masm.storeFloat32(ScratchFloat32Reg, getAdjustedAddress(to));
     }
 }
 
 void
-MoveEmitterMIPS::emitDoubleMove(const MoveOperand& from, const MoveOperand& to)
-{
-    // Ensure that we can use ScratchDoubleReg in memory move.
-    MOZ_ASSERT_IF(from.isFloatReg(), from.floatReg() != ScratchDoubleReg);
-    MOZ_ASSERT_IF(to.isFloatReg(), to.floatReg() != ScratchDoubleReg);
-
-    if (from.isFloatReg()) {
-        if (to.isFloatReg()) {
-            masm.moveDouble(from.floatReg(), to.floatReg());
-        } else if (to.isGeneralRegPair()) {
-            // Used for passing double parameter in a2,a3 register pair.
-            // Two moves are added for one double parameter by
-            // MacroAssembler::passABIArg
-            MOZ_ASSERT(to.evenReg() == a2 && to.oddReg() == a3,
-                       "Invalid emitDoubleMove arguments.");
-            masm.moveFromDoubleLo(from.floatReg(), a2);
-            masm.moveFromDoubleHi(from.floatReg(), a3);
-        } else {
-            MOZ_ASSERT(to.isMemory());
-            masm.storeDouble(from.floatReg(), getAdjustedAddress(to));
-        }
-    } else if (to.isFloatReg()) {
-        MOZ_ASSERT(from.isMemory());
-        masm.loadDouble(getAdjustedAddress(from), to.floatReg());
-    } else if (to.isGeneralRegPair()) {
-        // Used for passing double parameter in a2,a3 register pair.
-        // Two moves are added for one double parameter by
-        // MacroAssembler::passABIArg
-        MOZ_ASSERT(from.isMemory());
-        MOZ_ASSERT(to.evenReg() == a2 && to.oddReg() == a3,
-                   "Invalid emitDoubleMove arguments.");
-        masm.loadPtr(getAdjustedAddress(from), a2);
-        masm.loadPtr(Address(from.base(), getAdjustedOffset(from) + sizeof(uint32_t)), a3);
-    } else {
-        MOZ_ASSERT(from.isMemory());
-        MOZ_ASSERT(to.isMemory());
-        masm.loadDouble(getAdjustedAddress(from), ScratchDoubleReg);
-        masm.storeDouble(ScratchDoubleReg, getAdjustedAddress(to));
-    }
-}
-
-void
-MoveEmitterMIPS::emit(const MoveOp& move)
+MoveEmitterMIPSShared::emit(const MoveOp& move)
 {
     const MoveOperand& from = move.from();
     const MoveOperand& to = move.to();
 
     if (move.isCycleEnd() && move.isCycleBegin()) {
         // A fun consequence of aliased registers is you can have multiple
         // cycles at once, and one can end exactly where another begins.
         breakCycle(from, to, move.endCycleType(), move.cycleBeginSlot());
@@ -327,20 +167,20 @@ MoveEmitterMIPS::emit(const MoveOp& move
         emitMove(from, to);
         break;
       default:
         MOZ_CRASH("Unexpected move type");
     }
 }
 
 void
-MoveEmitterMIPS::assertDone()
+MoveEmitterMIPSShared::assertDone()
 {
     MOZ_ASSERT(inCycle_ == 0);
 }
 
 void
-MoveEmitterMIPS::finish()
+MoveEmitterMIPSShared::finish()
 {
     assertDone();
 
     masm.freeStack(masm.framePushed() - pushedAtStart_);
 }
copy from js/src/jit/mips32/MoveEmitter-mips32.h
copy to js/src/jit/mips-shared/MoveEmitter-mips-shared.h
--- a/js/src/jit/mips32/MoveEmitter-mips32.h
+++ b/js/src/jit/mips-shared/MoveEmitter-mips-shared.h
@@ -1,25 +1,26 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef jit_mips32_MoveEmitter_mips32_h
-#define jit_mips32_MoveEmitter_mips32_h
+#ifndef jit_mips_shared_MoveEmitter_mips_shared_h
+#define jit_mips_shared_MoveEmitter_mips_shared_h
 
 #include "jit/MacroAssembler.h"
 #include "jit/MoveResolver.h"
 
 namespace js {
 namespace jit {
 
-class MoveEmitterMIPS
+class MoveEmitterMIPSShared
 {
+  protected:
     uint32_t inCycle_;
     MacroAssembler& masm;
 
     // Original stack push value.
     uint32_t pushedAtStart_;
 
     // These store stack offsets to spill locations, snapshotting
     // codegen->framePushed_ at the time they were allocated. They are -1 if no
@@ -37,30 +38,38 @@ class MoveEmitterMIPS
     Register tempReg();
     FloatRegister tempFloatReg();
     Address cycleSlot(uint32_t slot, uint32_t subslot) const;
     int32_t getAdjustedOffset(const MoveOperand& operand);
     Address getAdjustedAddress(const MoveOperand& operand);
 
     void emitMove(const MoveOperand& from, const MoveOperand& to);
     void emitFloat32Move(const MoveOperand& from, const MoveOperand& to);
-    void emitDoubleMove(const MoveOperand& from, const MoveOperand& to);
-    void breakCycle(const MoveOperand& from, const MoveOperand& to,
-                    MoveOp::Type type, uint32_t slot);
-    void completeCycle(const MoveOperand& from, const MoveOperand& to,
-                       MoveOp::Type type, uint32_t slot);
+    virtual void emitDoubleMove(const MoveOperand& from, const MoveOperand& to) = 0;
+    virtual void breakCycle(const MoveOperand& from, const MoveOperand& to,
+                    MoveOp::Type type, uint32_t slot) = 0;
+    virtual void completeCycle(const MoveOperand& from, const MoveOperand& to,
+                       MoveOp::Type type, uint32_t slot) = 0;
     void emit(const MoveOp& move);
 
   public:
-    MoveEmitterMIPS(MacroAssembler& masm);
-    ~MoveEmitterMIPS();
+    MoveEmitterMIPSShared(MacroAssembler& masm)
+      : inCycle_(0),
+        masm(masm),
+        pushedAtCycle_(-1),
+        pushedAtSpill_(-1),
+        pushedAtStart_(masm.framePushed()),
+        spilledReg_(InvalidReg),
+        spilledFloatReg_(InvalidFloatReg)
+    { }
+    ~MoveEmitterMIPSShared() {
+        assertDone();
+    }
     void emit(const MoveResolver& moves);
     void finish();
 
     void setScratchRegister(Register reg) {}
 };
 
-typedef MoveEmitterMIPS MoveEmitter;
-
 } // namespace jit
 } // namespace js
 
-#endif /* jit_mips32_MoveEmitter_mips32_h */
+#endif /* jit_mips_shared_MoveEmitter_mips_shared_h */
--- a/js/src/jit/mips32/MoveEmitter-mips32.cpp
+++ b/js/src/jit/mips32/MoveEmitter-mips32.cpp
@@ -6,78 +6,16 @@
 
 #include "jit/mips32/MoveEmitter-mips32.h"
 
 #include "jit/MacroAssembler-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
-MoveEmitterMIPS::MoveEmitterMIPS(MacroAssembler& masm)
-  : inCycle_(0),
-    masm(masm),
-    pushedAtCycle_(-1),
-    pushedAtSpill_(-1),
-    spilledReg_(InvalidReg),
-    spilledFloatReg_(InvalidFloatReg)
-{
-    pushedAtStart_ = masm.framePushed();
-}
-
-void
-MoveEmitterMIPS::emit(const MoveResolver& moves)
-{
-    if (moves.numCycles()) {
-        // Reserve stack for cycle resolution
-        masm.reserveStack(moves.numCycles() * sizeof(double));
-        pushedAtCycle_ = masm.framePushed();
-    }
-
-    for (size_t i = 0; i < moves.numMoves(); i++)
-        emit(moves.getMove(i));
-}
-
-MoveEmitterMIPS::~MoveEmitterMIPS()
-{
-    assertDone();
-}
-
-Address
-MoveEmitterMIPS::cycleSlot(uint32_t slot, uint32_t subslot) const
-{
-    int32_t offset = masm.framePushed() - pushedAtCycle_;
-    MOZ_ASSERT(Imm16::IsInSignedRange(offset));
-    return Address(StackPointer, offset + slot * sizeof(double) + subslot);
-}
-
-int32_t
-MoveEmitterMIPS::getAdjustedOffset(const MoveOperand& operand)
-{
-    MOZ_ASSERT(operand.isMemoryOrEffectiveAddress());
-    if (operand.base() != StackPointer)
-        return operand.disp();
-
-    // Adjust offset if stack pointer has been moved.
-    return operand.disp() + masm.framePushed() - pushedAtStart_;
-}
-
-Address
-MoveEmitterMIPS::getAdjustedAddress(const MoveOperand& operand)
-{
-    return Address(operand.base(), getAdjustedOffset(operand));
-}
-
-
-Register
-MoveEmitterMIPS::tempReg()
-{
-    spilledReg_ = SecondScratchReg;
-    return SecondScratchReg;
-}
-
 void
 MoveEmitterMIPS::breakCycle(const MoveOperand& from, const MoveOperand& to,
                             MoveOp::Type type, uint32_t slotId)
 {
     // There is some pattern:
     //   (A -> B)
     //   (B -> A)
     //
@@ -171,86 +109,16 @@ MoveEmitterMIPS::completeCycle(const Mov
         }
         break;
       default:
         MOZ_CRASH("Unexpected move type");
     }
 }
 
 void
-MoveEmitterMIPS::emitMove(const MoveOperand& from, const MoveOperand& to)
-{
-    if (from.isGeneralReg()) {
-        // Second scratch register should not be moved by MoveEmitter.
-        MOZ_ASSERT(from.reg() != spilledReg_);
-
-        if (to.isGeneralReg())
-            masm.movePtr(from.reg(), to.reg());
-        else if (to.isMemory())
-            masm.storePtr(from.reg(), getAdjustedAddress(to));
-        else
-            MOZ_CRASH("Invalid emitMove arguments.");
-    } else if (from.isMemory()) {
-        if (to.isGeneralReg()) {
-            masm.loadPtr(getAdjustedAddress(from), to.reg());
-        } else if (to.isMemory()) {
-            masm.loadPtr(getAdjustedAddress(from), tempReg());
-            masm.storePtr(tempReg(), getAdjustedAddress(to));
-        } else {
-            MOZ_CRASH("Invalid emitMove arguments.");
-        }
-    } else if (from.isEffectiveAddress()) {
-        if (to.isGeneralReg()) {
-            masm.computeEffectiveAddress(getAdjustedAddress(from), to.reg());
-        } else if (to.isMemory()) {
-            masm.computeEffectiveAddress(getAdjustedAddress(from), tempReg());
-            masm.storePtr(tempReg(), getAdjustedAddress(to));
-        } else {
-            MOZ_CRASH("Invalid emitMove arguments.");
-        }
-    } else {
-        MOZ_CRASH("Invalid emitMove arguments.");
-    }
-}
-
-void
-MoveEmitterMIPS::emitFloat32Move(const MoveOperand& from, const MoveOperand& to)
-{
-    // Ensure that we can use ScratchFloat32Reg in memory move.
-    MOZ_ASSERT_IF(from.isFloatReg(), from.floatReg() != ScratchFloat32Reg);
-    MOZ_ASSERT_IF(to.isFloatReg(), to.floatReg() != ScratchFloat32Reg);
-
-    if (from.isFloatReg()) {
-        if (to.isFloatReg()) {
-            masm.moveFloat32(from.floatReg(), to.floatReg());
-        } else if (to.isGeneralReg()) {
-            // This should only be used when passing float parameter in a1,a2,a3
-            MOZ_ASSERT(to.reg() == a1 || to.reg() == a2 || to.reg() == a3);
-            masm.moveFromFloat32(from.floatReg(), to.reg());
-        } else {
-            MOZ_ASSERT(to.isMemory());
-            masm.storeFloat32(from.floatReg(), getAdjustedAddress(to));
-        }
-    } else if (to.isFloatReg()) {
-        MOZ_ASSERT(from.isMemory());
-        masm.loadFloat32(getAdjustedAddress(from), to.floatReg());
-    } else if (to.isGeneralReg()) {
-        MOZ_ASSERT(from.isMemory());
-        // This should only be used when passing float parameter in a1,a2,a3
-        MOZ_ASSERT(to.reg() == a1 || to.reg() == a2 || to.reg() == a3);
-        masm.loadPtr(getAdjustedAddress(from), to.reg());
-    } else {
-        MOZ_ASSERT(from.isMemory());
-        MOZ_ASSERT(to.isMemory());
-        masm.loadFloat32(getAdjustedAddress(from), ScratchFloat32Reg);
-        masm.storeFloat32(ScratchFloat32Reg, getAdjustedAddress(to));
-    }
-}
-
-void
 MoveEmitterMIPS::emitDoubleMove(const MoveOperand& from, const MoveOperand& to)
 {
     // Ensure that we can use ScratchDoubleReg in memory move.
     MOZ_ASSERT_IF(from.isFloatReg(), from.floatReg() != ScratchDoubleReg);
     MOZ_ASSERT_IF(to.isFloatReg(), to.floatReg() != ScratchDoubleReg);
 
     if (from.isFloatReg()) {
         if (to.isFloatReg()) {
@@ -281,66 +149,8 @@ MoveEmitterMIPS::emitDoubleMove(const Mo
         masm.loadPtr(Address(from.base(), getAdjustedOffset(from) + sizeof(uint32_t)), a3);
     } else {
         MOZ_ASSERT(from.isMemory());
         MOZ_ASSERT(to.isMemory());
         masm.loadDouble(getAdjustedAddress(from), ScratchDoubleReg);
         masm.storeDouble(ScratchDoubleReg, getAdjustedAddress(to));
     }
 }
-
-void
-MoveEmitterMIPS::emit(const MoveOp& move)
-{
-    const MoveOperand& from = move.from();
-    const MoveOperand& to = move.to();
-
-    if (move.isCycleEnd() && move.isCycleBegin()) {
-        // A fun consequence of aliased registers is you can have multiple
-        // cycles at once, and one can end exactly where another begins.
-        breakCycle(from, to, move.endCycleType(), move.cycleBeginSlot());
-        completeCycle(from, to, move.type(), move.cycleEndSlot());
-        return;
-    }
-
-    if (move.isCycleEnd()) {
-        MOZ_ASSERT(inCycle_);
-        completeCycle(from, to, move.type(), move.cycleEndSlot());
-        MOZ_ASSERT(inCycle_ > 0);
-        inCycle_--;
-        return;
-    }
-
-    if (move.isCycleBegin()) {
-        breakCycle(from, to, move.endCycleType(), move.cycleBeginSlot());
-        inCycle_++;
-    }
-
-    switch (move.type()) {
-      case MoveOp::FLOAT32:
-        emitFloat32Move(from, to);
-        break;
-      case MoveOp::DOUBLE:
-        emitDoubleMove(from, to);
-        break;
-      case MoveOp::INT32:
-        MOZ_ASSERT(sizeof(uintptr_t) == sizeof(int32_t));
-      case MoveOp::GENERAL:
-        emitMove(from, to);
-        break;
-      default:
-        MOZ_CRASH("Unexpected move type");
-    }
-}
-
-void
-MoveEmitterMIPS::assertDone()
-{
-    MOZ_ASSERT(inCycle_ == 0);
-}
-
-void
-MoveEmitterMIPS::finish()
-{
-    assertDone();
-
-    masm.freeStack(masm.framePushed() - pushedAtStart_);
-}
--- a/js/src/jit/mips32/MoveEmitter-mips32.h
+++ b/js/src/jit/mips32/MoveEmitter-mips32.h
@@ -2,65 +2,33 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_mips32_MoveEmitter_mips32_h
 #define jit_mips32_MoveEmitter_mips32_h
 
-#include "jit/MacroAssembler.h"
-#include "jit/MoveResolver.h"
+#include "jit/mips-shared/MoveEmitter-mips-shared.h"
 
 namespace js {
 namespace jit {
 
-class MoveEmitterMIPS
+class MoveEmitterMIPS : public MoveEmitterMIPSShared
 {
-    uint32_t inCycle_;
-    MacroAssembler& masm;
-
-    // Original stack push value.
-    uint32_t pushedAtStart_;
-
-    // These store stack offsets to spill locations, snapshotting
-    // codegen->framePushed_ at the time they were allocated. They are -1 if no
-    // stack space has been allocated for that particular spill.
-    int32_t pushedAtCycle_;
-    int32_t pushedAtSpill_;
-
-    // These are registers that are available for temporary use. They may be
-    // assigned InvalidReg. If no corresponding spill space has been assigned,
-    // then these registers do not need to be spilled.
-    Register spilledReg_;
-    FloatRegister spilledFloatReg_;
-
-    void assertDone();
-    Register tempReg();
-    FloatRegister tempFloatReg();
-    Address cycleSlot(uint32_t slot, uint32_t subslot) const;
-    int32_t getAdjustedOffset(const MoveOperand& operand);
-    Address getAdjustedAddress(const MoveOperand& operand);
-
-    void emitMove(const MoveOperand& from, const MoveOperand& to);
-    void emitFloat32Move(const MoveOperand& from, const MoveOperand& to);
     void emitDoubleMove(const MoveOperand& from, const MoveOperand& to);
     void breakCycle(const MoveOperand& from, const MoveOperand& to,
                     MoveOp::Type type, uint32_t slot);
     void completeCycle(const MoveOperand& from, const MoveOperand& to,
                        MoveOp::Type type, uint32_t slot);
-    void emit(const MoveOp& move);
 
   public:
-    MoveEmitterMIPS(MacroAssembler& masm);
-    ~MoveEmitterMIPS();
-    void emit(const MoveResolver& moves);
-    void finish();
-
-    void setScratchRegister(Register reg) {}
+    MoveEmitterMIPS(MacroAssembler& masm)
+      : MoveEmitterMIPSShared(masm)
+    { }
 };
 
 typedef MoveEmitterMIPS MoveEmitter;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_mips32_MoveEmitter_mips32_h */
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -466,16 +466,17 @@ elif CONFIG['JS_CODEGEN_ARM64']:
         ]
 elif CONFIG['JS_CODEGEN_MIPS32']:
     UNIFIED_SOURCES += [
         'jit/mips-shared/Architecture-mips-shared.cpp',
         'jit/mips-shared/Assembler-mips-shared.cpp',
         'jit/mips-shared/Bailouts-mips-shared.cpp',
         'jit/mips-shared/BaselineIC-mips-shared.cpp',
         'jit/mips-shared/Lowering-mips-shared.cpp',
+        'jit/mips-shared/MoveEmitter-mips-shared.cpp',
     ]
     if CONFIG['JS_CODEGEN_MIPS32']:
         UNIFIED_SOURCES += [
             'jit/mips32/Architecture-mips32.cpp',
             'jit/mips32/Assembler-mips32.cpp',
             'jit/mips32/Bailouts-mips32.cpp',
             'jit/mips32/BaselineCompiler-mips32.cpp',
             'jit/mips32/BaselineIC-mips32.cpp',