Bug 1121613 - Move MacroAssemblerSpecific::Push to the generic MacroAssembler. r=jandem
authorNicolas B. Pierron <nicolas.b.pierron@mozilla.com>
Wed, 25 Mar 2015 17:34:19 +0100
changeset 264590 97a152aae8c01d15e8b3523ca6d56ec22c6c91ab
parent 264589 f1e01692260ccc719d32f56fc159c6c5a583a4b7
child 264591 5f4090384ba436121e229951d32ce243cb4b7a01
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1121613
milestone39.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 1121613 - Move MacroAssemblerSpecific::Push to the generic MacroAssembler. r=jandem
js/src/jit/MacroAssembler.cpp
js/src/jit/MacroAssembler.h
js/src/jit/arm/MacroAssembler-arm.cpp
js/src/jit/arm/MacroAssembler-arm.h
js/src/jit/arm/MoveEmitter-arm.cpp
js/src/jit/arm/MoveEmitter-arm.h
js/src/jit/mips/MacroAssembler-mips.cpp
js/src/jit/mips/MacroAssembler-mips.h
js/src/jit/mips/MoveEmitter-mips.cpp
js/src/jit/mips/MoveEmitter-mips.h
js/src/jit/none/Trampoline-none.cpp
js/src/jit/shared/MacroAssembler-x86-shared.cpp
js/src/jit/shared/MacroAssembler-x86-shared.h
js/src/jit/shared/MoveEmitter-x86-shared.cpp
js/src/jit/shared/MoveEmitter-x86-shared.h
js/src/jit/x64/MacroAssembler-x64.cpp
js/src/jit/x64/MacroAssembler-x64.h
js/src/jit/x86/MacroAssembler-x86.cpp
js/src/jit/x86/MacroAssembler-x86.h
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -2029,55 +2029,16 @@ MacroAssembler::convertValueToFloatingPo
         return true;
     }
 
     MOZ_ASSERT(v.isObject());
     jump(fail);
     return true;
 }
 
-void
-MacroAssembler::PushEmptyRooted(VMFunction::RootType rootType)
-{
-    switch (rootType) {
-      case VMFunction::RootNone:
-        MOZ_CRASH("Handle must have root type");
-      case VMFunction::RootObject:
-      case VMFunction::RootString:
-      case VMFunction::RootPropertyName:
-      case VMFunction::RootFunction:
-      case VMFunction::RootCell:
-        Push(ImmPtr(nullptr));
-        break;
-      case VMFunction::RootValue:
-        Push(UndefinedValue());
-        break;
-    }
-}
-
-void
-MacroAssembler::popRooted(VMFunction::RootType rootType, Register cellReg,
-                          const ValueOperand &valueReg)
-{
-    switch (rootType) {
-      case VMFunction::RootNone:
-        MOZ_CRASH("Handle must have root type");
-      case VMFunction::RootObject:
-      case VMFunction::RootString:
-      case VMFunction::RootPropertyName:
-      case VMFunction::RootFunction:
-      case VMFunction::RootCell:
-        Pop(cellReg);
-        break;
-      case VMFunction::RootValue:
-        Pop(valueReg);
-        break;
-    }
-}
-
 bool
 MacroAssembler::convertConstantOrRegisterToFloatingPoint(JSContext *cx, ConstantOrRegister src,
                                                          FloatRegister output, Label *fail,
                                                          MIRType outputType)
 {
     if (src.constant())
         return convertValueToFloatingPoint(cx, src.value(), output, fail, outputType);
 
@@ -2558,8 +2519,176 @@ MacroAssembler::alignJitStackBasedOnNArg
         branchTestPtr(Assembler::NonZero, StackPointer, Imm32(JitStackAlignment - 1), &end);
         subPtr(Imm32(sizeof(Value)), StackPointer);
         bind(&end);
         assertStackAlignment(JitStackAlignment, sizeof(Value));
     } else {
         andPtr(Imm32(~(JitStackAlignment - 1)), StackPointer);
     }
 }
+
+// ===============================================================
+// Stack manipulation functions.
+
+void
+MacroAssembler::PushRegsInMask(RegisterSet set)
+{
+    PushRegsInMask(set, FloatRegisterSet());
+}
+
+void
+MacroAssembler::PushRegsInMask(GeneralRegisterSet set)
+{
+    PushRegsInMask(RegisterSet(set, FloatRegisterSet()));
+}
+
+void
+MacroAssembler::PopRegsInMask(RegisterSet set)
+{
+    PopRegsInMaskIgnore(set, RegisterSet());
+}
+
+void
+MacroAssembler::PopRegsInMask(RegisterSet set, FloatRegisterSet simdSet)
+{
+    PopRegsInMaskIgnore(set, RegisterSet(), simdSet);
+}
+
+void
+MacroAssembler::PopRegsInMask(GeneralRegisterSet set)
+{
+    PopRegsInMask(RegisterSet(set, FloatRegisterSet()));
+}
+
+void
+MacroAssembler::PopRegsInMaskIgnore(RegisterSet set, RegisterSet ignore)
+{
+    PopRegsInMaskIgnore(set, ignore, FloatRegisterSet());
+}
+
+void
+MacroAssembler::Push(jsid id, Register scratchReg)
+{
+    if (JSID_IS_GCTHING(id)) {
+        // If we're pushing a gcthing, then we can't just push the tagged jsid
+        // value since the GC won't have any idea that the push instruction
+        // carries a reference to a gcthing.  Need to unpack the pointer,
+        // push it using ImmGCPtr, and then rematerialize the id at runtime.
+
+        if (JSID_IS_STRING(id)) {
+            JSString *str = JSID_TO_STRING(id);
+            MOZ_ASSERT(((size_t)str & JSID_TYPE_MASK) == 0);
+            MOZ_ASSERT(JSID_TYPE_STRING == 0x0);
+            Push(ImmGCPtr(str));
+        } else {
+            MOZ_ASSERT(JSID_IS_SYMBOL(id));
+            JS::Symbol *sym = JSID_TO_SYMBOL(id);
+            movePtr(ImmGCPtr(sym), scratchReg);
+            orPtr(Imm32(JSID_TYPE_SYMBOL), scratchReg);
+            Push(scratchReg);
+        }
+    } else {
+        Push(ImmWord(JSID_BITS(id)));
+    }
+}
+
+void
+MacroAssembler::Push(TypedOrValueRegister v)
+{
+    if (v.hasValue()) {
+        Push(v.valueReg());
+    } else if (IsFloatingPointType(v.type())) {
+        FloatRegister reg = v.typedReg().fpu();
+        if (v.type() == MIRType_Float32) {
+            convertFloat32ToDouble(reg, ScratchDoubleReg);
+            reg = ScratchDoubleReg;
+        }
+        Push(reg);
+    } else {
+        Push(ValueTypeFromMIRType(v.type()), v.typedReg().gpr());
+    }
+}
+
+void
+MacroAssembler::Push(ConstantOrRegister v)
+{
+    if (v.constant())
+        Push(v.value());
+    else
+        Push(v.reg());
+}
+
+void
+MacroAssembler::Push(const ValueOperand &val)
+{
+    pushValue(val);
+    framePushed_ += sizeof(Value);
+}
+
+void
+MacroAssembler::Push(const Value &val)
+{
+    pushValue(val);
+    framePushed_ += sizeof(Value);
+}
+
+void
+MacroAssembler::Push(JSValueType type, Register reg)
+{
+    pushValue(type, reg);
+    framePushed_ += sizeof(Value);
+}
+
+void
+MacroAssembler::PushValue(const Address &addr)
+{
+    MOZ_ASSERT(addr.base != StackPointer);
+    pushValue(addr);
+    framePushed_ += sizeof(Value);
+}
+
+void
+MacroAssembler::PushEmptyRooted(VMFunction::RootType rootType)
+{
+    switch (rootType) {
+      case VMFunction::RootNone:
+        MOZ_CRASH("Handle must have root type");
+      case VMFunction::RootObject:
+      case VMFunction::RootString:
+      case VMFunction::RootPropertyName:
+      case VMFunction::RootFunction:
+      case VMFunction::RootCell:
+        Push(ImmPtr(nullptr));
+        break;
+      case VMFunction::RootValue:
+        Push(UndefinedValue());
+        break;
+    }
+}
+
+void
+MacroAssembler::popRooted(VMFunction::RootType rootType, Register cellReg,
+                          const ValueOperand &valueReg)
+{
+    switch (rootType) {
+      case VMFunction::RootNone:
+        MOZ_CRASH("Handle must have root type");
+      case VMFunction::RootObject:
+      case VMFunction::RootString:
+      case VMFunction::RootPropertyName:
+      case VMFunction::RootFunction:
+      case VMFunction::RootCell:
+        Pop(cellReg);
+        break;
+      case VMFunction::RootValue:
+        Pop(valueReg);
+        break;
+    }
+}
+
+void
+MacroAssembler::adjustStack(int amount)
+{
+    if (amount > 0)
+        freeStack(amount);
+    else if (amount < 0)
+        reserveStack(-amount);
+}
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -27,16 +27,36 @@
 #include "jit/AtomicOp.h"
 #include "jit/IonInstrumentation.h"
 #include "jit/JitCompartment.h"
 #include "jit/VMFunctions.h"
 #include "vm/ProxyObject.h"
 #include "vm/Shape.h"
 #include "vm/UnboxedObject.h"
 
+// This Macro is only a hint for code readers that the function is defined in a
+// specific macro assembler, and not in the generic macro assembler.  Thus, when
+// looking for the implementation one might find if the function is implemented
+// and where it is supposed to be implemented.
+# define PER_ARCH
+
+#if defined(JS_CODEGEN_X86)
+# define ONLY_X86_X64
+#elif defined(JS_CODEGEN_X64)
+# define ONLY_X86_X64
+#elif defined(JS_CODEGEN_ARM)
+# define ONLY_X86_X64 = delete
+#elif defined(JS_CODEGEN_MIPS)
+# define ONLY_X86_X64 = delete
+#elif defined(JS_CODEGEN_NONE)
+# define ONLY_X86_X64 = delete
+#else
+# error "Unknown architecture!"
+#endif
+
 #ifdef IS_LITTLE_ENDIAN
 #define IMM32_16ADJ(X) X << 16
 #else
 #define IMM32_16ADJ(X) X
 #endif
 
 namespace js {
 namespace jit {
@@ -267,16 +287,57 @@ class MacroAssembler : public MacroAssem
     MoveResolver &moveResolver() {
         return moveResolver_;
     }
 
     size_t instructionsSize() const {
         return size();
     }
 
+  public:
+    // ===============================================================
+    // Stack manipulation functions.
+
+    void PushRegsInMask(RegisterSet set, FloatRegisterSet simdSet) PER_ARCH;
+    void PushRegsInMask(RegisterSet set);
+    void PushRegsInMask(GeneralRegisterSet set);
+
+    void PopRegsInMask(RegisterSet set);
+    void PopRegsInMask(RegisterSet set, FloatRegisterSet simdSet);
+    void PopRegsInMask(GeneralRegisterSet set);
+    void PopRegsInMaskIgnore(RegisterSet set, RegisterSet ignore,
+                             FloatRegisterSet simdSet) PER_ARCH;
+    void PopRegsInMaskIgnore(RegisterSet set, RegisterSet ignore);
+
+    void Push(const Operand op) PER_ARCH ONLY_X86_X64;
+    void Push(Register reg) PER_ARCH;
+    void Push(const Imm32 imm) PER_ARCH;
+    void Push(const ImmWord imm) PER_ARCH;
+    void Push(const ImmPtr imm) PER_ARCH;
+    void Push(const ImmGCPtr ptr) PER_ARCH;
+    void Push(FloatRegister reg) PER_ARCH;
+    void Push(jsid id, Register scratchReg);
+    void Push(TypedOrValueRegister v);
+    void Push(ConstantOrRegister v);
+    void Push(const ValueOperand &val);
+    void Push(const Value &val);
+    void Push(JSValueType type, Register reg);
+    void PushValue(const Address &addr);
+    void PushEmptyRooted(VMFunction::RootType rootType);
+
+    void Pop(const Operand op) PER_ARCH ONLY_X86_X64;
+    void Pop(Register reg) PER_ARCH;
+    void Pop(FloatRegister t) PER_ARCH ONLY_X86_X64;
+    void Pop(const ValueOperand &val) PER_ARCH;
+    void popRooted(VMFunction::RootType rootType, Register cellReg, const ValueOperand &valueReg);
+
+    void adjustStack(int amount);
+
+  public:
+
     // Emits a test of a value against all types in a TypeSet. A scratch
     // register is required.
     template <typename Source, typename TypeSet>
     void guardTypeSet(const Source &address, const TypeSet *types, BarrierKind kind, Register scratch, Label *miss);
     template <typename TypeSet>
     void guardObjectType(Register obj, const TypeSet *types, Register scratch, Label *miss);
     template <typename Source>
     void guardType(const Source &address, TypeSet::Type type, Register scratch, Label *miss);
@@ -510,37 +571,16 @@ class MacroAssembler : public MacroAssem
             storeCallResultValue(dest.typedReg());
     }
 
     template <typename T>
     Register extractString(const T &source, Register scratch) {
         return extractObject(source, scratch);
     }
 
-    void PushRegsInMask(RegisterSet set, FloatRegisterSet simdSet);
-    void PushRegsInMask(RegisterSet set) {
-        PushRegsInMask(set, FloatRegisterSet());
-    }
-    void PushRegsInMask(GeneralRegisterSet set) {
-        PushRegsInMask(RegisterSet(set, FloatRegisterSet()));
-    }
-    void PopRegsInMask(RegisterSet set) {
-        PopRegsInMaskIgnore(set, RegisterSet());
-    }
-    void PopRegsInMask(RegisterSet set, FloatRegisterSet simdSet) {
-        PopRegsInMaskIgnore(set, RegisterSet(), simdSet);
-    }
-    void PopRegsInMask(GeneralRegisterSet set) {
-        PopRegsInMask(RegisterSet(set, FloatRegisterSet()));
-    }
-    void PopRegsInMaskIgnore(RegisterSet set, RegisterSet ignore, FloatRegisterSet simdSet);
-    void PopRegsInMaskIgnore(RegisterSet set, RegisterSet ignore) {
-        PopRegsInMaskIgnore(set, ignore, FloatRegisterSet());
-    }
-
     void branchIfFunctionHasNoScript(Register fun, Label *label) {
         // 16-bit loads are slow and unaligned 32-bit loads may be too so
         // perform an aligned 32-bit load and adjust the bitmask accordingly.
         MOZ_ASSERT(JSFunction::offsetOfNargs() % sizeof(uint32_t) == 0);
         MOZ_ASSERT(JSFunction::offsetOfFlags() == JSFunction::offsetOfNargs() + 2);
         Address address(fun, JSFunction::offsetOfNargs());
         int32_t bit = IMM32_16ADJ(JSFunction::INTERPRETED);
         branchTest32(Assembler::Zero, address, Imm32(bit), label);
@@ -552,96 +592,16 @@ class MacroAssembler : public MacroAssem
         MOZ_ASSERT(JSFunction::offsetOfFlags() == JSFunction::offsetOfNargs() + 2);
         Address address(fun, JSFunction::offsetOfNargs());
         int32_t bit = IMM32_16ADJ(JSFunction::INTERPRETED);
         branchTest32(Assembler::NonZero, address, Imm32(bit), label);
     }
 
     void branchIfNotInterpretedConstructor(Register fun, Register scratch, Label *label);
 
-    using MacroAssemblerSpecific::Push;
-    using MacroAssemblerSpecific::Pop;
-
-    void Push(jsid id, Register scratchReg) {
-        if (JSID_IS_GCTHING(id)) {
-            // If we're pushing a gcthing, then we can't just push the tagged jsid
-            // value since the GC won't have any idea that the push instruction
-            // carries a reference to a gcthing.  Need to unpack the pointer,
-            // push it using ImmGCPtr, and then rematerialize the id at runtime.
-
-            if (JSID_IS_STRING(id)) {
-                JSString *str = JSID_TO_STRING(id);
-                MOZ_ASSERT(((size_t)str & JSID_TYPE_MASK) == 0);
-                MOZ_ASSERT(JSID_TYPE_STRING == 0x0);
-                Push(ImmGCPtr(str));
-            } else {
-                MOZ_ASSERT(JSID_IS_SYMBOL(id));
-                JS::Symbol *sym = JSID_TO_SYMBOL(id);
-                movePtr(ImmGCPtr(sym), scratchReg);
-                orPtr(Imm32(JSID_TYPE_SYMBOL), scratchReg);
-                Push(scratchReg);
-            }
-        } else {
-            Push(ImmWord(JSID_BITS(id)));
-        }
-    }
-
-    void Push(TypedOrValueRegister v) {
-        if (v.hasValue()) {
-            Push(v.valueReg());
-        } else if (IsFloatingPointType(v.type())) {
-            FloatRegister reg = v.typedReg().fpu();
-            if (v.type() == MIRType_Float32) {
-                convertFloat32ToDouble(reg, ScratchDoubleReg);
-                reg = ScratchDoubleReg;
-            }
-            Push(reg);
-        } else {
-            Push(ValueTypeFromMIRType(v.type()), v.typedReg().gpr());
-        }
-    }
-
-    void Push(ConstantOrRegister v) {
-        if (v.constant())
-            Push(v.value());
-        else
-            Push(v.reg());
-    }
-
-    void Push(const ValueOperand &val) {
-        pushValue(val);
-        framePushed_ += sizeof(Value);
-    }
-
-    void Push(const Value &val) {
-        pushValue(val);
-        framePushed_ += sizeof(Value);
-    }
-
-    void Push(JSValueType type, Register reg) {
-        pushValue(type, reg);
-        framePushed_ += sizeof(Value);
-    }
-
-    void PushValue(const Address &addr) {
-        MOZ_ASSERT(addr.base != StackPointer);
-        pushValue(addr);
-        framePushed_ += sizeof(Value);
-    }
-
-    void PushEmptyRooted(VMFunction::RootType rootType);
-    void popRooted(VMFunction::RootType rootType, Register cellReg, const ValueOperand &valueReg);
-
-    void adjustStack(int amount) {
-        if (amount > 0)
-            freeStack(amount);
-        else if (amount < 0)
-            reserveStack(-amount);
-    }
-
     void bumpKey(Int32Key *key, int diff) {
         if (key->isRegister())
             add32(Imm32(diff), key->reg());
         else
             key->bumpConstant(diff);
     }
 
     void storeKey(const Int32Key &key, const Address &dest) {
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/Casting.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MathAlgorithms.h"
 
 #include "jit/arm/Simulator-arm.h"
 #include "jit/Bailouts.h"
 #include "jit/BaselineFrame.h"
 #include "jit/JitFrames.h"
+#include "jit/MacroAssembler.h"
 #include "jit/MoveEmitter.h"
 
 using namespace js;
 using namespace jit;
 
 using mozilla::Abs;
 using mozilla::BitwiseCast;
 
@@ -1787,21 +1788,21 @@ MacroAssemblerARM::ma_vstr(VFPRegister s
 }
 
 void
 MacroAssemblerARMCompat::buildFakeExitFrame(Register scratch, uint32_t *offset)
 {
     DebugOnly<uint32_t> initialDepth = framePushed();
     uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
 
-    Push(Imm32(descriptor)); // descriptor_
+    asMasm().Push(Imm32(descriptor)); // descriptor_
 
     enterNoPool(2);
     DebugOnly<uint32_t> offsetBeforePush = currentOffset();
-    Push(pc); // actually pushes $pc + 8.
+    asMasm().Push(pc); // actually pushes $pc + 8.
 
     // Consume an additional 4 bytes. The start of the next instruction will
     // then be 8 bytes after the instruction for Push(pc); this offset can
     // therefore be fed to the safepoint.
     ma_nop();
     uint32_t pseudoReturnOffset = currentOffset();
     leaveNoPool();
 
@@ -1812,36 +1813,36 @@ MacroAssemblerARMCompat::buildFakeExitFr
 }
 
 bool
 MacroAssemblerARMCompat::buildOOLFakeExitFrame(void *fakeReturnAddr)
 {
     DebugOnly<uint32_t> initialDepth = framePushed();
     uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
 
-    Push(Imm32(descriptor)); // descriptor_
-    Push(ImmPtr(fakeReturnAddr));
+    asMasm().Push(Imm32(descriptor)); // descriptor_
+    asMasm().Push(ImmPtr(fakeReturnAddr));
 
     return true;
 }
 
 void
 MacroAssemblerARMCompat::callWithExitFrame(Label *target)
 {
     uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
-    Push(Imm32(descriptor)); // descriptor
+    asMasm().Push(Imm32(descriptor)); // descriptor
 
     ma_callJitHalfPush(target);
 }
 
 void
 MacroAssemblerARMCompat::callWithExitFrame(JitCode *target)
 {
     uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
-    Push(Imm32(descriptor)); // descriptor
+    asMasm().Push(Imm32(descriptor)); // descriptor
 
     addPendingJump(m_buffer.nextOffset(), ImmPtr(target->raw()), Relocation::JITCODE);
     RelocStyle rs;
     if (HasMOVWT())
         rs = L_MOVWT;
     else
         rs = L_LDR;
 
@@ -1849,17 +1850,17 @@ MacroAssemblerARMCompat::callWithExitFra
     ma_callJitHalfPush(ScratchRegister);
 }
 
 void
 MacroAssemblerARMCompat::callWithExitFrame(JitCode *target, Register dynStack)
 {
     ma_add(Imm32(framePushed()), dynStack);
     makeFrameDescriptor(dynStack, JitFrame_IonJS);
-    Push(dynStack); // descriptor
+    asMasm().Push(dynStack); // descriptor
 
     addPendingJump(m_buffer.nextOffset(), ImmPtr(target->raw()), Relocation::JITCODE);
     RelocStyle rs;
     if (HasMOVWT())
         rs = L_MOVWT;
     else
         rs = L_LDR;
 
@@ -1908,90 +1909,16 @@ MacroAssemblerARMCompat::freeStack(uint3
 }
 void
 MacroAssemblerARMCompat::freeStack(Register amount)
 {
     ma_add(amount, sp);
 }
 
 void
-MacroAssembler::PushRegsInMask(RegisterSet set, FloatRegisterSet simdSet)
-{
-    MOZ_ASSERT(!SupportsSimd() && simdSet.size() == 0);
-    int32_t diffF = set.fpus().getPushSizeInBytes();
-    int32_t diffG = set.gprs().size() * sizeof(intptr_t);
-
-    if (set.gprs().size() > 1) {
-        adjustFrame(diffG);
-        startDataTransferM(IsStore, StackPointer, DB, WriteBack);
-        for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) {
-            diffG -= sizeof(intptr_t);
-            transferReg(*iter);
-        }
-        finishDataTransfer();
-    } else {
-        reserveStack(diffG);
-        for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) {
-            diffG -= sizeof(intptr_t);
-            storePtr(*iter, Address(StackPointer, diffG));
-        }
-    }
-    MOZ_ASSERT(diffG == 0);
-
-    adjustFrame(diffF);
-    diffF += transferMultipleByRuns(set.fpus(), IsStore, StackPointer, DB);
-    MOZ_ASSERT(diffF == 0);
-}
-
-void
-MacroAssembler::PopRegsInMaskIgnore(RegisterSet set, RegisterSet ignore, FloatRegisterSet simdSet)
-{
-    MOZ_ASSERT(!SupportsSimd() && simdSet.size() == 0);
-    int32_t diffG = set.gprs().size() * sizeof(intptr_t);
-    int32_t diffF = set.fpus().getPushSizeInBytes();
-    const int32_t reservedG = diffG;
-    const int32_t reservedF = diffF;
-
-    // ARM can load multiple registers at once, but only if we want back all
-    // the registers we previously saved to the stack.
-    if (ignore.empty(true)) {
-        diffF -= transferMultipleByRuns(set.fpus(), IsLoad, StackPointer, IA);
-        adjustFrame(-reservedF);
-    } else {
-        TypedRegisterSet<VFPRegister> fpset = set.fpus().reduceSetForPush();
-        TypedRegisterSet<VFPRegister> fpignore = ignore.fpus().reduceSetForPush();
-        for (FloatRegisterBackwardIterator iter(fpset); iter.more(); iter++) {
-            diffF -= (*iter).size();
-            if (!fpignore.has(*iter))
-                loadDouble(Address(StackPointer, diffF), *iter);
-        }
-        freeStack(reservedF);
-    }
-    MOZ_ASSERT(diffF == 0);
-
-    if (set.gprs().size() > 1 && ignore.empty(false)) {
-        startDataTransferM(IsLoad, StackPointer, IA, WriteBack);
-        for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) {
-            diffG -= sizeof(intptr_t);
-            transferReg(*iter);
-        }
-        finishDataTransfer();
-        adjustFrame(-reservedG);
-    } else {
-        for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) {
-            diffG -= sizeof(intptr_t);
-            if (!ignore.has(*iter))
-                loadPtr(Address(StackPointer, diffG), *iter);
-        }
-        freeStack(reservedG);
-    }
-    MOZ_ASSERT(diffG == 0);
-}
-
-void
 MacroAssemblerARMCompat::add32(Register src, Register dest)
 {
     ma_add(src, dest, SetCond);
 }
 
 void
 MacroAssemblerARMCompat::add32(Imm32 imm, Register dest)
 {
@@ -4072,17 +3999,17 @@ MacroAssemblerARMCompat::callWithABIPre(
     reserveStack(*stackAdjust);
 
     // Position all arguments.
     {
         enoughMemory_ = enoughMemory_ && moveResolver_.resolve();
         if (!enoughMemory_)
             return;
 
-        MoveEmitter emitter(*this);
+        MoveEmitter emitter(asMasm());
         emitter.emit(moveResolver_);
         emitter.finish();
     }
     for (int i = 0; i < 4; i++) {
         if (floatArgsInGPRValid[i]) {
             MoveOperand from = floatArgsInGPR[i];
             Register to0 = Register::FromCode(i);
             Register to1;
@@ -5183,8 +5110,153 @@ MacroAssemblerARMCompat::profilerEnterFr
     storePtr(ImmPtr(nullptr), Address(scratch, JitActivation::offsetOfLastProfilingCallSite()));
 }
 
 void
 MacroAssemblerARMCompat::profilerExitFrame()
 {
     branch(GetJitContext()->runtime->jitRuntime()->getProfilerExitFrameTail());
 }
+
+MacroAssembler &
+MacroAssemblerARMCompat::asMasm()
+{
+    return *static_cast<MacroAssembler *>(this);
+}
+
+const MacroAssembler &
+MacroAssemblerARMCompat::asMasm() const
+{
+    return *static_cast<const MacroAssembler *>(this);
+}
+
+// ===============================================================
+// Stack manipulation functions.
+
+void
+MacroAssembler::PushRegsInMask(RegisterSet set, FloatRegisterSet simdSet)
+{
+    MOZ_ASSERT(!SupportsSimd() && simdSet.size() == 0);
+    int32_t diffF = set.fpus().getPushSizeInBytes();
+    int32_t diffG = set.gprs().size() * sizeof(intptr_t);
+
+    if (set.gprs().size() > 1) {
+        adjustFrame(diffG);
+        startDataTransferM(IsStore, StackPointer, DB, WriteBack);
+        for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) {
+            diffG -= sizeof(intptr_t);
+            transferReg(*iter);
+        }
+        finishDataTransfer();
+    } else {
+        reserveStack(diffG);
+        for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) {
+            diffG -= sizeof(intptr_t);
+            storePtr(*iter, Address(StackPointer, diffG));
+        }
+    }
+    MOZ_ASSERT(diffG == 0);
+
+    adjustFrame(diffF);
+    diffF += transferMultipleByRuns(set.fpus(), IsStore, StackPointer, DB);
+    MOZ_ASSERT(diffF == 0);
+}
+
+void
+MacroAssembler::PopRegsInMaskIgnore(RegisterSet set, RegisterSet ignore, FloatRegisterSet simdSet)
+{
+    MOZ_ASSERT(!SupportsSimd() && simdSet.size() == 0);
+    int32_t diffG = set.gprs().size() * sizeof(intptr_t);
+    int32_t diffF = set.fpus().getPushSizeInBytes();
+    const int32_t reservedG = diffG;
+    const int32_t reservedF = diffF;
+
+    // ARM can load multiple registers at once, but only if we want back all
+    // the registers we previously saved to the stack.
+    if (ignore.empty(true)) {
+        diffF -= transferMultipleByRuns(set.fpus(), IsLoad, StackPointer, IA);
+        adjustFrame(-reservedF);
+    } else {
+        TypedRegisterSet<VFPRegister> fpset = set.fpus().reduceSetForPush();
+        TypedRegisterSet<VFPRegister> fpignore = ignore.fpus().reduceSetForPush();
+        for (FloatRegisterBackwardIterator iter(fpset); iter.more(); iter++) {
+            diffF -= (*iter).size();
+            if (!fpignore.has(*iter))
+                loadDouble(Address(StackPointer, diffF), *iter);
+        }
+        freeStack(reservedF);
+    }
+    MOZ_ASSERT(diffF == 0);
+
+    if (set.gprs().size() > 1 && ignore.empty(false)) {
+        startDataTransferM(IsLoad, StackPointer, IA, WriteBack);
+        for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) {
+            diffG -= sizeof(intptr_t);
+            transferReg(*iter);
+        }
+        finishDataTransfer();
+        adjustFrame(-reservedG);
+    } else {
+        for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) {
+            diffG -= sizeof(intptr_t);
+            if (!ignore.has(*iter))
+                loadPtr(Address(StackPointer, diffG), *iter);
+        }
+        freeStack(reservedG);
+    }
+    MOZ_ASSERT(diffG == 0);
+}
+
+void
+MacroAssembler::Push(Register reg)
+{
+    ma_push(reg);
+    adjustFrame(sizeof(intptr_t));
+}
+
+void
+MacroAssembler::Push(const Imm32 imm)
+{
+    push(imm);
+    adjustFrame(sizeof(intptr_t));
+}
+
+void
+MacroAssembler::Push(const ImmWord imm)
+{
+    push(imm);
+    adjustFrame(sizeof(intptr_t));
+}
+
+void
+MacroAssembler::Push(const ImmPtr imm)
+{
+    Push(ImmWord(uintptr_t(imm.value)));
+}
+
+void
+MacroAssembler::Push(const ImmGCPtr ptr)
+{
+    push(ptr);
+    adjustFrame(sizeof(intptr_t));
+}
+
+void
+MacroAssembler::Push(FloatRegister reg)
+{
+    VFPRegister r = VFPRegister(reg);
+    ma_vpush(VFPRegister(reg));
+    adjustFrame(r.size());
+}
+
+void
+MacroAssembler::Pop(Register reg)
+{
+    ma_pop(reg);
+    adjustFrame(-sizeof(intptr_t));
+}
+
+void
+MacroAssembler::Pop(const ValueOperand &val)
+{
+    popValue(val);
+    framePushed_ -= sizeof(Value);
+}
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -470,18 +470,26 @@ private:
                 transferFloatReg(*iter);
             } while ((++iter).more() && (*iter).code() == (reg += sign));
             finishFloatTransfer();
         }
         return offset;
     }
 };
 
+class MacroAssembler;
+
 class MacroAssemblerARMCompat : public MacroAssemblerARM
 {
+  private:
+    // Perform a downcast. Should be removed by Bug 996602.
+    MacroAssembler &asMasm();
+    const MacroAssembler &asMasm() const;
+
+  private:
     bool inCall_;
     // Number of bytes the stack is adjusted inside a call to C. Calls to C may
     // not be nested.
     uint32_t args_;
     // The actual number of arguments that were passed, used to assert that the
     // initial number of arguments declared was correct.
     uint32_t passedArgs_;
     uint32_t passedArgTypes_;
@@ -1223,24 +1231,17 @@ class MacroAssemblerARMCompat : public M
         else
             push(Imm32(jv.s.payload.i32));
     }
     void pushValue(JSValueType type, Register reg) {
         push(ImmTag(JSVAL_TYPE_TO_TAG(type)));
         ma_push(reg);
     }
     void pushValue(const Address &addr);
-    void Push(const ValueOperand &val) {
-        pushValue(val);
-        framePushed_ += sizeof(Value);
-    }
-    void Pop(const ValueOperand &val) {
-        popValue(val);
-        framePushed_ -= sizeof(Value);
-    }
+
     void storePayload(const Value &val, Operand dest);
     void storePayload(Register src, Operand dest);
     void storePayload(const Value &val, const BaseIndex &dest);
     void storePayload(Register src, const BaseIndex &dest);
     void storeTypeTag(ImmTag tag, Operand dest);
     void storeTypeTag(ImmTag tag, const BaseIndex &dest);
 
     void makeFrameDescriptor(Register frameSizeReg, FrameType type) {
@@ -1250,40 +1251,16 @@ class MacroAssemblerARMCompat : public M
 
     void handleFailureWithHandlerTail(void *handler);
 
     /////////////////////////////////////////////////////////////////
     // Common interface.
     /////////////////////////////////////////////////////////////////
   public:
     // The following functions are exposed for use in platform-shared code.
-    void Push(Register reg) {
-        ma_push(reg);
-        adjustFrame(sizeof(intptr_t));
-    }
-    void Push(const Imm32 imm) {
-        push(imm);
-        adjustFrame(sizeof(intptr_t));
-    }
-    void Push(const ImmWord imm) {
-        push(imm);
-        adjustFrame(sizeof(intptr_t));
-    }
-    void Push(const ImmPtr imm) {
-        Push(ImmWord(uintptr_t(imm.value)));
-    }
-    void Push(const ImmGCPtr ptr) {
-        push(ptr);
-        adjustFrame(sizeof(intptr_t));
-    }
-    void Push(FloatRegister t) {
-        VFPRegister r = VFPRegister(t);
-        ma_vpush(VFPRegister(t));
-        adjustFrame(r.size());
-    }
 
     CodeOffsetLabel PushWithPatch(ImmWord word) {
         framePushed_ += sizeof(word.value);
         return pushWithPatch(word);
     }
     CodeOffsetLabel PushWithPatch(ImmPtr imm) {
         return PushWithPatch(ImmWord(uintptr_t(imm.value)));
     }
@@ -1292,20 +1269,16 @@ class MacroAssemblerARMCompat : public M
         pushWithPadding(reg, extraSpace);
         adjustFrame(sizeof(intptr_t) + extraSpace.value);
     }
     void PushWithPadding(const Imm32 imm, const Imm32 extraSpace) {
         pushWithPadding(imm, extraSpace);
         adjustFrame(sizeof(intptr_t) + extraSpace.value);
     }
 
-    void Pop(Register reg) {
-        ma_pop(reg);
-        adjustFrame(-sizeof(intptr_t));
-    }
     void implicitPop(uint32_t args) {
         MOZ_ASSERT(args % sizeof(intptr_t) == 0);
         adjustFrame(-args);
     }
     uint32_t framePushed() const {
         return framePushed_;
     }
     void setFramePushed(uint32_t framePushed) {
--- a/js/src/jit/arm/MoveEmitter-arm.cpp
+++ b/js/src/jit/arm/MoveEmitter-arm.cpp
@@ -4,17 +4,17 @@
  * 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/arm/MoveEmitter-arm.h"
 
 using namespace js;
 using namespace js::jit;
 
-MoveEmitterARM::MoveEmitterARM(MacroAssemblerARMCompat &masm)
+MoveEmitterARM::MoveEmitterARM(MacroAssembler &masm)
   : inCycle_(0),
     masm(masm),
     pushedAtCycle_(-1),
     pushedAtSpill_(-1),
     spilledReg_(InvalidReg),
     spilledFloatReg_(InvalidFloatReg)
 {
     pushedAtStart_ = masm.framePushed();
--- a/js/src/jit/arm/MoveEmitter-arm.h
+++ b/js/src/jit/arm/MoveEmitter-arm.h
@@ -13,17 +13,17 @@
 namespace js {
 namespace jit {
 
 class CodeGenerator;
 
 class MoveEmitterARM
 {
     uint32_t inCycle_;
-    MacroAssemblerARMCompat &masm;
+    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_;
@@ -47,17 +47,17 @@ class MoveEmitterARM
     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:
-    MoveEmitterARM(MacroAssemblerARMCompat &masm);
+    MoveEmitterARM(MacroAssembler &masm);
     ~MoveEmitterARM();
     void emit(const MoveResolver &moves);
     void finish();
 
     void setScratchRegister(Register reg) {}
 };
 
 typedef MoveEmitterARM MoveEmitter;
--- a/js/src/jit/mips/MacroAssembler-mips.cpp
+++ b/js/src/jit/mips/MacroAssembler-mips.cpp
@@ -8,16 +8,17 @@
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MathAlgorithms.h"
 
 #include "jit/Bailouts.h"
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineRegisters.h"
 #include "jit/JitFrames.h"
+#include "jit/MacroAssembler.h"
 #include "jit/mips/Simulator-mips.h"
 #include "jit/MoveEmitter.h"
 
 using namespace js;
 using namespace jit;
 
 using mozilla::Abs;
 
@@ -1492,63 +1493,63 @@ void
 MacroAssemblerMIPSCompat::buildFakeExitFrame(Register scratch, uint32_t *offset)
 {
     mozilla::DebugOnly<uint32_t> initialDepth = framePushed();
 
     CodeLabel cl;
     ma_li(scratch, cl.dest());
 
     uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
-    Push(Imm32(descriptor));
-    Push(scratch);
+    asMasm().Push(Imm32(descriptor));
+    asMasm().Push(scratch);
 
     bind(cl.src());
     *offset = currentOffset();
 
     MOZ_ASSERT(framePushed() == initialDepth + ExitFrameLayout::Size());
     addCodeLabel(cl);
 }
 
 bool
 MacroAssemblerMIPSCompat::buildOOLFakeExitFrame(void *fakeReturnAddr)
 {
     uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
 
-    Push(Imm32(descriptor)); // descriptor_
-    Push(ImmPtr(fakeReturnAddr));
+    asMasm().Push(Imm32(descriptor)); // descriptor_
+    asMasm().Push(ImmPtr(fakeReturnAddr));
 
     return true;
 }
 
 void
 MacroAssemblerMIPSCompat::callWithExitFrame(Label *target)
 {
     uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
-    Push(Imm32(descriptor)); // descriptor
+    asMasm().Push(Imm32(descriptor)); // descriptor
 
     ma_callJitHalfPush(target);
 }
 
 void
 MacroAssemblerMIPSCompat::callWithExitFrame(JitCode *target)
 {
     uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
-    Push(Imm32(descriptor)); // descriptor
+    asMasm().Push(Imm32(descriptor)); // descriptor
 
     addPendingJump(m_buffer.nextOffset(), ImmPtr(target->raw()), Relocation::JITCODE);
     ma_liPatchable(ScratchRegister, ImmPtr(target->raw()));
     ma_callJitHalfPush(ScratchRegister);
 }
 
 void
 MacroAssemblerMIPSCompat::callWithExitFrame(JitCode *target, Register dynStack)
 {
     ma_addu(dynStack, dynStack, Imm32(framePushed()));
     makeFrameDescriptor(dynStack, JitFrame_IonJS);
-    Push(dynStack); // descriptor
+    asMasm().Push(dynStack); // descriptor
 
     addPendingJump(m_buffer.nextOffset(), ImmPtr(target->raw()), Relocation::JITCODE);
     ma_liPatchable(ScratchRegister, ImmPtr(target->raw()));
     ma_callJitHalfPush(ScratchRegister);
 }
 
 void
 MacroAssemblerMIPSCompat::callJit(Register callee)
@@ -1581,75 +1582,16 @@ MacroAssemblerMIPSCompat::freeStack(uint
 
 void
 MacroAssemblerMIPSCompat::freeStack(Register amount)
 {
     as_addu(StackPointer, StackPointer, amount);
 }
 
 void
-MacroAssembler::PushRegsInMask(RegisterSet set, FloatRegisterSet simdSet)
-{
-    MOZ_ASSERT(!SupportsSimd() && simdSet.size() == 0);
-    int32_t diffF = set.fpus().getPushSizeInBytes();
-    int32_t diffG = set.gprs().size() * sizeof(intptr_t);
-
-    reserveStack(diffG);
-    for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) {
-        diffG -= sizeof(intptr_t);
-        storePtr(*iter, Address(StackPointer, diffG));
-    }
-    MOZ_ASSERT(diffG == 0);
-
-    // Double values have to be aligned. We reserve extra space so that we can
-    // start writing from the first aligned location.
-    // We reserve a whole extra double so that the buffer has even size.
-    ma_and(SecondScratchReg, sp, Imm32(~(ABIStackAlignment - 1)));
-    reserveStack(diffF + sizeof(double));
-
-    for (FloatRegisterForwardIterator iter(set.fpus().reduceSetForPush()); iter.more(); iter++) {
-        if ((*iter).code() % 2 == 0)
-            as_sd(*iter, SecondScratchReg, -diffF);
-        diffF -= sizeof(double);
-    }
-    MOZ_ASSERT(diffF == 0);
-}
-
-void
-MacroAssembler::PopRegsInMaskIgnore(RegisterSet set, RegisterSet ignore, FloatRegisterSet simdSet)
-{
-    MOZ_ASSERT(!SupportsSimd() && simdSet.size() == 0);
-    int32_t diffG = set.gprs().size() * sizeof(intptr_t);
-    int32_t diffF = set.fpus().getPushSizeInBytes();
-    const int32_t reservedG = diffG;
-    const int32_t reservedF = diffF;
-
-    // Read the buffer form the first aligned location.
-    ma_addu(SecondScratchReg, sp, Imm32(reservedF + sizeof(double)));
-    ma_and(SecondScratchReg, SecondScratchReg, Imm32(~(ABIStackAlignment - 1)));
-
-    for (FloatRegisterForwardIterator iter(set.fpus().reduceSetForPush()); iter.more(); iter++) {
-        if (!ignore.has(*iter) && ((*iter).code() % 2 == 0))
-            // Use assembly l.d because we have alligned the stack.
-            as_ld(*iter, SecondScratchReg, -diffF);
-        diffF -= sizeof(double);
-    }
-    freeStack(reservedF + sizeof(double));
-    MOZ_ASSERT(diffF == 0);
-
-    for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) {
-        diffG -= sizeof(intptr_t);
-        if (!ignore.has(*iter))
-            loadPtr(Address(StackPointer, diffG), *iter);
-    }
-    freeStack(reservedG);
-    MOZ_ASSERT(diffG == 0);
-}
-
-void
 MacroAssemblerMIPSCompat::add32(Register src, Register dest)
 {
     as_addu(dest, dest, src);
 }
 
 void
 MacroAssemblerMIPSCompat::add32(Imm32 imm, Register dest)
 {
@@ -3422,17 +3364,17 @@ MacroAssemblerMIPSCompat::callWithABIPre
     ma_sw(ra, Address(StackPointer, *stackAdjust - sizeof(intptr_t)));
 
     // Position all arguments.
     {
         enoughMemory_ = enoughMemory_ && moveResolver_.resolve();
         if (!enoughMemory_)
             return;
 
-        MoveEmitter emitter(*this);
+        MoveEmitter emitter(asMasm());
         emitter.emit(moveResolver_);
         emitter.finish();
     }
 
     checkStackAlignment();
 }
 
 void
@@ -3704,8 +3646,140 @@ MacroAssemblerMIPSCompat::profilerEnterF
     storePtr(ImmPtr(nullptr), Address(scratch, JitActivation::offsetOfLastProfilingCallSite()));
 }
 
 void
 MacroAssemblerMIPSCompat::profilerExitFrame()
 {
     branch(GetJitContext()->runtime->jitRuntime()->getProfilerExitFrameTail());
 }
+
+MacroAssembler &
+MacroAssemblerMIPSCompat::asMasm()
+{
+    return *static_cast<MacroAssembler *>(this);
+}
+
+const MacroAssembler &
+MacroAssemblerMIPSCompat::asMasm() const
+{
+    return *static_cast<const MacroAssembler *>(this);
+}
+
+// ===============================================================
+// Stack manipulation functions.
+
+void
+MacroAssembler::PushRegsInMask(RegisterSet set, FloatRegisterSet simdSet)
+{
+    MOZ_ASSERT(!SupportsSimd() && simdSet.size() == 0);
+    int32_t diffF = set.fpus().getPushSizeInBytes();
+    int32_t diffG = set.gprs().size() * sizeof(intptr_t);
+
+    reserveStack(diffG);
+    for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) {
+        diffG -= sizeof(intptr_t);
+        storePtr(*iter, Address(StackPointer, diffG));
+    }
+    MOZ_ASSERT(diffG == 0);
+
+    // Double values have to be aligned. We reserve extra space so that we can
+    // start writing from the first aligned location.
+    // We reserve a whole extra double so that the buffer has even size.
+    ma_and(SecondScratchReg, sp, Imm32(~(ABIStackAlignment - 1)));
+    reserveStack(diffF + sizeof(double));
+
+    for (FloatRegisterForwardIterator iter(set.fpus().reduceSetForPush()); iter.more(); iter++) {
+        if ((*iter).code() % 2 == 0)
+            as_sd(*iter, SecondScratchReg, -diffF);
+        diffF -= sizeof(double);
+    }
+    MOZ_ASSERT(diffF == 0);
+}
+
+void
+MacroAssembler::PopRegsInMaskIgnore(RegisterSet set, RegisterSet ignore, FloatRegisterSet simdSet)
+{
+    MOZ_ASSERT(!SupportsSimd() && simdSet.size() == 0);
+    int32_t diffG = set.gprs().size() * sizeof(intptr_t);
+    int32_t diffF = set.fpus().getPushSizeInBytes();
+    const int32_t reservedG = diffG;
+    const int32_t reservedF = diffF;
+
+    // Read the buffer form the first aligned location.
+    ma_addu(SecondScratchReg, sp, Imm32(reservedF + sizeof(double)));
+    ma_and(SecondScratchReg, SecondScratchReg, Imm32(~(ABIStackAlignment - 1)));
+
+    for (FloatRegisterForwardIterator iter(set.fpus().reduceSetForPush()); iter.more(); iter++) {
+        if (!ignore.has(*iter) && ((*iter).code() % 2 == 0))
+            // Use assembly l.d because we have alligned the stack.
+            as_ld(*iter, SecondScratchReg, -diffF);
+        diffF -= sizeof(double);
+    }
+    freeStack(reservedF + sizeof(double));
+    MOZ_ASSERT(diffF == 0);
+
+    for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) {
+        diffG -= sizeof(intptr_t);
+        if (!ignore.has(*iter))
+            loadPtr(Address(StackPointer, diffG), *iter);
+    }
+    freeStack(reservedG);
+    MOZ_ASSERT(diffG == 0);
+}
+
+void
+MacroAssembler::Push(Register reg)
+{
+    ma_push(reg);
+    adjustFrame(sizeof(intptr_t));
+}
+
+void
+MacroAssembler::Push(const Imm32 imm)
+{
+    ma_li(ScratchRegister, imm);
+    ma_push(ScratchRegister);
+    adjustFrame(sizeof(intptr_t));
+}
+
+void
+MacroAssembler::Push(const ImmWord imm)
+{
+    ma_li(ScratchRegister, Imm32(imm.value));
+    ma_push(ScratchRegister);
+    adjustFrame(sizeof(intptr_t));
+}
+
+void
+MacroAssembler::Push(const ImmPtr imm)
+{
+    Push(ImmWord(uintptr_t(imm.value)));
+}
+
+void
+MacroAssembler::Push(const ImmGCPtr ptr)
+{
+    ma_li(ScratchRegister, ptr);
+    ma_push(ScratchRegister);
+    adjustFrame(sizeof(intptr_t));
+}
+
+void
+MacroAssembler::Push(FloatRegister f)
+{
+    ma_push(f);
+    adjustFrame(sizeof(double));
+}
+
+void
+MacroAssembler::Pop(Register reg)
+{
+    ma_pop(reg);
+    adjustFrame(-sizeof(intptr_t));
+}
+
+void
+MacroAssembler::Pop(const ValueOperand &val)
+{
+    popValue(val);
+    framePushed_ -= sizeof(Value);
+}
--- a/js/src/jit/mips/MacroAssembler-mips.h
+++ b/js/src/jit/mips/MacroAssembler-mips.h
@@ -328,18 +328,26 @@ class MacroAssemblerMIPS : public Assemb
         ma_lw(ScratchRegister, lhs);
         ma_li(SecondScratchReg, Imm32(uint32_t(imm.value)));
         ma_cmp_set(dst, ScratchRegister, SecondScratchReg, c);
     }
     void ma_cmp_set_double(Register dst, FloatRegister lhs, FloatRegister rhs, DoubleCondition c);
     void ma_cmp_set_float32(Register dst, FloatRegister lhs, FloatRegister rhs, DoubleCondition c);
 };
 
+class MacroAssembler;
+
 class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
 {
+  private:
+    // Perform a downcast. Should be removed by Bug 996602.
+    MacroAssembler &asMasm();
+    const MacroAssembler &asMasm() const;
+
+  private:
     // Number of bytes the stack is adjusted inside a call to C. Calls to C may
     // not be nested.
     bool inCall_;
     uint32_t args_;
     // The actual number of arguments that were passed, used to assert that
     // the initial number of arguments declared was correct.
     uint32_t passedArgs_;
     uint32_t passedArgTypes_;
@@ -938,24 +946,17 @@ public:
         else
             push(Imm32(jv.s.payload.i32));
     }
     void pushValue(JSValueType type, Register reg) {
         push(ImmTag(JSVAL_TYPE_TO_TAG(type)));
         ma_push(reg);
     }
     void pushValue(const Address &addr);
-    void Push(const ValueOperand &val) {
-        pushValue(val);
-        framePushed_ += sizeof(Value);
-    }
-    void Pop(const ValueOperand &val) {
-        popValue(val);
-        framePushed_ -= sizeof(Value);
-    }
+
     void storePayload(const Value &val, Address dest);
     void storePayload(Register src, Address dest);
     void storePayload(const Value &val, const BaseIndex &dest);
     void storePayload(Register src, const BaseIndex &dest);
     void storeTypeTag(ImmTag tag, Address dest);
     void storeTypeTag(ImmTag tag, const BaseIndex &dest);
 
     void makeFrameDescriptor(Register frameSizeReg, FrameType type) {
@@ -1154,55 +1155,24 @@ public:
     void atomicXor16(const T &value, const S &mem) {
         MOZ_CRASH("NYI");
     }
     template <typename T, typename S>
     void atomicXor32(const T &value, const S &mem) {
         MOZ_CRASH("NYI");
     }
 
-    void Push(Register reg) {
-        ma_push(reg);
-        adjustFrame(sizeof(intptr_t));
-    }
-    void Push(const Imm32 imm) {
-        ma_li(ScratchRegister, imm);
-        ma_push(ScratchRegister);
-        adjustFrame(sizeof(intptr_t));
-    }
-    void Push(const ImmWord imm) {
-        ma_li(ScratchRegister, Imm32(imm.value));
-        ma_push(ScratchRegister);
-        adjustFrame(sizeof(intptr_t));
-    }
-    void Push(const ImmPtr imm) {
-        Push(ImmWord(uintptr_t(imm.value)));
-    }
-    void Push(const ImmGCPtr ptr) {
-        ma_li(ScratchRegister, ptr);
-        ma_push(ScratchRegister);
-        adjustFrame(sizeof(intptr_t));
-    }
-    void Push(FloatRegister f) {
-        ma_push(f);
-        adjustFrame(sizeof(double));
-    }
-
     CodeOffsetLabel PushWithPatch(ImmWord word) {
         framePushed_ += sizeof(word.value);
         return pushWithPatch(word);
     }
     CodeOffsetLabel PushWithPatch(ImmPtr imm) {
         return PushWithPatch(ImmWord(uintptr_t(imm.value)));
     }
 
-    void Pop(Register reg) {
-        ma_pop(reg);
-        adjustFrame(-sizeof(intptr_t));
-    }
     void implicitPop(uint32_t args) {
         MOZ_ASSERT(args % sizeof(intptr_t) == 0);
         adjustFrame(-args);
     }
     uint32_t framePushed() const {
         return framePushed_;
     }
     void setFramePushed(uint32_t framePushed) {
--- a/js/src/jit/mips/MoveEmitter-mips.cpp
+++ b/js/src/jit/mips/MoveEmitter-mips.cpp
@@ -4,17 +4,17 @@
  * 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/mips/MoveEmitter-mips.h"
 
 using namespace js;
 using namespace js::jit;
 
-MoveEmitterMIPS::MoveEmitterMIPS(MacroAssemblerMIPSCompat &masm)
+MoveEmitterMIPS::MoveEmitterMIPS(MacroAssembler &masm)
   : inCycle_(0),
     masm(masm),
     pushedAtCycle_(-1),
     pushedAtSpill_(-1),
     spilledReg_(InvalidReg),
     spilledFloatReg_(InvalidFloatReg)
 {
     pushedAtStart_ = masm.framePushed();
--- a/js/src/jit/mips/MoveEmitter-mips.h
+++ b/js/src/jit/mips/MoveEmitter-mips.h
@@ -13,17 +13,17 @@
 namespace js {
 namespace jit {
 
 class CodeGenerator;
 
 class MoveEmitterMIPS
 {
     uint32_t inCycle_;
-    MacroAssemblerMIPSCompat &masm;
+    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_;
@@ -47,17 +47,17 @@ class MoveEmitterMIPS
     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(MacroAssemblerMIPSCompat &masm);
+    MoveEmitterMIPS(MacroAssembler &masm);
     ~MoveEmitterMIPS();
     void emit(const MoveResolver &moves);
     void finish();
 
     void setScratchRegister(Register reg) {}
 };
 
 typedef MoveEmitterMIPS MoveEmitter;
--- a/js/src/jit/none/Trampoline-none.cpp
+++ b/js/src/jit/none/Trampoline-none.cpp
@@ -29,22 +29,16 @@ JitCode *JitRuntime::generateExceptionTa
 JitCode *JitRuntime::generateBailoutTailStub(JSContext *) { MOZ_CRASH(); }
 
 FrameSizeClass FrameSizeClass::FromDepth(uint32_t) { MOZ_CRASH(); }
 FrameSizeClass FrameSizeClass::ClassLimit() { MOZ_CRASH(); }
 uint32_t FrameSizeClass::frameSize() const { MOZ_CRASH(); }
 
 void DispatchIonCache::initializeAddCacheState(LInstruction *, AddCacheState *) { MOZ_CRASH(); }
 
-void MacroAssembler::PushRegsInMask(RegisterSet, FloatRegisterSet) { MOZ_CRASH(); }
-void MacroAssembler::clampDoubleToUint8(FloatRegister, Register) { MOZ_CRASH(); }
-void MacroAssembler::PopRegsInMaskIgnore(RegisterSet, RegisterSet, FloatRegisterSet) { MOZ_CRASH(); }
-void MacroAssembler::alignFrameForICArguments(AfterICSaveLive &) { MOZ_CRASH(); }
-void MacroAssembler::restoreFrameAlignmentForICArguments(AfterICSaveLive &) { MOZ_CRASH(); }
-
 const Register ABIArgGenerator::NonArgReturnReg0 = { Registers::invalid_reg };
 const Register ABIArgGenerator::NonArgReturnReg1 = {  Registers::invalid_reg };
 const Register ABIArgGenerator::NonArg_VolatileReg = {  Registers::invalid_reg };
 const Register ABIArgGenerator::NonReturn_VolatileReg0 = {  Registers::invalid_reg };
 const Register ABIArgGenerator::NonReturn_VolatileReg1 = {  Registers::invalid_reg };
 
 BailoutFrameInfo::BailoutFrameInfo(const JitActivationIterator &iter, BailoutStack *bailout)
 {
@@ -56,8 +50,28 @@ BailoutFrameInfo::BailoutFrameInfo(const
     MOZ_CRASH();
 }
 
 bool ICCompare_Int32::Compiler::generateStubCode(MacroAssembler &) { MOZ_CRASH(); }
 bool ICCompare_Double::Compiler::generateStubCode(MacroAssembler &) { MOZ_CRASH(); }
 bool ICBinaryArith_Int32::Compiler::generateStubCode(MacroAssembler &) { MOZ_CRASH(); }
 bool ICUnaryArith_Int32::Compiler::generateStubCode(MacroAssembler &) { MOZ_CRASH(); }
 JitCode *JitRuntime::generateProfilerExitFrameTailStub(JSContext *) { MOZ_CRASH(); }
+
+// ===============================================================
+// Stack manipulation functions.
+
+void MacroAssembler::PushRegsInMask(RegisterSet set, FloatRegisterSet simdSet) { MOZ_CRASH(); }
+void MacroAssembler::PopRegsInMaskIgnore(RegisterSet set, RegisterSet ignore,
+                                         FloatRegisterSet simdSet)
+{
+    MOZ_CRASH();
+}
+
+void MacroAssembler::Push(Register reg) { MOZ_CRASH(); }
+void MacroAssembler::Push(const Imm32 imm) { MOZ_CRASH(); }
+void MacroAssembler::Push(const ImmWord imm) { MOZ_CRASH(); }
+void MacroAssembler::Push(const ImmPtr imm) { MOZ_CRASH(); }
+void MacroAssembler::Push(const ImmGCPtr ptr) { MOZ_CRASH(); }
+void MacroAssembler::Push(FloatRegister reg) { MOZ_CRASH(); }
+
+void MacroAssembler::Pop(Register reg) { MOZ_CRASH(); }
+void MacroAssembler::Pop(const ValueOperand &val) { MOZ_CRASH(); }
--- a/js/src/jit/shared/MacroAssembler-x86-shared.cpp
+++ b/js/src/jit/shared/MacroAssembler-x86-shared.cpp
@@ -7,16 +7,182 @@
 #include "jit/shared/MacroAssembler-x86-shared.h"
 
 #include "jit/JitFrames.h"
 #include "jit/MacroAssembler.h"
 
 using namespace js;
 using namespace js::jit;
 
+// Note: this function clobbers the input register.
+void
+MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output)
+{
+    MOZ_ASSERT(input != ScratchDoubleReg);
+    Label positive, done;
+
+    // <= 0 or NaN --> 0
+    zeroDouble(ScratchDoubleReg);
+    branchDouble(DoubleGreaterThan, input, ScratchDoubleReg, &positive);
+    {
+        move32(Imm32(0), output);
+        jump(&done);
+    }
+
+    bind(&positive);
+
+    // Add 0.5 and truncate.
+    loadConstantDouble(0.5, ScratchDoubleReg);
+    addDouble(ScratchDoubleReg, input);
+
+    Label outOfRange;
+
+    // Truncate to int32 and ensure the result <= 255. This relies on the
+    // processor setting output to a value > 255 for doubles outside the int32
+    // range (for instance 0x80000000).
+    vcvttsd2si(input, output);
+    branch32(Assembler::Above, output, Imm32(255), &outOfRange);
+    {
+        // Check if we had a tie.
+        convertInt32ToDouble(output, ScratchDoubleReg);
+        branchDouble(DoubleNotEqual, input, ScratchDoubleReg, &done);
+
+        // It was a tie. Mask out the ones bit to get an even value.
+        // See also js_TypedArray_uint8_clamp_double.
+        and32(Imm32(~1), output);
+        jump(&done);
+    }
+
+    // > 255 --> 255
+    bind(&outOfRange);
+    {
+        move32(Imm32(255), output);
+    }
+
+    bind(&done);
+}
+
+// Builds an exit frame on the stack, with a return address to an internal
+// non-function. Returns offset to be passed to markSafepointAt().
+void
+MacroAssemblerX86Shared::buildFakeExitFrame(Register scratch, uint32_t *offset)
+{
+    mozilla::DebugOnly<uint32_t> initialDepth = framePushed();
+
+    CodeLabel cl;
+    mov(cl.dest(), scratch);
+
+    uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
+    asMasm().Push(Imm32(descriptor));
+    asMasm().Push(scratch);
+
+    bind(cl.src());
+    *offset = currentOffset();
+
+    MOZ_ASSERT(framePushed() == initialDepth + ExitFrameLayout::Size());
+    addCodeLabel(cl);
+}
+
+void
+MacroAssemblerX86Shared::callWithExitFrame(Label *target)
+{
+    uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
+    asMasm().Push(Imm32(descriptor));
+    call(target);
+}
+
+void
+MacroAssemblerX86Shared::callWithExitFrame(JitCode *target)
+{
+    uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
+    asMasm().Push(Imm32(descriptor));
+    call(target);
+}
+
+void
+MacroAssembler::alignFrameForICArguments(AfterICSaveLive &aic)
+{
+    // Exists for MIPS compatibility.
+}
+
+void
+MacroAssembler::restoreFrameAlignmentForICArguments(AfterICSaveLive &aic)
+{
+    // Exists for MIPS compatibility.
+}
+
+bool
+MacroAssemblerX86Shared::buildOOLFakeExitFrame(void *fakeReturnAddr)
+{
+    uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
+    asMasm().Push(Imm32(descriptor));
+    asMasm().Push(ImmPtr(fakeReturnAddr));
+    return true;
+}
+
+void
+MacroAssemblerX86Shared::branchNegativeZero(FloatRegister reg,
+                                            Register scratch,
+                                            Label *label,
+                                            bool maybeNonZero)
+{
+    // Determines whether the low double contained in the XMM register reg
+    // is equal to -0.0.
+
+#if defined(JS_CODEGEN_X86)
+    Label nonZero;
+
+    // if not already compared to zero
+    if (maybeNonZero) {
+        // Compare to zero. Lets through {0, -0}.
+        zeroDouble(ScratchDoubleReg);
+
+        // If reg is non-zero, jump to nonZero.
+        branchDouble(DoubleNotEqual, reg, ScratchDoubleReg, &nonZero);
+    }
+    // Input register is either zero or negative zero. Retrieve sign of input.
+    vmovmskpd(reg, scratch);
+
+    // If reg is 1 or 3, input is negative zero.
+    // If reg is 0 or 2, input is a normal zero.
+    branchTest32(NonZero, scratch, Imm32(1), label);
+
+    bind(&nonZero);
+#elif defined(JS_CODEGEN_X64)
+    vmovq(reg, scratch);
+    cmpq(Imm32(1), scratch);
+    j(Overflow, label);
+#endif
+}
+
+void
+MacroAssemblerX86Shared::branchNegativeZeroFloat32(FloatRegister reg,
+                                                   Register scratch,
+                                                   Label *label)
+{
+    vmovd(reg, scratch);
+    cmp32(scratch, Imm32(1));
+    j(Overflow, label);
+}
+
+MacroAssembler &
+MacroAssemblerX86Shared::asMasm()
+{
+    return *static_cast<MacroAssembler *>(this);
+}
+
+const MacroAssembler &
+MacroAssemblerX86Shared::asMasm() const
+{
+    return *static_cast<const MacroAssembler *>(this);
+}
+
+// ===============================================================
+// Stack manipulation functions.
+
 void
 MacroAssembler::PushRegsInMask(RegisterSet set, FloatRegisterSet simdSet)
 {
     FloatRegisterSet doubleSet(FloatRegisterSet::Subtract(set.fpus(), simdSet));
     MOZ_ASSERT_IF(simdSet.empty(), doubleSet == set.fpus());
     doubleSet = doubleSet.reduceSetForPush();
     unsigned numSimd = simdSet.size();
     unsigned numDouble = doubleSet.size();
@@ -123,158 +289,83 @@ MacroAssembler::PopRegsInMaskIgnore(Regi
             if (!ignore.has(*iter))
                 loadPtr(Address(StackPointer, diffG), *iter);
         }
         freeStack(reservedG);
     }
     MOZ_ASSERT(diffG == 0);
 }
 
-// Note: this function clobbers the input register.
 void
-MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output)
+MacroAssembler::Push(const Operand op)
 {
-    MOZ_ASSERT(input != ScratchDoubleReg);
-    Label positive, done;
-
-    // <= 0 or NaN --> 0
-    zeroDouble(ScratchDoubleReg);
-    branchDouble(DoubleGreaterThan, input, ScratchDoubleReg, &positive);
-    {
-        move32(Imm32(0), output);
-        jump(&done);
-    }
-
-    bind(&positive);
-
-    // Add 0.5 and truncate.
-    loadConstantDouble(0.5, ScratchDoubleReg);
-    addDouble(ScratchDoubleReg, input);
-
-    Label outOfRange;
+    push(op);
+    framePushed_ += sizeof(intptr_t);
+}
 
-    // Truncate to int32 and ensure the result <= 255. This relies on the
-    // processor setting output to a value > 255 for doubles outside the int32
-    // range (for instance 0x80000000).
-    vcvttsd2si(input, output);
-    branch32(Assembler::Above, output, Imm32(255), &outOfRange);
-    {
-        // Check if we had a tie.
-        convertInt32ToDouble(output, ScratchDoubleReg);
-        branchDouble(DoubleNotEqual, input, ScratchDoubleReg, &done);
-
-        // It was a tie. Mask out the ones bit to get an even value.
-        // See also js_TypedArray_uint8_clamp_double.
-        and32(Imm32(~1), output);
-        jump(&done);
-    }
-
-    // > 255 --> 255
-    bind(&outOfRange);
-    {
-        move32(Imm32(255), output);
-    }
-
-    bind(&done);
+void
+MacroAssembler::Push(Register reg)
+{
+    push(reg);
+    framePushed_ += sizeof(intptr_t);
 }
 
-// Builds an exit frame on the stack, with a return address to an internal
-// non-function. Returns offset to be passed to markSafepointAt().
 void
-MacroAssemblerX86Shared::buildFakeExitFrame(Register scratch, uint32_t *offset)
+MacroAssembler::Push(const Imm32 imm)
 {
-    mozilla::DebugOnly<uint32_t> initialDepth = framePushed();
-
-    CodeLabel cl;
-    mov(cl.dest(), scratch);
+    push(imm);
+    framePushed_ += sizeof(intptr_t);
+}
 
-    uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
-    Push(Imm32(descriptor));
-    Push(scratch);
+void
+MacroAssembler::Push(const ImmWord imm)
+{
+    push(imm);
+    framePushed_ += sizeof(intptr_t);
+}
 
-    bind(cl.src());
-    *offset = currentOffset();
-
-    MOZ_ASSERT(framePushed() == initialDepth + ExitFrameLayout::Size());
-    addCodeLabel(cl);
+void
+MacroAssembler::Push(const ImmPtr imm)
+{
+    Push(ImmWord(uintptr_t(imm.value)));
 }
 
 void
-MacroAssemblerX86Shared::callWithExitFrame(Label *target)
+MacroAssembler::Push(const ImmGCPtr ptr)
 {
-    uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
-    Push(Imm32(descriptor));
-    call(target);
-}
-
-void
-MacroAssemblerX86Shared::callWithExitFrame(JitCode *target)
-{
-    uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
-    Push(Imm32(descriptor));
-    call(target);
+    push(ptr);
+    framePushed_ += sizeof(intptr_t);
 }
 
 void
-MacroAssembler::alignFrameForICArguments(AfterICSaveLive &aic)
-{
-    // Exists for MIPS compatibility.
-}
-
-void
-MacroAssembler::restoreFrameAlignmentForICArguments(AfterICSaveLive &aic)
+MacroAssembler::Push(FloatRegister t)
 {
-    // Exists for MIPS compatibility.
-}
-
-bool
-MacroAssemblerX86Shared::buildOOLFakeExitFrame(void *fakeReturnAddr)
-{
-    uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
-    Push(Imm32(descriptor));
-    Push(ImmPtr(fakeReturnAddr));
-    return true;
+    push(t);
+    framePushed_ += sizeof(double);
 }
 
 void
-MacroAssemblerX86Shared::branchNegativeZero(FloatRegister reg,
-                                            Register scratch,
-                                            Label *label,
-                                            bool maybeNonZero)
+MacroAssembler::Pop(const Operand op)
 {
-    // Determines whether the low double contained in the XMM register reg
-    // is equal to -0.0.
-
-#if defined(JS_CODEGEN_X86)
-    Label nonZero;
-
-    // if not already compared to zero
-    if (maybeNonZero) {
-        // Compare to zero. Lets through {0, -0}.
-        zeroDouble(ScratchDoubleReg);
-
-        // If reg is non-zero, jump to nonZero.
-        branchDouble(DoubleNotEqual, reg, ScratchDoubleReg, &nonZero);
-    }
-    // Input register is either zero or negative zero. Retrieve sign of input.
-    vmovmskpd(reg, scratch);
-
-    // If reg is 1 or 3, input is negative zero.
-    // If reg is 0 or 2, input is a normal zero.
-    branchTest32(NonZero, scratch, Imm32(1), label);
-
-    bind(&nonZero);
-#elif defined(JS_CODEGEN_X64)
-    vmovq(reg, scratch);
-    cmpq(Imm32(1), scratch);
-    j(Overflow, label);
-#endif
+    pop(op);
+    framePushed_ -= sizeof(intptr_t);
 }
 
 void
-MacroAssemblerX86Shared::branchNegativeZeroFloat32(FloatRegister reg,
-                                                   Register scratch,
-                                                   Label *label)
+MacroAssembler::Pop(Register reg)
+{
+    pop(reg);
+    framePushed_ -= sizeof(intptr_t);
+}
+
+void
+MacroAssembler::Pop(FloatRegister reg)
 {
-    vmovd(reg, scratch);
-    cmp32(scratch, Imm32(1));
-    j(Overflow, label);
+    pop(reg);
+    framePushed_ -= sizeof(double);
 }
+
+void
+MacroAssembler::Pop(const ValueOperand &val)
+{
+    popValue(val);
+    framePushed_ -= sizeof(Value);
+}
--- a/js/src/jit/shared/MacroAssembler-x86-shared.h
+++ b/js/src/jit/shared/MacroAssembler-x86-shared.h
@@ -31,18 +31,25 @@
 #else
   #define CHECK_BYTEREG(reg) (void)0
   #define CHECK_BYTEREGS(r1, r2) (void)0
 #endif
 
 namespace js {
 namespace jit {
 
+class MacroAssembler;
+
 class MacroAssemblerX86Shared : public Assembler
 {
+  private:
+    // Perform a downcast. Should be removed by Bug 996602.
+    MacroAssembler &asMasm();
+    const MacroAssembler &asMasm() const;
+
   protected:
     // Bytes pushed onto the frame by the callee; includes frameDepth_. This is
     // needed to compute offsets to stack slots while temporary space has been
     // reserved for unexpected spills or C++ function calls. It is maintained
     // by functions which track stack alignment, which for clear distinction
     // use StudlyCaps (for example, Push, Pop).
     uint32_t framePushed_;
 
@@ -623,42 +630,24 @@ class MacroAssemblerX86Shared : public A
     }
     void branchTest32(Condition cond, const Address &address, Imm32 imm, Label *label) {
         MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned);
         test32(Operand(address), imm);
         j(cond, label);
     }
 
     // The following functions are exposed for use in platform-shared code.
-    template <typename T>
-    void Push(const T &t) {
-        push(t);
-        framePushed_ += sizeof(intptr_t);
-    }
-    void Push(FloatRegister t) {
-        push(t);
-        framePushed_ += sizeof(double);
-    }
     CodeOffsetLabel PushWithPatch(ImmWord word) {
         framePushed_ += sizeof(word.value);
         return pushWithPatch(word);
     }
     CodeOffsetLabel PushWithPatch(ImmPtr imm) {
         return PushWithPatch(ImmWord(uintptr_t(imm.value)));
     }
 
-    template <typename T>
-    void Pop(const T &t) {
-        pop(t);
-        framePushed_ -= sizeof(intptr_t);
-    }
-    void Pop(FloatRegister t) {
-        pop(t);
-        framePushed_ -= sizeof(double);
-    }
     void implicitPop(uint32_t args) {
         MOZ_ASSERT(args % sizeof(intptr_t) == 0);
         framePushed_ -= args;
     }
     uint32_t framePushed() const {
         return framePushed_;
     }
     void setFramePushed(uint32_t framePushed) {
--- a/js/src/jit/shared/MoveEmitter-x86-shared.cpp
+++ b/js/src/jit/shared/MoveEmitter-x86-shared.cpp
@@ -4,17 +4,17 @@
  * 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/shared/MoveEmitter-x86-shared.h"
 
 using namespace js;
 using namespace js::jit;
 
-MoveEmitterX86::MoveEmitterX86(MacroAssemblerSpecific &masm)
+MoveEmitterX86::MoveEmitterX86(MacroAssembler &masm)
   : inCycle_(false),
     masm(masm),
     pushedAtCycle_(-1)
 {
     pushedAtStart_ = masm.framePushed();
 }
 
 // Examine the cycle in moves starting at position i. Determine if it's a
--- a/js/src/jit/shared/MoveEmitter-x86-shared.h
+++ b/js/src/jit/shared/MoveEmitter-x86-shared.h
@@ -2,34 +2,28 @@
  * 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_MoveEmitter_x86_shared_h
 #define jit_MoveEmitter_x86_shared_h
 
-#if defined(JS_CODEGEN_X86)
-# include "jit/x86/MacroAssembler-x86.h"
-#elif defined(JS_CODEGEN_X64)
-# include "jit/x64/MacroAssembler-x64.h"
-#else
-# error "Wrong architecture. Only x86 and x64 should build this file!"
-#endif
+#include "jit/MacroAssembler.h"
 #include "jit/MoveResolver.h"
 
 namespace js {
 namespace jit {
 
 class CodeGenerator;
 
 class MoveEmitterX86
 {
     bool inCycle_;
-    MacroAssemblerSpecific &masm;
+    MacroAssembler &masm;
 
     // Original stack push value.
     uint32_t pushedAtStart_;
 
     // This is a store stack offset for the cycle-break spill slot, snapshotting
     // codegen->framePushed_ at the time it is allocated. -1 if not allocated.
     int32_t pushedAtCycle_;
 
@@ -53,17 +47,17 @@ class MoveEmitterX86
     void emitFloat32Move(const MoveOperand &from, const MoveOperand &to);
     void emitDoubleMove(const MoveOperand &from, const MoveOperand &to);
     void emitFloat32X4Move(const MoveOperand &from, const MoveOperand &to);
     void emitInt32X4Move(const MoveOperand &from, const MoveOperand &to);
     void breakCycle(const MoveOperand &to, MoveOp::Type type);
     void completeCycle(const MoveOperand &to, MoveOp::Type type);
 
   public:
-    explicit MoveEmitterX86(MacroAssemblerSpecific &masm);
+    explicit MoveEmitterX86(MacroAssembler &masm);
     ~MoveEmitterX86();
     void emit(const MoveResolver &moves);
     void finish();
 
     void setScratchRegister(Register reg) {
 #ifdef JS_CODEGEN_X86
         scratchRegister_.emplace(reg);
 #endif
--- a/js/src/jit/x64/MacroAssembler-x64.cpp
+++ b/js/src/jit/x64/MacroAssembler-x64.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/x64/MacroAssembler-x64.h"
 
 #include "jit/Bailouts.h"
 #include "jit/BaselineFrame.h"
 #include "jit/JitCompartment.h"
 #include "jit/JitFrames.h"
+#include "jit/MacroAssembler.h"
 #include "jit/MoveEmitter.h"
 
 using namespace js;
 using namespace js::jit;
 
 void
 MacroAssemblerX64::loadConstantDouble(double d, FloatRegister dest)
 {
@@ -283,17 +284,17 @@ MacroAssemblerX64::callWithABIPre(uint32
     reserveStack(*stackAdjust);
 
     // Position all arguments.
     {
         enoughMemory_ &= moveResolver_.resolve();
         if (!enoughMemory_)
             return;
 
-        MoveEmitter emitter(*this);
+        MoveEmitter emitter(asMasm());
         emitter.emit(moveResolver_);
         emitter.finish();
     }
 
 #ifdef DEBUG
     {
         Label good;
         testPtr(rsp, Imm32(ABIStackAlignment - 1));
@@ -500,16 +501,25 @@ template void
 MacroAssemblerX64::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const Address &dest,
                                      MIRType slotType);
 
 template void
 MacroAssemblerX64::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex &dest,
                                      MIRType slotType);
 
 void
+MacroAssemblerX64::callWithExitFrame(JitCode *target, Register dynStack)
+{
+    addPtr(Imm32(framePushed()), dynStack);
+    makeFrameDescriptor(dynStack, JitFrame_IonJS);
+    asMasm().Push(dynStack);
+    call(target);
+}
+
+void
 MacroAssemblerX64::branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label *label)
 {
     MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
     MOZ_ASSERT(ptr != temp);
     MOZ_ASSERT(ptr != ScratchReg);
 
     const Nursery &nursery = GetJitContext()->runtime->gcNursery();
     movePtr(ImmWord(-ptrdiff_t(nursery.start())), ScratchReg);
@@ -543,8 +553,20 @@ MacroAssemblerX64::profilerEnterFrame(Re
     storePtr(ImmPtr(nullptr), Address(scratch, JitActivation::offsetOfLastProfilingCallSite()));
 }
 
 void
 MacroAssemblerX64::profilerExitFrame()
 {
     jmp(GetJitContext()->runtime->jitRuntime()->getProfilerExitFrameTail());
 }
+
+MacroAssembler &
+MacroAssemblerX64::asMasm()
+{
+    return *static_cast<MacroAssembler *>(this);
+}
+
+const MacroAssembler &
+MacroAssemblerX64::asMasm() const
+{
+    return *static_cast<const MacroAssembler *>(this);
+}
--- a/js/src/jit/x64/MacroAssembler-x64.h
+++ b/js/src/jit/x64/MacroAssembler-x64.h
@@ -29,16 +29,22 @@ struct ImmTag : public Imm32
 {
     explicit ImmTag(JSValueTag tag)
       : Imm32(tag)
     { }
 };
 
 class MacroAssemblerX64 : public MacroAssemblerX86Shared
 {
+  private:
+    // Perform a downcast. Should be removed by Bug 996602.
+    MacroAssembler &asMasm();
+    const MacroAssembler &asMasm() const;
+
+  private:
     // Number of bytes the stack is adjusted inside a call to C. Calls to C may
     // not be nested.
     bool inCall_;
     uint32_t args_;
     uint32_t passedIntArgs_;
     uint32_t passedFloatArgs_;
     uint32_t stackForCall_;
     bool dynamicAlignment_;
@@ -79,18 +85,16 @@ class MacroAssemblerX64 : public MacroAs
 
     void setupABICall(uint32_t arg);
 
   protected:
     MoveResolver moveResolver_;
 
   public:
     using MacroAssemblerX86Shared::call;
-    using MacroAssemblerX86Shared::Push;
-    using MacroAssemblerX86Shared::Pop;
     using MacroAssemblerX86Shared::callWithExitFrame;
     using MacroAssemblerX86Shared::branch32;
     using MacroAssemblerX86Shared::branchTest32;
     using MacroAssemblerX86Shared::load32;
     using MacroAssemblerX86Shared::store32;
 
     MacroAssemblerX64()
       : inCall_(false)
@@ -210,20 +214,16 @@ class MacroAssemblerX64 : public MacroAs
         if (payload != dest.valueReg())
             movq(payload, dest.valueReg());
         mov(ImmShiftedTag(type), ScratchReg);
         orq(ScratchReg, dest.valueReg());
     }
     void pushValue(ValueOperand val) {
         push(val.valueReg());
     }
-    void Push(const ValueOperand &val) {
-        pushValue(val);
-        framePushed_ += sizeof(Value);
-    }
     void popValue(ValueOperand val) {
         pop(val.valueReg());
     }
     void pushValue(const Value &val) {
         jsval_layout jv = JSVAL_TO_IMPL(val);
         if (val.isMarkable()) {
             movWithPatch(ImmWord(jv.asBits), ScratchReg);
             writeDataRelocation(val);
@@ -234,20 +234,16 @@ class MacroAssemblerX64 : public MacroAs
     }
     void pushValue(JSValueType type, Register reg) {
         boxValue(type, reg, ScratchReg);
         push(ScratchReg);
     }
     void pushValue(const Address &addr) {
         push(Operand(addr));
     }
-    void Pop(const ValueOperand &val) {
-        popValue(val);
-        framePushed_ -= sizeof(Value);
-    }
 
     void moveValue(const Value &val, Register dest) {
         jsval_layout jv = JSVAL_TO_IMPL(val);
         movWithPatch(ImmWord(jv.asBits), dest);
         writeDataRelocation(val);
     }
     void moveValue(const Value &src, const ValueOperand &dest) {
         moveValue(src, dest.valueReg());
@@ -1444,22 +1440,17 @@ class MacroAssemblerX64 : public MacroAs
 
     void handleFailureWithHandlerTail(void *handler);
 
     void makeFrameDescriptor(Register frameSizeReg, FrameType type) {
         shlq(Imm32(FRAMESIZE_SHIFT), frameSizeReg);
         orq(Imm32(type), frameSizeReg);
     }
 
-    void callWithExitFrame(JitCode *target, Register dynStack) {
-        addPtr(Imm32(framePushed()), dynStack);
-        makeFrameDescriptor(dynStack, JitFrame_IonJS);
-        Push(dynStack);
-        call(target);
-    }
+    void callWithExitFrame(JitCode *target, Register dynStack);
 
     // See CodeGeneratorX64 calls to noteAsmJSGlobalAccess.
     void patchAsmJSGlobalAccess(CodeOffsetLabel patchAt, uint8_t *code, uint8_t *globalData,
                                 unsigned globalDataOffset)
     {
         uint8_t *nextInsn = code + patchAt.offset();
         MOZ_ASSERT(nextInsn <= globalData);
         uint8_t *target = globalData + globalDataOffset;
--- a/js/src/jit/x86/MacroAssembler-x86.cpp
+++ b/js/src/jit/x86/MacroAssembler-x86.cpp
@@ -6,16 +6,17 @@
 
 #include "jit/x86/MacroAssembler-x86.h"
 
 #include "mozilla/Casting.h"
 
 #include "jit/Bailouts.h"
 #include "jit/BaselineFrame.h"
 #include "jit/JitFrames.h"
+#include "jit/MacroAssembler.h"
 #include "jit/MoveEmitter.h"
 
 #include "jsscriptinlines.h"
 
 using namespace js;
 using namespace js::jit;
 
 MacroAssemblerX86::Double *
@@ -277,17 +278,17 @@ MacroAssemblerX86::callWithABIPre(uint32
     reserveStack(*stackAdjust);
 
     // Position all arguments.
     {
         enoughMemory_ &= moveResolver_.resolve();
         if (!enoughMemory_)
             return;
 
-        MoveEmitter emitter(*this);
+        MoveEmitter emitter(asMasm());
         emitter.emit(moveResolver_);
         emitter.finish();
     }
 
 #ifdef DEBUG
     {
         // Check call alignment.
         Label good;
@@ -494,16 +495,25 @@ template void
 MacroAssemblerX86::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const Address &dest,
                                      MIRType slotType);
 
 template void
 MacroAssemblerX86::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex &dest,
                                      MIRType slotType);
 
 void
+MacroAssemblerX86::callWithExitFrame(JitCode *target, Register dynStack)
+{
+    addPtr(ImmWord(framePushed()), dynStack);
+    makeFrameDescriptor(dynStack, JitFrame_IonJS);
+    asMasm().Push(dynStack);
+    call(target);
+}
+
+void
 MacroAssemblerX86::branchPtrInNurseryRange(Condition cond, Register ptr, Register temp,
                                            Label *label)
 {
     MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
     MOZ_ASSERT(ptr != temp);
     MOZ_ASSERT(temp != InvalidReg);  // A temp register is required for x86.
 
     const Nursery &nursery = GetJitContext()->runtime->gcNursery();
@@ -536,8 +546,20 @@ MacroAssemblerX86::profilerEnterFrame(Re
     storePtr(ImmPtr(nullptr), Address(scratch, JitActivation::offsetOfLastProfilingCallSite()));
 }
 
 void
 MacroAssemblerX86::profilerExitFrame()
 {
     jmp(GetJitContext()->runtime->jitRuntime()->getProfilerExitFrameTail());
 }
+
+MacroAssembler &
+MacroAssemblerX86::asMasm()
+{
+    return *static_cast<MacroAssembler *>(this);
+}
+
+const MacroAssembler &
+MacroAssemblerX86::asMasm() const
+{
+    return *static_cast<const MacroAssembler *>(this);
+}
--- a/js/src/jit/x86/MacroAssembler-x86.h
+++ b/js/src/jit/x86/MacroAssembler-x86.h
@@ -13,16 +13,22 @@
 #include "jit/MoveResolver.h"
 #include "jit/shared/MacroAssembler-x86-shared.h"
 
 namespace js {
 namespace jit {
 
 class MacroAssemblerX86 : public MacroAssemblerX86Shared
 {
+  private:
+    // Perform a downcast. Should be removed by Bug 996602.
+    MacroAssembler &asMasm();
+    const MacroAssembler &asMasm() const;
+
+  private:
     // Number of bytes the stack is adjusted inside a call to C. Calls to C may
     // not be nested.
     bool inCall_;
     uint32_t args_;
     uint32_t passedArgs_;
     uint32_t stackForCall_;
     bool dynamicAlignment_;
 
@@ -72,18 +78,16 @@ class MacroAssemblerX86 : public MacroAs
     }
     Operand tagOf(const BaseIndex &address) {
         return Operand(address.base, address.index, address.scale, address.offset + 4);
     }
 
     void setupABICall(uint32_t args);
 
   public:
-    using MacroAssemblerX86Shared::Push;
-    using MacroAssemblerX86Shared::Pop;
     using MacroAssemblerX86Shared::callWithExitFrame;
     using MacroAssemblerX86Shared::branch32;
     using MacroAssemblerX86Shared::branchTest32;
     using MacroAssemblerX86Shared::load32;
     using MacroAssemblerX86Shared::store32;
     using MacroAssemblerX86Shared::call;
 
     MacroAssemblerX86()
@@ -233,24 +237,16 @@ class MacroAssemblerX86 : public MacroAs
     void pushValue(JSValueType type, Register reg) {
         push(ImmTag(JSVAL_TYPE_TO_TAG(type)));
         push(reg);
     }
     void pushValue(const Address &addr) {
         push(tagOf(addr));
         push(payloadOf(addr));
     }
-    void Push(const ValueOperand &val) {
-        pushValue(val);
-        framePushed_ += sizeof(Value);
-    }
-    void Pop(const ValueOperand &val) {
-        popValue(val);
-        framePushed_ -= sizeof(Value);
-    }
     void storePayload(const Value &val, Operand dest) {
         jsval_layout jv = JSVAL_TO_IMPL(val);
         if (val.isMarkable())
             movl(ImmGCPtr((gc::Cell *)jv.s.payload.ptr), ToPayload(dest));
         else
             movl(Imm32(jv.s.payload.i32), ToPayload(dest));
     }
     void storePayload(Register src, Operand dest) {
@@ -1206,22 +1202,17 @@ class MacroAssemblerX86 : public MacroAs
     // Used from within an Exit frame to handle a pending exception.
     void handleFailureWithHandlerTail(void *handler);
 
     void makeFrameDescriptor(Register frameSizeReg, FrameType type) {
         shll(Imm32(FRAMESIZE_SHIFT), frameSizeReg);
         orl(Imm32(type), frameSizeReg);
     }
 
-    void callWithExitFrame(JitCode *target, Register dynStack) {
-        addPtr(ImmWord(framePushed()), dynStack);
-        makeFrameDescriptor(dynStack, JitFrame_IonJS);
-        Push(dynStack);
-        call(target);
-    }
+    void callWithExitFrame(JitCode *target, Register dynStack);
 
     void branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label *label);
     void branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label *label);
 
     // Instrumentation for entering and leaving the profiler.
     void profilerEnterFrame(Register framePtr, Register scratch);
     void profilerExitFrame();
 };